Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

container/encapsulate: Support copying commit metadata keys #234

Merged
merged 1 commit into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ enum ContainerOpts {
#[structopt(name = "label", long, short)]
labels: Vec<String>,

/// Propagate an OSTree commit metadata key to container label
#[structopt(name = "copymeta", long)]
copy_meta_keys: Vec<String>,

/// Corresponds to the Dockerfile `CMD` instruction.
#[structopt(long)]
cmd: Option<Vec<String>>,
Expand Down Expand Up @@ -365,15 +369,19 @@ async fn container_export(
rev: &str,
imgref: &ImageReference,
labels: BTreeMap<String, String>,
copy_meta_keys: Vec<String>,
cmd: Option<Vec<String>>,
) -> Result<()> {
let repo = &ostree::Repo::open_at(libc::AT_FDCWD, repo, gio::NONE_CANCELLABLE)?;
let config = Config {
labels: Some(labels),
cmd,
};
let opts = Some(Default::default());
let pushed = crate::container::encapsulate(repo, rev, &config, opts, imgref).await?;
let opts = crate::container::ExportOpts {
copy_meta_keys,
..Default::default()
};
let pushed = crate::container::encapsulate(repo, rev, &config, Some(opts), imgref).await?;
println!("{}", pushed);
Ok(())
}
Expand Down Expand Up @@ -492,6 +500,7 @@ where
rev,
imgref,
labels,
copy_meta_keys,
cmd,
} => {
let labels: Result<BTreeMap<_, _>> = labels
Expand All @@ -503,7 +512,7 @@ where
Ok((k.to_string(), v.to_string()))
})
.collect();
container_export(&repo, &rev, &imgref, labels?, cmd).await
container_export(&repo, &rev, &imgref, labels?, copy_meta_keys, cmd).await
}
ContainerOpts::Image(opts) => match opts {
ContainerImageOpts::List { repo } => {
Expand Down
39 changes: 36 additions & 3 deletions lib/src/container/encapsulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use super::{ocidir, OstreeImageReference, Transport};
use super::{ImageReference, SignatureSource, OSTREE_COMMIT_LABEL};
use crate::container::skopeo;
use crate::tar as ostree_tar;
use anyhow::Context;
use anyhow::Result;
use anyhow::{anyhow, Context, Result};
use fn_error_context::context;
use gio::glib;
use oci_spec::image as oci_image;
Expand All @@ -22,7 +21,6 @@ use tracing::{instrument, Level};
/// schema, it's not actually useful today. But, we keep it
/// out of principle.
const BLOB_OSTREE_ANNOTATION: &str = "ostree.encapsulated";

/// Configuration for the generated container.
#[derive(Debug, Default)]
pub struct Config {
Expand All @@ -46,6 +44,32 @@ fn export_ostree_ref(
w.complete()
}

fn commit_meta_to_labels<'a>(
meta: &glib::VariantDict,
keys: impl IntoIterator<Item = &'a str>,
labels: &mut HashMap<String, String>,
) -> Result<()> {
for k in keys {
let v = meta
.lookup::<String>(k)
.context("Expected string for commit metadata value")?
.ok_or_else(|| anyhow!("Could not find commit metadata key: {}", k))?;
labels.insert(k.to_string(), v);
}
// Copy standard metadata keys `ostree.bootable` and `ostree.linux`.
// Bootable is an odd one out in being a boolean.
if let Some(v) = meta.lookup::<bool>(*ostree::METADATA_KEY_BOOTABLE)? {
labels.insert(ostree::METADATA_KEY_BOOTABLE.to_string(), v.to_string());
}
// Handle any other string-typed values here.
for k in &[&ostree::METADATA_KEY_LINUX] {
if let Some(v) = meta.lookup::<String>(k)? {
labels.insert(k.to_string(), v);
}
}
Ok(())
}

/// Generate an OCI image from a given ostree root
#[context("Building oci")]
fn build_oci(
Expand Down Expand Up @@ -76,6 +100,13 @@ fn build_oci(
let mut ctrcfg = oci_image::Config::default();
let mut imgcfg = oci_image::ImageConfiguration::default();
let labels = ctrcfg.labels_mut().get_or_insert_with(Default::default);

commit_meta_to_labels(
&commit_meta,
opts.copy_meta_keys.iter().map(|k| k.as_str()),
labels,
)?;

let mut manifest = ocidir::new_empty_manifest().build().unwrap();

if let Some(version) =
Expand Down Expand Up @@ -198,6 +229,8 @@ async fn build_impl(
pub struct ExportOpts {
/// If true, perform gzip compression of the tar layers.
pub compress: bool,
/// A set of commit metadata keys to copy as image labels.
pub copy_meta_keys: Vec<String>,
}

/// Given an OSTree repository and ref, generate a container image.
Expand Down
13 changes: 10 additions & 3 deletions lib/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ fn generate_test_repo(dir: &Utf8Path) -> Result<Utf8PathBuf> {
indoc! {"
cd {dir}
ostree --repo=repo init --mode=archive
ostree --repo=repo commit -b {testref} --bootable --no-bindings --add-metadata-string=version=42.0 --gpg-homedir={gpghome} --gpg-sign={keyid} \
--add-detached-metadata-string=my-detached-key=my-detached-value --tree=tar=exampleos.tar.zst >/dev/null
ostree --repo=repo commit -b {testref} --bootable --no-bindings --add-metadata-string=version=42.0 --add-metadata-string=buildsys.checksum=41af286dc0b172ed2f1ca934fd2278de4a1192302ffa07087cea2682e7d372e3 --gpg-homedir={gpghome} --gpg-sign={keyid} \
--add-detached-metadata-string=my-detached-key=my-detached-value --tree=tar=exampleos.tar.zst >/dev/null
ostree --repo=repo show {testref} >/dev/null
"},
testref = TESTREF,
Expand Down Expand Up @@ -464,11 +464,15 @@ async fn test_container_import_export() -> Result<()> {
),
cmd: Some(vec!["/bin/bash".to_string()]),
};
let opts = ostree_ext::container::ExportOpts {
copy_meta_keys: vec!["buildsys.checksum".to_string()],
..Default::default()
};
let digest = ostree_ext::container::encapsulate(
&fixture.srcrepo,
TESTREF,
&config,
None,
Some(opts),
&srcoci_imgref,
)
.await
Expand All @@ -479,6 +483,9 @@ async fn test_container_import_export() -> Result<()> {
assert!(inspect.contains(r#""version": "42.0""#));
assert!(inspect.contains(r#""foo": "bar""#));
assert!(inspect.contains(r#""test": "value""#));
assert!(inspect.contains(
r#""buildsys.checksum": "41af286dc0b172ed2f1ca934fd2278de4a1192302ffa07087cea2682e7d372e3""#
));

let srcoci_unverified = OstreeImageReference {
sigverify: SignatureSource::ContainerPolicyAllowInsecure,
Expand Down