Skip to content

OCPBUGS-100060: staticpod: add installer precondition hook - #2387

Open
mkowalski wants to merge 1 commit into
openshift:masterfrom
mkowalski:ocpbugs-100060-installer-precondition
Open

OCPBUGS-100060: staticpod: add installer precondition hook#2387
mkowalski wants to merge 1 commit into
openshift:masterfrom
mkowalski:ocpbugs-100060-installer-precondition

Conversation

@mkowalski

@mkowalski mkowalski commented Jul 29, 2026

Copy link
Copy Markdown

Summary

First of two PRs for OCPBUGS-100060: etcd quorum loss during upgrades when the etcd-operator's revision installer restarts an etcd member while MCO is simultaneously rebooting another master (2/3 members down, ~2min leaderless, cluster-wide API outage returning 429 storage is (re)initializing).

  • The installer controller creates installer pods unconditionally once a node has a pending target revision; the installer pod replaces the operand static-pod manifest, restarting the operand. The etcd-operator's QuorumChecker gates only revision creation (WithRevisionControllerPrecondition), so per-node installs of an existing revision proceed with no safety check. Evidence: in both incident runs the installer killed master-1's etcd 150–156s before master-0 finished its MCO reboot (run 2075907197388197888, run 2077192709163978752).
  • Adds WithInstallerPrecondition(func(ctx, nodeName) (safe bool, reason string, err error)) to InstallerController and the static-pod controllers Builder. Consulted immediately before ensureInstallerPod; when unmet, emits InstallerPreconditionNotMet and requeues (15s) instead of restarting the operand. nil precondition preserves existing behavior for all other operators.
  • Companion PR in cluster-etcd-operator wires this to a quorum/cordon safety check (IsSafeToRestartMember).

Test plan

  • gofmt, go vet, go build ./pkg/operator/staticpod/...
  • go test ./pkg/operator/staticpod/controller/installer/ — new TestCreateInstallerPodPrecondition (unmet delays pod + consults correct node; met allows; error fails sync); existing tests pass (internal/atomicdir TestSwap fails identically on pristine master in my environment — pre-existing, unrelated)

This PR was generated using AI. Please verify before acting on it.

Summary by CodeRabbit

  • New Features
    • Added an optional safety check before creating installer pods.
    • Installer pod creation is postponed when node conditions are unsafe, helping avoid unnecessary operand restarts.
    • Added warning events and automatic retry when the precondition is not met.
    • Errors from the safety check now stop synchronization and prevent pod creation.

The installer controller creates installer pods unconditionally once a node
has a pending target revision.  The installer pod replaces the operand static
pod manifest, restarting the operand.  For etcd this can break quorum: the
cluster-etcd-operator's quorum checks gate only revision creation, so an
installer pod can restart an etcd member while another control plane node is
simultaneously down for a machine-config reboot (OCPBUGS-100060: two of three
members down, ~2 minutes without an etcd leader, cluster-wide API outage).

Add WithInstallerPrecondition to the installer controller and the static pod
controllers builder.  The precondition is consulted immediately before an
installer pod is created for a node; when unmet the controller emits an
InstallerPreconditionNotMet event and requeues (15s) instead of restarting
the operand.  A nil precondition preserves the existing behavior.

Assisted-By: Claude Fable 5
@openshift-ci-robot openshift-ci-robot added jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jul 29, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@mkowalski: This pull request references Jira Issue OCPBUGS-100060, which is invalid:

  • expected the bug to target the "5.0.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Summary

First of two PRs for OCPBUGS-100060: etcd quorum loss during upgrades when the etcd-operator's revision installer restarts an etcd member while MCO is simultaneously rebooting another master (2/3 members down, ~2min leaderless, cluster-wide API outage returning 429 storage is (re)initializing).

  • The installer controller creates installer pods unconditionally once a node has a pending target revision; the installer pod replaces the operand static-pod manifest, restarting the operand. The etcd-operator's QuorumChecker gates only revision creation (WithRevisionControllerPrecondition), so per-node installs of an existing revision proceed with no safety check. Evidence: in both incident runs the installer killed master-1's etcd 150–156s before master-0 finished its MCO reboot (run 2075907197388197888, run 2077192709163978752).
  • Adds WithInstallerPrecondition(func(ctx, nodeName) (safe bool, reason string, err error)) to InstallerController and the static-pod controllers Builder. Consulted immediately before ensureInstallerPod; when unmet, emits InstallerPreconditionNotMet and requeues (15s) instead of restarting the operand. nil precondition preserves existing behavior for all other operators.
  • Companion PR in cluster-etcd-operator wires this to a quorum/cordon safety check (IsSafeToRestartMember).

Test plan

  • gofmt, go vet, go build ./pkg/operator/staticpod/...
  • go test ./pkg/operator/staticpod/controller/installer/ — new TestCreateInstallerPodPrecondition (unmet delays pod + consults correct node; met allows; error fails sync); existing tests pass (internal/atomicdir TestSwap fails identically on pristine master in my environment — pre-existing, unrelated)

This PR was generated using AI. Please verify before acting on it.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Walkthrough

The installer controller now accepts an optional node-level precondition, evaluates it before installer pod creation, requeues when unsafe, fails on callback errors, and exposes builder wiring and tests for each outcome.

