pub const FEDORA_ONLY_ACTIONS: &[&str] = &[
"GetSystemState",
"AddLayeredPackage",
"RemoveLayeredPackage",
"ReplaceLayeredPackage",
"RemoveBasePackage",
"ResetLayeredPackageOverride",
"GetLayeredPackages",
"GetDeploymentHistory",
"ListDeployments",
"CleanupDeployments",
"RollbackDeployment",
"PinDeployment",
"UnpinDeployment",
"RebaseSystem",
"GetKernelArguments",
"SetKernelArguments",
"UpdateSystem",
"InstallPackages",
"RemovePackages",
"GetPendingUpdates",
"ListPackageRepositories",
"AddPackageRepository",
"RemovePackageRepository",
"EnablePackageRepository",
"DisablePackageRepository",
];
pub const NON_CANONICAL_ON_DEBIAN: &[&str] = &[
"GetFirewallState",
"ConfigureFirewall",
"ListToolboxes",
"CreateToolbox",
"RemoveToolbox",
];
pub const DEBIAN_ONLY_ACTIONS: &[&str] = &[
"AptUpdate",
"AptUpgrade",
"AptInstall",
"AptRemove",
"AptPurge",
"AptAutoremove",
"AptHold",
"AptUnhold",
"AptSearch",
"AptListInstalled",
"AptShow",
"AptListUpgradable",
"AptHistoryList",
"ConfigureUnattendedUpgrades",
"GetAptPins",
"SetAptPin",
"RemoveAptPin",
"GrubGetKargs",
"GrubSetKargs",
];
pub const UBUNTU_ONLY_ACTIONS: &[&str] = &[
"AddPpa",
"RemovePpa",
"CheckPendingReboot",
"UbuntuReleaseUpgrade",
"ProStatus",
"ProAttach",
"ProDetach",
"EnableProService",
"DisableProService",
"LivepatchStatus",
];
pub const NON_CANONICAL_ON_DEBIAN_HOST: &[&str] = &[
"SnapInstall",
"SnapRemove",
"SnapRefresh",
"SnapHold",
"SnapUnhold",
"SnapList",
"SnapInfo",
"SnapRevert",
"SnapClassicInstall",
"NetplanGetConfig",
"NetplanApply",
"NetplanSet",
"NetplanGenerate",
"MultipassList",
];
pub const NON_CANONICAL_ON_FEDORA: &[&str] = &[
"GetHostState",
"SnapInstall",
"SnapRemove",
"SnapRefresh",
"SnapHold",
"SnapUnhold",
"SnapList",
"SnapInfo",
"SnapRevert",
"SnapClassicInstall",
"UfwEnable",
"UfwDisable",
"UfwAllow",
"UfwDeny",
"UfwReset",
"UfwStatus",
"DistroboxList",
"DistroboxCreate",
"DistroboxRemove",
"NetplanGetConfig",
"NetplanApply",
"NetplanSet",
"NetplanGenerate",
"AppArmorStatus",
"AppArmorEnforce",
"AppArmorComplain",
"CloudInitStatus",
"UbuntuInstallFlatpak",
"UbuntuRemoveFlatpak",
"UbuntuUpdateFlatpak",
"UbuntuListFlatpaks",
"Fail2banStatus",
"Fail2banBanIp",
"Fail2banUnbanIp",
"ConfigureFail2banJail",
"MultipassList",
"UfwDeleteRule",
"UfwLimit",
];
pub fn action_requires_distro(action: &str) -> bool {
[
FEDORA_ONLY_ACTIONS,
DEBIAN_ONLY_ACTIONS,
UBUNTU_ONLY_ACTIONS,
]
.iter()
.any(|list| list.contains(&action))
}
pub fn action_requires_supported_host(action: &str) -> bool {
action_requires_distro(action)
|| [
NON_CANONICAL_ON_DEBIAN,
NON_CANONICAL_ON_DEBIAN_HOST,
NON_CANONICAL_ON_FEDORA,
]
.iter()
.any(|list| list.contains(&action))
}
pub fn action_matches_distro(action: &str, distro: &crate::distro::DistroId) -> bool {
use crate::distro::{DistroFamily, DistroId};
if UBUNTU_ONLY_ACTIONS.contains(&action) {
matches!(distro, DistroId::Ubuntu { .. })
} else if DEBIAN_ONLY_ACTIONS.contains(&action) {
distro.family() == DistroFamily::Debian
} else if FEDORA_ONLY_ACTIONS.contains(&action) {
distro.family() == DistroFamily::Fedora
} else {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn family_lists_are_disjoint() {
let lists = [
FEDORA_ONLY_ACTIONS,
DEBIAN_ONLY_ACTIONS,
UBUNTU_ONLY_ACTIONS,
];
for (index, list) in lists.iter().enumerate() {
for action in *list {
for other in &lists[index + 1..] {
assert!(
!other.contains(action),
"{action} has conflicting hard fences"
);
}
}
}
}
#[test]
fn family_lists_have_no_duplicates() {
for list in [
FEDORA_ONLY_ACTIONS,
DEBIAN_ONLY_ACTIONS,
UBUNTU_ONLY_ACTIONS,
NON_CANONICAL_ON_DEBIAN,
NON_CANONICAL_ON_DEBIAN_HOST,
NON_CANONICAL_ON_FEDORA,
] {
let mut sorted = list.to_vec();
sorted.sort_unstable();
let unique = sorted.len();
sorted.dedup();
assert_eq!(unique, sorted.len(), "duplicate action in family list");
}
}
#[test]
fn planner_preferences_are_not_execution_fences() {
for action in NON_CANONICAL_ON_DEBIAN
.iter()
.chain(NON_CANONICAL_ON_DEBIAN_HOST)
.chain(NON_CANONICAL_ON_FEDORA)
{
for fence in [
FEDORA_ONLY_ACTIONS,
DEBIAN_ONLY_ACTIONS,
UBUNTU_ONLY_ACTIONS,
] {
assert!(
!fence.contains(action),
"{action} is both portable and hard-fenced"
);
}
}
}
#[test]
fn ubuntu_identity_is_stricter_than_debian_family() {
use crate::distro::DistroId;
let ubuntu = DistroId::Ubuntu {
major: 24,
minor: 4,
};
let debian = DistroId::Debian { version: Some(13) };
let derivative = DistroId::Other {
id: "linuxmint".into(),
version_id: None,
id_like: vec!["ubuntu".into(), "debian".into()],
};
for action in [
"AddPpa",
"RemovePpa",
"ProAttach",
"LivepatchStatus",
"CheckPendingReboot",
] {
assert!(action_matches_distro(action, &ubuntu), "{action}");
assert!(!action_matches_distro(action, &debian), "{action}");
assert!(!action_matches_distro(action, &derivative), "{action}");
assert!(action_requires_distro(action), "{action}");
}
for action in ["AptInstall", "AptUpdate"] {
assert!(action_matches_distro(action, &ubuntu), "{action}");
assert!(action_matches_distro(action, &debian), "{action}");
}
assert!(
!derivative.is_supported(),
"Debian-family membership must not enable an unrecognised derivative"
);
}
}