Skip to content

Commit bcd318f

Browse files
committed
ostree-ext/store: don't include filtered files in metadata
Filtered files are only determined at the time we import a layer. So if that layer is already imported, we won't have that information available. That in turn means that the metadata is state-dependent, which in turn means that the commit digest is not reproducible. We still want to provide the filtered files warning though. Just make this information part of the LayeredImageState object instead. The obvious downside of that is that now we only get that warning the first time the layer is imported and it's no longer part of the commit object itself. One way to make this more sticky is to attach it to the individual layers' commits instead, and then the merge commit can coalesce them. Related: #1346
1 parent 7bae37f commit bcd318f

File tree

6 files changed

+66
-71
lines changed

6 files changed

+66
-71
lines changed

lib/src/deploy.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,7 @@ pub(crate) async fn prepare_for_pull(
376376

377377
#[context("Pulling")]
378378
pub(crate) async fn pull_from_prepared(
379-
repo: &ostree::Repo,
380379
imgref: &ImageReference,
381-
target_imgref: Option<&OstreeImageReference>,
382380
quiet: bool,
383381
prog: ProgressWriter,
384382
mut prepared_image: PreparedImportMeta,
@@ -424,11 +422,9 @@ pub(crate) async fn pull_from_prepared(
424422
let import = import?;
425423
let imgref_canonicalized = imgref.clone().canonicalize()?;
426424
tracing::debug!("Canonicalized image reference: {imgref_canonicalized:#}");
427-
let ostree_imgref = &OstreeImageReference::from(imgref_canonicalized);
428-
let wrote_imgref = target_imgref.as_ref().unwrap_or(&ostree_imgref);
429425

430426
if let Some(msg) =
431-
ostree_container::store::image_filtered_content_warning(repo, &wrote_imgref.imgref)
427+
ostree_container::store::image_filtered_content_warning(&import.filtered_files)
432428
.context("Image content warning")?
433429
{
434430
crate::journal::journal_print(libsystemd::logging::Priority::Notice, &msg);
@@ -446,15 +442,9 @@ pub(crate) async fn pull(
446442
) -> Result<Box<ImageState>> {
447443
match prepare_for_pull(repo, imgref, target_imgref).await? {
448444
PreparedPullResult::AlreadyPresent(existing) => Ok(existing),
449-
PreparedPullResult::Ready(prepared_image_meta) => Ok(pull_from_prepared(
450-
repo,
451-
imgref,
452-
target_imgref,
453-
quiet,
454-
prog,
455-
prepared_image_meta,
456-
)
457-
.await?),
445+
PreparedPullResult::Ready(prepared_image_meta) => {
446+
Ok(pull_from_prepared(imgref, quiet, prog, prepared_image_meta).await?)
447+
}
458448
}
459449
}
460450

lib/src/install.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -793,22 +793,15 @@ async fn install_container(
793793
let repo = &sysroot.repo();
794794
repo.set_disable_fsync(true);
795795

796-
let pulled_image =
797-
match prepare_for_pull(repo, &spec_imgref, Some(&state.target_imgref)).await? {
798-
PreparedPullResult::AlreadyPresent(existing) => existing,
799-
PreparedPullResult::Ready(image_meta) => {
800-
check_disk_space(root_setup.physical_root.as_fd(), &image_meta, &spec_imgref)?;
801-
pull_from_prepared(
802-
repo,
803-
&spec_imgref,
804-
Some(&state.target_imgref),
805-
false,
806-
ProgressWriter::default(),
807-
image_meta,
808-
)
809-
.await?
810-
}
811-
};
796+
let pulled_image = match prepare_for_pull(repo, &spec_imgref, Some(&state.target_imgref))
797+
.await?
798+
{
799+
PreparedPullResult::AlreadyPresent(existing) => existing,
800+
PreparedPullResult::Ready(image_meta) => {
801+
check_disk_space(root_setup.physical_root.as_fd(), &image_meta, &spec_imgref)?;
802+
pull_from_prepared(&spec_imgref, false, ProgressWriter::default(), image_meta).await?
803+
}
804+
};
812805

813806
repo.set_disable_fsync(false);
814807

ostree-ext/src/cli.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ async fn container_store(
906906
}
907907
let import = import?;
908908
if let Some(msg) =
909-
ostree_container::store::image_filtered_content_warning(repo, &imgref.imgref)?
909+
ostree_container::store::image_filtered_content_warning(&import.filtered_files)?
910910
{
911911
eprintln!("{msg}")
912912
}
@@ -1263,7 +1263,6 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
12631263
ostree::Sysroot::new_default()
12641264
};
12651265
sysroot.load(gio::Cancellable::NONE)?;
1266-
let repo = &sysroot.repo();
12671266
let kargs = karg.as_deref();
12681267
let kargs = kargs.map(|v| {
12691268
let r: Vec<_> = v.iter().map(|s| s.as_str()).collect();
@@ -1321,10 +1320,8 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
13211320
Some(options),
13221321
)
13231322
.await?;
1324-
let wrote_imgref = target_imgref.as_ref().unwrap_or(&imgref);
13251323
if let Some(msg) = ostree_container::store::image_filtered_content_warning(
1326-
repo,
1327-
&wrote_imgref.imgref,
1324+
&state.filtered_files,
13281325
)? {
13291326
eprintln!("{msg}")
13301327
}

ostree-ext/src/container/store.rs

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ pub(crate) const META_MANIFEST_DIGEST: &str = "ostree.manifest-digest";
5858
const META_MANIFEST: &str = "ostree.manifest";
5959
/// The key injected into the merge commit with the image configuration serialized as JSON.
6060
const META_CONFIG: &str = "ostree.container.image-config";
61-
/// Value of type `a{sa{su}}` containing number of filtered out files
62-
pub const META_FILTERED: &str = "ostree.tar-filtered";
63-
/// The type used to store content filtering information with `META_FILTERED`.
61+
/// The type used to store content filtering information.
6462
pub type MetaFilteredData = HashMap<String, HashMap<String, u32>>;
6563

6664
/// The ref prefixes which point to ostree deployments. (TODO: Add an official API for this)
@@ -138,6 +136,8 @@ pub struct LayeredImageState {
138136
/// in the future we should probably instead just proxy a signature object
139137
/// instead, but this is sufficient for now.
140138
pub verify_text: Option<String>,
139+
/// Files that were filtered out during the import.
140+
pub filtered_files: Option<MetaFilteredData>,
141141
}
142142

143143
impl LayeredImageState {
@@ -969,7 +969,7 @@ impl ImageImporter {
969969
let ostree_ref = ref_for_image(&target_imgref.imgref)?;
970970

971971
let mut layer_commits = Vec::new();
972-
let mut layer_filtered_content: MetaFilteredData = HashMap::new();
972+
let mut layer_filtered_content: Option<MetaFilteredData> = None;
973973
let have_derived_layers = !import.layers.is_empty();
974974
tracing::debug!("Processing layers: {}", import.layers.len());
975975
for layer in import.layers {
@@ -1024,7 +1024,9 @@ impl ImageImporter {
10241024
write!(msg, "...and {n_rest} more").unwrap();
10251025
}
10261026
tracing::debug!("Found filtered toplevels: {msg}");
1027-
layer_filtered_content.insert(layer.layer.digest().to_string(), filtered_owned);
1027+
layer_filtered_content
1028+
.get_or_insert_default()
1029+
.insert(layer.layer.digest().to_string(), filtered_owned);
10281030
} else {
10291031
tracing::debug!("No filtered content");
10301032
}
@@ -1064,8 +1066,6 @@ impl ImageImporter {
10641066
"ostree.importer.version",
10651067
env!("CARGO_PKG_VERSION").to_variant(),
10661068
);
1067-
let filtered = layer_filtered_content.to_variant();
1068-
metadata.insert(META_FILTERED, filtered);
10691069
let metadata = metadata.to_variant();
10701070

10711071
let timestamp = timestamp_of_manifest_or_config(&import.manifest, &import.config)
@@ -1191,6 +1191,7 @@ impl ImageImporter {
11911191
.await?;
11921192
// We can at least avoid re-verifying the base commit.
11931193
state.verify_text = import.verify_text;
1194+
state.filtered_files = layer_filtered_content;
11941195
Ok(state)
11951196
}
11961197
}
@@ -1354,6 +1355,7 @@ pub fn query_image_commit(repo: &ostree::Repo, commit: &str) -> Result<Box<Layer
13541355
cached_update,
13551356
// we can't cross-reference with a remote here
13561357
verify_text: None,
1358+
filtered_files: None,
13571359
});
13581360
tracing::debug!("Wrote merge commit {}", state.merge_commit);
13591361
Ok(state)
@@ -1726,36 +1728,26 @@ pub fn count_layer_references(repo: &ostree::Repo) -> Result<u32> {
17261728
Ok(n as u32)
17271729
}
17281730

1729-
/// Given an image, if it has any non-ostree compatible content, return a suitable
1730-
/// warning message.
1731+
/// Generate a suitable warning message from given list of filtered files, if any.
17311732
pub fn image_filtered_content_warning(
1732-
repo: &ostree::Repo,
1733-
image: &ImageReference,
1733+
filtered_files: &Option<MetaFilteredData>,
17341734
) -> Result<Option<String>> {
17351735
use std::fmt::Write;
17361736

1737-
let ostree_ref = ref_for_image(image)?;
1738-
let rev = repo.require_rev(&ostree_ref)?;
1739-
let commit_obj = repo.load_commit(rev.as_str())?.0;
1740-
let commit_meta = &glib::VariantDict::new(Some(&commit_obj.child_value(0)));
1741-
1742-
let r = commit_meta
1743-
.lookup::<MetaFilteredData>(META_FILTERED)?
1744-
.filter(|v| !v.is_empty())
1745-
.map(|v| {
1746-
let mut filtered = HashMap::<&String, u32>::new();
1747-
for paths in v.values() {
1748-
for (k, v) in paths {
1749-
let e = filtered.entry(k).or_default();
1750-
*e += v;
1751-
}
1737+
let r = filtered_files.as_ref().map(|v| {
1738+
let mut filtered = BTreeMap::<&String, u32>::new();
1739+
for paths in v.values() {
1740+
for (k, v) in paths {
1741+
let e = filtered.entry(k).or_default();
1742+
*e += v;
17521743
}
1753-
let mut buf = "Image contains non-ostree compatible file paths:".to_string();
1754-
for (k, v) in filtered {
1755-
write!(buf, " {k}: {v}").unwrap();
1756-
}
1757-
buf
1758-
});
1744+
}
1745+
let mut buf = "Image contains non-ostree compatible file paths:".to_string();
1746+
for (k, v) in filtered {
1747+
write!(buf, " {k}: {v}").unwrap();
1748+
}
1749+
buf
1750+
});
17591751
Ok(r)
17601752
}
17611753

ostree-ext/src/fixture.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,8 @@ impl Fixture {
939939
temprootd.create_dir_with("usr/bin", &db)?;
940940
temprootd.write("usr/bin/newderivedfile", "newderivedfile v0")?;
941941
temprootd.write("usr/bin/newderivedfile3", "newderivedfile3 v0")?;
942+
temprootd.create_dir_with("run", &db)?;
943+
temprootd.write("run/filtered", "data")?;
942944
crate::integrationtest::generate_derived_oci(derived_path, temproot, tag)?;
943945
Ok(())
944946
}

ostree-ext/tests/it/main.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,7 @@ async fn test_container_chunked() -> Result<()> {
10311031
assert_eq!(store::list_images(fixture.destrepo()).unwrap().len(), 1);
10321032

10331033
assert!(
1034-
store::image_filtered_content_warning(fixture.destrepo(), &imgref.imgref)
1034+
store::image_filtered_content_warning(&import.filtered_files)
10351035
.unwrap()
10361036
.is_none()
10371037
);
@@ -1122,11 +1122,32 @@ r usr/bin/bash bash-v0
11221122

11231123
// We want to test explicit layer pruning
11241124
imp.disable_gc();
1125-
let _import = imp.import(prep).await.unwrap();
1125+
let import = imp.import(prep).await.unwrap();
11261126
assert_eq!(store::list_images(fixture.destrepo()).unwrap().len(), 2);
11271127

1128+
assert_eq!(
1129+
store::image_filtered_content_warning(&import.filtered_files)
1130+
.unwrap()
1131+
.unwrap(),
1132+
"Image contains non-ostree compatible file paths: filtered: 1"
1133+
);
1134+
1135+
// redo it but with the layers already imported and sanity-check that the
1136+
// merge commit is the same
1137+
let merge_commit = import.merge_commit;
1138+
store::remove_image(fixture.destrepo(), &derived_imgref.imgref).unwrap();
1139+
let mut imp =
1140+
store::ImageImporter::new(fixture.destrepo(), &derived_imgref, Default::default()).await?;
1141+
let prep = match imp.prepare().await.unwrap() {
1142+
store::PrepareResult::AlreadyPresent(_) => panic!("should not be already imported"),
1143+
store::PrepareResult::Ready(r) => r,
1144+
};
1145+
let import = imp.import(prep).await.context("reimport").unwrap();
1146+
assert_eq!(import.merge_commit, merge_commit);
1147+
1148+
// but this time we don't get the warning because we didn't reimport the derived layer
11281149
assert!(
1129-
store::image_filtered_content_warning(fixture.destrepo(), &derived_imgref.imgref)
1150+
store::image_filtered_content_warning(&import.filtered_files)
11301151
.unwrap()
11311152
.is_none()
11321153
);
@@ -1225,7 +1246,7 @@ async fn test_container_var_content() -> Result<()> {
12251246
.query_exists(gio::Cancellable::NONE));
12261247

12271248
assert!(
1228-
store::image_filtered_content_warning(fixture.destrepo(), &derived_imgref.imgref)
1249+
store::image_filtered_content_warning(&import.filtered_files)
12291250
.unwrap()
12301251
.is_none()
12311252
);

0 commit comments

Comments
 (0)