Changes

Installer precondition gating

Layer / File(s) Summary
Precondition contract and builder wiring
pkg/operator/staticpod/controller/installer/installer_controller.go, pkg/operator/staticpod/controllers.go
Adds the precondition callback type and controller option, stores it in the builder, and forwards it during installer controller construction.
Precondition enforcement and validation
pkg/operator/staticpod/controller/installer/installer_controller.go, pkg/operator/staticpod/controller/installer/installer_controller_test.go
Checks node safety before installer pod creation, emits a warning and requeues when unsafe, returns callback errors, and tests unsafe, safe, and error outcomes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Builder
  participant InstallerController
  participant InstallerPreconditionFunc
  participant InstallerPod
  Builder->>InstallerController: configure precondition
  InstallerController->>InstallerPreconditionFunc: check node safety
  InstallerPreconditionFunc-->>InstallerController: safe, reason, or error
  alt safe
    InstallerController->>InstallerPod: create installer pod
  else unsafe
    InstallerController-->>InstallerController: emit warning and requeue
  else error
    InstallerController-->>InstallerController: fail sync
  end
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error The new Warningf event logs the node name and caller-provided reason, and the default recorder is a logging recorder, so internal hostnames can leak. Remove or sanitize node names/free-form reasons from the logged message; keep only non-sensitive identifiers or redact sensitive fields.
✅ Passed checks (14 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 main change: adding an installer precondition hook to staticpod controllers.
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.
Stable And Deterministic Test Names ✅ Passed The added test titles are static, descriptive strings; no dynamic pod/node/UUID/timestamp values appear in titles.
Test Structure And Quality ✅ Passed PASS: The new test uses isolated t.Run subtests, fresh fake clients per case, no cluster waits/timeouts needed, and failure messages; it matches existing table-driven test patterns.
Microshift Test Compatibility ✅ Passed Added test is a Go unit test, not Ginkgo e2e, and it only uses fake clients/core v1 resources—no MicroShift-unsupported APIs or assumptions.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the only new test is a Go unit test in installer_controller_test.go, with no SNO assumptions or skip-needed markers.
Topology-Aware Scheduling Compatibility ✅ Passed The PR only adds an installer precondition hook and tests; it doesn't add node selectors, affinity, tolerations, replica logic, or other topology-dependent scheduling constraints.
Ote Binary Stdout Contract ✅ Passed PASS: The PR only adds controller/builder wiring and tests; no main/init/TestMain/BeforeSuite/RunSpecs stdout writes or stdout logging setup were introduced.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The PR only adds a Go unit test with fake clients; no Ginkgo e2e tests, IPv4 literals, or external/public network access were added.
No-Weak-Crypto ✅ Passed Touched files add only installer precondition wiring/tests; no weak-crypto APIs, custom crypto, or secret comparisons were introduced.
Container-Privileges ✅ Passed No privilege-related settings were added in the PR diff; only controller wiring changed, and no touched lines set privileged/host access or allowPrivilegeEscalation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@openshift-ci
openshift-ci Bot requested review from dgrisonnet and p0lyn0mial July 29, 2026 13:33
@openshift-ci

openshift-ci Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: mkowalski
Once this PR has been reviewed and has the lgtm label, please assign dgrisonnet for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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

@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 `@pkg/operator/staticpod/controller/installer/installer_controller_test.go`:
- Around line 2821-2831: Update the test around the InstallerController Sync
loop to use the controller’s queued/requeue behavior rather than relying only on
manual Sync calls. Assert that an unmet precondition schedules a 15-second
requeue and that the event recorder contains the expected
InstallerPreconditionNotMet warning, while preserving the existing no-pod and
checked-node assertions.
🪄 Autofix (Beta)

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: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f3ed9673-b482-4a7f-bee5-0222f2be555d

📥 Commits

Reviewing files that changed from the base of the PR and between ed1b434 and 60e2727.

📒 Files selected for processing (3)
  • pkg/operator/staticpod/controller/installer/installer_controller.go
  • pkg/operator/staticpod/controller/installer/installer_controller_test.go
  • pkg/operator/staticpod/controllers.go

Comment on lines +2821 to +2831
for i := 0; i < 3; i++ {
if err := c.Sync(context.TODO(), factory.NewSyncContext("InstallerController", *eventRecorder)); err != nil {
t.Fatal(err)
}
}
if getPod() != nil {
t.Fatalf("expected no installer pod while the precondition is unmet")
}
if checkedNode != "test-node-1" {
t.Fatalf("expected precondition to be consulted for test-node-1, got %q", checkedNode)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Assert the delayed requeue and warning event.

Manual Sync calls bypass queue timing, so this still passes if an unmet precondition retries immediately or omits InstallerPreconditionNotMet. Assert the 15-second requeue and recorded event.

🤖 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 `@pkg/operator/staticpod/controller/installer/installer_controller_test.go`
around lines 2821 - 2831, Update the test around the InstallerController Sync
loop to use the controller’s queued/requeue behavior rather than relying only on
manual Sync calls. Assert that an unmet precondition schedules a 15-second
requeue and that the event recorder contains the expected
InstallerPreconditionNotMet warning, while preserving the existing no-pod and
checked-node assertions.

@openshift-ci

openshift-ci Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@mkowalski: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants