From fbee2cfe395e50138c6da72ea56e3756e2a7ef73 Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Wed, 3 Jul 2019 16:59:00 +0000 Subject: [PATCH] identity: introspect basearch This introspects node basearch from rpm-ostree, instead of using build-time configuration and the internal mapping table. --- src/identity/basearch.rs | 38 ------------- src/identity/mod.rs | 3 +- src/rpm_ostree/cli_status.rs | 81 ++++++++++++++++++++++----- src/rpm_ostree/mod.rs | 2 +- tests/fixtures/rpm-ostree-status.json | 60 ++++++++++++++++++++ 5 files changed, 129 insertions(+), 55 deletions(-) delete mode 100644 src/identity/basearch.rs create mode 100644 tests/fixtures/rpm-ostree-status.json diff --git a/src/identity/basearch.rs b/src/identity/basearch.rs deleted file mode 100644 index e79fa5db..00000000 --- a/src/identity/basearch.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Fedora CoreOS base architecture. - -use cfg_if::cfg_if; -use failure::Fallible; - -// TODO(lucab): consider sourcing this directly from somewhere in OS rootfs. - -// Map Rust target to Fedora basearch. -cfg_if! { - if #[cfg(target_arch = "aarch64")] { - static BASEARCH: &str = "aarch64"; - } else if #[cfg(target_arch = "powerpc64le")] { - static BASEARCH: &str = "ppc64le"; - } else if #[cfg(target_arch = "x86_64")] { - static BASEARCH: &str = "x86_64"; - } else { - static BASEARCH: &str = "unsupported"; - } -} - -/// Fetch base architecture value. -pub(crate) fn read_basearch() -> Fallible { - if BASEARCH == "unsupported" { - // For forward compatibility, we log a message but keep going. - log::error!("unsupported base architecture"); - } - - Ok(BASEARCH.to_string()) -} - -#[cfg(test)] -mod tests { - #[test] - fn basic_basearch() { - let label = super::read_basearch().unwrap(); - assert!(!label.is_empty()); - } -} diff --git a/src/identity/mod.rs b/src/identity/mod.rs index c5e0aaaa..cc2a99be 100644 --- a/src/identity/mod.rs +++ b/src/identity/mod.rs @@ -1,4 +1,3 @@ -mod basearch; mod platform; use crate::config::inputs; @@ -58,7 +57,7 @@ impl Identity { /// Try to build default agent identity. pub fn try_default() -> Fallible { - let basearch = basearch::read_basearch()?; + let basearch = rpm_ostree::basearch()?; let current_os = rpm_ostree::booted().context("failed to introspect booted OS image")?; let node_uuid = { let app_id = id128::Id128::try_from_slice(APP_ID) diff --git a/src/rpm_ostree/cli_status.rs b/src/rpm_ostree/cli_status.rs index 822a8b2c..f47e3c2e 100644 --- a/src/rpm_ostree/cli_status.rs +++ b/src/rpm_ostree/cli_status.rs @@ -18,10 +18,19 @@ pub struct DeploymentJSON { booted: bool, #[serde(rename = "base-checksum")] base_checksum: Option, + #[serde(rename = "base-commit-meta")] + base_metadata: BaseCommitMetaJSON, checksum: String, version: String, } +/// Metadata from base commit (only fields relevant to zincati). +#[derive(Debug, Deserialize)] +struct BaseCommitMetaJSON { + #[serde(rename = "coreos-assembler.basearch")] + basearch: String, +} + impl DeploymentJSON { /// Convert into `Release`. pub fn into_release(self) -> Release { @@ -39,30 +48,74 @@ impl DeploymentJSON { } } +/// Return base architecture for booted deployment. +pub fn basearch() -> Fallible { + let status = status_json(true)?; + let json = booted_json(status)?; + Ok(json.base_metadata.basearch) +} + /// Find the booted deployment. pub fn booted() -> Fallible { - let cmd = std::process::Command::new("rpm-ostree") - .arg("status") + let status = status_json(true)?; + let json = booted_json(status)?; + Ok(json.into_release()) +} + +/// Return JSON object for booted deployment. +fn booted_json(status: StatusJSON) -> Fallible { + let booted = status + .deployments + .into_iter() + .find(|d| d.booted) + .ok_or_else(|| format_err!("no booted deployment found"))?; + + ensure!(!booted.base_revision().is_empty(), "empty base revision"); + ensure!(!booted.version.is_empty(), "empty version"); + ensure!(!booted.base_metadata.basearch.is_empty(), "empty basearch"); + Ok(booted) +} + +/// Introspect deployments (rpm-ostree status). +fn status_json(booted_only: bool) -> Fallible { + let mut cmd = std::process::Command::new("rpm-ostree"); + cmd.arg("status"); + + // Try to request the minimum scope we need. + if booted_only { + cmd.arg("--booted"); + } + + let cmdrun = cmd .arg("--json") - .arg("--booted") .output() .with_context(|e| format_err!("failed to run rpm-ostree: {}", e))?; - if !cmd.status.success() { + if !cmdrun.status.success() { bail!( "rpm-ostree status failed:\n{}", - String::from_utf8_lossy(&cmd.stderr) + String::from_utf8_lossy(&cmdrun.stderr) ); } - let status: StatusJSON = serde_json::from_slice(&cmd.stdout)?; + let status: StatusJSON = serde_json::from_slice(&cmdrun.stdout)?; + Ok(status) +} - let booted = status - .deployments - .into_iter() - .find(|d| d.booted) - .ok_or_else(|| format_err!("no booted deployment found"))?; +#[cfg(test)] +mod tests { + use super::*; - ensure!(!booted.base_revision().is_empty(), "empty base revision"); - ensure!(!booted.version.is_empty(), "empty version"); - Ok(booted.into_release()) + fn mock_status() -> Fallible { + let fp = std::fs::File::open("tests/fixtures/rpm-ostree-status.json").unwrap(); + let mut bufrd = std::io::BufReader::new(fp); + let status: StatusJSON = serde_json::from_reader(bufrd)?; + Ok(status) + } + + #[test] + fn mock_booted_basearch() { + let status = mock_status().unwrap(); + let booted = booted_json(status).unwrap(); + assert_eq!(booted.base_metadata.basearch, "x86_64"); + } } diff --git a/src/rpm_ostree/mod.rs b/src/rpm_ostree/mod.rs index 601ebb23..392dfb80 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::booted; +pub use cli_status::{basearch, booted}; mod actor; pub use actor::{FinalizeDeployment, RpmOstreeClient, StageDeployment}; diff --git a/tests/fixtures/rpm-ostree-status.json b/tests/fixtures/rpm-ostree-status.json new file mode 100644 index 00000000..539fd47b --- /dev/null +++ b/tests/fixtures/rpm-ostree-status.json @@ -0,0 +1,60 @@ +{ + "deployments" : [ + { + "unlocked" : "none", + "requested-local-packages" : [ + ], + "base-commit-meta" : { + "coreos-assembler.config-dirty" : "true", + "coreos-assembler.config-gitrev" : "e5ffda727170e124c7f7d8782e256b0462bf9869", + "coreos-assembler.basearch" : "x86_64", + "rpmostree.inputhash" : "0e2ba98bac847c8a8cc5ffd42822a41d53ef6719ccaf6a0094559768aa24da33", + "version" : "30.1", + "rpmostree.rpmmd-repos" : [ + { + "id" : "fedora-coreos-pool", + "timestamp" : 7435743550895030272 + }, + { + "id" : "fedora", + "timestamp" : -7689964138319052800 + }, + { + "id" : "fedora-updates", + "timestamp" : -1292510703390818304 + }, + { + "id" : "coreos-assembler-local-overrides", + "timestamp" : 1402332922660257792 + } + ] + }, + "base-removals" : [ + ], + "gpg-enabled" : false, + "origin" : "fedora/x86_64/coreos/testing-devel", + "osname" : "fedora-coreos", + "pinned" : false, + "requested-base-local-replacements" : [ + ], + "checksum" : "ce718b90ce19c2cac07fa45efdce91aad208a508c98e9f7f572f5aaba76f569a", + "regenerate-initramfs" : false, + "id" : "fedora-coreos-ce718b90ce19c2cac07fa45efdce91aad208a508c98e9f7f572f5aaba76f569a.0", + "version" : "30.1", + "requested-packages" : [ + ], + "requested-base-removals" : [ + ], + "serial" : 0, + "base-local-replacements" : [ + ], + "timestamp" : 1561753297, + "booted" : true, + "packages" : [ + ] + } + ], + "transaction" : null, + "cached-update" : null +} +