Skip to content

Commit

Permalink
Add bootc-reinstall binary
Browse files Browse the repository at this point in the history
# Background

The current usage instructions for bootc involve a long podman
invocation.

# Issue

It's hard to remember and type the long podman invocation, making the
usage of bootc difficult for users.

See https://issues.redhat.com/browse/BIFROST-610 and https://issues.redhat.com/browse/BIFROST-611

(Epic https://issues.redhat.com/browse/BIFROST-594)

# Solution

We want to make the usage of bootc easier by providing a new Fedora/RHEL
subpackage that includes a new binary `bootc-reinstall`. This binary
will simplify the usage of bootc by providing a simple command line
interface (configured either through CLI flags or a configuration file)
with an interactive prompt that allows users to reinstall the current
system using bootc.

The commandline will handle helping the user choose SSH keys / users and
ensure and warn the user about the destructive nature of the operation,
and eventually issues they might run into in the various clouds (e.g.
missing cloud agent on the target image)

# Implementation

Added new reinstall-cli crate that outputs the new bootc-reinstall
binary. Modified the bootc.spec file to generate the new subpackage
which includes this binary.

This new crate depends on the existing utils crate.

Refactored the tracing initialization from the bootc binary into the
utils crate so that it can be reused by the new crate.

The new CLI can either be configured through commandline flags or
through a configuration file in a path set by the environment variable
`BOOTC_REINSTALL_CONFIG`.

The configuration file is a YAML file.

# Limitations

Only root SSH keys are supported. The multi user selection TUI is
implemented, but if you choose anything other than root you will get an
error.

# Try

Try out instructions:

```bash
# Make srpm
cargo xtask package-srpm

# Mock group
sudo usermod -a -G mock $(whoami)
newgrp mock

# Build RPM for RHEL
mock --rebuild -r rhel+epel-9-x86_64 --rebuild target/bootc-*.src.rpm
```

Then install the RPM (`/var/lib/mock/rhel+epel-9-x86_64/result/bootc-reinstall-2*.el9.x86_64.rpm`) on [a rhel9 gcp vm](https://console.cloud.google.com/compute/instanceTemplates/details/rhel9-dev-1?project=bifrost-devel&authuser=1&inv=1&invt=Abn-jg) instance template

# TODO

Missing docs, missing functionality.
Everything is in alpha stage. User choice / SSH keys / prompt disabling
should also eventually be supported to be configured through commandline
arguments or the configuration file.
  • Loading branch information
omertuc committed Jan 29, 2025
1 parent acba25f commit 19ebfe9
Show file tree
Hide file tree
Showing 18 changed files with 525 additions and 26 deletions.
78 changes: 73 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"cli",
"reinstall-cli",
"lib",
"ostree-ext",
"utils",
Expand Down Expand Up @@ -60,6 +61,7 @@ tempfile = "3.10.1"
tracing = "0.1.40"
tokio = ">= 1.37.0"
tokio-util = { features = ["io-util"], version = "0.7.10" }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

# See https://github.com/coreos/cargo-vendor-filterer
[workspace.metadata.vendor-filter]
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ all:

install:
install -D -m 0755 -t $(DESTDIR)$(prefix)/bin target/release/bootc
install -D -m 0755 -t $(DESTDIR)$(prefix)/bin target/release/bootc-reinstall
install -d -m 0755 $(DESTDIR)$(prefix)/lib/bootc/bound-images.d
install -d -m 0755 $(DESTDIR)$(prefix)/lib/bootc/kargs.d
ln -s /sysroot/ostree/bootc/storage $(DESTDIR)$(prefix)/lib/bootc/storage
Expand Down
9 changes: 3 additions & 6 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,16 @@ default-run = "bootc"

# See https://github.com/coreos/cargo-vendor-filterer
[package.metadata.vendor-filter]
# This list of platforms is not intended to be exclusive, feel free
# to extend it. But missing a platform will only matter for the case where
# a dependent crate is *only* built on that platform.
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu", "riscv64gc-unknown-linux-gnu"]
# For now we only care about tier 1+2 Linux. (In practice, it's unlikely there is a tier3-only Linux dependency)
platforms = ["*-unknown-linux-gnu"]

[dependencies]
anyhow = { workspace = true }
bootc-lib = { version = "1.0", path = "../lib" }
clap = { workspace = true }
bootc-utils = { path = "../utils" }
tokio = { workspace = true, features = ["macros"] }
log = "0.4.21"
tracing = { workspace = true }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

[lints]
workspace = true
19 changes: 4 additions & 15 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
//! The main entrypoint for bootc, which just performs global initialization, and then
//! calls out into the library.
use anyhow::Result;

/// The code called after we've done process global init and created
/// an async runtime.
async fn async_main() -> Result<()> {
// Don't include timestamps and such because they're not really useful and
// too verbose, and plus several log targets such as journald will already
// include timestamps.
let format = tracing_subscriber::fmt::format()
.without_time()
.with_target(false)
.compact();
// Log to stderr by default
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.event_format(format)
.with_writer(std::io::stderr)
.init();
tracing::trace!("starting");
bootc_utils::initialize_tracing();

tracing::trace!("starting bootc");

// As you can see, the role of this file is mostly to just be a shim
// to call into the code that lives in the internal shared library.
bootc_lib::cli::run_from_iter(std::env::args()).await
Expand Down
16 changes: 16 additions & 0 deletions contrib/packaging/bootc.spec
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,30 @@ Provides: ostree-cli(ostree-container)
%description
%{summary}

%package reinstall
Summary: Utility to reinstall the current system using bootc
Requires: podman
# The reinstall subpackage intentionally does not require bootc, as it pulls in many unnecessary dependencies

%description reinstall
This package provides a utility to simplify reinstalling the current system to a given bootc image.

%prep
%autosetup -p1 -a1
%cargo_prep -v vendor

%build
# Build the main bootc binary
%if 0%{?fedora} || 0%{?rhel} >= 10
%cargo_build %{?with_rhsm:-f rhsm}
%else
%cargo_build %{?with_rhsm:--features rhsm}
%endif

# Build the bootc-reinstall binary
%global cargo_args -p bootc-reinstall
%cargo_build

%cargo_vendor_manifest
%cargo_license_summary
%{cargo_license} > LICENSE.dependencies
Expand Down Expand Up @@ -104,5 +117,8 @@ make install-ostree-hooks DESTDIR=%{?buildroot}
%{_docdir}/bootc/*
%{_mandir}/man*/bootc*

%files reinstall
%{_bindir}/bootc-reinstall

%changelog
%autochangelog
1 change: 1 addition & 0 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ pub(crate) async fn get_storage() -> Result<crate::store::Storage> {
crate::store::Storage::new(sysroot, &global_run)
}

/// Ensure that the current process is running as root and has CAP_SYS_ADMIN.
#[context("Querying root privilege")]
pub(crate) fn require_root(is_container: bool) -> Result<()> {
ensure!(
Expand Down
31 changes: 31 additions & 0 deletions reinstall-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "bootc-reinstall"
version = "0.1.9"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/containers/bootc"
readme = "README.md"
publish = false
# For now don't bump this above what is currently shipped in RHEL9.
rust-version = "1.75.0"

# See https://github.com/coreos/cargo-vendor-filterer
[package.metadata.vendor-filter]
# For now we only care about tier 1+2 Linux. (In practice, it's unlikely there is a tier3-only Linux dependency)
platforms = ["*-unknown-linux-gnu"]

[dependencies]
anyhow = { workspace = true }
bootc-utils = { path = "../utils" }
clap = { workspace = true, features = ["derive"] }
dialoguer = "0.11.0"
log = "0.4.21"
rustix = { workspace = true }
serde_json = { version = "1.0.93", features = ["preserve_order"] }
serde_yaml = "0.9.22"
tracing = { workspace = true }
uzers = "0.12.1"
itertools = "0.14.0"

[lints]
workspace = true
2 changes: 2 additions & 0 deletions reinstall-cli/sample_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The bootc container image to install
bootc_image: quay.io/fedora/fedora-bootc:41
7 changes: 7 additions & 0 deletions reinstall-cli/src/config/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use clap::Parser;

#[derive(Parser)]
pub(crate) struct Cli {
/// The bootc container image to install, e.g. quay.io/fedora/fedora-bootc:41
pub(crate) bootc_image: String,
}
Loading

0 comments on commit 19ebfe9

Please sign in to comment.