diff --git a/lib/src/cli.rs b/lib/src/cli.rs index c1513502..1cf30d68 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -129,6 +129,10 @@ pub(crate) enum ContainerOpts { #[clap(name = "label", long, short)] labels: Vec, + #[clap(long)] + /// Path to Docker-formatted authentication file. + authfile: Option, + /// Propagate an OSTree commit metadata key to container label #[clap(name = "copymeta", long)] copy_meta_keys: Vec, @@ -624,6 +628,7 @@ async fn container_export( rev: &str, imgref: &ImageReference, labels: BTreeMap, + authfile: Option, copy_meta_keys: Vec, copy_meta_opt_keys: Vec, cmd: Option>, @@ -636,6 +641,7 @@ async fn container_export( let opts = crate::container::ExportOpts { copy_meta_keys, copy_meta_opt_keys, + authfile, skip_compression: compression_fast, // TODO rename this in the struct at the next semver break ..Default::default() }; @@ -847,6 +853,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> { rev, imgref, labels, + authfile, copy_meta_keys, copy_meta_opt_keys, cmd, @@ -867,6 +874,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> { &rev, &imgref, labels?, + authfile, copy_meta_keys, copy_meta_opt_keys, cmd, diff --git a/lib/src/container/encapsulate.rs b/lib/src/container/encapsulate.rs index 6b6347c1..ac4dd08a 100644 --- a/lib/src/container/encapsulate.rs +++ b/lib/src/container/encapsulate.rs @@ -348,6 +348,8 @@ async fn build_impl( let tempdest = tempdir.path().join("d"); let tempdest = tempdest.to_str().unwrap(); + // Minor TODO: refactor to avoid clone + let authfile = opts.authfile.clone(); let tempoci = build_oci( repo, ostree_ref, @@ -359,7 +361,7 @@ async fn build_impl( contentmeta, )?; - let digest = skopeo::copy(&tempoci, dest).await?; + let digest = skopeo::copy(&tempoci, dest, authfile.as_deref()).await?; Some(digest) }; if let Some(digest) = digest { @@ -377,7 +379,7 @@ async fn build_impl( } /// Options controlling commit export into OCI -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct ExportOpts { /// If true, do not perform gzip compression of the tar layers. pub skip_compression: bool, @@ -387,6 +389,8 @@ pub struct ExportOpts { pub copy_meta_opt_keys: Vec, /// Maximum number of layers to use pub max_layers: Option, + /// Path to Docker-formatted authentication file. + pub authfile: Option, // TODO semver-break: remove this /// Use only the standard OCI version label pub no_legacy_version_label: bool, diff --git a/lib/src/container/skopeo.rs b/lib/src/container/skopeo.rs index 2ae9210c..9aca1a41 100644 --- a/lib/src/container/skopeo.rs +++ b/lib/src/container/skopeo.rs @@ -4,6 +4,7 @@ use super::ImageReference; use anyhow::{Context, Result}; use serde::Deserialize; use std::io::Read; +use std::path::Path; use std::process::Stdio; use tokio::process::Command; @@ -58,12 +59,16 @@ pub(crate) fn spawn(mut cmd: Command) -> Result { } /// Use skopeo to copy a container image. -pub(crate) async fn copy(src: &ImageReference, dest: &ImageReference) -> Result { +pub(crate) async fn copy(src: &ImageReference, dest: &ImageReference, authfile: Option<&Path>) -> Result { let digestfile = tempfile::NamedTempFile::new()?; let mut cmd = new_cmd(); cmd.stdout(std::process::Stdio::null()).arg("copy"); cmd.arg("--digestfile"); cmd.arg(digestfile.path()); + if let Some(authfile) = authfile { + cmd.arg("--authfile"); + cmd.arg(authfile); + } cmd.args(&[src.to_string(), dest.to_string()]); let proc = super::skopeo::spawn(cmd)?; let output = proc.wait_with_output().await?; diff --git a/lib/src/container/update_detachedmeta.rs b/lib/src/container/update_detachedmeta.rs index 0e7eba80..0b07b8e1 100644 --- a/lib/src/container/update_detachedmeta.rs +++ b/lib/src/container/update_detachedmeta.rs @@ -29,7 +29,7 @@ pub async fn update_detached_metadata( }; // Full copy of the source image - let pulled_digest: String = skopeo::copy(src, &tempsrc_ref) + let pulled_digest: String = skopeo::copy(src, &tempsrc_ref, None) .await .context("Creating temporary copy to OCI dir")?; @@ -124,7 +124,7 @@ pub async fn update_detached_metadata( // Finally, copy the mutated image back to the target. For chunked images, // because we only changed one layer, skopeo should know not to re-upload shared blobs. - crate::container::skopeo::copy(&tempsrc_ref, dest) + crate::container::skopeo::copy(&tempsrc_ref, dest, None) .await .context("Copying to destination") }