diff --git a/src/cli/container/compose.rs b/src/cli/container/compose.rs index f801020..46a6d1d 100644 --- a/src/cli/container/compose.rs +++ b/src/cli/container/compose.rs @@ -159,6 +159,7 @@ impl From for Service { environment, expose, annotations, + group_add, healthcheck, hostname, init, @@ -193,7 +194,6 @@ impl From for Service { cgroup_parent, device_cgroup_rules, extra_hosts, - group_add, ipc, uts, log_options, @@ -319,6 +319,7 @@ pub struct Quadlet { pub environment: ListOrMap, pub expose: IndexSet, pub annotations: ListOrMap, + pub group_add: IndexSet, pub healthcheck: Option, pub hostname: Option, pub init: bool, @@ -356,7 +357,6 @@ pub struct PodmanArgs { pub cgroup_parent: Option, pub device_cgroup_rules: IndexSet, pub extra_hosts: IndexMap, - pub group_add: IndexSet, pub ipc: Option, pub uts: Option, pub log_options: IndexMap>, diff --git a/src/cli/container/podman.rs b/src/cli/container/podman.rs index 09bfde1..94f671f 100644 --- a/src/cli/container/podman.rs +++ b/src/cli/container/podman.rs @@ -182,12 +182,6 @@ pub struct PodmanArgs { #[arg(long, value_name = "ENTRY")] gpus: Vec, - /// Assign additional groups to the primary user running within the container process - /// - /// Can be specified multiple times - #[arg(long, value_name = "GROUP")] - group_add: Vec, - /// Customize the entry that is written to the /etc/group file within the container #[arg(long, value_name = "ENTRY")] group_entry: Option, @@ -458,7 +452,6 @@ impl TryFrom for PodmanArgs { cgroup_parent, device_cgroup_rules, extra_hosts, - group_add, ipc, uts, log_options, @@ -526,7 +519,6 @@ impl TryFrom for PodmanArgs { .into_iter() .map(|(host, ip)| format!("{host}:{ip}")) .collect(), - group_add: group_add.into_iter().map(Into::into).collect(), ipc: ipc .map(validate_ipc) .transpose() diff --git a/src/cli/container/quadlet.rs b/src/cli/container/quadlet.rs index 9c178f9..224f342 100644 --- a/src/cli/container/quadlet.rs +++ b/src/cli/container/quadlet.rs @@ -153,6 +153,14 @@ pub struct QuadletOptions { #[arg(long, value_name = "[FLAGS]CONTAINER_GID:FROM_GID[:AMOUNT]")] gidmap: Vec, + /// Assign additional groups to the primary user running within the container process + /// + /// Converts to "GroupAdd=GROUP" + /// + /// Can be specified multiple times + #[arg(long, value_name = "GROUP")] + group_add: Vec, + /// Set or alter a healthcheck command for the container /// /// Converts to "HealthCmd=COMMAND" @@ -465,6 +473,7 @@ impl From for crate::quadlet::Container { env_host: environment_host, expose: expose_host_port, gidmap: gid_map, + group_add, health_cmd, health_interval, health_on_failure, @@ -535,6 +544,7 @@ impl From for crate::quadlet::Container { expose_host_port, gid_map, group, + group_add, health_cmd, health_interval, health_on_failure, @@ -598,6 +608,7 @@ impl TryFrom for QuadletOptions { environment, expose, annotations, + group_add, healthcheck, hostname, init, @@ -681,6 +692,7 @@ impl TryFrom for QuadletOptions { env: environment.into_list().into_iter().collect(), expose: expose.iter().map(ToString::to_string).collect(), annotation: annotations.into_list().into_iter().collect(), + group_add: group_add.into_iter().map(Into::into).collect(), health_cmd, health_interval, health_timeout, diff --git a/src/quadlet.rs b/src/quadlet.rs index 0481276..baf429e 100644 --- a/src/quadlet.rs +++ b/src/quadlet.rs @@ -313,13 +313,17 @@ pub enum PodmanVersion { V4_8, /// Podman v5.0 - #[value(name = "5.0", aliases = ["latest", "5.0.0", "5.0.1", "5.0.2", "5.0.3"])] + #[value(name = "5.0", aliases = ["5.0.0", "5.0.1", "5.0.2", "5.0.3"])] V5_0, + + /// Podman v5.1 + #[value(name = "5.1", aliases = ["latest", "5.1.0"])] + V5_1, } impl PodmanVersion { /// Latest supported version of Podman with regards to Quadlet. - pub const LATEST: Self = Self::V5_0; + pub const LATEST: Self = Self::V5_1; /// Podman version as a static string slice. pub const fn as_str(self) -> &'static str { @@ -330,6 +334,7 @@ impl PodmanVersion { Self::V4_7 => "4.7", Self::V4_8 => "4.8", Self::V5_0 => "5.0", + Self::V5_1 => "5.1", } } } diff --git a/src/quadlet/container.rs b/src/quadlet/container.rs index 7483e72..6310e41 100644 --- a/src/quadlet/container.rs +++ b/src/quadlet/container.rs @@ -104,6 +104,9 @@ pub struct Container { /// The (numeric) GID to run as inside the container. pub group: Option, + /// Assign additional groups to the primary user running within the container process. + pub group_add: Vec, + /// Set or alter a healthcheck command for a container. pub health_cmd: Option, @@ -305,6 +308,10 @@ impl Display for Container { impl Downgrade for Container { fn downgrade(&mut self, version: PodmanVersion) -> Result<(), DowngradeError> { + if version < PodmanVersion::V5_1 { + self.remove_v5_1_options(); + } + if version < PodmanVersion::V5_0 { if self.notify.is_healthy() { if version < PodmanVersion::V4_7 { @@ -359,6 +366,14 @@ macro_rules! extract { } impl Container { + /// Remove Quadlet options added in Podman v5.1.0 + fn remove_v5_1_options(&mut self) { + let options = extract!(self, OptionsV5_1 { group_add }); + + self.push_args(options) + .expect("OptionsV5_1 serializable as args"); + } + /// Remove Quadlet options added in Podman v5.0.0 fn remove_v5_0_options(&mut self) { let options = extract!( @@ -502,6 +517,13 @@ impl Container { } } +/// Container Quadlet options added in Podman v5.1.0 +#[derive(Serialize, Debug)] +#[serde(rename_all = "kebab-case")] +struct OptionsV5_1 { + group_add: Vec, +} + /// Container Quadlet options added in Podman v5.0.0 #[derive(Serialize, Debug)] #[serde(rename_all = "kebab-case")] diff --git a/src/quadlet/container/mount.rs b/src/quadlet/container/mount.rs index ca0f07b..9d84e5d 100644 --- a/src/quadlet/container/mount.rs +++ b/src/quadlet/container/mount.rs @@ -387,6 +387,10 @@ pub struct Image { skip_serializing_if = "Not::not" )] pub read_write: bool, + + /// Mount only a specific path within the image, instead of the whole image. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub subpath: Option, } /// Volume type [`Mount`]. @@ -512,14 +516,16 @@ mod tests { #[test] fn image() { - let string = "type=image,source=fedora,destination=/fedora-image,readwrite=true"; + let string = + "type=image,source=fedora,destination=/fedora-image,readwrite=true,subpath=path"; let mount: Mount = string.parse().unwrap(); assert_eq!( mount, Mount::Image(Image { source: "fedora".into(), destination: "/fedora-image".into(), - read_write: true + read_write: true, + subpath: Some("path".into()), }), ); assert_eq!(mount.to_string(), string);