-
Notifications
You must be signed in to change notification settings - Fork 27
feat: Add external input support for container encapsulation #652
Changes from 9 commits
f300504
ad24d8c
050916e
92b12be
8c94e05
ff6a6da
33b2606
05f13f8
1061e93
e855c51
dbef2af
f3162f9
cb33dad
3ea0f76
862c1ec
8fda049
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,23 +12,28 @@ use cap_std_ext::cap_std; | |
use cap_std_ext::prelude::CapStdExtDirExt; | ||
use clap::{Parser, Subcommand}; | ||
use fn_error_context::context; | ||
use indexmap::IndexMap; | ||
use io_lifetimes::AsFd; | ||
use ostree::{gio, glib}; | ||
use std::borrow::Cow; | ||
use std::collections::BTreeMap; | ||
use std::ffi::OsString; | ||
use std::fs::File; | ||
use std::io::{BufReader, BufWriter, Write}; | ||
use std::num::NonZeroU32; | ||
use std::path::PathBuf; | ||
use std::process::Command; | ||
use tokio::sync::mpsc::Receiver; | ||
|
||
use crate::chunking::{ObjectMetaSized, ObjectSourceMetaSized}; | ||
use crate::commit::container_commit; | ||
use crate::container::store::{ExportToOCIOpts, ImportProgress, LayerProgress, PreparedImport}; | ||
use crate::container::{self as ostree_container, ManifestDiff}; | ||
use crate::container::{Config, ImageReference, OstreeImageReference}; | ||
use crate::objectsource::ObjectSourceMeta; | ||
use crate::sysroot::SysrootLock; | ||
use ostree_container::store::{ImageImporter, PrepareResult}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
/// Parse an [`OstreeImageReference`] from a CLI arguemnt. | ||
pub fn parse_imgref(s: &str) -> Result<OstreeImageReference> { | ||
|
@@ -165,6 +170,10 @@ pub(crate) enum ContainerOpts { | |
/// Compress at the fastest level (e.g. gzip level 1) | ||
#[clap(long)] | ||
compression_fast: bool, | ||
|
||
/// Path to a JSON-formatted content meta object. | ||
#[clap(long)] | ||
contentmeta: Option<Utf8PathBuf>, | ||
}, | ||
|
||
/// Perform build-time checking and canonicalization. | ||
|
@@ -699,6 +708,19 @@ async fn container_import( | |
Ok(()) | ||
} | ||
|
||
/// Grouping of metadata about an object. | ||
#[derive(Debug, Default, Serialize, Deserialize)] | ||
pub struct RawMeta { | ||
/// When the image was created. Sync it with the io.container.image.created label. | ||
pub created: Option<String>, | ||
/// Top level labels, to be prefixed to the ones with --label | ||
pub labels: Option<BTreeMap<String, String>>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm OK having these here, but it seems like it'd make more sense for them to be separate CLI arguments? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think its valuable to have 2 sources for this, as currently rechunk will also generate the labels and the created tag and harmonize them. Since it pulls the rpm database it can do fancy stuff such as variable substitution. So this acts as a way of passing them through the 2 commands cleanly. Having to refeed them into arguments would be hell. I expect if anyone else tried to extend this they would agree. Here is how the action example for the layers looks right now: CLI may override the file. |
||
/// ContentId to layer annotation | ||
pub layers: IndexMap<String, String>, | ||
/// OSTree hash to layer ContentId | ||
pub mapping: IndexMap<String, String>, | ||
antheas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/// Export a container image with an encapsulated ostree commit. | ||
#[allow(clippy::too_many_arguments)] | ||
async fn container_export( | ||
|
@@ -712,22 +734,75 @@ async fn container_export( | |
container_config: Option<Utf8PathBuf>, | ||
cmd: Option<Vec<String>>, | ||
compression_fast: bool, | ||
contentmeta: Option<Utf8PathBuf>, | ||
) -> Result<()> { | ||
let config = Config { | ||
labels: Some(labels), | ||
cmd, | ||
}; | ||
let container_config = if let Some(container_config) = container_config { | ||
serde_json::from_reader(File::open(container_config).map(BufReader::new)?)? | ||
} else { | ||
None | ||
}; | ||
|
||
let mut contentmeta_data = None; | ||
let mut created = None; | ||
let mut labels = labels.clone(); | ||
if let Some(contentmeta) = contentmeta { | ||
let raw: Option<RawMeta> = | ||
antheas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
serde_json::from_reader(File::open(contentmeta).map(BufReader::new)?)?; | ||
if let Some(raw) = raw { | ||
created = raw.created; | ||
|
||
contentmeta_data = Some(ObjectMetaSized { | ||
map: raw | ||
.mapping | ||
.into_iter() | ||
.map(|(k, v)| (k.into(), v.into())) | ||
.collect(), | ||
sizes: raw | ||
.layers | ||
.into_iter() | ||
.map(|(k, v)| ObjectSourceMetaSized { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't we just directly parse this data from the input JSON? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of this fork only reuses ostree-rs-ext as an exporter. If we wanted to use it with its grouping algorithm, it would make sense to do it that. Right now this section is fighting with the existing code to make it work as an exporter and by exposing a cleaner API for the json file. |
||
meta: ObjectSourceMeta { | ||
identifier: k.clone().into(), | ||
name: v.into(), | ||
srcid: k.clone().into(), | ||
change_frequency: if k == "unpackaged" { std::u32::MAX } else { 1 }, | ||
change_time_offset: 1, | ||
}, | ||
size: 1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Size of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This section is what was required to make it work without restructuring the code. A proper implementation would remove this, incl. the dangling "Reserved for New Packages" layer at the end. |
||
}) | ||
.collect(), | ||
}); | ||
// Allow --label to override labels from the content metadata | ||
if let Some(raw_labels) = raw.labels { | ||
labels = raw_labels.into_iter().chain(labels.into_iter()).collect(); | ||
}; | ||
antheas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
anyhow::bail!("Content metadata must be a JSON object") | ||
} | ||
} | ||
|
||
// Use enough layers so that each package ends in its own layer | ||
// while respecting the layer ordering. | ||
let max_layers = if let Some(contentmeta_data) = &contentmeta_data { | ||
NonZeroU32::new((contentmeta_data.sizes.len() + 1).try_into().unwrap()) | ||
} else { | ||
None | ||
}; | ||
|
||
let config = Config { | ||
labels: Some(labels), | ||
cmd, | ||
}; | ||
|
||
let opts = crate::container::ExportOpts { | ||
copy_meta_keys, | ||
copy_meta_opt_keys, | ||
container_config, | ||
authfile, | ||
skip_compression: compression_fast, // TODO rename this in the struct at the next semver break | ||
contentmeta: contentmeta_data.as_ref(), | ||
max_layers, | ||
created, | ||
..Default::default() | ||
}; | ||
let pushed = crate::container::encapsulate(repo, rev, &config, Some(opts), imgref).await?; | ||
|
@@ -958,6 +1033,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> { | |
config, | ||
cmd, | ||
compression_fast, | ||
contentmeta, | ||
} => { | ||
let labels: Result<BTreeMap<_, _>> = labels | ||
.into_iter() | ||
|
@@ -980,6 +1056,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> { | |
config, | ||
cmd, | ||
compression_fast, | ||
contentmeta, | ||
) | ||
.await | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah...you have changes to that too!
The bump to 0.2 is in #653 at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind doing (again) at least a draft PR to the repo with hhd-dev/ocidir-rs@9f6095c and provide a bit of reproducer instructions around how skopeo is broken?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh I see it's in the PR text:
Hmmmm...ok. I will look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://docs.rs/olpc-cjson/latest/olpc_cjson/
Hopefully this helps
Only occurs if \n is included in a label. It would have been nice to generate fancy descriptions. Although the only place that reads them (ghcr) omits \n so new lines are not shown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, right thanks. This is probably then best tracked at containers/ocidir-rs#10 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, since I do not think my fork is a proper fix it did not make sense to PR it.