a failed container start shows the unit's own journal lines in the op output
This commit is contained in:
parent
894349459a
commit
9a773349a8
3 changed files with 203 additions and 5 deletions
|
|
@ -799,16 +799,23 @@ fn make_log_callback(
|
|||
|
||||
/// Put a failed non-streaming op's error into its build-log row, which has no
|
||||
/// other output: `start` runs through `call`, not the streaming protocol. The
|
||||
/// error carries `nixos-container`'s stderr.
|
||||
/// error carries `nixos-container`'s stderr; `journal` is the container
|
||||
/// unit's own log from hive-priv. An `Err` there (an older hive-priv does not
|
||||
/// know the op) leaves the stderr alone.
|
||||
fn record_failure(
|
||||
logs: Option<&crate::build_logs::BuildLogs>,
|
||||
log_id: Option<i64>,
|
||||
err: &anyhow::Error,
|
||||
journal: Result<String>,
|
||||
) {
|
||||
if let (Some(h), Some(id)) = (logs, log_id) {
|
||||
for line in format!("{err:#}").lines() {
|
||||
h.append_stderr(id, line);
|
||||
}
|
||||
match journal {
|
||||
Ok(text) => text.lines().for_each(|l| h.append_stderr(id, l)),
|
||||
Err(e) => tracing::debug!(error = ?e, "start journal unavailable"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -879,9 +886,13 @@ async fn priv_run_inner(
|
|||
None => Err(missing_toplevel(kind, name)),
|
||||
},
|
||||
"start" => {
|
||||
let attempt = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_secs());
|
||||
let started = crate::priv_client::start_container(name).await;
|
||||
if let Err(e) = &started {
|
||||
record_failure(logs.as_deref(), log_id, e);
|
||||
let journal = crate::priv_client::start_failure_log(name, attempt).await;
|
||||
record_failure(logs.as_deref(), log_id, e, journal);
|
||||
}
|
||||
started
|
||||
}
|
||||
|
|
|
|||
|
|
@ -529,9 +529,33 @@ fn a_failed_start_puts_its_error_in_the_op_output() {
|
|||
.start("updo", "start", "nixos-container start h-updo", None)
|
||||
.expect("start");
|
||||
let err = anyhow::anyhow!("nixos-container start h-updo failed (exit status: 1): Job failed");
|
||||
record_failure(Some(&logs), Some(id), &err);
|
||||
let journal = Ok("Creating mount point failed: Read-only file system".to_owned());
|
||||
record_failure(Some(&logs), Some(id), &err, journal);
|
||||
let full = logs.get_full(id).expect("get").expect("row");
|
||||
assert!(full.stderr.contains("Job failed"), "{}", full.stderr);
|
||||
record_failure(Some(&logs), None, &err);
|
||||
record_failure(None, Some(id), &err);
|
||||
assert!(
|
||||
full.stderr.contains("Read-only file system"),
|
||||
"{}",
|
||||
full.stderr
|
||||
);
|
||||
record_failure(Some(&logs), None, &err, Ok(String::new()));
|
||||
record_failure(None, Some(id), &err, Ok(String::new()));
|
||||
}
|
||||
|
||||
/// An older hive-priv rejects the op; the start's own error is still recorded.
|
||||
#[test]
|
||||
fn an_unknown_journal_op_keeps_the_start_error() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let logs = crate::build_logs::BuildLogs::open(dir.path()).expect("open");
|
||||
let id = logs
|
||||
.start("updo", "start", "nixos-container start h-updo", None)
|
||||
.expect("start");
|
||||
let err = anyhow::anyhow!("Job failed");
|
||||
let unknown = Err(anyhow::anyhow!(
|
||||
"parse request: unknown variant `start_failure_log`"
|
||||
));
|
||||
record_failure(Some(&logs), Some(id), &err, unknown);
|
||||
let full = logs.get_full(id).expect("get").expect("row");
|
||||
assert!(full.stderr.contains("Job failed"), "{}", full.stderr);
|
||||
assert!(!full.stderr.contains("unknown variant"), "{}", full.stderr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,6 +216,19 @@ pub async fn start_container(name: &str) -> Result<()> {
|
|||
.await?)
|
||||
}
|
||||
|
||||
/// The container unit's journal lines since `since_unix`. An older hive-priv
|
||||
/// answers an error (it does not know the op).
|
||||
pub async fn start_failure_log(name: &str, since_unix: u64) -> Result<String> {
|
||||
let (stdout, _) = check(
|
||||
call(&PrivRequest::StartFailureLog {
|
||||
name: name.to_owned(),
|
||||
since_unix,
|
||||
})
|
||||
.await?,
|
||||
)?;
|
||||
Ok(stdout)
|
||||
}
|
||||
|
||||
pub async fn stop_container(name: &str) -> Result<()> {
|
||||
ok(call(&PrivRequest::StopContainer {
|
||||
name: name.to_owned(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue