Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ COPY . /src
FROM scratch as packaging
COPY contrib/packaging /

# Default empty stage for ostree override RPMs. When BOOTC_ostree_src is set,
# the Justfile passes --build-context ostree-packages=<dir> which overrides
# this stage with the actual RPMs. When not set, this empty stage ensures
# the Dockerfile still builds without errors.
FROM scratch as ostree-packages

# This image installs build deps, pulls in our source code, and installs updated
# bootc binaries in /out. The intention is that the target rootfs is extracted from /out
# back into a final stage (without the build deps etc) below.
Expand All @@ -27,6 +33,17 @@ ARG initramfs=1
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
/run/packaging/install-buildroot
# Install ostree override RPMs into the buildroot if provided via BOOTC_ostree_src.
# This ensures bootc compiles and links against the patched ostree (ostree-devel,
# ostree-libs, ostree). When the directory is empty, nothing is installed.
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=ostree-packages,src=/,target=/run/ostree-packages <<EORUN
set -xeuo pipefail
if ls /run/ostree-packages/*.rpm 2>/dev/null; then
echo "Installing ostree override into buildroot"
rpm -Uvh --oldpackage /run/ostree-packages/*.rpm
fi
EORUN
# Now copy the rest of the source
COPY --from=src /src /src
WORKDIR /src
Expand Down Expand Up @@ -162,6 +179,16 @@ ARG rootfs=""
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
/run/packaging/configure-rootfs "${variant}" "${rootfs}"
# Install ostree override RPMs into the final image if provided via BOOTC_ostree_src.
# Only ostree and ostree-libs are installed here (not ostree-devel).
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=ostree-packages,src=/,target=/run/ostree-packages <<EORUN
set -xeuo pipefail
if ls /run/ostree-packages/ostree-2*.rpm /run/ostree-packages/ostree-libs-*.rpm 2>/dev/null; then
echo "Installing ostree override RPMs into final image"
rpm -Uvh --oldpackage /run/ostree-packages/ostree-2*.rpm /run/ostree-packages/ostree-libs-*.rpm
fi
EORUN
# Override with our built package
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
Expand Down
41 changes: 38 additions & 3 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ buildroot_base := env("BOOTC_buildroot_base", "quay.io/centos/centos:stream10")
extra_src := env("BOOTC_extra_src", "")
# Set to "1" to disable auto-detection of local Rust dependencies
no_auto_local_deps := env("BOOTC_no_auto_local_deps", "")
# Optional: path to an ostree source tree to build and inject into the image.
# When set, ostree is built from source inside a container matching the base
# image distro, and the resulting RPMs override the stock ostree packages in
# both the buildroot (so bootc links against the patched libostree) and the
# final image. This pattern can be reused for other dependency overrides.
# Example: BOOTC_ostree_src=/path/to/ostree just build
ostree_src := env("BOOTC_ostree_src", "")
# Version to assign to the override ostree RPMs. This should be set to the
# next unreleased ostree version so the override is always newer than stock.
ostree_version := env("BOOTC_ostree_version", "2026.1")

# Internal variables
nocache := env("BOOTC_nocache", "")
Expand All @@ -64,13 +74,14 @@ buildargs := base_buildargs \

# Build container image from current sources (default target)
[group('core')]
build: package _keygen && _pull-lbi-images
build: _build-ostree-rpms package _keygen && _pull-lbi-images
#!/bin/bash
set -xeuo pipefail
test -d target/packages
pkg_path=$(realpath target/packages)
ostree_pkg_path=$(realpath target/ostree-packages)
eval $(just _git-build-vars)
podman build {{_nocache_arg}} --build-arg=image_version=${VERSION} --build-context "packages=${pkg_path}" -t {{base_img}} {{buildargs}} .
podman build {{_nocache_arg}} --build-arg=image_version=${VERSION} --build-context "packages=${pkg_path}" --build-context "ostree-packages=${ostree_pkg_path}" -t {{base_img}} {{buildargs}} .

# Show available build variants and current configuration
[group('core')]
Expand Down Expand Up @@ -321,7 +332,9 @@ package:
if [[ -z "{{no_auto_local_deps}}" ]]; then
local_deps_args=$(cargo xtask local-rust-deps)
fi
podman build {{base_buildargs}} --build-arg=SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} --build-arg=pkgversion=${VERSION} -t localhost/bootc-pkg --target=build $local_deps_args .
mkdir -p target/ostree-packages
ostree_pkg_path=$(realpath target/ostree-packages)
podman build {{base_buildargs}} --build-arg=SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} --build-arg=pkgversion=${VERSION} --build-context "ostree-packages=${ostree_pkg_path}" -t localhost/bootc-pkg --target=build $local_deps_args .
mkdir -p "${packages}"
rm -vf "${packages}"/*.rpm
podman run --rm localhost/bootc-pkg tar -C /out/ -cf - . | tar -C "${packages}"/ -xvf -
Expand Down Expand Up @@ -359,6 +372,28 @@ _git-build-vars:
echo "SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}"
echo "VERSION=${VERSION}"

# Build ostree RPMs from source if BOOTC_ostree_src is set.
# The RPMs are built inside a container matching the base image distro.
# When BOOTC_ostree_src is not set, this creates an empty directory (no-op).
_build-ostree-rpms:
#!/bin/bash
set -xeuo pipefail
mkdir -p target/ostree-packages
if [ -z "{{ostree_src}}" ]; then exit 0; fi
echo "Building ostree {{ostree_version}} from source: {{ostree_src}}"
rm -f target/ostree-packages/*.rpm
podman build \
--build-context ostree-src={{ostree_src}} \
--build-arg=base={{base}} \
--build-arg=ostree_version={{ostree_version}} \
-t localhost/ostree-build \
-f contrib/packaging/Dockerfile.ostree-override .
cid=$(podman create localhost/ostree-build)
podman cp "${cid}:/" target/ostree-packages/
podman rm "${cid}"
echo "ostree override RPMs:"
ls -la target/ostree-packages/

_keygen:
./hack/generate-secureboot-keys

Expand Down
113 changes: 113 additions & 0 deletions contrib/packaging/Dockerfile.ostree-override
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Build ostree RPMs from source, matching the base image distro.
#
# This Dockerfile is used by the BOOTC_ostree_src mechanism in the Justfile
# to build a patched ostree and inject it into the bootc test image. It builds
# ostree RPMs inside a container matching the base image so the resulting RPMs
# are compatible with the target distro.
#
# The ostree source is provided via the `ostree-src` build context.
# The version is overridden to ensure the built RPMs are always newer than
# the stock packages.
#
# Usage (via Justfile):
# BOOTC_ostree_src=/path/to/ostree just build
#
# Direct usage:
# podman build --build-context ostree-src=/path/to/ostree \
# --build-arg=base=quay.io/centos-bootc/centos-bootc:stream10 \
# --build-arg=ostree_version=2026.1 \
# -f contrib/packaging/Dockerfile.ostree-override .

ARG base=quay.io/centos-bootc/centos-bootc:stream10

FROM $base as ostree-build
# Install ostree build dependencies
RUN <<EORUN
set -xeuo pipefail
. /usr/lib/os-release
case "${ID}${ID_LIKE:-}" in
*centos*|*rhel*)
dnf config-manager --set-enabled crb
;;
esac
dnf -y builddep ostree
dnf -y install git rpm-build autoconf automake libtool make xz
EORUN

# Copy ostree source from build context
COPY --from=ostree-src / /ostree-src
WORKDIR /ostree-src

# Build ostree RPMs with the specified version
ARG ostree_version=2026.1
RUN <<EORUN
set -xeuo pipefail
git config --global --add safe.directory /ostree-src

# Initialize submodules if needed
if ! test -f libglnx/README.md || ! test -f bsdiff/README.md; then
git submodule update --init
fi

# Clean up any stale build artifacts from the source tree
rm -rf ostree-distgit *.tar.xz *.src.rpm x86_64/ ostree.spec

# Create source tarball with the target version as the directory prefix.
# We can't use ci/make-git-snapshot.sh because it hardcodes the directory
# name from git-describe, which won't match our overridden version.
GITREV=$(git rev-parse HEAD)
PKG_VER="libostree-${ostree_version}"
git archive --format=tar --prefix="${PKG_VER}/" "${GITREV}" > "${PKG_VER}.tar.tmp"
git submodule status | while read line; do
rev=$(echo ${line} | cut -f 1 -d ' ')
path=$(echo ${line} | cut -f 2 -d ' ')
(cd "${path}"; git archive --format=tar --prefix="${PKG_VER}/${path}/" "${rev}") > submodule.tar
tar -A -f "${PKG_VER}.tar.tmp" submodule.tar
rm submodule.tar
done
mv "${PKG_VER}.tar.tmp" "${PKG_VER}.tar"
xz "${PKG_VER}.tar"

# Get spec file: use local one if present, otherwise fetch from dist-git
if ! test -f ostree.spec; then
rm -rf ostree-distgit
. /usr/lib/os-release
case "${ID}" in
centos|rhel)
git clone --depth=1 https://gitlab.com/redhat/centos-stream/rpms/ostree.git ostree-distgit || \
git clone --depth=1 https://src.fedoraproject.org/rpms/ostree ostree-distgit
;;
*)
git clone --depth=1 https://src.fedoraproject.org/rpms/ostree ostree-distgit
;;
esac
cp ostree-distgit/ostree.spec .
fi

# Set the target version and strip any distro patches
sed -i -e '/^Patch/d' -e "s,^Version:.*,Version: ${ostree_version}," ostree.spec

# Build SRPM
ci/rpmbuild-cwd -bs ostree.spec

# Install any missing build deps from the SRPM
if test "$(id -u)" = 0; then
dnf builddep -y *.src.rpm
fi

# Build binary RPMs
ci/rpmbuild-cwd --rebuild *.src.rpm

# Collect the RPMs we need
mkdir -p /out
cp x86_64/ostree-${ostree_version}*.rpm \
x86_64/ostree-libs-${ostree_version}*.rpm \
x86_64/ostree-devel-${ostree_version}*.rpm \
/out/
echo "Built ostree override RPMs:"
ls -la /out/
EORUN

# Final stage: just the RPMs
FROM scratch
COPY --from=ostree-build /out/ /
66 changes: 66 additions & 0 deletions crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,54 @@ pub(crate) enum InternalsOpts {
},
}

/// Options for the `set-options-for-source` subcommand.
#[derive(Debug, Parser, PartialEq, Eq)]
pub(crate) struct SetOptionsForSourceOpts {
/// The name of the source that owns these kernel arguments.
///
/// Must contain only alphanumeric characters, hyphens, or underscores.
/// Examples: "tuned", "admin", "bootc-kargs-d"
#[clap(long)]
pub(crate) source: String,

/// The kernel arguments to set for this source.
///
/// If not provided, the source is removed and its options are
/// dropped from the merged `options` line.
#[clap(long)]
pub(crate) options: Option<String>,
}

/// Operations on Boot Loader Specification (BLS) entries.
///
/// These commands support managing kernel arguments from multiple independent
/// sources (e.g., TuneD, admin, bootc kargs.d) by tracking argument ownership
/// via `x-options-source-<name>` extension keys in BLS config files.
///
/// See <https://github.com/ostreedev/ostree/pull/3570>
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
pub(crate) enum LoaderEntriesOpts {
/// Set or update the kernel arguments owned by a specific source.
///
/// Each source's arguments are tracked via `x-options-source-<name>`
/// keys in BLS config files. The `options` line is recomputed as the
/// merge of all tracked sources plus any untracked (pre-existing) options.
///
/// This stages a new deployment with the updated kernel arguments.
///
/// ## Examples
///
/// Add TuneD kernel arguments:
/// bootc loader-entries set-options-for-source --source tuned --options "isolcpus=1-3 nohz_full=1-3"
///
/// Update TuneD kernel arguments:
/// bootc loader-entries set-options-for-source --source tuned --options "isolcpus=0-7"
///
/// Remove TuneD kernel arguments:
/// bootc loader-entries set-options-for-source --source tuned
SetOptionsForSource(SetOptionsForSourceOpts),
}

#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
pub(crate) enum StateOpts {
/// Remove all ostree deployments from this system
Expand Down Expand Up @@ -820,6 +868,11 @@ pub(crate) enum Opt {
/// Stability: This interface may change in the future.
#[clap(subcommand, hide = true)]
Image(ImageOpts),
/// Operations on Boot Loader Specification (BLS) entries.
///
/// Manage kernel arguments from multiple independent sources.
#[clap(subcommand)]
LoaderEntries(LoaderEntriesOpts),
/// Execute the given command in the host mount namespace
#[clap(hide = true)]
ExecInHostMountNamespace {
Expand Down Expand Up @@ -1864,6 +1917,19 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
crate::install::install_finalize(&root_path).await
}
},
Opt::LoaderEntries(opts) => match opts {
LoaderEntriesOpts::SetOptionsForSource(opts) => {
prepare_for_write()?;
let storage = get_storage().await?;
let sysroot = storage.get_ostree()?;
crate::loader_entries::set_options_for_source_staged(
sysroot,
&opts.source,
opts.options.as_deref(),
)?;
Ok(())
}
},
Opt::ExecInHostMountNamespace { args } => {
crate::install::exec_in_host_mountns(args.as_slice())
}
Expand Down
1 change: 1 addition & 0 deletions crates/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pub(crate) mod journal;
mod k8sapitypes;
mod kernel;
mod lints;
mod loader_entries;
mod lsm;
pub(crate) mod metadata;
mod parsers;
Expand Down
Loading
Loading