Skip to content

Conversation

wfchandler
Copy link
Contributor

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.

@wfchandler wfchandler force-pushed the wc/bundle-mtime branch 4 times, most recently from 44b389f to c202a87 Compare October 15, 2025 19:19
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.
@wfchandler wfchandler marked this pull request as ready for review October 16, 2025 01:04
@papertigers
Copy link
Contributor

I think this is good information to pass along in the zip file, however I believe we already have access to the mtime from oxlog, if you look at get_zone_logs_inner() we are grabbing <LogFIle>.path instead of using the LogFile type given to us directly from oxlog. It's probably better if we thread that through everywhere since we already paid for the stat call already and have the data. I realize that's pretty much a complete rework of this PR but is more efficient, thoughts?

@wfchandler
Copy link
Contributor Author

I think this is good information to pass along in the zip file, however I believe we already have access to the mtime from oxlog, if you look at get_zone_logs_inner() we are grabbing <LogFIle>.path instead of using the LogFile type given to us directly from oxlog. It's probably better if we thread that through everywhere since we already paid for the stat call already and have the data. I realize that's pretty much a complete rework of this PR but is more efficient, thoughts?

Great point, I've updated to use the cached mtime when available.

Oxlog is already collecting the file mtime for us when it find the logs.
Avoid `stat`ing the files a second time and pass the cached time to the
zip archive.
if file_type.is_file() {
let src = entry.path();

let system_mtime = entry.metadata().and_then(|m| m.modified())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two questions about this...

Should we ignore errors here so that we still include files that we fail to read an mtime from and continue collecting the bundle?

Since we are getting the logs in a zip file and then unpacking them on a sled where a bundle is being collected are we sure that the unzip process is preserving original modification times? I believe it does hence the entire point of this PR haha but it would be good to maybe test that the zip crate is doing this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we ignore errors here so that we still include files that we fail to read an mtime from and continue collecting the bundle?

Makes sense, since mtime is just a nice to have. Updated in 72d5f84

Since we are getting the logs in a zip file and then unpacking them on a sled where a bundle is being collected are we sure that the unzip process is preserving original modification times?

Yes, but it's good to be sure. I modified app::background::tasks::support_bundle_collector::test::test_collect_chunked to write out the bundle:

diff --git a/nexus/src/app/background/tasks/support_bundle_collector.rs b/nexus/src/app/background/tasks/support_bundle_collector.rs
index 6c22e9f38..5a661c6ba 100644
--- a/nexus/src/app/background/tasks/support_bundle_collector.rs
+++ b/nexus/src/app/background/tasks/support_bundle_collector.rs
@@ -2149,9 +2149,7 @@ mod test {
             .support_bundle_download(
                 &opctx,
                 observed_bundle.id.into(),
-                SupportBundleQueryType::Path {
-                    file_path: "bundle_id.txt".to_string(),
-                },
+                SupportBundleQueryType::Whole,
                 head,
                 range,
             )
@@ -2161,6 +2159,7 @@ mod test {
         // Read the body to bytes, then convert to string
         let body_bytes =
             response.into_body().collect().await.unwrap().to_bytes();
+        std::fs::write("/tmp/test_bundle.zip", &body_bytes).unwrap();
         let body_string = String::from_utf8(body_bytes.to_vec()).unwrap();
 
         // Verify the content matches the bundle ID

The mtime of files in the zip are set as expected:

wfc@illumos:~$ unzip -l /tmp/test_bundle.zip 
Archive:  /tmp/test_bundle.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
       36  10-21-2025 01:58   bundle_id.txt
        0  01-01-1980 00:00   rack/
        0  01-01-1980 00:00   rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/
        0  01-01-1980 00:00   rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/
        0  01-01-1980 00:00   rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/
      729  10-21-2025 01:58   rack/c19a698f-c6f9-4a17-ae30-20d711b8f7dc/sled/b6d65341-167c-41df-9b5c-41cded99c229/sled.txt
   200694  10-21-2025 01:58   reconfigurator_state.json
      239  10-21-2025 01:58   sled_info.json
        0  01-01-1980 00:00   sp_task_dumps/
        0  01-01-1980 00:00   sp_task_dumps/sled_0/
      590  10-21-2025 01:58   sp_task_dumps/sled_0/dump-0.zip
        0  01-01-1980 00:00   sp_task_dumps/sled_1/
      590  10-21-2025 01:58   sp_task_dumps/sled_1/dump-0.zip
        0  01-01-1980 00:00   sp_task_dumps/switch_0/
      588  10-21-2025 01:58   sp_task_dumps/switch_0/dump-0.zip
        0  01-01-1980 00:00   sp_task_dumps/switch_1/
      588  10-21-2025 01:58   sp_task_dumps/switch_1/dump-0.zip
---------                     -------
   204054                     17 files

{
let logfile = f.path();
let system_mtime =
f.metadata().and_then(|m| m.modified()).inspect_err(|e| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

metadata() here doesn't follow symlinks...which I believe is okay since we are operating from the gz context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, this is fine today. On the one hand I don't foresee us copying over symlinks from a sled zip, but there's no harm in handling them just in case. Switched to Utf8Path::metadata which will traverse symlinks in fa07354.

mtime is a nice to have, just default to 1980 if any part of getting it
fails.
Handle getting mtime for symlinks by calling `metadata` on the dirent's
path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants