1use super::{command_mechanism, ActionSpec};
15use sysknife_types::RiskLevel;
16
17const ACTION_STEPS: &str = "/usr/lib/sysknife/action-steps";
23
24pub fn specs() -> Vec<ActionSpec> {
25 vec![
26 install_packages(&["podman"]),
27 remove_packages(&["podman"]),
28 get_layered_packages(),
29 add_layered_package("podman"),
30 remove_layered_package("podman"),
31 replace_layered_package("vim", "neovim"),
32 reset_layered_package_override(),
33 remove_base_package("gedit"),
34 get_pending_updates(),
35 ]
36}
37
38pub fn install_packages(packages: &[&str]) -> ActionSpec {
39 ActionSpec {
40 action_name: "InstallPackages",
41 mechanism: command_mechanism(
43 "sudo",
44 [ACTION_STEPS, "rpm-ostree", "install"]
45 .into_iter()
46 .chain(packages.iter().copied()),
47 ),
48 risk_level: RiskLevel::High,
49 reboot_required: true,
50 rollback_available: true,
51 }
52}
53
54pub fn remove_packages(packages: &[&str]) -> ActionSpec {
55 ActionSpec {
56 action_name: "RemovePackages",
57 mechanism: command_mechanism(
59 "sudo",
60 [ACTION_STEPS, "rpm-ostree", "uninstall"]
61 .into_iter()
62 .chain(packages.iter().copied()),
63 ),
64 risk_level: RiskLevel::High,
65 reboot_required: true,
66 rollback_available: true,
67 }
68}
69
70pub fn get_layered_packages() -> ActionSpec {
71 ActionSpec {
72 action_name: "GetLayeredPackages",
73 mechanism: command_mechanism("rpm-ostree", ["status", "--json"]),
74 risk_level: RiskLevel::Low,
75 reboot_required: false,
76 rollback_available: false,
77 }
78}
79
80pub fn add_layered_package(package: &str) -> ActionSpec {
81 ActionSpec {
82 action_name: "AddLayeredPackage",
83 mechanism: command_mechanism("sudo", [ACTION_STEPS, "rpm-ostree", "install", package]),
85 risk_level: RiskLevel::High,
86 reboot_required: true,
87 rollback_available: true,
88 }
89}
90
91pub fn remove_layered_package(package: &str) -> ActionSpec {
92 ActionSpec {
93 action_name: "RemoveLayeredPackage",
94 mechanism: command_mechanism("sudo", [ACTION_STEPS, "rpm-ostree", "uninstall", package]),
96 risk_level: RiskLevel::High,
97 reboot_required: true,
98 rollback_available: true,
99 }
100}
101
102pub fn replace_layered_package(old: &str, new: &str) -> ActionSpec {
103 ActionSpec {
109 action_name: "ReplaceLayeredPackage",
110 mechanism: command_mechanism("sudo", [ACTION_STEPS, "rpm-ostree", "replace", old, new]),
111 risk_level: RiskLevel::High,
112 reboot_required: true,
113 rollback_available: true,
114 }
115}
116
117pub fn remove_base_package(package: &str) -> ActionSpec {
118 ActionSpec {
123 action_name: "RemoveBasePackage",
124 mechanism: command_mechanism(
126 "sudo",
127 [ACTION_STEPS, "rpm-ostree", "override-remove", package],
128 ),
129 risk_level: RiskLevel::High,
130 reboot_required: true,
131 rollback_available: true,
132 }
133}
134
135pub fn get_pending_updates() -> ActionSpec {
136 ActionSpec {
142 action_name: "GetPendingUpdates",
143 mechanism: command_mechanism("rpm-ostree", ["upgrade", "--check"]),
144 risk_level: RiskLevel::Low,
145 reboot_required: false,
146 rollback_available: false,
147 }
148}
149
150pub fn reset_layered_package_override() -> ActionSpec {
151 ActionSpec {
152 action_name: "ResetLayeredPackageOverride",
153 mechanism: command_mechanism("sudo", ["rpm-ostree", "override", "reset", "--all"]),
154 risk_level: RiskLevel::High,
155 reboot_required: true,
156 rollback_available: true,
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::actions::{exclusive_resource, ActionMechanism, ExclusiveResource};
164
165 fn argv(spec: &ActionSpec) -> (&'static str, Vec<&str>) {
166 match &spec.mechanism {
167 ActionMechanism::Command { program, args } => {
168 (*program, args.iter().map(String::as_str).collect())
169 }
170 other => panic!("{} is not a command: {other:?}", spec.action_name),
171 }
172 }
173
174 #[test]
178 fn layering_actions_with_an_operand_go_through_the_helper_verb() {
179 for (spec, expected) in [
180 (
181 install_packages(&["podman", "vim"]),
182 vec![ACTION_STEPS, "rpm-ostree", "install", "podman", "vim"],
183 ),
184 (
185 add_layered_package("podman"),
186 vec![ACTION_STEPS, "rpm-ostree", "install", "podman"],
187 ),
188 (
189 remove_packages(&["podman", "vim"]),
190 vec![ACTION_STEPS, "rpm-ostree", "uninstall", "podman", "vim"],
191 ),
192 (
193 remove_layered_package("podman"),
194 vec![ACTION_STEPS, "rpm-ostree", "uninstall", "podman"],
195 ),
196 (
197 replace_layered_package("vim", "neovim"),
198 vec![ACTION_STEPS, "rpm-ostree", "replace", "vim", "neovim"],
199 ),
200 (
201 remove_base_package("gedit"),
202 vec![ACTION_STEPS, "rpm-ostree", "override-remove", "gedit"],
203 ),
204 ] {
205 let (program, args) = argv(&spec);
206 assert_eq!(program, "sudo", "{}", spec.action_name);
207 assert_eq!(args, expected, "{}", spec.action_name);
208 assert!(
209 args.iter().all(|a| !a.starts_with('-')),
210 "{} sends an option; the helper adds rpm-ostree's flags itself: {args:?}",
211 spec.action_name
212 );
213 }
214 }
215
216 #[test]
218 fn reset_override_keeps_its_fixed_argv() {
219 let reset = reset_layered_package_override();
220 let (program, args) = argv(&reset);
221 assert_eq!(program, "sudo");
222 assert_eq!(args, ["rpm-ostree", "override", "reset", "--all"]);
223 }
224
225 #[test]
228 fn layering_actions_still_contend_for_the_rpm_ostree_lock() {
229 for spec in specs().into_iter().chain([
230 add_layered_package("apt-get"),
231 install_packages(&["flatpak"]),
232 ]) {
233 assert_eq!(
234 exclusive_resource(&spec),
235 Some(ExclusiveResource::RpmOstree),
236 "{} lost the rpm-ostree lock",
237 spec.action_name
238 );
239 }
240 }
241}