Skip to content

Commit dc4d978

Browse files
committed
Capture log file mtimes in support bundles
Currently the mtime for log files captured in support bundles is always set as the default value of 1980-01-01T00:00. To more easily sort files by time, it would be convenient to capture their actual mtimes in the bundle zip. Attempt to get the mtime for logs captured by `sled-diagnostics`, falling back on the default if this fails, and copy the mtime into the final bundle zip in nexus.
1 parent 773799e commit dc4d978

File tree

6 files changed

+54
-6
lines changed

6 files changed

+54
-6
lines changed

Cargo.lock

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

nexus/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ update-common.workspace = true
143143
update-engine.workspace = true
144144
omicron-workspace-hack.workspace = true
145145
omicron-uuid-kinds.workspace = true
146-
zip.workspace = true
146+
zip = { workspace = true, features = ["chrono"] }
147147

148148
[dev-dependencies]
149149
async-bb8-diesel.workspace = true

nexus/src/app/background/tasks/support_bundle_collector.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1158,10 +1158,15 @@ fn recursively_add_directory_to_zipfile(
11581158

11591159
let file_type = entry.file_type()?;
11601160
if file_type.is_file() {
1161+
let src = entry.path();
1162+
let mtime: chrono::DateTime<chrono::Utc> =
1163+
src.metadata().and_then(|s| s.modified())?.into();
1164+
let zip_time = zip::DateTime::try_from(mtime.naive_utc())?;
1165+
11611166
let opts = FullFileOptions::default()
1167+
.last_modified_time(zip_time)
11621168
.compression_method(zip::CompressionMethod::Deflated)
11631169
.large_file(true);
1164-
let src = entry.path();
11651170

11661171
zip.start_file_from_path(dst, opts)?;
11671172
let mut file = std::fs::File::open(&src)?;

sled-diagnostics/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ workspace = true
1010
anyhow.workspace = true
1111
camino.workspace = true
1212
cfg-if.workspace = true
13+
chrono.workspace = true
1314
fs-err = { workspace = true, features = ["tokio"] }
1415
futures.workspace = true
1516
illumos-utils.workspace = true
@@ -25,7 +26,7 @@ serde.workspace = true
2526
slog.workspace = true
2627
thiserror.workspace = true
2728
tokio = { workspace = true, features = ["full"] }
28-
zip = { workspace = true, features = ["zstd"] }
29+
zip = { workspace = true, features = ["chrono","zstd"] }
2930

3031
[dev-dependencies]
3132
omicron-common.workspace = true

sled-diagnostics/src/logs.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::{
1010
sync::LazyLock,
1111
};
1212

13+
use anyhow::Context;
1314
use camino::{Utf8Path, Utf8PathBuf};
1415
use fs_err::File;
1516
use illumos_utils::zfs::{
@@ -715,10 +716,24 @@ fn write_log_to_zip<W: Write + Seek>(
715716
};
716717

717718
let mut src = File::open(&snapshot_logfile)?;
719+
720+
let mtime = get_log_mtime(snapshot_logfile)
721+
.inspect_err(|e| {
722+
warn!(
723+
logger,
724+
"sled-diagnostics unable to get mtime for logfile";
725+
"error" => %e,
726+
"logfile" => %snapshot_logfile,
727+
);
728+
})
729+
.ok()
730+
.unwrap_or_else(zip::DateTime::default_for_write);
731+
718732
let zip_path = format!("{service}/{logtype}/{log_name}");
719733
zip.start_file_from_path(
720734
zip_path,
721735
FullFileOptions::default()
736+
.last_modified_time(mtime)
722737
.compression_method(zip::CompressionMethod::Zstd)
723738
.compression_level(Some(3))
724739
// NB: From the docs
@@ -746,6 +761,17 @@ fn write_log_to_zip<W: Write + Seek>(
746761
Ok(())
747762
}
748763

764+
fn get_log_mtime(log_path: &Utf8Path) -> anyhow::Result<zip::DateTime> {
765+
let mtime = log_path
766+
.metadata()
767+
.and_then(|s| s.modified())
768+
.context("failed to stat path")?;
769+
770+
let datetime: chrono::DateTime<chrono::Utc> = mtime.into();
771+
zip::DateTime::try_from(datetime.naive_utc())
772+
.context("failed to convert file mtime to zip-compatible time")
773+
}
774+
749775
/// A log file that is found in oxlog's "extra" bucket of service logs.
750776
#[derive(Debug, PartialEq)]
751777
enum ExtraLogKind<'a> {
@@ -1202,6 +1228,12 @@ mod illumos_tests {
12021228
let mut logfile_handle =
12031229
fs_err::tokio::File::create_new(&logfile).await.unwrap();
12041230
logfile_handle.write_all(data.as_bytes()).await.unwrap();
1231+
1232+
// 2025-10-15T00:00:00
1233+
let mtime = std::time::SystemTime::UNIX_EPOCH
1234+
+ std::time::Duration::from_secs(1760486400);
1235+
let std_file = logfile_handle.into_std().await.into_file();
1236+
std_file.set_modified(mtime).unwrap();
12051237
}
12061238

12071239
// Populate some file with similar names that should be skipped over
@@ -1237,12 +1269,20 @@ mod illumos_tests {
12371269

12381270
zip.finish().unwrap();
12391271

1240-
// Confirm the zip has our file and data
1272+
let expected_zip_mtime =
1273+
zip::DateTime::from_date_and_time(2025, 10, 15, 0, 0, 0)
1274+
.unwrap();
1275+
1276+
// Confirm the zip has our file and data with the right mtime
12411277
let mut archive =
12421278
ZipArchive::new(File::open(zipfile_path).unwrap()).unwrap();
12431279
for (name, data) in logfile_to_data {
12441280
let mut file_in_zip =
12451281
archive.by_name(&format!("mg-ddm/current/{name}")).unwrap();
1282+
1283+
let mtime = file_in_zip.last_modified().unwrap();
1284+
assert_eq!(mtime, expected_zip_mtime, "file mtime matches");
1285+
12461286
let mut contents = String::new();
12471287
file_in_zip.read_to_string(&mut contents).unwrap();
12481288

workspace-hack/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ x509-cert = { version = "0.2.5" }
150150
zerocopy-c38e5c1d305a1b54 = { package = "zerocopy", version = "0.8.27", default-features = false, features = ["derive", "simd"] }
151151
zerocopy-ca01ad9e24f5d932 = { package = "zerocopy", version = "0.7.35", features = ["derive", "simd"] }
152152
zeroize = { version = "1.8.1", features = ["std", "zeroize_derive"] }
153-
zip-164d15cefe24d7eb = { package = "zip", version = "4.2.0", default-features = false, features = ["bzip2", "deflate", "zstd"] }
153+
zip-164d15cefe24d7eb = { package = "zip", version = "4.2.0", default-features = false, features = ["bzip2", "chrono", "deflate", "zstd"] }
154154
zip-3b31131e45eafb45 = { package = "zip", version = "0.6.6", default-features = false, features = ["bzip2", "deflate"] }
155155

156156
[build-dependencies]
@@ -291,7 +291,7 @@ x509-cert = { version = "0.2.5" }
291291
zerocopy-c38e5c1d305a1b54 = { package = "zerocopy", version = "0.8.27", default-features = false, features = ["derive", "simd"] }
292292
zerocopy-ca01ad9e24f5d932 = { package = "zerocopy", version = "0.7.35", features = ["derive", "simd"] }
293293
zeroize = { version = "1.8.1", features = ["std", "zeroize_derive"] }
294-
zip-164d15cefe24d7eb = { package = "zip", version = "4.2.0", default-features = false, features = ["bzip2", "deflate", "zstd"] }
294+
zip-164d15cefe24d7eb = { package = "zip", version = "4.2.0", default-features = false, features = ["bzip2", "chrono", "deflate", "zstd"] }
295295
zip-3b31131e45eafb45 = { package = "zip", version = "0.6.6", default-features = false, features = ["bzip2", "deflate"] }
296296

297297
[target.x86_64-unknown-linux-gnu.dependencies]

0 commit comments

Comments
 (0)