syncable_cli/analyzer/hadolint/rules/
dl3012.rs

1//! DL3012: Multiple HEALTHCHECK instructions
2//!
3//! Only one HEALTHCHECK instruction is allowed per stage.
4
5use crate::analyzer::hadolint::parser::instruction::Instruction;
6use crate::analyzer::hadolint::rules::{custom_rule, CustomRule, RuleState};
7use crate::analyzer::hadolint::shell::ParsedShell;
8use crate::analyzer::hadolint::types::Severity;
9
10pub fn rule() -> CustomRule<impl Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync> {
11    custom_rule(
12        "DL3012",
13        Severity::Error,
14        "Multiple `HEALTHCHECK` instructions.",
15        |state, line, instr, _shell| {
16            match instr {
17                Instruction::From(_) => {
18                    // Reset healthcheck count for new stage
19                    state.data.set_int("healthcheck_count", 0);
20                }
21                Instruction::Healthcheck(_) => {
22                    let count = state.data.get_int("healthcheck_count");
23                    if count > 0 {
24                        state.add_failure(
25                            "DL3012",
26                            Severity::Error,
27                            "Multiple `HEALTHCHECK` instructions.",
28                            line,
29                        );
30                    }
31                    state.data.set_int("healthcheck_count", count + 1);
32                }
33                _ => {}
34            }
35        },
36    )
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::analyzer::hadolint::lint::{lint, LintResult};
43    use crate::analyzer::hadolint::config::HadolintConfig;
44
45    fn lint_dockerfile(content: &str) -> LintResult {
46        lint(content, &HadolintConfig::default())
47    }
48
49    #[test]
50    fn test_single_healthcheck() {
51        let result = lint_dockerfile(
52            "FROM ubuntu:20.04\nHEALTHCHECK CMD curl -f http://localhost/ || exit 1"
53        );
54        assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3012"));
55    }
56
57    #[test]
58    fn test_multiple_healthchecks() {
59        let result = lint_dockerfile(
60            "FROM ubuntu:20.04\nHEALTHCHECK CMD curl http://localhost/\nHEALTHCHECK CMD wget http://localhost/"
61        );
62        assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3012"));
63    }
64
65    #[test]
66    fn test_healthcheck_different_stages() {
67        let result = lint_dockerfile(
68            "FROM ubuntu:20.04 AS builder\nHEALTHCHECK CMD curl http://localhost/\nFROM ubuntu:20.04\nHEALTHCHECK CMD wget http://localhost/"
69        );
70        assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3012"));
71    }
72
73    #[test]
74    fn test_no_healthcheck() {
75        let result = lint_dockerfile("FROM ubuntu:20.04\nRUN echo hello");
76        assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3012"));
77    }
78}