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>
I was trying to cut over to computing the digest directly
from the mounted filesystem in build-sealed because it's WAY
cleaner but ran face first into
https://github.com/containers/composefs-rs/issues/132
What *really* helped me debug this was this patch which adds
support for printing the dumpfile corresponding to the two
composefs trees so I could `diff -u` on them.
Signed-off-by: Colin Walters <walters@verbum.org>
Change `BootedStorage::new()` to return `Result<Option<Self>>` instead
of taking a `prep_for_write` boolean parameter. This makes the API more
idiomatic and clearer about when the system is not booted via bootc.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
For ostree systems, before making any changes to the repo/boot entries
we call `prepare_for_write`. Before this we were calling it
unconditionally even if the command was `bootc status`, which was
causing test failures.
We pass in a flag now to `BootedStorage::new` which will conditionally
call `prepare_for_write`. We also need this to make sure we keep the API
more or less similar to what we had before
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Signed-off-by: Colin Walters <walters@verbum.org>
Check for verity inside the json returned by `bootc status --json`
and compare it with the compsefs digest from kernel cmdline
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Signed-off-by: Colin Walters <walters@verbum.org>
There were a few checks that asserted ostree booted system even for
systems booted via composefs. Move the checks in `Storage::new`
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Signed-off-by: Colin Walters <walters@verbum.org>
Refactor `get_sysroot_parent_dev()` to accept a `physical_root: &Dir`
parameter instead of hardcoding access to `/sysroot`. This continues
the ongoing work to eliminate global state access in the composefs
backend, following the same pattern as the recent BootedComposefs
and BootedOstree refactoring.
The function now uses `inspect_filesystem_of_dir()` which operates
on a Dir fd.
All callers updated to pass `&storage.physical_root` or open a Dir
for the upgrade code paths. Helper functions like `list_bootloader_entries()`
and `delete_depl_boot_entries()` also updated to accept and pass down
the physical_root parameter.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Add a `cwd: Option<&Dir>` parameter to `run_findmnt()` to support
running findmnt with a working directory context. This allows
inspecting filesystems using a Dir fd rather than requiring
absolute paths.
Also add `inspect_filesystem_of_dir()` as a convenience wrapper
that inspects the filesystem of a Dir by calling findmnt on "."
with the directory as cwd.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Update composefs_gc(), composefs_backend_finalize(), and get_etc_diff() to accept
Storage and BootedComposefs parameters instead of calling composefs_deployment_status()
and opening hardcoded paths.
Changes:
- composefs_gc() now accepts storage and booted_cfs parameters
- composefs_backend_finalize() uses storage.physical_root instead of hardcoded "/sysroot"
- get_etc_diff() uses storage.physical_root instead of hardcoded "/sysroot"
- Update CLI dispatchers to use BootedStorageKind pattern for finalize and config-diff
- Remove unused imports from finalize.rs (open_dir, CWD)
This eliminates remaining composefs_deployment_status() calls from gc.rs and finalize.rs,
and removes several hardcoded "/sysroot" paths.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Update composefs command functions to accept and use Storage and BootedComposefs
parameters instead of calling composefs_deployment_status() which scrapes global
state.
Changes:
- Add get_composefs_status(storage, booted_cfs) helper in status.rs
- Update upgrade_composefs() to use passed composefs.repo and get_composefs_status()
- Update switch_composefs() to accept and use storage/booted_cfs parameters
- Update composefs_rollback() to use storage.physical_root for opening directories
- Update delete_composefs_deployment() to accept and pass through parameters
- Update CLI dispatchers to pass storage and booted_cfs to all composefs functions
This eliminates composefs_deployment_status() calls from command functions,
completing the pattern established with BootedOstree for ostree commands.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Apply the same pattern used in upgrade() to switch, rollback, and edit
commands. This eliminates redundant composefs_booted() calls from the
main CLI code paths by consistently using storage.kind() for dispatch.
Changes:
- Split switch/rollback/edit into dispatcher + _ostree() helper functions
- Update get_status_require_booted() to return BootedOstree instead of
separate deployment/sysroot components
- Update soft_reboot_rollback() to accept &BootedOstree
- Update deploy::rollback() and install_reset() for new types
- All command verbs now follow consistent pattern:
storage.kind()? → match BootedStorageKind → call helper with &BootedOstree
This change eliminates all composefs_booted() calls from crates/lib/src/cli.rs
main code paths, passing boot context data down from BootedStorage instead.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Refactor the storage layer to introduce new types that better encapsulate
boot context:
- Add BootedStorage wrapper that detects ostree vs composefs boot
- Add BootedOstree struct holding sysroot reference and deployment
- Add BootedComposefs struct holding repository and cmdline
- Add BootedStorageKind enum to discriminate between backends
This allows passing boot context as a cohesive unit rather than separate
parameters. Update the upgrade code path to use these new types:
- Change get_status() to accept &BootedOstree instead of separate params
- Update handle_staged_soft_reboot() similarly
- Update upgrade() to use structs throughout
Add comprehensive documentation to all new types and methods in store/mod.rs.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Use implicit return expressions instead of explicit `return` statements
for the final expression in functions, following Rust conventions.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
This commit refactors the kernel_cmdline parsing implementation to
reduce code duplication and provide a clearer separation of concerns.
Key changes:
1. Introduced CmdlineIterBytes as a dedicated iterator for splitting
the command line into raw parameter byte slices. This centralizes
the quote-aware whitespace splitting logic in one place.
2. Refactored CmdlineIter to wrap CmdlineIterBytes instead of
duplicating the splitting logic. This eliminates redundant code
and ensures consistent behavior.
3. Consolidated Parameter::parse and Parameter::parse_one into a
single parse() method. Parameter::parse now uses CmdlineIterBytes
directly to find the first parameter, then passes it to
parse_internal() which assumes the input is already a single
parameter.
4. Added iter_bytes() and iter_str() methods to Cmdline for cases
where users need raw byte slices or strings without full Parameter
parsing overhead.
5. Removed utf8::Parameter::parse_one() in favor of a simplified
parse() that wraps the bytes implementation.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
If `/boot` is a separate partition, the kernel + initrd paths need to be
absolute to `/`, which was not the case until now as they were always
absolute to the actual rootfs
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Add a new `add` method to both `bytes::Cmdline` and `utf8::Cmdline`
that appends parameters without modifying existing ones. Unlike
`add_or_modify`, this method preserves duplicate keys with different
values (e.g., multiple `console=` parameters), which is valid for
certain kernel parameters.
The method returns `Action::Added` when a new parameter is appended,
and `Action::Existed` when the exact parameter already exists.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
This makes it ergonomic to take two `Cmdline`s and update one by
appending the parameters in the other.
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
In some cases it will be important to differentiate whether we added a
net-new parameter vs. modifying an existing parameter.
Motivated by trying to update bootc_kargs to use kernel_cmdline. We
want to parse the booted kargs, and then merge with any args defined
in kargs.d. But it should error if a value in kargs.d already exists
in the booted system with a different value. The previous API did not
provide enough visibility to make this determiniation.
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
This is an obvious thing one might want to do. Ensures we can make an
owned Cmdline directly from an owned String.
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
build-sys: Run most parts with `--network=none`
Why? It just shows that we have put some thought into our
build system and care about reproducibility, hermetic builds etc.
And yes of course, `--network=bridge` should probably have been
required as an opt-in in Dockerfile, but oh well. It's not too
bad to sprinkle `--network=none` in some places. The biggest one
is wrapping `make`.
Signed-off-by: Colin Walters <walters@verbum.org>
In the future, we may want to "shard" tests across multiple runners,
but as is right now it's basically just adding overhead to copy
the disk image as an artifact across the job.
While we're here, clean things up further to match the general
principle that GHA flows should mostly just be running `just`.
The logic from build.sh to map from strings -> containers though
moves into the GHA for now.
Signed-off-by: Colin Walters <walters@verbum.org>
Handle the case when an update is already staged. Handle the case when
the staged image is not the same as the update image. Implement `--check`
option
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
We need more space than ostree as we have UKIs and UKI addons
We might also need to store UKIs for pinned deployments
Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
Add logic to copy the /boot mount specification from the existing
/etc/fstab to the newly created stateroot during factory reset. This
ensures that the /boot partition configuration is preserved across
the reset operation.
Includes helper function read_boot_fstab_entry() to parse /etc/fstab
and locate the /boot mount entry, along with comprehensive unit tests.
Assisted-by: Claude Code
Signed-off-by: ckyrouac <ckyrouac@redhat.com>
Add ostree version check to has_soft_reboot_capability() to ensure
soft reboot is disabled when ostree < 2025.7 and ostree= karg is
missing.
This prevents attempting soft reboots on older ostree versions that
have a bug when validating kargs during a factory reset.
Signed-off-by: ckyrouac <ckyrouac@redhat.com>
While composefs is still experimental, after looking at this
I think the feature gating we're doing has a pretty high "pain:gain"
ratio - in other words, the risk we're mitigating by having it
off is very low.
Since composefs is a focus of development, let's just remove
the feature gate. We have good CI coverage for the non-composefs
case.
Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>