Skip to main content

sysknife_daemon/actions/
cloudinit.rs

1//! cloud-init status action (Ubuntu / Debian).
2//!
3//! cloud-init is the industry-standard tool for early-boot configuration of
4//! cloud instances. On Ubuntu it is installed by default; on Fedora Atomic,
5//! `ignition` handles first-boot configuration instead.
6//!
7//! This action is marked Ubuntu-only because the gap analysis identifies
8//! cloud-init as an Ubuntu-flavoured primitive: Fedora Atomic instances use
9//! Ignition, not cloud-init, and querying `cloud-init status` on a Fedora
10//! Atomic host is meaningless.
11
12use super::{command_mechanism, ActionSpec};
13use sysknife_types::RiskLevel;
14
15// ---------------------------------------------------------------------------
16// specs() — for action_consistency tests
17// ---------------------------------------------------------------------------
18
19/// Return one representative `ActionSpec` for the cloud-init status action.
20pub fn specs() -> Vec<ActionSpec> {
21    vec![cloud_init_status()]
22}
23
24// ---------------------------------------------------------------------------
25// Action constructors
26// ---------------------------------------------------------------------------
27
28/// Show the current cloud-init run status (`cloud-init status --long`).
29///
30/// Risk: Low. Read-only; shows which cloud-init stages have run and whether
31/// any errors occurred during instance provisioning.
32pub fn cloud_init_status() -> ActionSpec {
33    ActionSpec {
34        action_name: "CloudInitStatus",
35        mechanism: command_mechanism("cloud-init", ["status", "--long"]),
36        risk_level: RiskLevel::Low,
37        reboot_required: false,
38        rollback_available: false,
39    }
40}
41
42// ---------------------------------------------------------------------------
43// Tests
44// ---------------------------------------------------------------------------
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::actions::ActionMechanism;
50
51    fn extract_args(spec: &ActionSpec) -> (&'static str, Vec<String>) {
52        match &spec.mechanism {
53            ActionMechanism::Command { program, args } => (*program, args.clone()),
54            _ => panic!("expected Command mechanism"),
55        }
56    }
57
58    #[test]
59    fn cloud_init_status_action_name() {
60        assert_eq!(cloud_init_status().action_name, "CloudInitStatus");
61    }
62
63    #[test]
64    fn cloud_init_status_risk_is_low() {
65        assert_eq!(cloud_init_status().risk_level, RiskLevel::Low);
66    }
67
68    #[test]
69    fn cloud_init_status_argv() {
70        let spec = cloud_init_status();
71        let (prog, args) = extract_args(&spec);
72        assert_eq!(prog, "cloud-init");
73        let a: Vec<&str> = args.iter().map(String::as_str).collect();
74        assert_eq!(a, vec!["status", "--long"]);
75    }
76
77    #[test]
78    fn cloud_init_status_no_sudo() {
79        let (prog, _) = extract_args(&cloud_init_status());
80        assert_ne!(prog, "sudo");
81    }
82
83    #[test]
84    fn specs_covers_cloud_init_status() {
85        let names: Vec<&str> = specs().iter().map(|s| s.action_name).collect();
86        assert!(
87            names.contains(&"CloudInitStatus"),
88            "specs() missing CloudInitStatus"
89        );
90    }
91}