Skip to content
Merged
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "podlet"
version = "0.3.2-alpha.4"
version = "0.3.2-alpha.5"
authors = ["Paul Nettleton <k9@k9withabone.dev>"]
edition = "2024"
rust-version = "1.85"
Expand Down
15 changes: 14 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod artifact;
mod build;
mod compose;
mod container;
Expand Down Expand Up @@ -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,
};
Expand Down Expand Up @@ -623,6 +624,16 @@ enum PodmanCommands {
#[command(subcommand)]
image: Box<Image>,
},

/// 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<PodmanCommands> for quadlet::Resource {
Expand All @@ -635,6 +646,7 @@ impl From<PodmanCommands> for quadlet::Resource {
PodmanCommands::Volume { volume } => volume.into(),
PodmanCommands::Build { build } => (*build).into(),
PodmanCommands::Image { image } => (*image).into(),
PodmanCommands::Artifact { artifact } => artifact.into(),
}
}
}
Expand Down Expand Up @@ -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(),
}
}
}
Expand Down
138 changes: 138 additions & 0 deletions src/cli/artifact.rs
Original file line number Diff line number Diff line change
@@ -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<Artifact> for quadlet::Artifact {
fn from(value: Artifact) -> Self {
let Artifact::Pull { pull } = value;
pull.into()
}
}

impl From<Artifact> 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<PathBuf>,

/// 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<PathBuf>,

/// 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<String>,

/// 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<DecryptionKey>,

/// 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<u64>,

/// 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<String>,

/// 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<bool>,

/// The location from which the artifact image is obtained.
pub source: String,
}

impl From<Pull> 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,
}
}
}
31 changes: 20 additions & 11 deletions src/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ pub struct Build {
#[arg(long, value_name = "PATH")]
authfile: Option<PathBuf>,

/// 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<String>,

/// Set custom DNS servers.
///
/// Converts to "DNS=IP_ADDRESS".
Expand Down Expand Up @@ -116,6 +125,12 @@ pub struct Build {
#[arg(long, value_name = "GROUP | keep-groups")]
group_add: Vec<String>,

/// Path to an alternative `.containerignore` file.
///
/// Converts to "IgnoreFile=PATH".
#[arg(long, value_name = "PATH")]
ignorefile: Option<PathBuf>,

/// The name assigned to the resulting image if the build process completes successfully.
///
/// Converts to "ImageTag=IMAGE_NAME".
Expand Down Expand Up @@ -226,13 +241,15 @@ impl From<Build> for quadlet::Build {
annotation,
arch,
authfile,
build_arg,
dns,
dns_option,
dns_search,
env,
file,
force_rm,
group_add,
ignorefile,
tag,
label,
network,
Expand All @@ -254,13 +271,15 @@ impl From<Build> for quadlet::Build {
annotation,
arch,
auth_file: authfile,
build_arg,
dns: dns.into(),
dns_option,
dns_search,
environment: env,
file,
force_rm,
group_add,
ignore_file: ignorefile,
image_tag: tag,
label,
network,
Expand Down Expand Up @@ -338,7 +357,6 @@ impl TryFrom<service::Build> 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}"))
Expand Down Expand Up @@ -386,6 +404,7 @@ impl TryFrom<service::Build> 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(),
Expand Down Expand Up @@ -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<String>,

/// Specifies a file containing lines of build arguments of the form `arg=value`.
///
/// Can be specified multiple times.
Expand Down Expand Up @@ -587,10 +600,6 @@ struct PodmanArgs {
#[default = true]
identity_label: bool,

/// Path to an alternative `.containerignore` file.
#[arg(long, value_name = "PATH")]
ignorefile: Option<PathBuf>,

/// Write the built image's ID to a file.
#[arg(long, value_name = "IMAGE_ID_FILE")]
iidfile: Option<PathBuf>,
Expand Down
14 changes: 8 additions & 6 deletions src/cli/container/podman.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ pub struct PodmanArgs {
#[arg(long, value_name = "DEVICE:WEIGHT")]
blkio_weight_device: Vec<String>,

/// Use certificates in the specified directory to connect to the registry
#[arg(long, value_name = "PATH")]
cert_dir: Option<PathBuf>,

/// Specify the cgroup file to write to and its value
///
/// Can be specified multiple times
Expand Down Expand Up @@ -110,6 +114,10 @@ pub struct PodmanArgs {
#[arg(long, value_name = "NODES")]
cpuset_mems: Option<String>,

/// The username and/or password to use to authenticate with the registry, if required
#[arg(long, value_name = "[USERNAME][:PASSWORD]")]
creds: Option<String>,

/// Key needed to decrypt the image
#[arg(long, value_name = "KEY[:PASSPHRASE]")]
decryption_key: Option<String>,
Expand Down Expand Up @@ -184,12 +192,6 @@ pub struct PodmanArgs {
#[arg(long, value_name = "NAME")]
hostuser: Vec<String>,

/// 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<String>,
Expand Down
9 changes: 9 additions & 0 deletions src/cli/container/quadlet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,13 @@ pub struct QuadletOptions {
#[arg(short, long, value_name = "NAME")]
hostname: Option<String>,

/// 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"
Expand Down Expand Up @@ -564,6 +571,7 @@ impl From<QuadletOptions> for crate::quadlet::Container {
health_startup_timeout,
health_timeout,
hostname: host_name,
http_proxy,
ip,
ip6,
mut label,
Expand Down Expand Up @@ -646,6 +654,7 @@ impl From<QuadletOptions> for crate::quadlet::Container {
health_startup_timeout,
health_timeout,
host_name,
http_proxy,
ip,
ip6,
label,
Expand Down
Loading
Loading