Compare commits
1 changed files with 8 additions and 99 deletions
|
|
@ -731,7 +731,8 @@ async fn exec_send_agent_snapshot_to_fd(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared body for `CreateContainer` / `UpdateContainer`: validate the
|
/// Shared body for `CreateContainer` / `UpdateContainer`: validate the
|
||||||
/// name, build the toplevel ourselves, and apply it (streaming line
|
/// name, build the toplevel ourselves, and run
|
||||||
|
/// `nixos-container <verb> … --system-path <built>` (streaming line
|
||||||
/// events to `writer` when `stream` is set).
|
/// events to `writer` when `stream` is set).
|
||||||
///
|
///
|
||||||
/// **Both verbs build explicitly now — not just `update`.** The first cut
|
/// **Both verbs build explicitly now — not just `update`.** The first cut
|
||||||
|
|
@ -746,15 +747,6 @@ async fn exec_send_agent_snapshot_to_fd(
|
||||||
/// something we control. Building here for both verbs removes `buildFlake()`
|
/// something we control. Building here for both verbs removes `buildFlake()`
|
||||||
/// from the picture entirely — there's no shared `.tmp` left to race on,
|
/// from the picture entirely — there's no shared `.tmp` left to race on,
|
||||||
/// so there's nothing left to reason about staying in sync with.
|
/// so there's nothing left to reason about staying in sync with.
|
||||||
///
|
|
||||||
/// **`update` no longer calls `nixos-container update` at all.** That
|
|
||||||
/// action's own version-compat probe runs unconditionally before
|
|
||||||
/// `--system-path` is ever honored, dying on every agent's update.
|
|
||||||
/// [`swap_container_profile`] replicates exactly what `update`'s own
|
|
||||||
/// action does *past* that probe (confirmed against its source): `nix-env
|
|
||||||
/// --set` the per-container profile, then `systemctl reload` if the
|
|
||||||
/// container is running. `create` is untouched — it isn't the failing
|
|
||||||
/// verb.
|
|
||||||
async fn container_flake_action(
|
async fn container_flake_action(
|
||||||
verb: &str,
|
verb: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
@ -770,11 +762,12 @@ async fn container_flake_action(
|
||||||
// UI that shows build progress: nothing until the longest phase
|
// UI that shows build progress: nothing until the longest phase
|
||||||
// finishes, then everything at once.
|
// finishes, then everything at once.
|
||||||
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 args = [
|
||||||
if verb == "update" {
|
verb,
|
||||||
return swap_container_profile(&system_name, &toplevel, stream, writer).await;
|
&container_system_name(name),
|
||||||
}
|
"--system-path",
|
||||||
let args = [verb, &system_name, "--system-path", &toplevel];
|
&toplevel,
|
||||||
|
];
|
||||||
if stream {
|
if stream {
|
||||||
container_run_streaming(&args, writer).await
|
container_run_streaming(&args, writer).await
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -782,90 +775,6 @@ async fn container_flake_action(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a prebuilt toplevel to an existing container directly, without
|
|
||||||
/// going through `nixos-container update` (see `container_flake_action`'s
|
|
||||||
/// doc comment for why). Mirrors `nixos-container.pl`'s own `update`
|
|
||||||
/// action verbatim, past its version probe: point the per-container `nix-
|
|
||||||
/// env` profile at the new toplevel, then reload the container unit *if*
|
|
||||||
/// it's currently running — the container's own next start already reads
|
|
||||||
/// from the profile, so a stopped container needs nothing further.
|
|
||||||
///
|
|
||||||
/// Both steps are near-instant in the happy case, but still stream-
|
|
||||||
/// forwarded like the rest of this operation: a failure here (e.g. a
|
|
||||||
/// wedged `systemctl reload`) is exactly when the caller most wants the
|
|
||||||
/// 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 set_out = Command::new("nix-env")
|
|
||||||
.args(["-p", &profile, "--set", toplevel])
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.context("invoke nix-env --set")?;
|
|
||||||
let mut stdout = String::from_utf8_lossy(&set_out.stdout).into_owned();
|
|
||||||
let mut stderr = String::from_utf8_lossy(&set_out.stderr).into_owned();
|
|
||||||
log_and_forward(&stdout, &stderr, stream, writer).await;
|
|
||||||
if !set_out.status.success() {
|
|
||||||
bail!(
|
|
||||||
"nix-env -p {profile} --set failed ({}): {}",
|
|
||||||
set_out.status,
|
|
||||||
stderr.trim()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let unit = format!("container@{system_name}");
|
|
||||||
let state_out = Command::new("systemctl")
|
|
||||||
.args(["show", "--property=ActiveState", "--value", &unit])
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.context("query container ActiveState")?;
|
|
||||||
let active = String::from_utf8_lossy(&state_out.stdout).trim() == "active";
|
|
||||||
if active {
|
|
||||||
let reload_out = Command::new("systemctl")
|
|
||||||
.args(["reload", &unit])
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.context("invoke systemctl reload")?;
|
|
||||||
let r_stdout = String::from_utf8_lossy(&reload_out.stdout).into_owned();
|
|
||||||
let r_stderr = String::from_utf8_lossy(&reload_out.stderr).into_owned();
|
|
||||||
log_and_forward(&r_stdout, &r_stderr, stream, writer).await;
|
|
||||||
if !reload_out.status.success() {
|
|
||||||
bail!(
|
|
||||||
"systemctl reload {unit} failed ({}): {}",
|
|
||||||
reload_out.status,
|
|
||||||
r_stderr.trim()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
stdout.push_str(&r_stdout);
|
|
||||||
stderr.push_str(&r_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