From de798dd04b1e0abb77a08be5dcb7e56f0157de4e Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 19 Nov 2022 09:27:07 -0500 Subject: [PATCH] Exit if booted into a container image This is part of https://github.com/coreos/fedora-coreos-tracker/issues/1263 We don't yet have an official stance on how zincati and custom container images interact. Today, zincati just crash loops. This changes things so that we exit (but still with an error) if we detect the booted system is using a container image origin. One nicer thing here is that the unit status is also updated, e.g. `systemctl status zincati` will show: `Status: "Automatic updates disabled; booted into container image ..."` --- dist/systemd/system/zincati.service | 2 ++ src/main.rs | 15 +++++++++++---- src/rpm_ostree/cli_status.rs | 30 +++++++++++++++++++++++++---- src/rpm_ostree/mod.rs | 2 +- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/dist/systemd/system/zincati.service b/dist/systemd/system/zincati.service index 1837a092..d2e94cb9 100644 --- a/dist/systemd/system/zincati.service +++ b/dist/systemd/system/zincati.service @@ -21,6 +21,8 @@ Environment=ZINCATI_VERBOSITY="-v" Type=notify ExecStart=/usr/libexec/zincati agent ${ZINCATI_VERBOSITY} Restart=on-failure +# This status signals a non-restartable condition +RestartPreventExitStatus=7 RestartSec=10s [Install] diff --git a/src/main.rs b/src/main.rs index e8c4f03c..2c73832f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,20 +56,27 @@ fn run() -> i32 { match cli_opts.run() { Ok(_) => libc::EXIT_SUCCESS, Err(e) => { - log_error_chain(e); - libc::EXIT_FAILURE + log_error_chain(&e); + if e.root_cause() + .downcast_ref::() + .is_some() + { + 7 + } else { + libc::EXIT_FAILURE + } } } } /// Pretty-print a chain of errors, as a series of error-priority log messages. -fn log_error_chain(err_chain: anyhow::Error) { +fn log_error_chain(err_chain: &anyhow::Error) { let mut chain_iter = err_chain.chain(); let top_err = match chain_iter.next() { Some(e) => e.to_string(), None => "(unspecified failure)".to_string(), }; - log::error!("critical error: {}", top_err); + log::error!("error: {}", top_err); for err in chain_iter { log::error!(" -> {}", err); } diff --git a/src/rpm_ostree/cli_status.rs b/src/rpm_ostree/cli_status.rs index fcc08abe..dda17a44 100644 --- a/src/rpm_ostree/cli_status.rs +++ b/src/rpm_ostree/cli_status.rs @@ -37,6 +37,18 @@ lazy_static::lazy_static! { )).unwrap(); } +/// An error which should not result in a retry/restart. +#[derive(Debug, Clone)] +pub struct FatalError(String); + +impl std::fmt::Display for FatalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::error::Error for FatalError {} + /// JSON output from `rpm-ostree status --json` #[derive(Clone, Debug, Deserialize)] pub struct StatusJson { @@ -48,6 +60,7 @@ pub struct StatusJson { #[serde(rename_all = "kebab-case")] pub struct DeploymentJson { booted: bool, + container_image_reference: Option, base_checksum: Option, #[serde(rename = "base-commit-meta")] base_metadata: BaseCommitMetaJson, @@ -62,7 +75,7 @@ pub struct DeploymentJson { #[derive(Clone, Debug, Deserialize)] struct BaseCommitMetaJson { #[serde(rename = "fedora-coreos.stream")] - stream: String, + stream: Option, } impl DeploymentJson { @@ -85,12 +98,21 @@ impl DeploymentJson { /// Parse the booted deployment from status object. pub fn parse_booted(status: &StatusJson) -> Result { - let json = booted_json(status)?; - Ok(json.into_release()) + let status = booted_json(status)?; + if let Some(img) = status.container_image_reference.as_ref() { + let msg = format!("Automatic updates disabled; booted into container image {img}"); + crate::utils::update_unit_status(&msg); + return Err(anyhow::Error::new(FatalError(msg))); + } + Ok(status.into_release()) } fn fedora_coreos_stream_from_deployment(deploy: &DeploymentJson) -> Result { - let stream = deploy.base_metadata.stream.as_str(); + let stream = deploy + .base_metadata + .stream + .as_ref() + .ok_or_else(|| anyhow!("Missing `fedora-coreos.stream` in commit metadata"))?; ensure!(!stream.is_empty(), "empty stream value"); Ok(stream.to_string()) } diff --git a/src/rpm_ostree/mod.rs b/src/rpm_ostree/mod.rs index 621882de..6ff91f55 100644 --- a/src/rpm_ostree/mod.rs +++ b/src/rpm_ostree/mod.rs @@ -1,7 +1,7 @@ mod cli_deploy; mod cli_finalize; mod cli_status; -pub use cli_status::{invoke_cli_status, parse_booted, parse_booted_updates_stream}; +pub use cli_status::{invoke_cli_status, parse_booted, parse_booted_updates_stream, FatalError}; mod actor; pub use actor::{