Skip to content

fix: Fix incorrect changed reporting in bootloader_settings module#222

Merged
spetrosi merged 1 commit into
linux-system-roles:mainfrom
spetrosi:check-mode-save-state
Jul 21, 2026
Merged

fix: Fix incorrect changed reporting in bootloader_settings module#222
spetrosi merged 1 commit into
linux-system-roles:mainfrom
spetrosi:check-mode-save-state

Conversation

@spetrosi

@spetrosi spetrosi commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Enhancement: Fix the module reporting incorrect changed status in several cases

Reason: Sometimes the role reported incorrect changed status.

Result: Now changed is computed once after the loop from the actions list. Add needs_replacement() to skip the remove+re-add cycle for previous: replaced when current args already match desired args.

Issue Tracker Tickets (Jira or BZ if any): https://redhat.atlassian.net/browse/RHEL-180862

Assisted-by: Cursor AI

Summary by CodeRabbit

  • Bug Fixes
    • Prevented unnecessary bootloader updates when replacement arguments already match the current configuration, including correct handling of reordered tokens.
    • Hardened boot-argument removal to safely do nothing when there are no existing boot arguments.
    • Improved “changed” reporting so it reflects whether any bootloader actions were actually prepared/executed.
  • Tests
    • Expanded unit tests for replacement token construction, idempotency, no-op behavior with empty arguments, and correct action/change tracking (including check mode).

@spetrosi
spetrosi requested a review from richm as a code owner July 20, 2026 17:17
@spetrosi

Copy link
Copy Markdown
Contributor Author

● Here are three scenarios where the old code misbehaves and ours fixes it:

  1. changed overwrite across iterations

bootloader_settings:
# Setting 1: Add a NEW arg to DEFAULT kernel
- kernel: DEFAULT
options:
- name: mitigations
value: auto
# Setting 2: Ensure 'quiet' is present (it already is)
- kernel: DEFAULT
options:
- name: quiet

Old behavior: Iteration 1 adds mitigations=auto → apply_command sets changed=True.
Iteration 2 finds quiet already present → mod_boot_args line 436 sets
changed=False. Module reports changed: false even though it just added
mitigations=auto.

New behavior: Both iterations record actions in result["actions"]. After the loop,
changed = len(actions) > 0 → changed: true. On a second run, neither iteration
generates actions → changed: false.

  1. previous: replaced never idempotent

bootloader_settings:
- kernel: DEFAULT
options:
- previous: replaced
- name: quiet
- name: ro

Assume the DEFAULT kernel already has exactly args="quiet ro".

Old behavior: rm_boot_args unconditionally generates grubby --remove-args='quiet
ro' → changed=True. Then mod_boot_args adds them back. Every run reports changed:
true and triggers handlers (grub rebuild, potentially a reboot) for no reason.

New behavior: needs_replacement() compares sorted(["quiet","ro"]) against
sorted(["quiet","ro"]) → equal → skips rm_boot_args. mod_boot_args finds both args
present → no commands. Reports changed: false.

  1. Empty args with previous: replaced

bootloader_settings:
- kernel:
path: /boot/vmlinuz-rescue
options:
- previous: replaced
- name: ro

Assume the rescue kernel has args="" (no args).

Old behavior: rm_boot_args generates grubby --remove-args='' (a no-op command) →
changed=True. Then mod_boot_args adds ro.

New behavior: rm_boot_args sees empty args → returns early, no no-op command
recorded. mod_boot_args adds ro → one clean action. On second run with args="ro",
needs_replacement finds it matches → no commands → changed: false.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: be7f64cd-a003-4931-a1f4-fafbf1fb2fba

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe4693 and 0a59605.

📒 Files selected for processing (2)
  • library/bootloader_settings.py
  • tests/unit/test_bootloader_settings.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • library/bootloader_settings.py
  • tests/unit/test_bootloader_settings.py

📝 Walkthrough

Walkthrough

Changes

The bootloader module now derives changed from recorded grubby actions, skips empty argument removals, and checks replacement idempotency by comparing desired and current tokens. Unit tests cover these behaviors and retain command/action assertions.

Bootloader settings behavior

