use super::{command_mechanism, ActionSpec};
use sysknife_types::RiskLevel;
pub fn specs() -> Vec<ActionSpec> {
vec![
netplan_get_config(),
netplan_apply(),
netplan_set("ethernets.eth0.dhcp4", "true"),
netplan_generate(),
]
}
pub fn netplan_get_config() -> ActionSpec {
ActionSpec {
action_name: "NetplanGetConfig",
mechanism: command_mechanism(
"find",
[
"/etc/netplan",
"-maxdepth",
"1",
"-name",
"*.yaml",
"-print",
"-exec",
"cat",
"{}",
"+",
],
),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn netplan_set(key: &str, value: &str) -> ActionSpec {
let kv = format!("{}={}", key, value);
ActionSpec {
action_name: "NetplanSet",
mechanism: command_mechanism("sudo", ["netplan", "set", &kv]),
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
pub fn netplan_generate() -> ActionSpec {
ActionSpec {
action_name: "NetplanGenerate",
mechanism: command_mechanism("sudo", ["netplan", "generate"]),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
pub fn netplan_apply() -> ActionSpec {
ActionSpec {
action_name: "NetplanApply",
mechanism: command_mechanism("sudo", ["netplan", "apply"]),
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::actions::ActionMechanism;
fn extract_cmd(spec: &ActionSpec) -> (&'static str, Vec<&str>) {
match &spec.mechanism {
ActionMechanism::Command { program, args } => {
(*program, args.iter().map(String::as_str).collect())
}
_ => panic!("expected Command mechanism"),
}
}
#[test]
fn netplan_get_config_action_name() {
assert_eq!(netplan_get_config().action_name, "NetplanGetConfig");
}
#[test]
fn netplan_get_config_risk_low() {
assert_eq!(netplan_get_config().risk_level, RiskLevel::Low);
}
#[test]
fn netplan_get_config_no_reboot() {
assert!(!netplan_get_config().reboot_required);
}
#[test]
fn netplan_get_config_uses_find_directly() {
let spec = netplan_get_config();
let (prog, args) = extract_cmd(&spec);
assert_eq!(prog, "find");
assert!(!args.contains(&"-c"));
assert!(args.contains(&"/etc/netplan"));
assert!(args.contains(&"*.yaml"));
}
#[test]
fn netplan_apply_action_name() {
assert_eq!(netplan_apply().action_name, "NetplanApply");
}
#[test]
fn netplan_apply_risk_high() {
assert_eq!(netplan_apply().risk_level, RiskLevel::High);
}
#[test]
fn netplan_apply_no_reboot() {
assert!(!netplan_apply().reboot_required);
}
#[test]
fn netplan_apply_argv() {
let spec = netplan_apply();
let (prog, args) = extract_cmd(&spec);
assert_eq!(prog, "sudo");
assert!(args.contains(&"netplan"));
assert!(args.contains(&"apply"));
}
#[test]
fn netplan_set_action_name() {
assert_eq!(
netplan_set("ethernets.eth0.dhcp4", "true").action_name,
"NetplanSet"
);
}
#[test]
fn netplan_set_risk_high() {
assert_eq!(
netplan_set("ethernets.eth0.dhcp4", "true").risk_level,
RiskLevel::High
);
}
#[test]
fn netplan_set_no_automatic_rollback() {
assert!(!netplan_set("ethernets.eth0.dhcp4", "true").rollback_available);
}
#[test]
fn netplan_set_no_reboot() {
assert!(!netplan_set("ethernets.eth0.dhcp4", "true").reboot_required);
}
#[test]
fn netplan_set_argv_simple_value() {
let spec = netplan_set("ethernets.eth0.dhcp4", "true");
let (prog, args) = extract_cmd(&spec);
assert_eq!(prog, "sudo");
assert!(args.contains(&"netplan"));
assert!(args.contains(&"set"));
assert!(
args.contains(&"ethernets.eth0.dhcp4=true"),
"expected key=value arg, got: {:?}",
args
);
}
#[test]
fn netplan_set_argv_never_contains_literal_quote_bytes() {
let spec = netplan_set("renderer", "NetworkManager");
let (_, args) = extract_cmd(&spec);
for a in &args {
assert!(
!a.contains('\''),
"argv must not contain literal '\\'' bytes"
);
}
}
#[test]
fn netplan_generate_action_name() {
assert_eq!(netplan_generate().action_name, "NetplanGenerate");
}
#[test]
fn netplan_generate_risk_medium() {
assert_eq!(netplan_generate().risk_level, RiskLevel::Medium);
}
#[test]
fn netplan_generate_no_reboot() {
assert!(!netplan_generate().reboot_required);
}
#[test]
fn netplan_generate_no_rollback() {
assert!(!netplan_generate().rollback_available);
}
#[test]
fn netplan_generate_argv() {
let spec = netplan_generate();
let (prog, args) = extract_cmd(&spec);
assert_eq!(prog, "sudo");
assert!(args.contains(&"netplan"));
assert!(args.contains(&"generate"));
}
#[test]
fn specs_covers_all_action_names() {
let expected = [
"NetplanGetConfig",
"NetplanApply",
"NetplanSet",
"NetplanGenerate",
];
let spec_names: Vec<&str> = specs().iter().map(|s| s.action_name).collect();
for name in &expected {
assert!(spec_names.contains(name), "specs() missing {name}");
}
}
}