mirror of
https://github.com/containers/bootc.git
synced 2026-02-05 15:45:53 +01:00
utils: Always print status to stderr
If we were waiting on a lock as part of `bootc status --format=json` this information message would end up in stderr, corrupting the output. Signed-off-by: Colin Walters <walters@verbum.org>
This commit is contained in:
@@ -158,10 +158,10 @@ pub(crate) fn medium_visibility_warning(s: &str) {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
}
|
||||
|
||||
/// Call an async task function, and write a message to stdout
|
||||
/// Call an async task function, and write a message to stderr
|
||||
/// with an automatic spinner to show that we're not blocked.
|
||||
/// Note that generally the called function should not output
|
||||
/// anything to stdout as this will interfere with the spinner.
|
||||
/// anything to stderr as this will interfere with the spinner.
|
||||
pub(crate) async fn async_task_with_spinner<F, T>(msg: &str, f: F) -> T
|
||||
where
|
||||
F: Future<Output = T>,
|
||||
@@ -175,8 +175,8 @@ where
|
||||
// We need to handle the case where we aren't connected to
|
||||
// a tty, so indicatif would show nothing by default.
|
||||
if pb.is_hidden() {
|
||||
print!("{msg}...");
|
||||
std::io::stdout().flush().unwrap();
|
||||
eprint!("{msg}...");
|
||||
std::io::stderr().flush().unwrap();
|
||||
}
|
||||
let r = f.await;
|
||||
let elapsed = HumanDuration(start_time.elapsed());
|
||||
@@ -185,7 +185,7 @@ where
|
||||
&format!("completed task in {elapsed}: {msg}"),
|
||||
);
|
||||
if pb.is_hidden() {
|
||||
println!("done ({elapsed})");
|
||||
eprintln!("done ({elapsed})");
|
||||
} else {
|
||||
pb.finish_with_message(format!("{msg}: done ({elapsed})"));
|
||||
}
|
||||
|
||||
@@ -1,32 +1,51 @@
|
||||
# Verify we can spawn multiple bootc status at the same time
|
||||
# Verify we can spawn multiple bootc status at the same time and get valid JSON
|
||||
use std assert
|
||||
use tap.nu
|
||||
|
||||
tap begin "concurrent bootc status"
|
||||
|
||||
# Fork via systemd-run
|
||||
# Create a temporary directory for output files
|
||||
let tmpdir = mktemp -d
|
||||
print $"Using temporary directory: ($tmpdir)"
|
||||
|
||||
# Number of concurrent invocations
|
||||
let n = 10
|
||||
0..$n | each { |v|
|
||||
# Clean up prior runs
|
||||
systemctl stop $"bootc-status-($v)" | complete
|
||||
}
|
||||
# Fork off a concurrent bootc status
|
||||
0..$n | each { |v|
|
||||
systemd-run --no-block -qr -u $"bootc-status-($v)" bootc status
|
||||
|
||||
# Create systemd unit files for concurrent bootc status commands.
|
||||
# Writing actual unit files allows proper dependency tracking.
|
||||
let units = 0..<$n | each { |v|
|
||||
let unit_name = $"bootc-status-test-($v).service"
|
||||
let outpath = $"($tmpdir)/($v).json"
|
||||
let unit_content = $"[Unit]
|
||||
Description=Test bootc status ($v)
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/sh -c 'bootc status --format=json > ($outpath)'
|
||||
"
|
||||
$unit_content | save -f $"/run/systemd/system/($unit_name)"
|
||||
$unit_name
|
||||
}
|
||||
|
||||
# Await completion
|
||||
0..$n | each { |v|
|
||||
loop {
|
||||
let r = systemctl is-active $"bootc-status-($v)" | complete
|
||||
if $r.exit_code == 0 {
|
||||
break
|
||||
}
|
||||
# check status
|
||||
systemctl status $"bootc-status-($v)" out> /dev/null
|
||||
# Clean it up
|
||||
systemctl reset-failed $"bootc-status-($v)"
|
||||
}
|
||||
# Reload systemd to pick up the new units.
|
||||
systemctl daemon-reload
|
||||
|
||||
# Use systemd-run to create a transient sync unit with After= and Requires=
|
||||
# dependencies on all worker units. --wait blocks until completion.
|
||||
let dep_args = $units | each { |u| [$"--property=After=($u)" $"--property=Requires=($u)"] } | flatten
|
||||
systemd-run --wait ...$dep_args -- true
|
||||
|
||||
# Verify each output file contains valid JSON with the expected structure.
|
||||
# This is a regression test for spinner output polluting stdout.
|
||||
for v in 0..<$n {
|
||||
let path = $"($tmpdir)/($v).json"
|
||||
# open automatically parses JSON files, so we get a record directly
|
||||
# If the file had spinner output mixed in, this would fail to parse
|
||||
let st = open $path
|
||||
assert equal $st.apiVersion org.containers.bootc/v1 $"($path) should contain valid bootc status JSON"
|
||||
}
|
||||
|
||||
# Clean up
|
||||
rm -rf $tmpdir
|
||||
|
||||
tap ok
|
||||
|
||||
Reference in New Issue
Block a user