diff --git a/Cargo.lock b/Cargo.lock index a40f390..b3fe49a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -853,7 +853,7 @@ dependencies = [ [[package]] name = "podlet" -version = "0.3.2-alpha.4" +version = "0.3.2-alpha.5" dependencies = [ "clap", "color-eyre", diff --git a/Cargo.toml b/Cargo.toml index 88c9015..c9d145f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "podlet" -version = "0.3.2-alpha.4" +version = "0.3.2-alpha.5" authors = ["Paul Nettleton "] edition = "2024" rust-version = "1.85" diff --git a/src/cli.rs b/src/cli.rs index 1603419..646b8bf 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,4 @@ +mod artifact; mod build; mod compose; mod container; @@ -38,7 +39,7 @@ use crate::quadlet::{ }; use self::{ - build::Build, compose::Compose, container::Container, generate::Generate, + artifact::Artifact, build::Build, compose::Compose, container::Container, generate::Generate, global_args::GlobalArgs, image::Image, install::Install, kube::Kube, network::Network, pod::Pod, volume::Volume, }; @@ -623,6 +624,16 @@ enum PodmanCommands { #[command(subcommand)] image: Box, }, + + /// Generate a Podman Quadlet `.artifact` file + /// + /// For details on options see: + /// https://docs.podman.io/en/stable/markdown/podman-artifact-pull.1.html + Artifact { + /// The \[Artifact\] section + #[command(subcommand)] + artifact: Artifact, + }, } impl From for quadlet::Resource { @@ -635,6 +646,7 @@ impl From for quadlet::Resource { PodmanCommands::Volume { volume } => volume.into(), PodmanCommands::Build { build } => (*build).into(), PodmanCommands::Image { image } => (*image).into(), + PodmanCommands::Artifact { artifact } => artifact.into(), } } } @@ -680,6 +692,7 @@ impl PodmanCommands { Self::Volume { volume } => volume.name(), Self::Build { build } => build.name(), Self::Image { image } => image.name(), + Self::Artifact { artifact } => artifact.name(), } } } diff --git a/src/cli/artifact.rs b/src/cli/artifact.rs new file mode 100644 index 0000000..fd320c6 --- /dev/null +++ b/src/cli/artifact.rs @@ -0,0 +1,138 @@ +//! The `podlet podman artifact pull` command and CLI options. + +use std::path::PathBuf; + +use clap::{Args, Subcommand}; + +use crate::{ + cli::image_to_name, + quadlet::{self, image::DecryptionKey}, +}; + +/// [`Subcommand`]s for `podlet podman artifact` +#[derive(Subcommand, Debug, Clone, PartialEq)] +pub enum Artifact { + /// Generate a Podman Quadlet `.artifact` file + /// + /// For details on options see: + /// https://docs.podman.io/en/stable/markdown/podman-pull.1.html and + /// https://docs.podman.io/en/stable/markdown/podman-systemd.unit.5.html#artifact-units-artifact + #[expect(clippy::doc_markdown, reason = "help links")] + #[group(skip)] + Pull { + #[command(flatten)] + pull: Pull, + }, +} + +impl Artifact { + /// Name suitable for use as the filename of the generated Quadlet file. + pub fn name(&self) -> &str { + let Self::Pull { + pull: Pull { source, .. }, + } = self; + + image_to_name(source) + } +} + +impl From for quadlet::Artifact { + fn from(value: Artifact) -> Self { + let Artifact::Pull { pull } = value; + pull.into() + } +} + +impl From for quadlet::Resource { + fn from(value: Artifact) -> Self { + quadlet::Artifact::from(value).into() + } +} + +/// [`Args`] for `podman artifact pull` +#[derive(Args, Default, Debug, Clone, PartialEq)] +pub struct Pull { + /// Path of the authentication file. + /// + /// Converts to "AuthFile=PATH". + #[arg(long, value_name = "PATH")] + pub authfile: Option, + + /// Use certificates at path (*.crt, *.cert, *.key) to connect to the registry. + /// + /// Converts to "CertDir=PATH". + #[arg(long, value_name = "PATH")] + pub cert_dir: Option, + + /// The username and/or password to use to authenticate with the registry, if required. + /// + /// Converts to "Creds=[USERNAME][:PASSWORD]". + #[arg(long, value_name = "[USERNAME][:PASSWORD]")] + pub creds: Option, + + /// The key and optional passphrase to be used for decryption of artifacts. + /// + /// Converts to "DecryptionKey=KEY[:PASSPHRASE]" + #[arg(long, value_name = "KEY[:PASSPHRASE]")] + pub decryption_key: Option, + + /// Suppress output information when pulling artifacts. + #[arg(short, long)] + pub quiet: bool, + + /// Number of times to retry pulling artifacts. + /// + /// Converts to "Retry=ATTEMPTS". + /// + /// Default is 3. + #[arg(long, value_name = "ATTEMPTS")] + #[arg(long)] + pub retry: Option, + + /// Duration of delay between retry attempts when pulling artifacts. + /// + /// Converts to "RetryDelay=DURATION". + /// + /// Default is to start at two seconds and then exponentially back off. + #[arg(long, value_name = "DURATION")] + pub retry_delay: Option, + + /// Require HTTPS and verify certificates when contacting registries. + /// + /// Converts to "TLSVerify=TLS_VERIFY". + #[expect(clippy::doc_markdown, reason = "Quadlet option")] + #[arg(long, num_args = 0..=1, require_equals = true, default_missing_value = "true")] + pub tls_verify: Option, + + /// The location from which the artifact image is obtained. + pub source: String, +} + +impl From for quadlet::Artifact { + fn from( + Pull { + authfile, + cert_dir, + creds, + decryption_key, + quiet, + retry, + retry_delay, + tls_verify, + source, + }: Pull, + ) -> Self { + Self { + artifact: source, + auth_file: authfile, + cert_dir, + creds, + decryption_key, + podman_args: None, + quiet, + retry, + retry_delay, + tls_verify, + } + } +} diff --git a/src/cli/build.rs b/src/cli/build.rs index f2f94f9..6800394 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -55,6 +55,15 @@ pub struct Build { #[arg(long, value_name = "PATH")] authfile: Option, + /// Specifies a build argument and its value. + /// + /// Converts to "BuildArg=ARG=VALUE". + /// + /// Can be specified multiple times. + #[expect(clippy::struct_field_names, reason = "CLI arg")] + #[arg(long, value_name = "ARG=VALUE")] + build_arg: Vec, + /// Set custom DNS servers. /// /// Converts to "DNS=IP_ADDRESS". @@ -116,6 +125,12 @@ pub struct Build { #[arg(long, value_name = "GROUP | keep-groups")] group_add: Vec, + /// Path to an alternative `.containerignore` file. + /// + /// Converts to "IgnoreFile=PATH". + #[arg(long, value_name = "PATH")] + ignorefile: Option, + /// The name assigned to the resulting image if the build process completes successfully. /// /// Converts to "ImageTag=IMAGE_NAME". @@ -226,6 +241,7 @@ impl From for quadlet::Build { annotation, arch, authfile, + build_arg, dns, dns_option, dns_search, @@ -233,6 +249,7 @@ impl From for quadlet::Build { file, force_rm, group_add, + ignorefile, tag, label, network, @@ -254,6 +271,7 @@ impl From for quadlet::Build { annotation, arch, auth_file: authfile, + build_arg, dns: dns.into(), dns_option, dns_search, @@ -261,6 +279,7 @@ impl From for quadlet::Build { file, force_rm, group_add, + ignore_file: ignorefile, image_tag: tag, label, network, @@ -338,7 +357,6 @@ impl TryFrom for Build { .into_iter() .map(|(hostname, ip)| format!("{hostname}:{ip}")) .collect(), - build_arg: args.into_list().into_iter().collect(), build_context: additional_contexts .iter() .map(|(id, context)| format!("{id}={context}")) @@ -386,6 +404,7 @@ impl TryFrom for Build { Ok(Self { file, + build_arg: args.into_list().into_iter().collect(), tag: tags.into_iter().map(Into::into).collect(), label: labels.into_list().into_iter().collect(), network: network.map(Into::into).into_iter().collect(), @@ -434,12 +453,6 @@ struct PodmanArgs { #[serde(skip_serializing_if = "Not::not")] all_platforms: bool, - /// Specifies a build argument and its value. - /// - /// Can be specified multiple times. - #[arg(long, value_name = "ARG=VALUE")] - build_arg: Vec, - /// Specifies a file containing lines of build arguments of the form `arg=value`. /// /// Can be specified multiple times. @@ -587,10 +600,6 @@ struct PodmanArgs { #[default = true] identity_label: bool, - /// Path to an alternative `.containerignore` file. - #[arg(long, value_name = "PATH")] - ignorefile: Option, - /// Write the built image's ID to a file. #[arg(long, value_name = "IMAGE_ID_FILE")] iidfile: Option, diff --git a/src/cli/container/podman.rs b/src/cli/container/podman.rs index 78d3a44..0e9d51f 100644 --- a/src/cli/container/podman.rs +++ b/src/cli/container/podman.rs @@ -52,6 +52,10 @@ pub struct PodmanArgs { #[arg(long, value_name = "DEVICE:WEIGHT")] blkio_weight_device: Vec, + /// Use certificates in the specified directory to connect to the registry + #[arg(long, value_name = "PATH")] + cert_dir: Option, + /// Specify the cgroup file to write to and its value /// /// Can be specified multiple times @@ -110,6 +114,10 @@ pub struct PodmanArgs { #[arg(long, value_name = "NODES")] cpuset_mems: Option, + /// The username and/or password to use to authenticate with the registry, if required + #[arg(long, value_name = "[USERNAME][:PASSWORD]")] + creds: Option, + /// Key needed to decrypt the image #[arg(long, value_name = "KEY[:PASSPHRASE]")] decryption_key: Option, @@ -184,12 +192,6 @@ pub struct PodmanArgs { #[arg(long, value_name = "NAME")] hostuser: Vec, - /// Set proxy environment variables in the container based on the host proxy vars - #[arg(long, action = ArgAction::Set, default_value_t = true)] - #[serde(skip_serializing_if = "skip_true")] - #[default = true] - http_proxy: bool, - /// How to handle the builtin image volumes #[arg(long, value_name = "bind | tmpfs | ignore")] image_volume: Option, diff --git a/src/cli/container/quadlet.rs b/src/cli/container/quadlet.rs index bdfcd5c..aa0f8ac 100644 --- a/src/cli/container/quadlet.rs +++ b/src/cli/container/quadlet.rs @@ -262,6 +262,13 @@ pub struct QuadletOptions { #[arg(short, long, value_name = "NAME")] hostname: Option, + /// Set proxy environment variables in the container based on the host proxy vars + /// + /// Converts to "HttpProxy=true|false" + #[arg(long, action = ArgAction::Set, default_value_t = true)] + #[default = true] + http_proxy: bool, + /// Specify a static IPv4 address for the container /// /// Converts to "IP=IPV4" @@ -564,6 +571,7 @@ impl From for crate::quadlet::Container { health_startup_timeout, health_timeout, hostname: host_name, + http_proxy, ip, ip6, mut label, @@ -646,6 +654,7 @@ impl From for crate::quadlet::Container { health_startup_timeout, health_timeout, host_name, + http_proxy, ip, ip6, label, diff --git a/src/cli/kube.rs b/src/cli/kube.rs index 627fd16..4be3659 100644 --- a/src/cli/kube.rs +++ b/src/cli/kube.rs @@ -44,7 +44,7 @@ impl Kube { pub fn name(&self) -> &str { let Kube::Play { play } = self; - play.file.name().unwrap_or("pod") + play.file.first().and_then(YamlFile::name).unwrap_or("pod") } } @@ -93,22 +93,36 @@ pub struct Play { /// The path to the Kubernetes YAML file to use /// /// Converts to "Yaml=FILE" - file: YamlFile, + /// + /// Multiple files can be specified + #[arg(required = true)] + file: Vec, } impl From for crate::quadlet::Kube { - fn from(mut value: Play) -> Self { - let auto_update = AutoUpdate::extract_from_annotations(&mut value.podman_args.annotation); - let podman_args = value.podman_args.to_string(); + fn from( + Play { + configmap, + log_driver, + network, + publish, + userns, + mut podman_args, + file, + }: Play, + ) -> Self { + let auto_update = AutoUpdate::extract_from_annotations(&mut podman_args.annotation); + let podman_args = podman_args.to_string(); + Self { auto_update, - config_map: value.configmap, - log_driver: value.log_driver, - network: value.network, + config_map: configmap, + log_driver, + network, podman_args: (!podman_args.is_empty()).then_some(podman_args), - publish_port: value.publish, - user_ns: value.userns, - yaml: value.file, + publish_port: publish, + user_ns: userns, + yaml: file, } } } @@ -163,6 +177,11 @@ pub struct PodmanArgs { #[serde(skip_serializing_if = "Not::not")] no_hosts: bool, + /// Do not prefix container name with pod name + #[arg(long)] + #[serde(skip_serializing_if = "Not::not")] + no_pod_prefix: bool, + /// Directory path for seccomp profiles #[arg(long, value_name = "PATH")] seccomp_profile_root: Option, diff --git a/src/quadlet.rs b/src/quadlet.rs index 7f16ff5..756db3b 100644 --- a/src/quadlet.rs +++ b/src/quadlet.rs @@ -1,3 +1,4 @@ +mod artifact; pub mod build; pub mod container; mod globals; @@ -25,6 +26,7 @@ use smart_default::SmartDefault; use thiserror::Error; pub use self::{ + artifact::Artifact, build::Build, container::Container, globals::Globals, @@ -178,6 +180,9 @@ pub enum JoinOption { /// `BindsTo=`, used in [Unit] sections. BindsTo, + /// `BuildArg=`, used in [Build] sections. + BuildArg, + /// `DropCapability=`, used in [Container] sections. DropCapability, @@ -226,6 +231,7 @@ impl JoinOption { Self::Annotation, Self::Before, Self::BindsTo, + Self::BuildArg, Self::DropCapability, Self::Environment, Self::Label, @@ -254,6 +260,7 @@ impl JoinOption { Self::Annotation => "Annotation", Self::Before => "Before", Self::BindsTo => "BindsTo", + Self::BuildArg => "BuildArg", Self::DropCapability => "DropCapability", Self::Environment => "Environment", Self::Label => "Label", @@ -287,6 +294,7 @@ impl FromStr for JoinOption { "Annotation" => Ok(Self::Annotation), "Before" => Ok(Self::Before), "BindsTo" => Ok(Self::BindsTo), + "BuildArg" => Ok(Self::BuildArg), "DropCapability" => Ok(Self::DropCapability), "Environment" => Ok(Self::Environment), "Label" => Ok(Self::Label), @@ -320,6 +328,7 @@ pub enum Resource { Volume(Volume), Build(Box), Image(Image), + Artifact(Artifact), } impl From for Resource { @@ -376,6 +385,12 @@ impl From for Resource { } } +impl From for Resource { + fn from(value: Artifact) -> Self { + Self::Artifact(value) + } +} + impl Resource { /// The extension that should be used for the generated file. pub fn extension(&self) -> &'static str { @@ -386,12 +401,13 @@ impl Resource { /// generated by Quadlet. pub fn name_to_service(&self, name: &str) -> String { let mut service = match self { - Self::Container(_) | Self::Kube(_) => String::from(name), + Self::Container(_) | Self::Kube(_) => name.to_owned(), Self::Pod(_) => format!("{name}-pod"), Self::Network(_) => format!("{name}-network"), Self::Volume(_) => format!("{name}-volume"), Self::Build(_) => format!("{name}-build"), Self::Image(_) => format!("{name}-image"), + Self::Artifact(_) => format!("{name}-artifact"), }; service.push_str(".service"); service @@ -408,12 +424,13 @@ impl HostPaths for Resource { Self::Volume(volume) => ResourceIter::Volume(volume.host_paths()), Self::Build(build) => ResourceIter::Build(build.host_paths()), Self::Image(image) => ResourceIter::Image(image.host_paths()), + Self::Artifact(artifact) => ResourceIter::Artifact(artifact.host_paths()), } } } /// [`Iterator`] for all [`Resource`] types. -enum ResourceIter { +enum ResourceIter { Container(C), Pod(P), Kube(K), @@ -421,9 +438,10 @@ enum ResourceIter { Volume(V), Build(B), Image(I), + Artifact(A), } -impl Iterator for ResourceIter +impl Iterator for ResourceIter where C: Iterator, P: Iterator, @@ -432,6 +450,7 @@ where V: Iterator, B: Iterator, I: Iterator, + A: Iterator, { type Item = Item; @@ -444,6 +463,7 @@ where Self::Volume(iter) => iter.next(), Self::Build(iter) => iter.next(), Self::Image(iter) => iter.next(), + Self::Artifact(iter) => iter.next(), } } } @@ -458,6 +478,7 @@ impl Downgrade for Resource { Self::Volume(volume) => volume.downgrade(version), Self::Build(build) => build.downgrade(version), Self::Image(image) => image.downgrade(version), + Self::Artifact(artifact) => artifact.downgrade(version), } } } @@ -472,6 +493,7 @@ pub enum ResourceKind { Volume, Build, Image, + Artifact, } impl ResourceKind { @@ -485,6 +507,7 @@ impl ResourceKind { Self::Volume => "volume", Self::Build => "build", Self::Image => "image", + Self::Artifact => "artifact", } } } @@ -505,6 +528,7 @@ impl From<&Resource> for ResourceKind { Resource::Volume(_) => Self::Volume, Resource::Build(_) => Self::Build, Resource::Image(_) => Self::Image, + Resource::Artifact(_) => Self::Artifact, } } } @@ -617,13 +641,17 @@ pub enum PodmanVersion { V5_5, /// Podman v5.6 - #[value(name = "5.6", aliases = ["latest", "5.6.0", "5.6.1", "5.6.2"])] + #[value(name = "5.6", aliases = ["5.6.0", "5.6.1", "5.6.2"])] V5_6, + + /// Podman v5.7 + #[value(name = "5.7", aliases = ["latest", "5.7.0", "5.7.1"])] + V5_7, } impl PodmanVersion { /// Latest supported version of Podman with regards to Quadlet. - pub const LATEST: Self = Self::V5_6; + pub const LATEST: Self = Self::V5_7; /// Podman version as a static string slice. pub const fn as_str(self) -> &'static str { @@ -640,6 +668,7 @@ impl PodmanVersion { Self::V5_4 => "5.4", Self::V5_5 => "5.5", Self::V5_6 => "5.6", + Self::V5_7 => "5.7", } } } diff --git a/src/quadlet/artifact.rs b/src/quadlet/artifact.rs new file mode 100644 index 0000000..3f8bc6f --- /dev/null +++ b/src/quadlet/artifact.rs @@ -0,0 +1,70 @@ +//! The [`Artifact`] Quadlet type. + +use std::{ops::Not, path::PathBuf}; + +use serde::Serialize; + +use crate::quadlet::{ + Downgrade, DowngradeError, HostPaths, PodmanVersion, ResourceKind, image::DecryptionKey, +}; + +/// Options for the \[Artifact\] section of a `.artifact` Quadlet file. +#[derive(Serialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "PascalCase")] +pub struct Artifact { + /// The artifact to pull from a registry onto the local machine. + #[expect(clippy::struct_field_names, reason = "Quadlet option")] + pub artifact: String, + + /// Path of the authentication file. + pub auth_file: Option, + + /// Use certificates at path (*.crt, *.cert, *.key) to connect to the registry. + pub cert_dir: Option, + + /// The credentials to use when contacting the registry in the format `[username[:password]]`. + pub creds: Option, + + /// The key and optional passphrase to be used for decryption of artifacts. + pub decryption_key: Option, + + /// A list of arguments passed directly to the end of the `podman artifact pull` command in the + /// generated file. + pub podman_args: Option, + + /// Suppress output information when pulling artifacts. + #[serde(skip_serializing_if = "Not::not")] + pub quiet: bool, + + /// Number of times to retry the artifact pull when a HTTP error occurs. + pub retry: Option, + + /// Delay between retries. + pub retry_delay: Option, + + /// Require HTTPS and verification of certificates when contacting registries. + #[serde(rename = "TLSVerify")] + pub tls_verify: Option, +} + +impl HostPaths for Artifact { + fn host_paths(&mut self) -> impl Iterator { + self.auth_file + .iter_mut() + .chain(&mut self.cert_dir) + .chain(self.decryption_key.host_paths()) + } +} + +impl Downgrade for Artifact { + fn downgrade(&mut self, version: PodmanVersion) -> Result<(), DowngradeError> { + if version < PodmanVersion::V5_7 { + return Err(DowngradeError::Kind { + kind: ResourceKind::Artifact, + supported_version: PodmanVersion::V5_7, + }); + } + + Ok(()) + } +} diff --git a/src/quadlet/build.rs b/src/quadlet/build.rs index 6d84772..a45eace 100644 --- a/src/quadlet/build.rs +++ b/src/quadlet/build.rs @@ -30,6 +30,11 @@ pub struct Build { /// Path of the authentication file. pub auth_file: Option, + /// Specifies build arguments and their values. + #[expect(clippy::struct_field_names, reason = "Quadlet option")] + #[serde(serialize_with = "seq_quote_whitespace")] + pub build_arg: Vec, + /// Set network-scoped DNS resolver/nameserver for the build container. #[serde(rename = "DNS")] pub dns: Dns, @@ -56,6 +61,9 @@ pub struct Build { /// Assign additional groups to the primary user running within the container process. pub group_add: Vec, + /// Path to an alternative `.containerignore` file. + pub ignore_file: Option, + /// Specifies the name which is assigned to the resulting image if the build process completes /// successfully. pub image_tag: Vec, @@ -105,6 +113,7 @@ impl HostPaths for Build { self.auth_file .iter_mut() .chain(self.file.host_paths()) + .chain(&mut self.ignore_file) .chain(self.secret.host_paths()) .chain(self.set_working_directory.host_paths()) } @@ -112,6 +121,16 @@ impl HostPaths for Build { impl Downgrade for Build { fn downgrade(&mut self, version: PodmanVersion) -> Result<(), DowngradeError> { + if version < PodmanVersion::V5_7 { + for build_arg in std::mem::take(&mut self.build_arg) { + self.push_arg("build-arg", &build_arg); + } + + if let Some(ignore_file) = self.ignore_file.take() { + self.push_arg("ignorefile", &ignore_file.as_os_str().to_string_lossy()); + } + } + if version < PodmanVersion::V5_5 { if let Some(retry) = self.retry.take() { self.push_arg_display("retry", retry); diff --git a/src/quadlet/container.rs b/src/quadlet/container.rs index 625e0e4..c38e505 100644 --- a/src/quadlet/container.rs +++ b/src/quadlet/container.rs @@ -145,6 +145,11 @@ pub struct Container { /// Sets the host name that is available inside the container. pub host_name: Option, + /// Whether proxy environment variables are passed into the container. + #[serde(skip_serializing_if = "skip_true")] + #[default = true] + pub http_proxy: bool, + /// The image to run in the container. pub image: String, @@ -316,6 +321,10 @@ pub struct Container { impl Downgrade for Container { fn downgrade(&mut self, version: PodmanVersion) -> Result<(), DowngradeError> { + if version < PodmanVersion::V5_7 { + self.remove_v5_7_options(); + } + if version < PodmanVersion::V5_6 { self.remove_v5_6_options()?; } @@ -374,6 +383,14 @@ macro_rules! extract { } impl Container { + /// Remove Quadlet options added in Podman v5.7.0 + fn remove_v5_7_options(&mut self) { + if !self.http_proxy { + self.http_proxy = true; + self.podman_args_push_str("--http-proxy=false"); + } + } + /// Remove Quadlet options added in Podman v5.6.0 fn remove_v5_6_options(&mut self) -> Result<(), DowngradeError> { self.mount.iter().try_for_each(|mount| { diff --git a/src/quadlet/image.rs b/src/quadlet/image.rs index 18e7fc5..20a3863 100644 --- a/src/quadlet/image.rs +++ b/src/quadlet/image.rs @@ -1,6 +1,7 @@ use std::{ convert::Infallible, fmt::{self, Display, Formatter, Write}, + iter, ops::Not, path::PathBuf, str::FromStr, @@ -71,15 +72,10 @@ pub struct Image { impl HostPaths for Image { fn host_paths(&mut self) -> impl Iterator { - let decryption_key = self - .decryption_key - .as_mut() - .map(|decryption_key| &mut decryption_key.key); - self.auth_file .iter_mut() .chain(&mut self.cert_dir) - .chain(decryption_key) + .chain(self.decryption_key.host_paths()) } } @@ -222,3 +218,9 @@ impl Serialize for DecryptionKey { serializer.collect_str(self) } } + +impl HostPaths for DecryptionKey { + fn host_paths(&mut self) -> impl Iterator { + iter::once(&mut self.key) + } +} diff --git a/src/quadlet/kube.rs b/src/quadlet/kube.rs index ae54702..001602d 100644 --- a/src/quadlet/kube.rs +++ b/src/quadlet/kube.rs @@ -40,7 +40,7 @@ pub struct Kube { /// The path, absolute or relative to the location of the unit file, or URL /// to the Kubernetes YAML file to use. - pub yaml: YamlFile, + pub yaml: Vec, } impl Kube { @@ -53,7 +53,7 @@ impl Kube { podman_args: None, publish_port: Vec::new(), user_ns: None, - yaml, + yaml: vec![yaml], } } @@ -66,6 +66,13 @@ impl Kube { impl Downgrade for Kube { fn downgrade(&mut self, version: PodmanVersion) -> Result<(), DowngradeError> { + if version < PodmanVersion::V5_7 && self.yaml.len() > 1 { + return Err(DowngradeError::Multiple { + quadlet_option: "Yaml", + supported_version: PodmanVersion::V5_7, + }); + } + if version < PodmanVersion::V4_7 { for auto_update in std::mem::take(&mut self.auto_update) { self.push_arg("annotation", &auto_update.to_annotation()); @@ -98,7 +105,9 @@ impl Downgrade for Kube { impl HostPaths for Kube { fn host_paths(&mut self) -> impl Iterator { - self.config_map.iter_mut().chain(self.yaml.as_path_mut()) + self.config_map + .iter_mut() + .chain(self.yaml.iter_mut().filter_map(YamlFile::as_path_mut)) } }