Layer / File(s) Summary
Command recording and changed aggregation
library/bootloader_settings.py, tests/unit/test_bootloader_settings.py
Commands are recorded through actions, changed is finalized in run_module, and empty boot-argument removals do not issue grubby commands.
Idempotent replaced-argument flow
library/bootloader_settings.py, tests/unit/test_bootloader_settings.py
Replacement tokens are built and compared with current kernel arguments before removal; tests cover ordering, duplicates, absent options, and matching or mismatching arguments.

Possibly related PRs

Suggested reviewers: richm

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description Format ⚠️ Warning Has Enhancement/Reason/Result, but lacks the required Signed-off-by section. Add a Signed-off-by: line with your name and email, per the PR template.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title uses Conventional Commits format and accurately summarizes the module's changed-reporting fix.
Description check ✅ Passed The description follows the required template and includes Enhancement, Reason, Result, and an issue tracker ticket.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.44%. Comparing base (63d7c0c) to head (134c160).
⚠️ Report is 127 commits behind head on main.

Files with missing lines Patch % Lines
library/bootloader_settings.py 88.88% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #222      +/-   ##
==========================================
+ Coverage   78.43%   80.44%   +2.00%     
==========================================
  Files           2        3       +1     
  Lines         255      363     +108     
==========================================
+ Hits          200      292      +92     
- Misses         55       71      +16     
Flag Coverage Δ
sanity ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
library/bootloader_settings.py (1)

471-497: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Guard against divergence between get_replaced_args and mod_boot_args.

needs_replacement relies on get_replaced_args producing exactly the arg set mod_boot_args would leave after a full removal (duplicate-name dedup, absent filtering, etc.). These two are computed independently. If mod_boot_args normalization ever changes without a matching update here, needs_replacement could return False while a replacement is still needed, silently leaving stale args — the exact idempotency failure this PR targets. Consider a unit test asserting the desired token set from get_replaced_args matches the post-replacement result of mod_boot_args for the duplicate/absent cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@library/bootloader_settings.py` around lines 471 - 497, Add a unit test
covering duplicate-name and absent options that compares the token set returned
by get_replaced_args with the arguments produced by mod_boot_args after a full
replacement/removal operation. Use this test to enforce that both paths apply
identical deduplication and absent filtering, preserving needs_replacement
idempotency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@library/bootloader_settings.py`:
- Around line 471-497: Add a unit test covering duplicate-name and absent
options that compares the token set returned by get_replaced_args with the
arguments produced by mod_boot_args after a full replacement/removal operation.
Use this test to enforce that both paths apply identical deduplication and
absent filtering, preserving needs_replacement idempotency.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 91948e37-72b1-4e07-a5f5-425dacb93e1f

📥 Commits

Reviewing files that changed from the base of the PR and between 9f60d38 and 4fe4693.

📒 Files selected for processing (2)
  • library/bootloader_settings.py
  • tests/unit/test_bootloader_settings.py

+ "arg_with_int_value_absent=1 arg_without_val_absent' --copy-default"
)
self.mock_module.run_command.assert_called_once_with(expected_cmd)
self.assertEqual(self.result["changed"], True)

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.

change this to self.assertEqual(self.result["changed"], False) if nothing is changed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes that's better

@spetrosi
spetrosi force-pushed the check-mode-save-state branch from 4fe4693 to 134c160 Compare July 20, 2026 18:16
@spetrosi

Copy link
Copy Markdown
Contributor Author

[citest]

@richm

richm commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

lgtm - just fix the python black formatting then merge

Fix the module reporting incorrect changed status in several cases:
- apply_command() unconditionally set changed=True on every write,
  even when the system was already in the desired state
- mod_boot_args() reset changed=False when no diff was found,
  overwriting a legitimate changed=True from a prior loop iteration
- previous: replaced always reported changed=True by running
  rm_boot_args + mod_boot_args even when current args already
  matched the desired state
- rm_boot_args generated a no-op grubby command when the kernel
  had no args to remove

Now changed is computed once after the loop from the actions list.
Add needs_replacement() to skip the remove+re-add cycle for
previous: replaced when current args already match desired args.

Update unit tests to match, add tests for new functions.

Assisted-by: Cursor AI
@spetrosi
spetrosi force-pushed the check-mode-save-state branch from 134c160 to 0a59605 Compare July 21, 2026 09:48
@spetrosi

Copy link
Copy Markdown
Contributor Author

[citest]

@spetrosi
spetrosi merged commit 9bc9805 into linux-system-roles:main Jul 21, 2026
24 of 53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants