This allows registries to distinguish "image pulls for bootc client
runs" from other skopeo/containers-image users. The user agent will
be in the format "bootc/<version> skopeo/<version>".
All places in bootc that create ImageProxyConfig now use a new helper
function that sets the user_agent_prefix field.
Closes: https://github.com/bootc-dev/bootc/issues/1686
Assisted-by: OpenCode (Sonnet 4)
Signed-off-by: Colin Walters <walters@verbum.org>
Add a TMT test that exercises the mount point check fix from PR #1904.
The test builds a container image with an embedded disk.yaml that creates
a partition layout WITHOUT a separate /boot partition - just root (/)
with /boot/efi as a separate mount point.
This partition layout triggers the bug from issue #1907 where bootc's
empty rootfs verification would fail with:
"Found entry in boot: efi"
The issue was that when /boot is a directory on the root filesystem
(not a separate partition), but /boot/efi IS a mount point on a different
device, the old code incorrectly saw "efi" as a regular directory entry
rather than recognizing it was a mount point boundary.
Verified that temporarily reverting the fix from PR #1904 causes this
test to fail with the expected error message.
This was already fixed by ab65078675
but we didn't realize at the time the scope.
Closes: https://github.com/bootc-dev/bootc/issues/1907
Signed-off-by: Colin Walters <walters@verbum.org>
Use copy-to-storage to add the booted container to podman storage
instead of pulling a remote image. This matches the pattern used
by other TMT tests and ensures we test the actual bootc under test.
Changes:
- Use localhost/bootc from copy-to-storage instead of remote image
- Disable LBIs via bind mount of /usr/share/empty
- Remove unnecessary host modifications (usr-overlay, dnf install, etc.)
- Use 100%FREE for root LV to ensure sufficient space for deployment
Assisted-by: OpenCode (Opus 4.5)
Signed-off-by: ckyrouac <ckyrouac@redhat.com>
Signed-off-by: Colin Walters <walters@verbum.org>
The "install to-filesystem with separate /var mount" test was causing
disk space issues on GitHub Actions runners due to its large disk
image requirements (12GB for partitions with LVM). Moving it to a TMT
test allows it to run in a dedicated VM where disk space is not as
constrained.
The test verifies that bootc install to-filesystem correctly handles
scenarios where /var is on a separate filesystem, which is a common
production setup.
Changes:
- Remove the test from Rust integration tests (install.rs)
- Add new TMT test: test-32-install-to-filesystem-var-mount.sh
- Add package requirements (parted, lvm2, dosfstools, e2fsprogs)
- Update tests.fmf and integration.fmf with new test entry
Assisted-by: Claude Code (Opus 4.5)
Signed-off-by: ckyrouac <ckyrouac@redhat.com>
If we were waiting on a lock as part of `bootc status --format=json`
this information message would end up in stderr, corrupting the output.
Signed-off-by: Colin Walters <walters@verbum.org>
The previous commit consolidated test content (nushell, cloud-init, etc.)
into the base image. This completes that work by removing the separate
`build-integration-test-image` target and updating all references.
Now `just build` produces the complete test-ready image directly,
simplifying the build pipeline and eliminating the intermediate
`localhost/bootc-integration` image.
Also adds SKIP_CONFIGS support for the coreos testing workflow, which
skips LBIs, test kargs, and install configs that would conflict with FCOS.
Signed-off-by: Colin Walters <walters@verbum.org>
Previously we were mounting a rw overlay on top of /usr using
`mount -t overlay -olowerdir=/usr,workdir=...,upperdir=... overlay /usr`
which caused the kernel to throw
`overlayfs: maximum fs stacking depth exceeded`
possibly because the mountpoint was the same as the lowerdir
Also, move the overlay mount BEFORE we mask off `/sysroot/ostree` else
bootc throws `error: Read only filesystem`
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Move the --mutate-in-place handling before get_storage() so it doesn't
require a fully booted ostree environment. This enables use cases like
Anaconda where we're chrooted into a non-booted system.
Closes: #1854
Assisted-by: OpenCode (claude-sonnet-4-20250514)
Signed-off-by: Colin Walters <walters@verbum.org>
We should match exactly the logic we use with containers-image-proxy.
- If bootc doesn't have auth setup, then we need to not let podman
fall back to the defaults
- Always pass a copy of the auth in a tempfile so we aren't
reliant on absolute paths as we're continually trying to
reduce our usage of those.
Signed-off-by: Colin Walters <walters@verbum.org>
Add an experimental --experimental-unified-storage flag to bootc install
that uses bootc's container storage (/usr/lib/bootc/storage) to pull
images first, then imports from there. This is the same approach used
for logically bound images (LBIs).
Background:
The unified storage approach allows bootc to share container images with
podman's storage, reducing disk space and enabling better integration
with podman.
Changes:
- Add --experimental-unified-storage CLI flag to install subcommands
- Add sysroot_path parameter to prepare_for_pull_unified() and pull_unified()
to handle the different mount points during install vs upgrade/switch
- Handle container-storage transport
- Skip pull in prepare_for_pull_unified() if image already exists in
bootc storage
- Add TMT test for install with unified storage flag
- Add TMT test for switching to unified storage on running system
The sysroot_path fix is needed because during install the target disk
is mounted at a specific path (e.g., /var/mnt), not /sysroot. Skopeo
needs the actual filesystem path to find the bootc storage.
Relates: #20
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Joseph Marrero Corchado <jmarrero@redhat.com>
Add a new --from-downloaded flag to bootc upgrade that allows users to
unlock a staged deployment created with --download-only without fetching
updates from the container image source.
This provides a way to apply already-downloaded updates without triggering
a fetch operation, which is useful for scheduled maintenance workflows where
the update was downloaded earlier and should now be applied at a scheduled
time.
Usage:
# Download update without applying
bootc upgrade --download-only
# Later: Apply the staged update (without fetching from image source)
bootc upgrade --from-downloaded
# Or: Apply staged update and reboot immediately
bootc upgrade --from-downloaded --apply
The flag conflicts with --check and --download-only as those operations
have different purposes. It can be combined with --apply to immediately
reboot after unlocking the staged deployment.
This commit also updates the documentation (upgrades.md) to describe all
three ways to apply a download-only update, and updates the download-only
test case (test-25) to use --from-downloaded instead of plain
'bootc upgrade' when clearing the download-only flag.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Wei Shi <wshi@redhat.com>
Main goal is to reduce signing logic duplication between the systemd-boot
and UKI generation.
However, this quickly snowballed into wanting to actually verify
by providing a custom secure boot keys to bcvk that things worked.
This depends on https://github.com/bootc-dev/bcvk/pull/170
Now as part of that, I ran into what I think are bugs in pesign;
this cuts things back over to using sbsign. I'll file a tracker for that
separately.
Finally as part of this, just remove the TMT example that builds
a sealed image but doesn't actually verify it works - it's already
drifted from what we do outside here. Ultimately what we need
is to shift some of this into the Fedora examples and we just
fetch it here anyways.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Add support for downloading and staging updates without automatic
application on reboot. This allows users to prepare updates and apply
them at a controlled time.
User-facing changes:
- Add --download-only flag to bootc upgrade command
- bootc upgrade --download-only: stages deployment in download-only mode
- bootc upgrade (no flags): clears download-only mode if present
- bootc upgrade --apply: clears download-only mode and immediately reboots
- bootc upgrade --check: read-only, doesn't change download-only state
- bootc status shows "Download-only: yes/no" for staged deployments in verbose mode
- Garbage collection automatically cleans up unreferenced images after staging
Implementation details:
- Internally uses OSTree finalization locking APIs
- Sets opts.locked in SysrootDeployTreeOpts when staging deployments
- Added change_finalization() method to SysrootLock wrapper
- Tracks lock state changes separately from image digest changes
- Field name in BootEntry is download_only (Rust), downloadOnly (JSON)
- Verbose status display uses "Download-only" label (matches Soft-reboot pattern)
- Uses deployment.is_finalization_locked() API (OSTree v2023.8+)
- Always emits downloadOnly field in JSON output for consistency
Testing and documentation:
- New dedicated test: test-25-download-only-upgrade.nu (4-boot workflow)
- Test verifies: switch → upgrade --download-only → reboot (stays old) →
re-stage → upgrade (clear) → reboot (applies)
- Updated docs/src/upgrades.md with comprehensive workflow examples
- Includes notes about reboot behavior and image switching
- Generated man pages and JSON schemas updated
- All test fixtures updated with downloadOnly field
The download-only flag is only available for upgrade, not switch.
The implementation is designed to support future composefs backend.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Wei Shi <wshi@redhat.com>
CentOS 9 lacks systemd.extra-unit.* support which is required for
--bind-storage-ro to work with bcvk. This was causing test failures
on centos-9 while working fine on Fedora.
Change the approach so tests express intent via `extra.try_bind_storage: true`
metadata, and xtask handles the details:
- Detect distro by running the container image and parsing os-release
- Pass distro to tmt via --context=distro=<id>-<version>
- Only add --bind-storage-ro when test wants it AND distro supports it
- When bind storage is available, also set BOOTC_upgrade_image env var
- Tests can detect missing $env.BOOTC_upgrade_image and fall back to
building the upgrade image locally
Add --upgrade-image CLI option to specify the upgrade image path,
replacing the old --env=BOOTC_upgrade_image approach.
Extract magic values to clear const declarations at the top of the file
for better maintainability.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
This ensures it all can work much more elegantly/naturally
with sealed UKI builds - we don't want to do the build-on-target
thing.
Signed-off-by: Colin Walters <walters@verbum.org>
Move TMT test runner code from xtask.rs to tmt module:
- `run_tmt()` and `tmt_provision()` functions
- Helper functions for VM management and SSH connectivity
- Related constants
Also refactor `update_integration()` to use serde_yaml::Value for
building YAML structures instead of string concatenation.
Add detailed error reporting for failed TMT tests:
- Assign run IDs using `tmt run --id`
- Display verbose reports with `tmt run -i {id} report -vvv`
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
We need to run most of our tests in a separate provisioned machine,
which means it needs an individual plan. And then we need a test
for that plan. And then we need the *actual test code*.
This "triplication" is a huge annoying pain.
TMT is soooo complicated, yet as far as I can tell it doesn't offer
us any tools to solve this. So we'll do it here, cut over to
generating the TMT stuff from metadata defined in the test file.
Hence adding a test is just:
- Write a new tests/booted/foo.nu
- `cargo xtask update-generated`
Signed-off-by: Colin Walters <walters@verbum.org>
The rhsm feature was not being propagated from the CLI crate to the
lib crate, causing `bootc internals publish-rhsm-facts` to never be
compiled in even when building with CARGO_FEATURES=rhsm.
I think this was broken when I refactored the build recently.
Change things so we build the manpages before the production
binary, ensuring the production binary always ends up with
the right feature flags.
Fixes: https://issues.redhat.com/browse/RHEL-130799
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Add check to prevent soft reboot when SELinux policies differ
between booted and target deployments, since policy is not
reloaded across soft reboots.
Assisted-by: Cursor (Auto)
Signed-off-by: gursewak1997 <gursmangat@gmail.com>
Fixes a regression where bootupd detection was happening before the
container was deployed, causing bootc to incorrectly check the host
system instead of the target container image. This led to false negatives
when the container had bootupd but the host didn't.
The fix moves bootloader detection into a new PostFetchState that's
created after the deployment is available, ensuring we check the actual
target filesystem.
Fixes: #1778
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
This implements readonly mounting of /sysroot for composefs systems,
matching the behavior that ostree systems already have. Previously,
composefs left /sysroot mounted read-write, which was inconsistent
and meant the readonly tests had to be skipped for composefs.
The implementation uses a direct `libc::syscall` wrapper for
`mount_setattr` since rustix doesn't yet provide this API. The
`MOUNT_ATTR_RDONLY` flag is applied to three mount
points during initramfs setup:
- The composefs rootfs image mount (becomes `/` after switch-root)
- The test root filesystem mount (used in testing scenarios)
- The sysroot clone mount (becomes `/sysroot` in the booted system)
With this change, the readonly /sysroot tests in test-status.nu
now run for both ostree and composefs systems without conditional
checks.
Assisted-by: Claude Code (Sonnet 4.5)
Co-authored-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Signed-off-by: Colin Walters <walters@verbum.org>
This fixes bootc's use of the Discoverable Partition Specification (DPS)
to properly support systemd-gpt-auto-generator. Previously, bootc was
incorrectly setting filesystem UUIDs to the DPS partition type UUID value,
which caused UUID collisions and prevented proper DPS functionality.
It's still a TODO on our side to support systemd-repart in this flow.
Note we go back to using random filesystem UUIDs with this, but
per above we should likely reinitialize them on boot via repart.
Note we remove root= parameter from kernel cmdline for composefs sealed images,
allowing systemd-gpt-auto-generator to auto-discover the root partition
and we test this.
Fixes: #1771
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Sometimes systemd daemons are still running old binaries and
response "Access denied" when send reboot request
Force a full sync before reboot and Allow more delay for
bootc to settle
Signed-off-by: Xiaofeng Wang <henrywangxf@me.com>
PR #1718 introduced a regression where /sysroot was left writable after
running `bootc status`. This occurred because BootedStorage::new()
unconditionally calls set_mount_namespace_in_use(), which tells ostree
it can safely remount /sysroot as writable. When sysroot.load() is called
without actually being in a mount namespace, it leaves the global /sysroot
writable.
Fix by introducing an Environment enum that detects the runtime environment
(ostree, composefs, container, or other) early in the execution flow. Callers
now detect the environment and call prepare_for_write() if needed before
constructing BootedStorage. This ensures a single call to prepare_for_write()
per execution path and eliminates the previous layering violation where storage
code called into CLI code.
The Environment abstraction also makes it clearer when mount namespace
setup is required and provides a foundation for future environment-specific
behavior.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
I'm changing the default fs for Fedora in our CI to be xfs arbitrarily.
This code SHOULD work on non-fsverity hosts, and the other
code path in `tests/build-sealed` does.
Also, the remainder of the stuff was dead code so just drop it.
Signed-off-by: Colin Walters <walters@verbum.org>
Part 1: Use bcvk
For local tests, right now testcloud+tmt doesn't support UEFI, see
https://github.com/teemtee/tmt/issues/4203
This is a blocker for us doing more testing with UKIs.
In this patch we switch to provisioning VMs with bcvk, which
fixes this - but beyond that a really compelling thing about
this is that bcvk is *also* designed to be ergonomic and efficient
beyond just being a test runner, with things like virtiofs
mounting of host container storage, etc.
In other words, bcvk is the preferred way to run local virt
with bootc, and this makes our TMT tests use it.
Now a major downside of this though is we're effectively
implementing a new "provisioner" for tmt (bypassing the
existing `virtual`). In the more medium term I think we
want to add `bcvk` as a provisioner option to tmt.
Anyways for now, this works by discovers test plans via `tmt plan ls`,
spawning a separate VM per test, and then using uses tmt's connect
provisioner to run tests targeting these externally provisioned
systems.
Part 2: Rework the Justfile and Dockerfile
This adds `base` and `variant` arguments which are propagated through
the system, and we have a new `variant` for sealed composefs.
The readonly tests now pass with composefs.
Drop the continuous repo tests...as while we could keep
that it's actually a whole *other* entry in this matrix.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
TMT does not support systemd soft-reboots - it only detects reboots
by checking if /proc/stat btime changes, which doesn't happen during
soft-reboots. This caused test-custom-selinux-policy to hang when
running with bcvk (which allows actual soft-reboots), while it
accidentally passed with testcloud (which forced full VM reboots).
Add bug-soft-reboot.md documenting this limitation and update both
test files to reference it. Also remove --soft-reboot=auto from
test-custom-selinux-policy since we can't test it with TMT anyway.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
We were using composefs-native and composefs-backend interchangeably.
Replace all instances of `composefs-native` with `composefs-backend`
Move all composefs-backend options to a single struct so that we can
test for boolean instead of testing for Some/None for composefs-backend
options
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Signed-off-by: Colin Walters <walters@verbum.org>
Ensure that if a custom policy is added in a
Containerfile, the resulting deployment has the expected
labels as well.
Assisted by Claude Code
Signed-off-by: Joseph Marrero Corchado <jmarrero@redhat.com>
Since we're running this under tmt, the "system" bootc binaries were
built from source and injected into the integration disk image. No
need to rebuild these inside of the test job itself.
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
Doing this as a distinct commit so as to not conflate future
modifications with the bulk code movement.
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
Add test-26-examples-build to validate that the bootc-bls and bootc-uki
example build scripts work correctly.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: John Eckersberg <jeckersb@redhat.com>