diff --git a/lib/src/fixtures/spec-booted-pinned.yaml b/lib/src/fixtures/spec-booted-pinned.yaml new file mode 100644 index 000000000..4d460e41c --- /dev/null +++ b/lib/src/fixtures/spec-booted-pinned.yaml @@ -0,0 +1,46 @@ +apiVersion: org.containers.bootc/v1alpha1 +kind: BootcHost +metadata: + name: host +spec: + image: + image: quay.io/centos-bootc/centos-bootc:stream9 + transport: registry + bootOrder: default +status: + staged: null + booted: + image: + image: + image: quay.io/centos-bootc/centos-bootc:stream9 + transport: registry + architecture: arm64 + version: stream9.20240807.0 + timestamp: null + imageDigest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 + cachedUpdate: null + incompatible: false + pinned: true + ostree: + checksum: 439f6bd2e2361bee292c1f31840d798c5ac5ba76483b8021dc9f7b0164ac0f48 + deploySerial: 0 + stateroot: default + otherDeployments: + - image: + image: + image: quay.io/centos-bootc/centos-bootc:stream9 + transport: registry + version: stream9.20240807.0 + timestamp: null + imageDigest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b37 + architecture: arm64 + cachedUpdate: null + incompatible: false + pinned: true + ostree: + checksum: 99b2cc3b6edce9ebaef6a6076effa5ee3e1dcff3523016ffc94a1b27c6c67e12 + deploySerial: 0 + stateroot: default + rollback: null + rollbackQueued: false + type: bootcHost diff --git a/lib/src/spec.rs b/lib/src/spec.rs index 69efe328b..f87f2424c 100644 --- a/lib/src/spec.rs +++ b/lib/src/spec.rs @@ -203,6 +203,10 @@ pub struct HostStatus { pub booted: Option, /// The previously booted image pub rollback: Option, + /// Other deployments (i.e. pinned) + #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] + pub other_deployments: Vec, /// Set to true if the rollback entry is queued for the next boot. #[serde(default)] pub rollback_queued: bool, diff --git a/lib/src/status.rs b/lib/src/status.rs index 9823e90b6..759e3ba9f 100644 --- a/lib/src/status.rs +++ b/lib/src/status.rs @@ -254,6 +254,12 @@ pub(crate) fn get_status( .map(|d| boot_entry_from_deployment(sysroot, d)) .transpose() .context("Rollback deployment")?; + let other_deployments = deployments + .other + .iter() + .map(|d| boot_entry_from_deployment(sysroot, d)) + .collect::>>() + .context("Other deployments")?; let spec = staged .as_ref() .or(booted.as_ref()) @@ -280,6 +286,7 @@ pub(crate) fn get_status( staged, booted, rollback, + other_deployments, rollback_queued, ty, }; @@ -362,7 +369,7 @@ fn write_row_name(mut out: impl Write, s: &str, prefix_len: usize) -> Result<()> /// Write the data for a container image based status. fn human_render_slot( mut out: impl Write, - slot: Slot, + slot: Option, entry: &crate::spec::BootEntry, image: &crate::spec::ImageStatus, ) -> Result<()> { @@ -376,9 +383,10 @@ fn human_render_slot( Cow::Owned(format!("{transport}:{imagename}")) }; let prefix = match slot { - Slot::Staged => " Staged image".into(), - Slot::Booted => format!("{} Booted image", crate::glyph::Glyph::BlackCircle), - Slot::Rollback => " Rollback image".into(), + Some(Slot::Staged) => " Staged image".into(), + Some(Slot::Booted) => format!("{} Booted image", crate::glyph::Glyph::BlackCircle), + Some(Slot::Rollback) => " Rollback image".into(), + _ => " Other image".into(), }; let prefix_len = prefix.chars().count(); writeln!(out, "{prefix}: {imageref}")?; @@ -409,6 +417,11 @@ fn human_render_slot( writeln!(out, "{timestamp}")?; } + if entry.pinned { + write_row_name(&mut out, "Pinned", prefix_len)?; + writeln!(out, "yes")?; + } + tracing::debug!("pinned={}", entry.pinned); Ok(()) @@ -417,20 +430,27 @@ fn human_render_slot( /// Output a rendering of a non-container boot entry. fn human_render_slot_ostree( mut out: impl Write, - slot: Slot, + slot: Option, entry: &crate::spec::BootEntry, ostree_commit: &str, ) -> Result<()> { // TODO consider rendering more ostree stuff here like rpm-ostree status does let prefix = match slot { - Slot::Staged => " Staged ostree".into(), - Slot::Booted => format!("{} Booted ostree", crate::glyph::Glyph::BlackCircle), - Slot::Rollback => " Rollback ostree".into(), + Some(Slot::Staged) => " Staged ostree".into(), + Some(Slot::Booted) => format!("{} Booted ostree", crate::glyph::Glyph::BlackCircle), + Some(Slot::Rollback) => " Rollback ostree".into(), + _ => " Other ostree".into(), }; let prefix_len = prefix.len(); writeln!(out, "{prefix}")?; write_row_name(&mut out, "Commit", prefix_len)?; writeln!(out, "{ostree_commit}")?; + + if entry.pinned { + write_row_name(&mut out, "Pinned", prefix_len)?; + writeln!(out, "yes")?; + } + tracing::debug!("pinned={}", entry.pinned); Ok(()) } @@ -449,14 +469,27 @@ fn human_readable_output_booted(mut out: impl Write, host: &Host) -> Result<()> writeln!(out)?; } if let Some(image) = &host_status.image { - human_render_slot(&mut out, slot_name, host_status, image)?; + human_render_slot(&mut out, Some(slot_name), host_status, image)?; } else if let Some(ostree) = host_status.ostree.as_ref() { - human_render_slot_ostree(&mut out, slot_name, host_status, &ostree.checksum)?; + human_render_slot_ostree(&mut out, Some(slot_name), host_status, &ostree.checksum)?; } else { writeln!(out, "Current {slot_name} state is unknown")?; } } } + + if !host.status.other_deployments.is_empty() { + for entry in &host.status.other_deployments { + writeln!(out)?; + + if let Some(image) = &entry.image { + human_render_slot(&mut out, None, entry, image)?; + } else if let Some(ostree) = entry.ostree.as_ref() { + human_render_slot_ostree(&mut out, None, entry, &ostree.checksum)?; + } + } + } + Ok(()) } @@ -491,7 +524,7 @@ mod tests { Staged image: quay.io/example/someimage:latest Digest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566 (arm64) Version: nightly (2023-10-14T19:22:15Z) - + ● Booted image: quay.io/example/someimage:latest Digest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34 (arm64) Version: nightly (2023-09-30T19:22:16Z) @@ -509,7 +542,7 @@ mod tests { let expected = indoc::indoc! { r" Staged ostree Commit: 1c24260fdd1be20f72a4a97a75c582834ee3431fbb0fa8e4f482bb219d633a45 - + ● Booted ostree Commit: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791 "}; @@ -525,7 +558,7 @@ mod tests { Staged image: quay.io/centos-bootc/centos-bootc:stream9 Digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 (s390x) Version: stream9.20240807.0 - + ● Booted ostree Commit: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791 "}; @@ -589,4 +622,23 @@ mod tests { Some(ImageSignature::OstreeRemote("fedora".into())) ); } + + #[test] + fn test_human_readable_booted_pinned_spec() { + // booted image, no staged/rollback + let w = human_status_from_spec_fixture(include_str!("fixtures/spec-booted-pinned.yaml")) + .expect("No spec found"); + let expected = indoc::indoc! { r" + ● Booted image: quay.io/centos-bootc/centos-bootc:stream9 + Digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 (arm64) + Version: stream9.20240807.0 + Pinned: yes + + Other image: quay.io/centos-bootc/centos-bootc:stream9 + Digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b37 (arm64) + Version: stream9.20240807.0 + Pinned: yes + "}; + similar_asserts::assert_eq!(w, expected); + } }