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