Skip to content

fix: Add a missing worker node guard file cleanup - #65

Open
jmontleon wants to merge 1 commit into
medik8s:mainfrom
jmontleon:add-missing-worker-cleanup
Open

fix: Add a missing worker node guard file cleanup#65
jmontleon wants to merge 1 commit into
medik8s:mainfrom
jmontleon:add-missing-worker-cleanup

Conversation

@jmontleon

@jmontleon jmontleon commented Aug 3, 2026

Copy link
Copy Markdown

After a hard reboot, the oc debug pod left behind by StopKubelet gets re-executed by kubelet (CRI-O cleans stale containers on boot, so kubelet sees no record of the pod having run and starts it fresh). This stops kubelet ~4 seconds after it starts, causing test flakes.

Write a guard file on the host (/var/tmp/.medik8s-kubelet-stop-guard) before stopping kubelet. On re-execution after reboot, the guard file is found and the stop is skipped. The guard is removed in BeforeEach (handles aborted prior runs) and JustAfterEach (normal cleanup).

Why we need this PR

Changes made

Which issue(s) this PR fixes

Test plan

Summary by CodeRabbit

  • Bug Fixes
    • Improved kubelet stop-guard handling to avoid duplicate stop operations.
    • Added more resilient guard cleanup when kubelet stopping fails.
    • Enhanced success/failure reporting for guard removal during remediation workflows.
  • Tests
    • Strengthened master and worker remediation test setup/teardown by asserting guard cleanup success and removing stale worker guards before execution.
    • Added best-effort guard removal during safety-net cleanup to reduce remediation test flakiness.

@openshift-ci
openshift-ci Bot requested review from mpryc and ugreener August 3, 2026 21:14
@openshift-ci openshift-ci Bot added the approved label Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 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: 9e128d0e-56d3-49b4-87e1-e663be636c24

📥 Commits

Reviewing files that changed from the base of the PR and between 11c924d and 5f44eff.

📒 Files selected for processing (4)
  • tests/internal/helpers/node_ops.go
  • tests/snr-operator/tests/helpers.go
  • tests/snr-operator/tests/master_remediation.go
  • tests/snr-operator/tests/worker_remediation.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/internal/helpers/node_ops.go
  • tests/snr-operator/tests/master_remediation.go
  • tests/snr-operator/tests/worker_remediation.go

📝 Walkthrough

Walkthrough

The PR centralizes the kubelet stop-guard path, reports skipped or failed guard operations, and strengthens stale-guard cleanup in master and worker remediation tests.

Changes

Kubelet stop-guard handling

Layer / File(s) Summary
StopKubelet guard handling
tests/internal/helpers/node_ops.go
StopKubelet uses the shared guard path, reports GUARD_SKIP, and removes the guard when kubelet stopping fails.
Remediation guard cleanup
tests/snr-operator/tests/helpers.go, tests/snr-operator/tests/master_remediation.go, tests/snr-operator/tests/worker_remediation.go
Setup asserts stale-guard removal. Teardown uses best-effort cleanup with warnings and report entries. Simultaneous remediation also cleans the worker guard.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: lgtm

Suggested reviewers: mpryc, gamado, ugreener

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: adding cleanup for a missing worker-node guard file.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@jmontleon

Copy link
Copy Markdown
Author

This PR also raises a warning if the before/after cleanup fails instead of silently ignoring it.

@qodo-2-for-medik8s

Copy link
Copy Markdown

PR Summary by Qodo

Fix SNR tests by reliably cleaning up kubelet stop guard on workers/masters

🐞 Bug fix 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Fail fast if a stale kubelet-stop guard cannot be removed before running remediation tests.
• Add missing worker-node guard cleanup for the master+worker simultaneous remediation scenario.
• Log/report guard cleanup failures during safety-net teardown instead of silently ignoring them.
Diagram

