hive-priv: stream-forward the profile-swap step's output too
Was skipping live-log forwarding for the swap step on the reasoning that it's near-instant in the happy case; mara pointed out a failure there is exactly when the caller most wants the line in the build log, not just a summary error. Factored the log+forward pattern into one helper, used by both the nix-env --set and systemctl reload steps.
This commit is contained in:
parent
05620a4080
commit
22db09ec66
1 changed files with 33 additions and 17 deletions
|
|
@ -772,7 +772,7 @@ async fn container_flake_action(
|
||||||
let toplevel = nix_build_toplevel(name, stream.then_some(&mut *writer)).await?;
|
let toplevel = nix_build_toplevel(name, stream.then_some(&mut *writer)).await?;
|
||||||
let system_name = container_system_name(name);
|
let system_name = container_system_name(name);
|
||||||
if verb == "update" {
|
if verb == "update" {
|
||||||
return swap_container_profile(&system_name, &toplevel).await;
|
return swap_container_profile(&system_name, &toplevel, stream, writer).await;
|
||||||
}
|
}
|
||||||
let args = [verb, &system_name, "--system-path", &toplevel];
|
let args = [verb, &system_name, "--system-path", &toplevel];
|
||||||
if stream {
|
if stream {
|
||||||
|
|
@ -790,10 +790,16 @@ async fn container_flake_action(
|
||||||
/// it's currently running — the container's own next start already reads
|
/// it's currently running — the container's own next start already reads
|
||||||
/// from the profile, so a stopped container needs nothing further.
|
/// from the profile, so a stopped container needs nothing further.
|
||||||
///
|
///
|
||||||
/// Deliberately not stream-forwarded to `writer` — both operations here
|
/// Both steps are near-instant in the happy case, but still stream-
|
||||||
/// are near-instant (a profile symlink swap, a `systemctl reload`), unlike
|
/// forwarded like the rest of this operation: a failure here (e.g. a
|
||||||
/// the multi-minute build `container_flake_action` already streams.
|
/// wedged `systemctl reload`) is exactly when the caller most wants the
|
||||||
async fn swap_container_profile(system_name: &str, toplevel: &str) -> Result<(String, String)> {
|
/// live line in the build log, not just a summary error afterward.
|
||||||
|
async fn swap_container_profile(
|
||||||
|
system_name: &str,
|
||||||
|
toplevel: &str,
|
||||||
|
stream: bool,
|
||||||
|
writer: &mut OwnedWriteHalf,
|
||||||
|
) -> Result<(String, String)> {
|
||||||
let profile = format!("/nix/var/nix/profiles/per-container/{system_name}/system");
|
let profile = format!("/nix/var/nix/profiles/per-container/{system_name}/system");
|
||||||
let set_out = Command::new("nix-env")
|
let set_out = Command::new("nix-env")
|
||||||
.args(["-p", &profile, "--set", toplevel])
|
.args(["-p", &profile, "--set", toplevel])
|
||||||
|
|
@ -802,12 +808,7 @@ async fn swap_container_profile(system_name: &str, toplevel: &str) -> Result<(St
|
||||||
.context("invoke nix-env --set")?;
|
.context("invoke nix-env --set")?;
|
||||||
let mut stdout = String::from_utf8_lossy(&set_out.stdout).into_owned();
|
let mut stdout = String::from_utf8_lossy(&set_out.stdout).into_owned();
|
||||||
let mut stderr = String::from_utf8_lossy(&set_out.stderr).into_owned();
|
let mut stderr = String::from_utf8_lossy(&set_out.stderr).into_owned();
|
||||||
for line in stdout.lines() {
|
log_and_forward(&stdout, &stderr, stream, writer).await;
|
||||||
tracing::info!(target: "nixos-container", "{line}");
|
|
||||||
}
|
|
||||||
for line in stderr.lines() {
|
|
||||||
tracing::warn!(target: "nixos-container", "{line}");
|
|
||||||
}
|
|
||||||
if !set_out.status.success() {
|
if !set_out.status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"nix-env -p {profile} --set failed ({}): {}",
|
"nix-env -p {profile} --set failed ({}): {}",
|
||||||
|
|
@ -831,12 +832,7 @@ async fn swap_container_profile(system_name: &str, toplevel: &str) -> Result<(St
|
||||||
.context("invoke systemctl reload")?;
|
.context("invoke systemctl reload")?;
|
||||||
let r_stdout = String::from_utf8_lossy(&reload_out.stdout).into_owned();
|
let r_stdout = String::from_utf8_lossy(&reload_out.stdout).into_owned();
|
||||||
let r_stderr = String::from_utf8_lossy(&reload_out.stderr).into_owned();
|
let r_stderr = String::from_utf8_lossy(&reload_out.stderr).into_owned();
|
||||||
for line in r_stdout.lines() {
|
log_and_forward(&r_stdout, &r_stderr, stream, writer).await;
|
||||||
tracing::info!(target: "nixos-container", "{line}");
|
|
||||||
}
|
|
||||||
for line in r_stderr.lines() {
|
|
||||||
tracing::warn!(target: "nixos-container", "{line}");
|
|
||||||
}
|
|
||||||
if !reload_out.status.success() {
|
if !reload_out.status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
"systemctl reload {unit} failed ({}): {}",
|
"systemctl reload {unit} failed ({}): {}",
|
||||||
|
|
@ -850,6 +846,26 @@ async fn swap_container_profile(system_name: &str, toplevel: &str) -> Result<(St
|
||||||
Ok((stdout, stderr))
|
Ok((stdout, stderr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Log a command's captured stdout/stderr the same way every other
|
||||||
|
/// `nixos-container`-adjacent shellout in this file does, and — when
|
||||||
|
/// `stream` is set — forward each line to the client as a live
|
||||||
|
/// [`PrivEvent::Line`] too, so a caller watching the build log sees these
|
||||||
|
/// lines exactly like any other step's, not just a summary on failure.
|
||||||
|
async fn log_and_forward(stdout: &str, stderr: &str, stream: bool, writer: &mut OwnedWriteHalf) {
|
||||||
|
for line in stdout.lines() {
|
||||||
|
tracing::info!(target: "nixos-container", "{line}");
|
||||||
|
if stream {
|
||||||
|
write_line_event(writer, PrivStream::Stdout, line).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for line in stderr.lines() {
|
||||||
|
tracing::warn!(target: "nixos-container", "{line}");
|
||||||
|
if stream {
|
||||||
|
write_line_event(writer, PrivStream::Stderr, line).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The explicit `nixosConfigurations.<name>.config.system.build.toplevel`
|
/// The explicit `nixosConfigurations.<name>.config.system.build.toplevel`
|
||||||
/// flake attr path — same construction `hive-c0re`'s own
|
/// flake attr path — same construction `hive-c0re`'s own
|
||||||
/// `lifecycle::prebuild_toplevel` uses, kept here as a pure function so
|
/// `lifecycle::prebuild_toplevel` uses, kept here as a pure function so
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue