loader-entries: Add set-options-for-source for source-tracked kargs#2114
loader-entries: Add set-options-for-source for source-tracked kargs#2114jmarrero merged 2 commits intobootc-dev:mainfrom
Conversation
|
Draft while I continue testing. |
There was a problem hiding this comment.
Code Review
This pull request introduces the loader-entries subcommand to manage kernel arguments from multiple independent sources by tracking them via extension keys in BLS configuration files. The implementation includes logic for merging source-specific options and staging new deployments. Review feedback identifies a logic gap in source discovery for staged deployments that could lead to incorrect merging or duplicate arguments, and also recommends replacing blocking synchronous I/O with asynchronous alternatives to prevent performance issues within the async executor.
cgwalters
left a comment
There was a problem hiding this comment.
I only did a quick skim, mostly style stuff so far
When set-options-for-source is called multiple times before rebooting, the second call needs to find the target source's previous value in the staged deployment's bootconfig (not the booted BLS, since we haven't rebooted). Without this, the old kargs from the first call would not be removed from the options line, causing duplication. Fix by explicitly checking the target source in the staged bootconfig after iterating known sources from the booted BLS entry. Addresses review feedback from PR bootc-dev#2114. Assisted-by: OpenCode (Claude claude-opus-4-6)
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces the bootc loader-entries set-options-for-source subcommand, enabling independent management of kernel arguments from multiple sources (e.g., TuneD, admin) via custom x-options-source-* BLS extension keys. The changes include a new loader_entries module, CLI integration, documentation, and integration tests, along with a mechanism to build and inject a patched ostree for development. Feedback identifies a fragile string matching logic for identifying BLS entries, a logic bug that could lead to dropped source keys when multiple updates occur before a reboot, and the requirement to enable the ostree version check to ensure necessary bootconfig-extra support.
0db16dc to
132ab41
Compare
Is how this was tested locally. |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to manage kernel arguments from multiple independent sources (such as TuneD or system administrators) by tracking ownership via x-options-source-<name> extension keys in Boot Loader Specification (BLS) entries. It adds a new bootc loader-entries set-options-for-source command, updates the build system to allow injecting custom-built ostree RPMs (required for the new bootconfig-extra support), and includes comprehensive integration tests and documentation. Feedback focuses on the ostree version check logic, potential edge cases in BLS key parsing, and maintaining consistency when handling empty 'tombstone' values in the boot configuration.
| let Some((source_name, value)) = rest.split_once(|c: char| c.is_ascii_whitespace()) else { | ||
| continue; | ||
| }; |
There was a problem hiding this comment.
The current logic for parsing x-options-source-* keys relies on split_once with whitespace. If a key exists in the BLS file but has no value (e.g., just x-options-source-tuned at the end of a line), it will be silently ignored. While a source without options might seem useless, it could be used to track the presence of a tool. Consider handling the case where no whitespace is present to at least recognize the source name.
| if content.lines().any(|line| { | ||
| line.starts_with("options ") | ||
| && line.split_ascii_whitespace().any(|arg| { | ||
| arg.strip_prefix("ostree=") | ||
| .is_some_and(|path| path.ends_with(&ostree_match)) | ||
| }) | ||
| }) { |
There was a problem hiding this comment.
Matching the BLS entry by manually parsing lines for options and checking for ostree= is somewhat fragile. While BootconfigParser doesn't expose key iteration, it might be safer to use it to parse the options line properly rather than relying on split_ascii_whitespace which might fail if arguments contain escaped spaces or other complex shell-like syntax.
| // them to empty string. BootconfigParser has no remove() API, so "" | ||
| // acts as a tombstone. An empty x-options-source-* key is harmless: | ||
| // extract_source_options_from_bls will parse it as an empty value, | ||
| // and the idempotency check skips empty values (!val.is_empty()). |
There was a problem hiding this comment.
Using an empty string as a 'tombstone' for BootconfigParser is a clever workaround for the lack of a remove() API. However, ensure that extract_source_options_from_bls consistently ignores these empty values. Currently, it inserts them into the map, and they are only filtered out during discovery in set_options_for_source_staged (line 252). It might be cleaner to filter them out directly in extract_source_options_from_bls if the value is empty after trimming.
References
- Maintain consistent handling of empty or 'tombstone' values across different parts of the module to avoid unexpected behavior.
cgwalters
left a comment
There was a problem hiding this comment.
A quick skim, looking sane so far but I didn't deep dive yet
|
|
||
| #[test] | ||
| fn test_source_name_validation() { | ||
| assert!(SourceName::parse("tuned").is_ok()); |
There was a problem hiding this comment.
Prefer table driven tests - here and below I think we could significantly reduce duplication
| # 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. |
There was a problem hiding this comment.
Hmm. Yeah, tricky. I'm ok to special case this one for now, but...OTOH...it really generalizes fast. Seems like it'd be cleanest with a cosa-style overrides/rpms and overrides/srpms or so
| let r = do -i { bootc loader-entries set-options-for-source --source "my_custom-src" --options "testvalid=1" } | complete | ||
| if $r.exit_code != 0 { | ||
| print $"FAILED: valid source name returned exit code ($r.exit_code)" | ||
| print $"stdout: ($r.stdout)" | ||
| print $"stderr: ($r.stderr)" | ||
| } | ||
| assert ($r.exit_code == 0) "valid source name should succeed" |
There was a problem hiding this comment.
I think we can just drop the | complete and let nushell error naturally
(For some reason at least Opus seems to love this pattern of "capture command output and print" but to me it's just better to use normal error handling)
| assert ($r.exit_code == 0) "valid source name should succeed" | ||
|
|
||
| # Clear it immediately (no --options = remove source) | ||
| let r = do -i { bootc loader-entries set-options-for-source --source "my_custom-src" } | complete |
| assert ($r.exit_code == 0) "clearing source should succeed" | ||
|
|
||
| # -- Add source kargs -- | ||
| bootc loader-entries set-options-for-source --source tuned --options "nohz=full isolcpus=1-3" |
There was a problem hiding this comment.
Let's test multiple sources
| 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 |
There was a problem hiding this comment.
Yeah this one looks not at all ostree specific.
BTW...since right now we always build a "from scratch" image the other way we could do this is synthesize a dnf repo with a priority I think
| if let Some(new_opts) = new_options { | ||
| if !new_opts.is_empty() { |
There was a problem hiding this comment.
style nit I prefer e.g. if let Some() = new_options.filter(|v| !v.is_empty()) or so
| /// We match by checking the `options` line for the deployment's ostree path | ||
| /// (which includes the stateroot, bootcsum, and bootserial). | ||
| fn read_bls_entry_for_deployment(deployment: &ostree::Deployment) -> Result<Option<String>> { | ||
| let sysroot_dir = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())?; |
There was a problem hiding this comment.
In the general case we must be able to operate on a non-default sysroot. Let's just thread through our global storage struct here.
| pub(crate) fn set_options_for_source_staged( | ||
| sysroot: &ostree_ext::sysroot::SysrootLock, |
There was a problem hiding this comment.
I think this is fine...that said, there might be use cases for doing this in a non-staged way?
Also...should think about this in a composefs-based setup.
On bootc systems with transient /etc, the state file
/etc/tuned/bootcmdline is lost after each reboot. This causes
TuneD to lose track of which kernel arguments it previously
set, leading to kargs stacking up on every reboot cycle.
bootc loader-entries set-options-for-source solves this by
recording kargs ownership directly in the BLS config on /boot,
which persists across reboots regardless of /etc transience.
With --source tuned, bootc automatically removes all previous
kargs from the "tuned" source and replaces them with the new
set, eliminating the need for TuneD to maintain its own state
for diffing.
This commit adds bootc source tracking to the bootloader plugin:
1. Detect set-options-for-source availability by checking
"bootc loader-entries set-options-for-source --help" exit
code during plugin init.
2. When available, use "bootc loader-entries
set-options-for-source --source tuned --options ..." for
applying kargs and the same command without --options for
clearing all TuneD-owned kargs on profile removal.
3. The bootc path is a top-level dispatch branch, checked
before rpm-ostree. Fallback chain: bootc -> rpm-ostree
legacy --delete/--append -> GRUB2. All existing code paths
are unchanged.
4. Continue writing to /etc/tuned/bootcmdline as best-effort
for diagnostics and backward compatibility, but do not
depend on it for correctness when bootc source tracking
is available.
5. Add 15 unit tests for the bootloader plugin covering:
bootc detection (3), apply (3), removal (2), init flag
derivation (2), and top-level dispatch (5).
Requires: bootc with loader-entries set-options-for-source
(bootc PR #2114) and ostree >= 2026.1
See: bootc-dev/bootc#899
See: bootc-dev/bootc#2114
Assisted-by: OpenCode (Claude Opus 4.6)
9c32f79 to
6637e3a
Compare
Add a new `bootc loader-entries set-options-for-source` command that
manages kernel arguments from independent sources (e.g. TuneD, admin)
by tracking ownership via `x-options-source-<name>` extension keys in
BLS config files. This solves the problem of karg accumulation on bootc
systems with transient /etc, where tools like TuneD lose their state
files on reboot and cannot track which kargs they previously set.
The command stages a new deployment with the updated kargs and source
keys. The kargs diff is computed by removing the old source's args and
adding the new ones, preserving all untracked options. Source keys
survive the staging roundtrip via ostree's `bootconfig-extra`
serialization (ostree >= 2026.1, version check active).
Staged deployment handling:
- No staged deployment: stages based on the booted commit
- Staged deployment exists (e.g. from `bootc upgrade`): replaces it
using the staged commit and origin, preserving pending upgrades while
layering the source kargs change on top
Includes a multi-reboot TMT integration test covering: input validation,
kargs surviving staging roundtrip, source replacement, multiple sources
coexisting (including pre-reboot), source removal, idempotency, system
kargs preservation, empty --options vs no --options, and staged
deployment interaction with bootc switch.
Example usage:
bootc loader-entries set-options-for-source --source tuned \
--options "isolcpus=1-3 nohz_full=1-3"
Closes: bootc-dev#899
See: ostreedev/ostree#3570
Assisted-by: OpenCode (Claude Opus 4.6)
Signed-off-by: Joseph Marrero Corchado <jmarrero@redhat.com>
…xtra The loader-entries set-options-for-source feature requires ostree >= 2026.1 for the bootconfig-extra serialization that preserves x-options-source-* BLS keys through staged deployment roundtrips. ostree 2026.1 is not yet in the stable repos for any of the supported platforms. This commit adds a temporary block to install it from: - CentOS Stream 9/10: direct Koji RPM URLs (c9s-candidate/c10s-candidate) - Fedora 43/44: updates-testing repo (Bodhi) The block is guarded by a version check (rpm -q ostree | grep "2026\."), so once base images ship ostree >= 2026.1, this is a no-op and can be safely removed without urgency. xref ostreedev/ostree#3570 Assisted-by: OpenCode (Claude Opus 4.6) Signed-off-by: Joseph Marrero Corchado <jmarrero@redhat.com>
On bootc systems with transient /etc, the state file
/etc/tuned/bootcmdline is lost after each reboot. This causes
TuneD to lose track of which kernel arguments it previously
set, leading to kargs stacking up on every reboot cycle.
bootc loader-entries set-options-for-source solves this by
recording kargs ownership directly in the BLS config on /boot,
which persists across reboots regardless of /etc transience.
With --source tuned, bootc automatically removes all previous
kargs from the "tuned" source and replaces them with the new
set, eliminating the need for TuneD to maintain its own state
for diffing.
This commit adds bootc source tracking to the bootloader plugin:
1. Detect set-options-for-source availability by checking
"bootc loader-entries set-options-for-source --help" exit
code during plugin init.
2. When available, use "bootc loader-entries
set-options-for-source --source tuned --options ..." for
applying kargs and the same command without --options for
clearing all TuneD-owned kargs on profile removal.
3. The bootc path is a top-level dispatch branch, checked
before rpm-ostree. Fallback chain: bootc -> rpm-ostree
legacy --delete/--append -> GRUB2. All existing code paths
are unchanged.
4. Continue writing to /etc/tuned/bootcmdline as best-effort
for diagnostics and backward compatibility, but do not
depend on it for correctness when bootc source tracking
is available.
5. Ensure the bootloader plugin is always loaded on bootc
systems, even for profiles without a [bootloader] section.
Without this, switching from a profile with kargs (e.g.
network-latency) to one without (e.g. throughput-performance)
after a reboot would leave stale kargs behind, because the
bootloader plugin would never be instantiated and its
unapply/clear logic would never run. The daemon now injects
a synthetic bootloader unit when bootc source tracking is
available, and the plugin declares the empty kargs state to
bootc so stale kargs from a previous profile are cleared.
6. Add 20 unit tests covering: bootc detection (3), apply (3),
removal (2), init flag derivation (2), top-level dispatch (5),
stale kargs clearing (2), and daemon bootloader unit
injection (3).
Requires: bootc with loader-entries set-options-for-source
(bootc PR #2114) and ostree >= 2026.1
See: bootc-dev/bootc#899
See: bootc-dev/bootc#2114
Assisted-by: OpenCode (Claude Opus 4.6)
Signed-off-by: Joseph Marrero Corchado <jmarrero@redhat.com>
Add a new
bootc loader-entries set-options-for-sourcecommand thatmanages kernel arguments from independent sources (e.g. TuneD, admin)
by tracking ownership via
x-options-source-<name>extension keys inBLS config files. This solves the problem of karg accumulation on bootc
systems with transient /etc, where tools like TuneD lose their state
files on reboot and can't track which kargs they previously set.
The command stages a new deployment with the updated kargs and source
keys. The kargs diff is computed by removing the old source's args and
adding the new ones, preserving all untracked options. Source keys
survive the staging roundtrip via ostree's
bootconfig-extraserialization (ostree >= 2026.1, version check).
Staged deployment handling:
bootc upgrade): replaces itusing the staged commit and origin, preserving pending upgrades while
layering the source kargs change on top
Includes a multi-reboot TMT integration test covering: input validation,
kargs surviving staging roundtrip, source replacement, multiple sources
coexisting, source removal, idempotency, system kargs preservation,
empty --options vs no --options, and staged deployment interaction with
bootc switch.
Example usage:
bootc loader-entries set-options-for-source --source tuned
--options "isolcpus=1-3 nohz_full=1-3"
See: ostreedev/ostree#3570
Assisted-by: OpenCode (Claude Opus 4.6)
Signed-off-by: Joseph Marrero Corchado jmarrero@redhat.com