From 29b3a6e7b897590ff969e91c941e7462fb8a1cf6 Mon Sep 17 00:00:00 2001 From: MSVSphere Packaging Team Date: Wed, 1 May 2024 03:33:19 +0300 Subject: [PATCH] import bootc-0.1.9-3.el9_4 --- .bootc.metadata | 2 + .gitignore | 2 + ...lback-verb-and-rollbackQueued-status.patch | 321 ++++++++++++++++++ SPECS/bootc.spec | 107 ++++++ 4 files changed, 432 insertions(+) create mode 100644 .bootc.metadata create mode 100644 .gitignore create mode 100644 SOURCES/0001-Add-a-rollback-verb-and-rollbackQueued-status.patch create mode 100644 SPECS/bootc.spec diff --git a/.bootc.metadata b/.bootc.metadata new file mode 100644 index 0000000..8b50d90 --- /dev/null +++ b/.bootc.metadata @@ -0,0 +1,2 @@ +f1b1bc4ec3e566de914724b4e9285d3068a5ecf7 SOURCES/bootc-0.1.9-vendor.tar.zstd +0be0692496faa3b690fe5a7402cbde4b49985b0f SOURCES/bootc-0.1.9.tar.zstd diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..824eefe --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +SOURCES/bootc-0.1.9-vendor.tar.zstd +SOURCES/bootc-0.1.9.tar.zstd diff --git a/SOURCES/0001-Add-a-rollback-verb-and-rollbackQueued-status.patch b/SOURCES/0001-Add-a-rollback-verb-and-rollbackQueued-status.patch new file mode 100644 index 0000000..b96ac6c --- /dev/null +++ b/SOURCES/0001-Add-a-rollback-verb-and-rollbackQueued-status.patch @@ -0,0 +1,321 @@ +From 04baad3080246b1ce26df7a783874b2b1b3ddaa4 Mon Sep 17 00:00:00 2001 +From: Colin Walters +Date: Sat, 9 Mar 2024 15:33:27 -0500 +Subject: [PATCH] Add a `rollback` verb and `rollbackQueued` status + +I'd really hoped to do something more declarative here, and +really flesh out the intersections with automated upgrades +and automated rollbacks. + +But, this just exposes the simple primitive, equivalent +to `rpm-ostree rollback`. + +Signed-off-by: Colin Walters +--- + lib/src/cli.rs | 33 ++++++++++++ + lib/src/deploy.rs | 61 ++++++++++++++++++++++- + lib/src/spec.rs | 40 +++++++++++++++ + lib/src/status.rs | 15 +++++- + tests/integration/playbooks/rollback.yaml | 4 +- + 5 files changed, 148 insertions(+), 5 deletions(-) + +diff --git a/lib/src/cli.rs b/lib/src/cli.rs +index 42bfbcc..623796a 100644 +--- a/lib/src/cli.rs ++++ b/lib/src/cli.rs +@@ -89,6 +89,10 @@ pub(crate) struct SwitchOpts { + pub(crate) target: String, + } + ++/// Options controlling rollback ++#[derive(Debug, Parser, PartialEq, Eq)] ++pub(crate) struct RollbackOpts {} ++ + /// Perform an edit operation + #[derive(Debug, Parser, PartialEq, Eq)] + pub(crate) struct EditOpts { +@@ -214,6 +218,18 @@ pub(crate) enum Opt { + /// This operates in a very similar fashion to `upgrade`, but changes the container image reference + /// instead. + Switch(SwitchOpts), ++ /// Change the bootloader entry ordering; the deployment under `rollback` will be queued for the next boot, ++ /// and the current will become rollback. If there is a `staged` entry (an unapplied, queued upgrade) ++ /// then it will be discarded. ++ /// ++ /// Note that absent any additional control logic, if there is an active agent doing automated upgrades ++ /// (such as the default `bootc-fetch-apply-updates.timer` and associated `.service`) the ++ /// change here may be reverted. It's recommended to only use this in concert with an agent that ++ /// is in active control. ++ /// ++ /// A systemd journal message will be logged with `MESSAGE_ID=26f3b1eb24464d12aa5e7b544a6b5468` in ++ /// order to detect a rollback invocation. ++ Rollback(RollbackOpts), + /// Apply full changes to the host specification. + /// + /// This command operates very similarly to `kubectl apply`; if invoked interactively, +@@ -500,6 +516,14 @@ async fn switch(opts: SwitchOpts) -> Result<()> { + Ok(()) + } + ++/// Implementation of the `bootc rollback` CLI command. ++#[context("Rollback")] ++async fn rollback(_opts: RollbackOpts) -> Result<()> { ++ prepare_for_write().await?; ++ let sysroot = &get_locked_sysroot().await?; ++ crate::deploy::rollback(sysroot).await ++} ++ + /// Implementation of the `bootc edit` CLI command. + #[context("Editing spec")] + async fn edit(opts: EditOpts) -> Result<()> { +@@ -522,7 +546,15 @@ async fn edit(opts: EditOpts) -> Result<()> { + println!("Edit cancelled, no changes made."); + return Ok(()); + } ++ host.spec.verify_transition(&new_host.spec)?; + let new_spec = RequiredHostSpec::from_spec(&new_host.spec)?; ++ ++ // We only support two state transitions right now; switching the image, ++ // or flipping the bootloader ordering. ++ if host.spec.boot_order != new_host.spec.boot_order { ++ return crate::deploy::rollback(sysroot).await; ++ } ++ + let fetched = crate::deploy::pull(sysroot, new_spec.image, opts.quiet).await?; + + // TODO gc old layers here +@@ -586,6 +618,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> { + match opt { + Opt::Upgrade(opts) => upgrade(opts).await, + Opt::Switch(opts) => switch(opts).await, ++ Opt::Rollback(opts) => rollback(opts).await, + Opt::Edit(opts) => edit(opts).await, + Opt::UsrOverlay => usroverlay().await, + #[cfg(feature = "install")] +diff --git a/lib/src/deploy.rs b/lib/src/deploy.rs +index 444ad8e..3eb31a8 100644 +--- a/lib/src/deploy.rs ++++ b/lib/src/deploy.rs +@@ -5,7 +5,7 @@ + use std::io::{BufRead, Write}; + + use anyhow::Ok; +-use anyhow::{Context, Result}; ++use anyhow::{anyhow, Context, Result}; + + use cap_std::fs::{Dir, MetadataExt}; + use cap_std_ext::cap_std; +@@ -19,8 +19,8 @@ use ostree_ext::ostree; + use ostree_ext::ostree::Deployment; + use ostree_ext::sysroot::SysrootLock; + +-use crate::spec::HostSpec; + use crate::spec::ImageReference; ++use crate::spec::{BootOrder, HostSpec}; + use crate::status::labels_of_config; + + // TODO use https://github.com/ostreedev/ostree-rs-ext/pull/493/commits/afc1837ff383681b947de30c0cefc70080a4f87a +@@ -276,6 +276,63 @@ pub(crate) async fn stage( + Ok(()) + } + ++/// Implementation of rollback functionality ++pub(crate) async fn rollback(sysroot: &SysrootLock) -> Result<()> { ++ const ROLLBACK_JOURNAL_ID: &str = "26f3b1eb24464d12aa5e7b544a6b5468"; ++ let repo = &sysroot.repo(); ++ let (booted_deployment, deployments, host) = crate::status::get_status_require_booted(sysroot)?; ++ ++ let new_spec = { ++ let mut new_spec = host.spec.clone(); ++ new_spec.boot_order = new_spec.boot_order.swap(); ++ new_spec ++ }; ++ ++ // Just to be sure ++ host.spec.verify_transition(&new_spec)?; ++ ++ let reverting = new_spec.boot_order == BootOrder::Default; ++ if reverting { ++ println!("notice: Reverting queued rollback state"); ++ } ++ let rollback_status = host ++ .status ++ .rollback ++ .ok_or_else(|| anyhow!("No rollback available"))?; ++ let rollback_image = rollback_status ++ .query_image(repo)? ++ .ok_or_else(|| anyhow!("Rollback is not container image based"))?; ++ let msg = format!("Rolling back to image: {}", rollback_image.manifest_digest); ++ libsystemd::logging::journal_send( ++ libsystemd::logging::Priority::Info, ++ &msg, ++ [ ++ ("MESSAGE_ID", ROLLBACK_JOURNAL_ID), ++ ("BOOTC_MANIFEST_DIGEST", &rollback_image.manifest_digest), ++ ] ++ .into_iter(), ++ )?; ++ // SAFETY: If there's a rollback status, then there's a deployment ++ let rollback_deployment = deployments.rollback.expect("rollback deployment"); ++ let new_deployments = if reverting { ++ [booted_deployment, rollback_deployment] ++ } else { ++ [rollback_deployment, booted_deployment] ++ }; ++ let new_deployments = new_deployments ++ .into_iter() ++ .chain(deployments.other) ++ .collect::>(); ++ tracing::debug!("Writing new deployments: {new_deployments:?}"); ++ sysroot.write_deployments(&new_deployments, gio::Cancellable::NONE)?; ++ if reverting { ++ println!("Next boot: current deployment"); ++ } else { ++ println!("Next boot: rollback deployment"); ++ } ++ Ok(()) ++} ++ + fn find_newest_deployment_name(deploysdir: &Dir) -> Result { + let mut dirs = Vec::new(); + for ent in deploysdir.entries()? { +diff --git a/lib/src/spec.rs b/lib/src/spec.rs +index 6de9639..5f6df93 100644 +--- a/lib/src/spec.rs ++++ b/lib/src/spec.rs +@@ -28,12 +28,27 @@ pub struct Host { + pub status: HostStatus, + } + ++/// Configuration for system boot ordering. ++ ++#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)] ++#[serde(rename_all = "camelCase")] ++pub enum BootOrder { ++ /// The staged or booted deployment will be booted next ++ #[default] ++ Default, ++ /// The rollback deployment will be booted next ++ Rollback, ++} ++ + #[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)] + #[serde(rename_all = "camelCase")] + /// The host specification + pub struct HostSpec { + /// The host image + pub image: Option, ++ /// If set, and there is a rollback deployment, it will be set for the next boot. ++ #[serde(default)] ++ pub boot_order: BootOrder, + } + + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +@@ -121,6 +136,9 @@ pub struct HostStatus { + pub booted: Option, + /// The previously booted image + pub rollback: Option, ++ /// Set to true if the rollback entry is queued for the next boot. ++ #[serde(default)] ++ pub rollback_queued: bool, + + /// The detected type of system + #[serde(rename = "type")] +@@ -152,6 +170,28 @@ impl Default for Host { + } + } + ++impl HostSpec { ++ /// Validate a spec state transition; some changes cannot be made simultaneously, ++ /// such as fetching a new image and doing a rollback. ++ pub(crate) fn verify_transition(&self, new: &Self) -> anyhow::Result<()> { ++ let rollback = self.boot_order != new.boot_order; ++ let image_change = self.image != new.image; ++ if rollback && image_change { ++ anyhow::bail!("Invalid state transition: rollback and image change"); ++ } ++ Ok(()) ++ } ++} ++ ++impl BootOrder { ++ pub(crate) fn swap(&self) -> Self { ++ match self { ++ BootOrder::Default => BootOrder::Rollback, ++ BootOrder::Rollback => BootOrder::Default, ++ } ++ } ++} ++ + impl Display for ImageReference { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // For the default of fetching from a remote registry, just output the image name +diff --git a/lib/src/status.rs b/lib/src/status.rs +index dba4889..e8f1fa5 100644 +--- a/lib/src/status.rs ++++ b/lib/src/status.rs +@@ -1,6 +1,6 @@ + use std::collections::VecDeque; + +-use crate::spec::{BootEntry, Host, HostSpec, HostStatus, HostType, ImageStatus}; ++use crate::spec::{BootEntry, BootOrder, Host, HostSpec, HostStatus, HostType, ImageStatus}; + use crate::spec::{ImageReference, ImageSignature}; + use anyhow::{Context, Result}; + use camino::Utf8Path; +@@ -224,11 +224,22 @@ pub(crate) fn get_status( + .iter() + .position(|d| d.is_staged()) + .map(|i| related_deployments.remove(i).unwrap()); ++ tracing::debug!("Staged: {staged:?}"); + // Filter out the booted, the caller already found that + if let Some(booted) = booted_deployment.as_ref() { + related_deployments.retain(|f| !f.equal(booted)); + } + let rollback = related_deployments.pop_front(); ++ let rollback_queued = match (booted_deployment.as_ref(), rollback.as_ref()) { ++ (Some(booted), Some(rollback)) => rollback.index() < booted.index(), ++ _ => false, ++ }; ++ let boot_order = if rollback_queued { ++ BootOrder::Rollback ++ } else { ++ BootOrder::Default ++ }; ++ tracing::debug!("Rollback queued={rollback_queued:?}"); + let other = { + related_deployments.extend(other_deployments); + related_deployments +@@ -262,6 +273,7 @@ pub(crate) fn get_status( + .and_then(|entry| entry.image.as_ref()) + .map(|img| HostSpec { + image: Some(img.image.clone()), ++ boot_order, + }) + .unwrap_or_default(); + +@@ -281,6 +293,7 @@ pub(crate) fn get_status( + staged, + booted, + rollback, ++ rollback_queued, + ty, + }; + Ok((deployments, host)) +diff --git a/tests/integration/playbooks/rollback.yaml b/tests/integration/playbooks/rollback.yaml +index a801656..e193ff5 100644 +--- a/tests/integration/playbooks/rollback.yaml ++++ b/tests/integration/playbooks/rollback.yaml +@@ -6,8 +6,8 @@ + failed_counter: "0" + + tasks: +- - name: rpm-ostree rollback +- command: rpm-ostree rollback ++ - name: bootc rollback ++ command: bootc rollback + become: true + + - name: Reboot to deploy new system +-- +2.43.0 + diff --git a/SPECS/bootc.spec b/SPECS/bootc.spec new file mode 100644 index 0000000..c7cba82 --- /dev/null +++ b/SPECS/bootc.spec @@ -0,0 +1,107 @@ +%bcond_without check + +Name: bootc +Version: 0.1.9 +Release: 3%{?dist} +Summary: Bootable container system + +# Apache-2.0 +# Apache-2.0 OR BSL-1.0 +# Apache-2.0 OR MIT +# Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT +# BSD-3-Clause +# MIT +# MIT OR Apache-2.0 +# Unlicense OR MIT +License: Apache-2.0 AND BSD-3-Clause AND MIT AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR MIT) AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND (Unlicense OR MIT) +URL: https://github.com/containers/bootc +Source0: %{url}/releases/download/v%{version}/bootc-%{version}.tar.zstd +Source1: %{url}/releases/download/v%{version}/bootc-%{version}-vendor.tar.zstd + +Patch0: 0001-Add-a-rollback-verb-and-rollbackQueued-status.patch + +# https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval +ExcludeArch: %{ix86} + +BuildRequires: make +BuildRequires: ostree-devel +BuildRequires: openssl-devel +%if 0%{?rhel} +BuildRequires: rust-toolset +%else +BuildRequires: cargo-rpm-macros >= 25 +%endif +BuildRequires: systemd + +# Backing storage tooling https://github.com/containers/composefs/issues/125 +Recommends: composefs +# For OS updates +Requires: skopeo +# For bootloader updates +Recommends: bootupd + +%description +%{summary} + +%prep +%autosetup -p1 -a1 +%if 0%{?rhel} +%cargo_prep -V 1 +%else +%cargo_prep -v vendor +%endif + +%build +%cargo_build +%if !0%{?rhel} +%cargo_vendor_manifest +%cargo_license_summary +%{cargo_license} > LICENSE.dependencies +%endif + +%install +%make_install INSTALL="install -p -c" + +%if %{with check} +%check +%cargo_test +%endif + +%files +%license LICENSE-MIT +%license LICENSE-APACHE +%if !0%{?rhel} +%license LICENSE.dependencies +%license cargo-vendor.txt +%endif +%doc README.md +%{_bindir}/bootc +%{_prefix}/lib/bootc/ +%{_prefix}/lib/systemd/system-generators/* +%{_unitdir}/* +%{_mandir}/man*/bootc* + + +%changelog +* Wed May 01 2024 MSVSphere Packaging Team - 0.1.9-3 +- Rebuilt for MSVSphere 9.3 + +* Thu Mar 28 2024 Colin Walters - 0.1.9-3 +- Backport rollback + Resolves: #RHEL-30879 + +* Wed Feb 14 2024 Colin Walters - 0.1.7-4 +- https://github.com/containers/bootc/releases/tag/v0.1.7 + +* Tue Jan 23 2024 Colin Walters - 0.1.6-2 +- https://github.com/containers/bootc/releases/tag/v0.1.6 + +* Fri Jan 12 2024 Joseph Marrero - 0.1.5-1 +- Update to https://github.com/containers/bootc/releases/tag/v0.1.5 + +* Thu Jan 11 2024 Colin Walters - 0.1.4-3 +- Loosen composefs requirement until it makes it into c9s + +* Mon Dec 11 2023 Colin Walters - 0.1.4-2 +- Initial import from fedora +