Skip to main content

sysknife_daemon/actions/
multipass.rs

1//! Multipass VM management actions (Ubuntu).
2//!
3//! Multipass is Canonical's lightweight VM manager. It creates and manages
4//! Ubuntu VMs with a single command. Typically installed via snap
5//! (`snap install multipass`).
6//!
7//! ## Risk classification
8//!
9//! `MultipassList` is read-only — Low / Observer.
10
11use super::{command_mechanism, ActionSpec};
12use sysknife_types::RiskLevel;
13
14// ---------------------------------------------------------------------------
15// specs() — for action_consistency tests
16// ---------------------------------------------------------------------------
17
18/// Return one representative `ActionSpec` per multipass action name.
19pub fn specs() -> Vec<ActionSpec> {
20    vec![multipass_list()]
21}
22
23// ---------------------------------------------------------------------------
24// Action constructors
25// ---------------------------------------------------------------------------
26
27/// List all Multipass VMs and their state (`multipass list`).
28///
29/// Risk: Low / Observer. Read-only; shows VM names, state (Running/Stopped),
30/// IPv4 addresses, and image versions. Does not start or modify any VM.
31pub fn multipass_list() -> ActionSpec {
32    ActionSpec {
33        action_name: "MultipassList",
34        mechanism: command_mechanism("multipass", ["list"]),
35        risk_level: RiskLevel::Low,
36        reboot_required: false,
37        rollback_available: false,
38    }
39}
40
41// ---------------------------------------------------------------------------
42// Tests
43// ---------------------------------------------------------------------------
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::actions::ActionMechanism;
49
50    fn extract_cmd(spec: &ActionSpec) -> (&'static str, Vec<&str>) {
51        match &spec.mechanism {
52            ActionMechanism::Command { program, args } => {
53                (*program, args.iter().map(String::as_str).collect())
54            }
55            _ => panic!("expected Command mechanism"),
56        }
57    }
58
59    #[test]
60    fn multipass_list_action_name() {
61        assert_eq!(multipass_list().action_name, "MultipassList");
62    }
63
64    #[test]
65    fn multipass_list_risk_low() {
66        assert_eq!(multipass_list().risk_level, RiskLevel::Low);
67    }
68
69    #[test]
70    fn multipass_list_no_reboot() {
71        assert!(!multipass_list().reboot_required);
72    }
73
74    #[test]
75    fn multipass_list_no_rollback() {
76        assert!(!multipass_list().rollback_available);
77    }
78
79    #[test]
80    fn multipass_list_argv() {
81        let spec = multipass_list();
82        let (prog, args) = extract_cmd(&spec);
83        assert_eq!(prog, "multipass");
84        assert!(args.contains(&"list"));
85    }
86
87    #[test]
88    fn specs_covers_all_action_names() {
89        let expected = ["MultipassList"];
90        let spec_names: Vec<&str> = specs().iter().map(|s| s.action_name).collect();
91        for name in &expected {
92            assert!(spec_names.contains(name), "specs() missing {name}");
93        }
94    }
95}