Skip to content
This repository has been archived by the owner on Nov 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #586 from cgwalters/container-export-prep
Browse files Browse the repository at this point in the history
container: Switch to cap-std when exporting to oci
  • Loading branch information
cgwalters authored Jan 23, 2024
2 parents 0665555 + b104a46 commit 3681ac3
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 26 deletions.
51 changes: 30 additions & 21 deletions lib/src/container/encapsulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::chunking::{Chunk, Chunking, ObjectMetaSized};
use crate::container::skopeo;
use crate::tar as ostree_tar;
use anyhow::{anyhow, Context, Result};
use camino::Utf8Path;
use cap_std::fs::Dir;
use cap_std_ext::cap_std;
use chrono::NaiveDateTime;
Expand All @@ -19,7 +20,6 @@ use ostree::gio;
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
use std::num::NonZeroU32;
use std::path::Path;
use tracing::instrument;

/// The label which may be used in addition to the standard OCI label.
Expand Down Expand Up @@ -160,17 +160,11 @@ fn export_chunked(
fn build_oci(
repo: &ostree::Repo,
rev: &str,
ocidir_path: &Path,
writer: &mut OciDir,
tag: Option<&str>,
config: &Config,
opts: ExportOpts,
) -> Result<ImageReference> {
if !ocidir_path.exists() {
std::fs::create_dir(ocidir_path).context("Creating OCI dir")?;
}
let ocidir = Dir::open_ambient_dir(ocidir_path, cap_std::ambient_authority())?;
let mut writer = ocidir::OciDir::ensure(&ocidir)?;

) -> Result<()> {
let commit = repo.require_rev(rev)?;
let commit = commit.as_str();
let (commit_v, _) = repo.load_commit(commit)?;
Expand Down Expand Up @@ -246,7 +240,7 @@ fn build_oci(
export_chunked(
repo,
commit,
&mut writer,
writer,
&mut manifest,
&mut imgcfg,
labels,
Expand Down Expand Up @@ -276,10 +270,7 @@ fn build_oci(
writer.replace_with_single_manifest(manifest, platform)?;
}

Ok(ImageReference {
transport: Transport::OciDir,
name: ocidir_path.to_str().unwrap().to_string(),
})
Ok(())
}

/// Interpret a filesystem path as optionally including a tag. Paths
Expand Down Expand Up @@ -308,19 +299,37 @@ async fn build_impl(
let digest = if dest.transport == Transport::OciDir {
let (path, tag) = parse_oci_path_and_tag(dest.name.as_str());
tracing::debug!("using OCI path={path} tag={tag:?}");
let _copied: ImageReference =
build_oci(repo, ostree_ref, Path::new(path), tag, config, opts)?;
if !Utf8Path::new(path).exists() {
std::fs::create_dir(path)?;
}
let ocidir = Dir::open_ambient_dir(path, cap_std::ambient_authority())?;
let mut ocidir = OciDir::ensure(&ocidir)?;
build_oci(repo, ostree_ref, &mut ocidir, tag, config, opts)?;
None
} else {
let tempdir = tempfile::tempdir_in("/var/tmp")?;
let tempdest = tempdir.path().join("d");
let tempdest = tempdest.to_str().unwrap();
let tempdir = {
let vartmp = Dir::open_ambient_dir("/var/tmp", cap_std::ambient_authority())?;
cap_std_ext::cap_tempfile::tempdir_in(&vartmp)?
};
let mut ocidir = OciDir::ensure(&tempdir)?;

// Minor TODO: refactor to avoid clone
let authfile = opts.authfile.clone();
let tempoci = build_oci(repo, ostree_ref, Path::new(tempdest), None, config, opts)?;
build_oci(repo, ostree_ref, &mut ocidir, None, config, opts)?;
drop(ocidir);

let digest = skopeo::copy(&tempoci, dest, authfile.as_deref()).await?;
// Pass the temporary oci directory as the current working directory for the skopeo process
let tempoci = ImageReference {
transport: Transport::OciDir,
name: ".".into(),
};
let digest = skopeo::copy(
&tempoci,
dest,
authfile.as_deref(),
Some(tempdir.try_clone()?),
)
.await?;
Some(digest)
};
if let Some(digest) = digest {
Expand Down
1 change: 1 addition & 0 deletions lib/src/container/ocidir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ pub fn new_empty_manifest() -> oci_image::ImageManifestBuilder {
impl OciDir {
/// Open the OCI directory at the target path; if it does not already
/// have the standard OCI metadata, it is created.
#[context("Opening OCI dir")]
pub fn ensure(dir: &Dir) -> Result<Self> {
let mut db = cap_std::fs::DirBuilder::new();
db.recursive(true).mode(0o755);
Expand Down
15 changes: 12 additions & 3 deletions lib/src/container/skopeo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
use super::ImageReference;
use anyhow::{Context, Result};
use cap_std_ext::cap_std::fs::Dir;
use cap_std_ext::cmdext::CapStdExtCommandExt;
use fn_error_context::context;
use serde::Deserialize;
use std::io::Read;
use std::path::Path;
Expand Down Expand Up @@ -45,10 +48,9 @@ pub(crate) fn container_policy_is_default_insecure() -> Result<bool> {
}

/// Create a Command builder for skopeo.
pub(crate) fn new_cmd() -> tokio::process::Command {
let mut cmd = Command::new("skopeo");
pub(crate) fn new_cmd() -> std::process::Command {
let mut cmd = std::process::Command::new("skopeo");
cmd.stdin(Stdio::null());
cmd.kill_on_drop(true);
cmd
}

Expand All @@ -59,21 +61,28 @@ pub(crate) fn spawn(mut cmd: Command) -> Result<tokio::process::Child> {
}

/// Use skopeo to copy a container image.
#[context("Skopeo copy")]
pub(crate) async fn copy(
src: &ImageReference,
dest: &ImageReference,
authfile: Option<&Path>,
cwd: Option<Dir>,
) -> Result<String> {
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(cwd) = cwd {
cmd.cwd_dir(cwd);
}
if let Some(authfile) = authfile {
cmd.arg("--authfile");
cmd.arg(authfile);
}
cmd.args(&[src.to_string(), dest.to_string()]);
let mut cmd = tokio::process::Command::from(cmd);
cmd.kill_on_drop(true);
let proc = super::skopeo::spawn(cmd)?;
let output = proc.wait_with_output().await?;
if !output.status.success() {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/container/update_detachedmeta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, None)
let pulled_digest: String = skopeo::copy(src, &tempsrc_ref, None, None)
.await
.context("Creating temporary copy to OCI dir")?;

Expand Down Expand Up @@ -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, None)
crate::container::skopeo::copy(&tempsrc_ref, dest, None, None)
.await
.context("Copying to destination")
}

0 comments on commit 3681ac3

Please sign in to comment.