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}