graph TD
A["Ginkgo BeforeEach"] --> B["RemoveKubeletStopGuard (master/worker)"] --> C["stopKubeletForRemediation"] --> D["helpers.StopKubelet (writes guard)"] --> E["SNR remediation + reboot"] --> F["Ginkgo JustAfterEach (Ready + guard cleanup)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Centralize guard cleanup in stopKubeletForRemediation
  • ➕ Reduces the chance future tests forget to remove the guard file
  • ➕ Keeps setup/teardown semantics consistent across suites
  • ➖ Less explicit at call sites; harder to reason about when cleanup happens
  • ➖ May require reworking helpers used by multiple suites
2. Make teardown guard cleanup fail the test
  • ➕ Prevents guard-file leaks from accumulating across CI runs
  • ➖ Can mask the primary test failure and increase flakiness if oc debug is transiently unavailable during teardown

Recommendation: The PR’s approach is appropriate: treat stale-guard removal in BeforeEach as a hard precondition (fail fast), and keep JustAfterEach cleanup best-effort with explicit warnings/report entries to avoid hiding the real failure. Centralizing cleanup in the stop helper could be considered later to prevent future omissions, but isn’t required to address the current flake.

Files changed (2) +33 / -4

Tests (2) +33 / -4
master_remediation.goAdd worker guard cleanup + stricter guard handling in setup/teardown +24/-2

Add worker guard cleanup + stricter guard handling in setup/teardown

• BeforeEach now asserts that the master guard file is removed successfully instead of ignoring errors. The simultaneous master+worker remediation test also removes any stale guard on the worker before starting. JustAfterEach performs best-effort guard cleanup with warnings and a report entry if removal fails.

tests/snr-operator/tests/master_remediation.go

worker_remediation.goMake guard removal required in setup and report failures during teardown +9/-2

Make guard removal required in setup and report failures during teardown

• BeforeEach now fails the test if the worker guard file cannot be removed, preventing stale state from contaminating runs. JustAfterEach no longer silently ignores guard cleanup errors; it logs a warning and adds a report entry to aid debugging.

tests/snr-operator/tests/worker_remediation.go

@qodo-2-for-medik8s

qodo-2-for-medik8s Bot commented Aug 3, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Worker guard cleanup not guaranteed ✗ Dismissed 🐞 Bug ☼ Reliability
Description
In the master+worker remediation test, the worker guard file is removed only on the happy-path near
the end of the test body, so any failure/abort before that point can leave the guard behind on the
worker. A stale guard makes future helpers.StopKubelet calls silently no-op (exit 0), which can
invalidate later tests that use the same node and don’t remove the guard first.
Code

tests/snr-operator/tests/master_remediation.go[R319-322]

+				By("Removing kubelet stop guard on worker")
+
+				if guardErr := helpers.RemoveKubeletStopGuard(ctx, targetWorkerName, snrparams.OcDebugTimeout); guardErr != nil {
+					GinkgoWriter.Printf(
Relevance

●●● Strong

Team repeatedly adds safety-net/DeferCleanup to avoid leaking state across tests (PR22, PR28, PR52).

PR-#22
PR-#28
PR-#52

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The worker guard is only removed at the end of the simultaneous test, while the suite safety-net
cleanup in JustAfterEach only targets the master. Because helpers.StopKubelet exits successfully
when the guard exists, leaving the guard behind can cause later kubelet-stop operations on the same
worker to be skipped, and other suites call helpers.StopKubelet without any guard removal step.

tests/snr-operator/tests/master_remediation.go[76-114]
tests/snr-operator/tests/master_remediation.go[240-327]
tests/internal/helpers/node_ops.go[71-107]
tests/far-operator/internal/farutils/node.go[19-25]
tests/mdr-operator/tests/mdr_helpers.go[133-154]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
In `master_remediation.go` (simultaneous master+worker test), worker guard removal is performed inline at the end of the test. If the spec exits early (assertion failure, timeout, interruption), the worker guard file can remain and cause later `StopKubelet` calls on that worker to be skipped (because `StopKubelet` exits 0 when the guard exists).

### Issue Context
The suite’s `JustAfterEach` safety-net currently cleans up only the master node guard. Other suites (e.g., FAR/MDR) call `helpers.StopKubelet` but do not clear the guard beforehand.

### Fix Focus Areas
- tests/snr-operator/tests/master_remediation.go[76-114]
- tests/snr-operator/tests/master_remediation.go[240-336]

### Suggested change
- Ensure worker guard cleanup runs even if the spec fails:
 - Option A (preferred): introduce a describe-level `targetWorkerNameForCleanup` variable set during the simultaneous test and cleared after cleanup; extend `JustAfterEach` to attempt guard removal for both master and worker targets (best-effort with warnings).
 - Option B: add `DeferCleanup` inside the simultaneous test immediately after selecting `targetWorkerName`, that waits for the worker to become Ready (best-effort) and then attempts `RemoveKubeletStopGuard`, logging/reporting warnings but not failing teardown.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Guard removal lacks retry ✗ Dismissed 🐞 Bug ☼ Reliability
Description
The PR changes stale guard removal in BeforeEach to a hard Expect(...).To(Succeed()) without
retry, so a transient oc debug failure will now fail the spec during setup. This is brittle given
the repo already documents transient oc debug failures (e.g., “unable to create the debug pod”) in
similar node operations.
Code

tests/snr-operator/tests/master_remediation.go[R64-65]

+			Expect(helpers.RemoveKubeletStopGuard(ctx, targetMasterName, snrparams.OcDebugTimeout)).To(Succeed(),
+				"Failed to remove kubelet stop guard on master %s", targetMasterName)
Relevance

●● Moderate

Mixed history: team likes polling for transient states (PR13) but also prefers failing on
setup/cleanup errors (PR22, PR52).

PR-#13
PR-#22
PR-#52

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
BeforeEach now requires RemoveKubeletStopGuard to succeed immediately. RemoveKubeletStopGuard
is an oc debug call, and the SNR test helpers explicitly document transient oc debug failures
like “unable to create the debug pod” as expected in similar workflows, making a single-attempt
setup assertion fragile.

tests/snr-operator/tests/master_remediation.go[61-66]
tests/snr-operator/tests/worker_remediation.go[65-70]
tests/internal/helpers/node_ops.go[98-107]
tests/snr-operator/tests/helpers.go[209-236]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`RemoveKubeletStopGuard` uses `oc debug` via `RunOnNode`. The PR now fails the spec immediately if a single `oc debug` attempt fails during `BeforeEach`, increasing flake risk.

### Issue Context
The codebase already recognizes transient `oc debug` failures (e.g., “unable to create the debug pod”) in `stopKubeletForRemediation` and suppresses them there, indicating these failures can occur.

### Fix Focus Areas
- tests/snr-operator/tests/master_remediation.go[61-66]
- tests/snr-operator/tests/worker_remediation.go[65-70]
- tests/snr-operator/tests/master_remediation.go[240-244]

### Suggested change
- Keep failing the test if the guard cannot be removed, but add bounded retries:
 - Wrap guard removal in `Eventually(func() error { return helpers.RemoveKubeletStopGuard(...) }, <totalTimeout>, <pollInterval>).Should(Succeed(), ...)`.
 - Use a reasonable total timeout/poll (e.g., 1-2 minutes total, 5-10s poll) and keep the per-attempt `OcDebugTimeout` as-is (or reduce per-attempt timeout if needed).
- Apply the same pattern to the worker guard removal at the start of the simultaneous master+worker test.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 13 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread tests/snr-operator/tests/master_remediation.go Outdated
Comment thread tests/snr-operator/tests/master_remediation.go
By("Removing any stale kubelet stop guard from a previous run")

_ = helpers.RemoveKubeletStopGuard(ctx, targetMasterName, snrparams.OcDebugTimeout)
Expect(helpers.RemoveKubeletStopGuard(ctx, targetMasterName, snrparams.OcDebugTimeout)).To(Succeed(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not introduced in this PR, but the cleanup improvements here make an existing helper contract more visible. In node_ops.go, the guard path /var/tmp/.medik8s-kubelet-stop-guard is duplicated as a string literal in both StopKubelet (shell one-liner) and RemoveKubeletStopGuard (rm -f argument). Extracting it to a shared const would prevent drift if the path ever changes. StopKubelet's doc comment should also mention the guard-file behavior and the caller's obligation to call RemoveKubeletStopGuard, since MDR tests also call this helper without cleanup.

Suggested follow-up in node_ops.go: add const kubeletStopGuardPath = "/var/tmp/.medik8s-kubelet-stop-guard" and update the StopKubelet doc comment to describe the guard file creation, skip-on-exist behavior, and the cleanup requirement.


By("Removing kubelet stop guard on worker")

if guardErr := helpers.RemoveKubeletStopGuard(ctx, targetWorkerName, snrparams.OcDebugTimeout); guardErr != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Follow-up for StopKubelet in node_ops.go: when the guard file exists and the stop is skipped, the function returns nil without logging, making the skip invisible in test output (logf is available but unused on this path). Also, if systemctl stop kubelet fails after the guard is created, the guard persists and makes later calls no-ops.

Fix: For observability, echo a sentinel on the guard-skip path ([ -f "$g" ] && echo GUARD_SKIP && exit 0) and log via logf in Go. For the stop-failure path: touch "$g" && systemctl stop kubelet || { rm -f "$g"; exit 1; }.


By("Removing any stale kubelet stop guard on worker")

Expect(helpers.RemoveKubeletStopGuard(ctx, targetWorkerName, snrparams.OcDebugTimeout)).To(Succeed(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The worker guard cleanup in the simultaneous test is inline in the test body. If the test aborts early (e.g., assertion failure after stopping kubelet on the worker but before the post-recovery cleanup at line 321 runs), JustAfterEach only cleans the master guard (targetMasterName), not the dynamically scoped worker. The stale worker guard would make the next run's StopKubelet on that worker a silent no-op.

The pre-test cleanup here (line 242) handles cross-run contamination, but intra-run abort leaves a gap. Consider registering a DeferCleanup immediately after selecting targetWorkerName so the worker guard is removed even on early abort.

Fix: After selecting targetWorkerName, add:

DeferCleanup(func() {
    if err := helpers.RemoveKubeletStopGuard(ctx, targetWorkerName, snrparams.OcDebugTimeout); err != nil {
        GinkgoWriter.Printf("WARNING: deferred worker guard cleanup failed on %s: %v\n", targetWorkerName, err)
    }
})

@jmontleon
jmontleon force-pushed the add-missing-worker-cleanup branch from 43f26a0 to 2c46bc5 Compare August 4, 2026 19:03
@jmontleon
jmontleon requested a review from ugreener August 4, 2026 19:04
@jmontleon

Copy link
Copy Markdown
Author

I think I addressed the issues you raised @ugreener. Please let me know if you still have concerns!

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@tests/snr-operator/tests/master_remediation.go`:
- Around line 219-226: Inside the DeferCleanup callback registering
RemoveKubeletStopGuard, add a By step with the description "Removing deferred
worker kubelet stop guard" before performing the cleanup, while preserving the
existing error logging behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a99d4e52-4a45-41f6-8ebb-2eec00b662a3

📥 Commits

Reviewing files that changed from the base of the PR and between 11c924d and 2c46bc5.

📒 Files selected for processing (3)
  • tests/internal/helpers/node_ops.go
  • tests/snr-operator/tests/master_remediation.go
  • tests/snr-operator/tests/worker_remediation.go

Comment thread tests/snr-operator/tests/master_remediation.go
@jmontleon
jmontleon force-pushed the add-missing-worker-cleanup branch from 2c46bc5 to 300dc00 Compare August 4, 2026 19:33
Comment thread tests/snr-operator/tests/master_remediation.go Outdated
After a hard reboot, the oc debug pod left behind by StopKubelet gets
re-executed by kubelet (CRI-O cleans stale containers on boot, so
kubelet sees no record of the pod having run and starts it fresh).
This stops kubelet ~4 seconds after it starts, causing test flakes.

Write a guard file on the host (/var/tmp/.medik8s-kubelet-stop-guard)
before stopping kubelet. On re-execution after reboot, the guard file
is found and the stop is skipped. The guard is removed in BeforeEach
(handles aborted prior runs) and JustAfterEach (normal cleanup).

Additional improvements from review feedback:
- Extract guard path to a shared const to prevent drift
- Log when the guard skip occurs for observability
- Roll back the guard file if systemctl stop kubelet fails
- Use DeferCleanup for the worker guard in the simultaneous test so
  it is removed even on early abort

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jmontleon
jmontleon force-pushed the add-missing-worker-cleanup branch from 300dc00 to 5f44eff Compare August 5, 2026 13:19
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@jmontleon
jmontleon requested a review from ugreener August 5, 2026 13:19

@ugreener ugreener 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.

/lgtm

@openshift-ci

openshift-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jmontleon, ugreener

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ugreener

ugreener commented Aug 5, 2026

Copy link
Copy Markdown

/test 4.22-konflux-e2e-snr-aws

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants