Skip to content

Commit b746eea

Browse files
authored
Merge pull request #1339 from champtar/repro
ostree-ext: make OCI history reproducible
2 parents c582017 + 20bf086 commit b746eea

File tree

10 files changed

+63
-30
lines changed

10 files changed

+63
-30
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ impl InstallAleph {
535535
l.get(oci_spec::image::ANNOTATION_CREATED)
536536
.map(|s| s.as_str())
537537
})
538-
.and_then(crate::status::try_deserialize_timestamp);
538+
.and_then(bootc_utils::try_deserialize_timestamp);
539539
let r = InstallAleph {
540540
image: src_imageref.imgref.name.clone(),
541541
version: imgstate.version().as_ref().map(|s| s.to_string()),

lib/src/status.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,6 @@ pub(crate) struct Deployments {
102102
pub(crate) other: VecDeque<ostree::Deployment>,
103103
}
104104

105-
pub(crate) fn try_deserialize_timestamp(t: &str) -> Option<chrono::DateTime<chrono::Utc>> {
106-
match chrono::DateTime::parse_from_rfc3339(t).context("Parsing timestamp") {
107-
Ok(t) => Some(t.into()),
108-
Err(e) => {
109-
tracing::warn!("Invalid timestamp in image: {:#}", e);
110-
None
111-
}
112-
}
113-
}
114-
115105
pub(crate) fn labels_of_config(
116106
config: &oci_spec::image::ImageConfiguration,
117107
) -> Option<&std::collections::HashMap<String, String>> {

lib/src/store/ostree_container.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::{Context, Result};
1+
use anyhow::Result;
22

33
use ostree_ext::container as ostree_container;
44
use ostree_ext::oci_spec;
@@ -52,7 +52,7 @@ fn create_imagestatus(
5252
.map(|s| s.as_str())
5353
})
5454
.or_else(|| config.created().as_deref())
55-
.and_then(try_deserialize_timestamp);
55+
.and_then(bootc_utils::try_deserialize_timestamp);
5656

5757
let version = ostree_container::version_for_config(config).map(ToOwned::to_owned);
5858
let architecture = config.architecture().to_string();
@@ -70,13 +70,3 @@ fn labels_of_config(
7070
) -> Option<&std::collections::HashMap<String, String>> {
7171
config.config().as_ref().and_then(|c| c.labels().as_ref())
7272
}
73-
74-
fn try_deserialize_timestamp(t: &str) -> Option<chrono::DateTime<chrono::Utc>> {
75-
match chrono::DateTime::parse_from_rfc3339(t).context("Parsing timestamp") {
76-
Ok(t) => Some(t.into()),
77-
Err(e) => {
78-
tracing::warn!("Invalid timestamp in image: {:#}", e);
79-
None
80-
}
81-
}
82-
}

ostree-ext/src/container/encapsulate.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,21 +133,34 @@ pub(crate) fn export_chunked(
133133
.uncompressed_sha256
134134
.clone();
135135

136+
let created = imgcfg
137+
.created()
138+
.as_deref()
139+
.and_then(bootc_utils::try_deserialize_timestamp)
140+
.unwrap_or_default();
136141
// Add the ostree layer
137-
ociw.push_layer(manifest, imgcfg, ostree_layer, description, None);
142+
ociw.push_layer_full(
143+
manifest,
144+
imgcfg,
145+
ostree_layer,
146+
None::<HashMap<String, String>>,
147+
description,
148+
created,
149+
);
138150
// Add the component/content layers
139151
let mut buf = [0; 8];
140152
let sep = COMPONENT_SEPARATOR.encode_utf8(&mut buf);
141153
for (layer, name, mut packages) in layers {
142154
let mut annotation_component_layer = HashMap::new();
143155
packages.sort();
144156
annotation_component_layer.insert(CONTENT_ANNOTATION.to_string(), packages.join(sep));
145-
ociw.push_layer(
157+
ociw.push_layer_full(
146158
manifest,
147159
imgcfg,
148160
layer,
149-
name.as_str(),
150161
Some(annotation_component_layer),
162+
name.as_str(),
163+
created,
151164
);
152165
}
153166

ostree-ext/src/container/store.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,12 +1490,22 @@ pub(crate) fn export_to_oci(
14901490
.get(i)
14911491
.and_then(|h| h.comment().as_deref())
14921492
.unwrap_or_default();
1493-
dest_oci.push_layer(
1493+
1494+
let previous_created = srcinfo
1495+
.configuration
1496+
.history()
1497+
.get(i)
1498+
.and_then(|h| h.created().as_deref())
1499+
.and_then(bootc_utils::try_deserialize_timestamp)
1500+
.unwrap_or_default();
1501+
1502+
dest_oci.push_layer_full(
14941503
&mut new_manifest,
14951504
&mut new_config,
14961505
layer,
1497-
previous_description,
14981506
previous_annotations,
1507+
previous_description,
1508+
previous_created,
14991509
)
15001510
}
15011511

ostree-ext/src/fixture.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use ocidir::cap_std::fs::{DirBuilder, DirBuilderExt as _};
2626
use ocidir::oci_spec::image::ImageConfigurationBuilder;
2727
use regex::Regex;
2828
use std::borrow::Cow;
29+
use std::collections::HashMap;
2930
use std::ffi::CString;
3031
use std::fmt::Write as _;
3132
use std::io::{self, Write};
@@ -1014,8 +1015,20 @@ impl NonOstreeFixture {
10141015
let bw = bw.into_inner()?;
10151016
let new_layer = bw.complete()?;
10161017

1017-
self.src_oci
1018-
.push_layer(&mut manifest, &mut config, new_layer, "root", None);
1018+
let created = config
1019+
.created()
1020+
.as_deref()
1021+
.and_then(bootc_utils::try_deserialize_timestamp)
1022+
.unwrap_or_default();
1023+
1024+
self.src_oci.push_layer_full(
1025+
&mut manifest,
1026+
&mut config,
1027+
new_layer,
1028+
None::<HashMap<String, String>>,
1029+
"root",
1030+
created,
1031+
);
10191032
let config = self.src_oci.write_config(config)?;
10201033

10211034
manifest.set_config(config);

utils/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ repository = "https://github.com/bootc-dev/bootc"
88

99
[dependencies]
1010
anyhow = { workspace = true }
11+
chrono = { workspace = true, features = ["std"] }
1112
rustix = { workspace = true }
1213
serde = { workspace = true, features = ["derive"] }
1314
serde_json = { workspace = true }

utils/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ mod path;
88
pub use path::*;
99
mod iterators;
1010
pub use iterators::*;
11+
mod timestamp;
12+
pub use timestamp::*;
1113
mod tracing_util;
1214
pub use tracing_util::*;

utils/src/timestamp.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use anyhow::Context;
2+
use chrono;
3+
4+
/// Try to parse an RFC 3339, warn on error.
5+
pub fn try_deserialize_timestamp(t: &str) -> Option<chrono::DateTime<chrono::Utc>> {
6+
match chrono::DateTime::parse_from_rfc3339(t).context("Parsing timestamp") {
7+
Ok(t) => Some(t.into()),
8+
Err(e) => {
9+
tracing::warn!("Invalid timestamp in image: {:#}", e);
10+
None
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)