Skip to main content

sysknife_daemon/actions/
layering.rs

1use super::{command_mechanism, ActionSpec};
2use sysknife_types::RiskLevel;
3
4pub fn specs() -> Vec<ActionSpec> {
5    vec![
6        install_packages(&["podman"]),
7        remove_packages(&["podman"]),
8        get_layered_packages(),
9        add_layered_package("podman"),
10        remove_layered_package("podman"),
11        replace_layered_package("vim", "neovim"),
12        reset_layered_package_override(),
13        remove_base_package("gedit"),
14        get_pending_updates(),
15    ]
16}
17
18pub fn install_packages(packages: &[&str]) -> ActionSpec {
19    ActionSpec {
20        action_name: "InstallPackages",
21        mechanism: command_mechanism(
22            "sudo",
23            std::iter::once("rpm-ostree")
24                .chain(std::iter::once("install"))
25                .chain(std::iter::once("--idempotent"))
26                .chain(packages.iter().copied()),
27        ),
28        risk_level: RiskLevel::High,
29        reboot_required: true,
30        rollback_available: true,
31    }
32}
33
34pub fn remove_packages(packages: &[&str]) -> ActionSpec {
35    ActionSpec {
36        action_name: "RemovePackages",
37        mechanism: command_mechanism(
38            "sudo",
39            std::iter::once("rpm-ostree")
40                .chain(std::iter::once("uninstall"))
41                .chain(packages.iter().copied()),
42        ),
43        risk_level: RiskLevel::High,
44        reboot_required: true,
45        rollback_available: true,
46    }
47}
48
49pub fn get_layered_packages() -> ActionSpec {
50    ActionSpec {
51        action_name: "GetLayeredPackages",
52        mechanism: command_mechanism("rpm-ostree", ["status", "--json"]),
53        risk_level: RiskLevel::Low,
54        reboot_required: false,
55        rollback_available: false,
56    }
57}
58
59pub fn add_layered_package(package: &str) -> ActionSpec {
60    ActionSpec {
61        action_name: "AddLayeredPackage",
62        mechanism: command_mechanism("sudo", ["rpm-ostree", "install", "--idempotent", package]),
63        risk_level: RiskLevel::High,
64        reboot_required: true,
65        rollback_available: true,
66    }
67}
68
69pub fn remove_layered_package(package: &str) -> ActionSpec {
70    ActionSpec {
71        action_name: "RemoveLayeredPackage",
72        mechanism: command_mechanism("sudo", ["rpm-ostree", "uninstall", package]),
73        risk_level: RiskLevel::High,
74        reboot_required: true,
75        rollback_available: true,
76    }
77}
78
79pub fn replace_layered_package(old: &str, new: &str) -> ActionSpec {
80    // Atomically swap one layered package for another in a single transaction.
81    // `rpm-ostree install NEW --uninstall OLD` produces one pending deployment
82    // that contains both changes — no intermediate deployment exists where
83    // neither package is present. The running system is unchanged until reboot.
84    ActionSpec {
85        action_name: "ReplaceLayeredPackage",
86        mechanism: command_mechanism("sudo", ["rpm-ostree", "install", new, "--uninstall", old]),
87        risk_level: RiskLevel::High,
88        reboot_required: true,
89        rollback_available: true,
90    }
91}
92
93pub fn remove_base_package(package: &str) -> ActionSpec {
94    // Override-remove a package that ships in the base OS image.
95    // Unlike `uninstall` (which removes layered packages), `override remove`
96    // hides the base package from the deployment without it ever having been
97    // explicitly installed by the user.
98    ActionSpec {
99        action_name: "RemoveBasePackage",
100        mechanism: command_mechanism("sudo", ["rpm-ostree", "override", "remove", package]),
101        risk_level: RiskLevel::High,
102        reboot_required: true,
103        rollback_available: true,
104    }
105}
106
107pub fn get_pending_updates() -> ActionSpec {
108    // Check for available OS updates without applying them.
109    // `--check` exits 77 when updates ARE available, 0 when up-to-date.
110    // Both exit codes produce informative stdout. The dispatcher special-cases
111    // exit 77 as informational (not an error) so the update list reaches the
112    // caller in both the "updates available" and "up-to-date" cases.
113    ActionSpec {
114        action_name: "GetPendingUpdates",
115        mechanism: command_mechanism("rpm-ostree", ["upgrade", "--check"]),
116        risk_level: RiskLevel::Low,
117        reboot_required: false,
118        rollback_available: false,
119    }
120}
121
122pub fn reset_layered_package_override() -> ActionSpec {
123    ActionSpec {
124        action_name: "ResetLayeredPackageOverride",
125        mechanism: command_mechanism("sudo", ["rpm-ostree", "override", "reset", "--all"]),
126        risk_level: RiskLevel::High,
127        reboot_required: true,
128        rollback_available: true,
129    }
130}