Skip to content

loader-entries: Add set-options-for-source for source-tracked kargs#2114

Merged
jmarrero merged 2 commits intobootc-dev:mainfrom
jmarrero:kargs-source
Apr 23, 2026
Merged

loader-entries: Add set-options-for-source for source-tracked kargs#2114
jmarrero merged 2 commits intobootc-dev:mainfrom
jmarrero:kargs-source

Conversation

@jmarrero
Copy link
Copy Markdown
Contributor

@jmarrero jmarrero commented Apr 1, 2026

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 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-extra
serialization (ostree >= 2026.1, version check).

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, 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

@github-actions github-actions Bot added the area/documentation Updates to the documentation label Apr 1, 2026
@bootc-bot bootc-bot Bot requested a review from ckyrouac April 1, 2026 21:13
@jmarrero
Copy link
Copy Markdown
Contributor Author

jmarrero commented Apr 1, 2026

Draft while I continue testing.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread crates/lib/src/loader_entries.rs Outdated
Comment thread crates/lib/src/loader_entries.rs Outdated
Copy link
Copy Markdown
Collaborator

@cgwalters cgwalters left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only did a quick skim, mostly style stuff so far

Comment thread crates/lib/src/loader_entries.rs Outdated
Comment thread crates/lib/src/loader_entries.rs Outdated
Comment thread crates/lib/src/loader_entries.rs Outdated
Comment thread crates/lib/src/loader_entries.rs Outdated
Comment thread crates/lib/src/loader_entries.rs Outdated
Comment thread crates/lib/src/loader_entries.rs Outdated
jmarrero added a commit to jmarrero/bootc that referenced this pull request Apr 2, 2026
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)
@jmarrero jmarrero changed the title Kargs source loader-entries: Add set-options-for-source for source-tracked kargs Apr 3, 2026
@jmarrero
Copy link
Copy Markdown
Contributor Author

jmarrero commented Apr 3, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread crates/lib/src/loader_entries.rs Outdated
Comment thread crates/lib/src/loader_entries.rs
Comment thread crates/lib/src/loader_entries.rs Outdated
@jmarrero jmarrero force-pushed the kargs-source branch 2 times, most recently from 0db16dc to 132ab41 Compare April 3, 2026 02:24
@jmarrero
Copy link
Copy Markdown
Contributor Author

jmarrero commented Apr 3, 2026

$ PATH="~/development/github/bootc-dev/bcvk/target/release:$PATH" BOOTC_ostree_src=~/development/github/ostreedev/ostree just test-tmt loader-entries-source 2>&1

Is how this was tested locally.

@jmarrero
Copy link
Copy Markdown
Contributor Author

jmarrero commented Apr 3, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread crates/lib/src/loader_entries.rs
Comment on lines +70 to +72
let Some((source_name, value)) = rest.split_once(|c: char| c.is_ascii_whitespace()) else {
continue;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Comment on lines +155 to +161
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))
})
}) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Comment on lines +309 to +312
// 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()).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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
  1. Maintain consistent handling of empty or 'tombstone' values across different parts of the module to avoid unexpected behavior.

@cgwalters cgwalters self-requested a review April 8, 2026 18:00
Copy link
Copy Markdown
Collaborator

@cgwalters cgwalters left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick skim, looking sane so far but I didn't deep dive yet

Comment thread crates/lib/src/loader_entries.rs Outdated

#[test]
fn test_source_name_validation() {
assert!(SourceName::parse("tuned").is_ok());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer table driven tests - here and below I think we could significantly reduce duplication

Comment thread Justfile Outdated
# 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.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +79 to +85
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"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

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"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's test multiple sources

Comment thread Dockerfile Outdated
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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread crates/lib/src/loader_entries.rs Outdated
Comment on lines +108 to +109
if let Some(new_opts) = new_options {
if !new_opts.is_empty() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit I prefer e.g. if let Some() = new_options.filter(|v| !v.is_empty()) or so

Comment thread crates/lib/src/loader_entries.rs Outdated
/// 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())?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +183 to +184
pub(crate) fn set_options_for_source_staged(
sysroot: &ostree_ext::sysroot::SysrootLock,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

cgwalters
cgwalters previously approved these changes Apr 16, 2026
jmarrero added a commit to jmarrero/tuned that referenced this pull request Apr 17, 2026
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)
@jmarrero jmarrero force-pushed the kargs-source branch 3 times, most recently from 9c32f79 to 6637e3a Compare April 18, 2026 00:15
@jmarrero jmarrero marked this pull request as ready for review April 20, 2026 21:29
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>
@jmarrero jmarrero merged commit 8ef2ae9 into bootc-dev:main Apr 23, 2026
93 of 103 checks passed
jmarrero added a commit to jmarrero/tuned that referenced this pull request Apr 24, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/documentation Updates to the documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants