Skip to content

Aggregate HTTPBoot and iPXE Conditions to Compute ServerBootConfiguration Readiness#292

Open
hardikdr wants to merge 2 commits intomainfrom
enh/dual-mode-httpboot-ipxe
Open

Aggregate HTTPBoot and iPXE Conditions to Compute ServerBootConfiguration Readiness#292
hardikdr wants to merge 2 commits intomainfrom
enh/dual-mode-httpboot-ipxe

Conversation

@hardikdr
Copy link
Copy Markdown
Member

@hardikdr hardikdr commented Mar 20, 2026

Summary by CodeRabbit

  • New Features

    • Added aggregated boot-readiness controller and two readiness condition types (HTTPBootReady, IPXEBootReady).
    • Exposed observedGeneration in HTTP/IPXE statuses and CRDs for clearer reconciliation visibility.
  • Bug Fixes

    • Status updates are conflict-safe with automatic retries and targeted readiness-condition patches.
    • Improved error reporting when readiness checks fail.
  • Tests

    • Added unit and integration tests covering readiness logic and state transitions.
  • Documentation

    • Updated API docs to include observedGeneration in status schemas.

@hardikdr hardikdr requested a review from defo89 March 20, 2026 13:17
@hardikdr hardikdr added the do-not-merge Do Not Merge label Mar 20, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a readiness controller that computes ServerBootConfiguration.Status.State from HTTP/IPXE readiness conditions; refactors HTTP/PXE controllers to set per-mode readiness conditions; adds a conflict-safe condition patch helper; records observedGeneration on boot config statuses; and includes tests and CRD schema updates.

Changes

Cohort / File(s) Summary
Readiness controller
internal/controller/serverbootconfiguration_readiness_controller.go
New ServerBootConfigurationReadinessReconciler, exported constants (HTTPBootReadyConditionType, IPXEBootReadyConditionType, BootConfigPendingReason), Reconcile/SetupWithManager compute desired Status.State from required conditions and apply conflict-safe status patches.
HTTP controller changes
internal/controller/serverbootconfiguration_http_controller.go
Replaced state-patching with single-condition (HTTPBootReady) status patches, map HTTPBootConfig state → metav1.Condition, use retry.RetryOnConflict for condition patching, updated logging/errors.
PXE controller changes
internal/controller/serverbootconfiguration_pxe_controller.go
Replaced state-patching with single-condition (IPXEBootReady) status patches; use PatchServerBootConfigCondition on failures; conflict-safe GET+Patch pattern and updated logs/messages.
Condition patch helper
internal/controller/serverbootconfig_helpers.go
Added PatchServerBootConfigCondition(ctx, c, namespacedName, condition) which gets the resource, sets/updates one metav1.Condition (defaults ObservedGeneration when 0), and status-patches using client.MergeFrom under retry.RetryOnConflict.
Main wiring
cmd/main.go
Instantiates and wires ServerBootConfigurationReadinessReconciler at startup (no enablement gate), sets RequireHTTPBoot/RequireIPXEBoot from flags, and exits on SetupWithManager failure.
Test harness
internal/controller/suite_test.go
Registers the readiness reconciler in test setup (RequireHTTPBoot: true, RequireIPXEBoot: true) and asserts SetupWithManager succeeds.
Readiness tests
internal/controller/serverbootconfiguration_readiness_controller_test.go
New Ginkgo/Gomega tests validating computeDesiredState and Reconcile outcomes across required-mode permutations, including lifecycle transitions and single-requirement behavior.
Status observedGeneration fields
api/v1alpha1/httpbootconfig_types.go, api/v1alpha1/ipxebootconfig_types.go
Added top-level ObservedGeneration int64 to HTTPBootConfigStatus and IPXEBootConfigStatus (JSON: observedGeneration,omitempty).
CRD updates
config/crd/bases/boot.ironcore.dev_httpbootconfigs.yaml, config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml
Added status.observedGeneration (integer, int64) schema entries for HTTPBootConfig and IPXEBootConfig CRDs.
Status patching refinements
internal/controller/httpbootconfig_controller.go, internal/controller/ipxebootconfig_controller.go
patchStatus now sets .Status.ObservedGeneration = Generation when patching and only short-circuits if both state and observedGeneration already match.
Bootstrap wiring tests
internal/controller/...
Test manager startup now includes readiness reconciler; existing HTTP/PXE reconcilers unchanged.

Sequence Diagram(s)

sequenceDiagram
    actor K8s as Kubernetes API
    participant HC as HTTP Controller
    participant PC as PXE Controller
    participant SBC as ServerBootConfiguration
    participant RC as Readiness Controller

    K8s->>HC: Reconcile HTTPBootConfig
    HC->>HC: validate/build UKI URL & state
    HC->>SBC: Patch `HTTPBootReady` condition (True/False/Unknown)

    K8s->>PC: Reconcile IPXEBootConfig
    PC->>PC: resolve image details & state
    PC->>SBC: Patch `IPXEBootReady` condition (True/False/Unknown)

    K8s->>RC: Reconcile ServerBootConfiguration (on condition change)
    RC->>SBC: GET current conditions
    RC->>RC: compute desired Status.State from required conditions
    RC->>SBC: Status.Patch Status.State if changed (Ready/Error/Pending)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement, api-change, size/XXL

Suggested reviewers

  • defo89
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is empty except for an unused template with blank bullet points and an empty issue reference, providing no context about the changes, rationale, or implementation details. Add a detailed description including: purpose of the readiness aggregation, how HTTPBoot and iPXE conditions are combined, key implementation changes, and any relevant issue or design discussion references.
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: aggregating HTTPBoot and iPXE conditions to compute ServerBootConfiguration readiness, which aligns with the core PR objective and file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enh/dual-mode-httpboot-ipxe

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/main.go`:
- Line 112: The readiness controller (serverBootConfigReadiness /
ServerBootConfigurationReadinessReconciler) must not be independently
disable-able because it's the sole writer of
ServerBootConfiguration.Status.State; update cmd/main.go so that when either
mode controller (HTTP or PXE reconcilers) is enabled the readiness controller is
automatically enabled, or else validate the --controllers allow-list at startup
and exit with an error if a mode controller is enabled while
serverBootConfigReadiness is disabled; modify the controller initialization or
add a startup check that references serverBootConfigReadiness and the mode
controller flags and either force-enable readiness or fail fast on the invalid
combination.

In `@internal/controller/serverbootconfig_helpers.go`:
- Around line 251-269: PatchServerBootConfigCondition currently does a single
MergeFrom status patch which can silently overwrite concurrent condition
updates; change it to an optimistic retry-on-conflict loop: fetch the latest
ServerBootConfiguration, set condition.ObservedGeneration if zero, call
apimeta.SetStatusCondition on the fetched object's Status.Conditions, attempt
c.Status().Patch(ctx, &obj, client.MergeFrom(base)), and if you get a conflict
(apierrors.IsConflict) re-fetch and retry a few times before failing. Apply the
same conflict-retry pattern to the two open-coded condition updates in
patchHTTPBootReadyConditionFromHTTPState and
patchIPXEBootReadyConditionFromIPXEState so each updater merges the latest
conditions and retries on conflict instead of silently overwriting.

In `@internal/controller/serverbootconfiguration_http_controller.go`:
- Around line 144-147: The code currently uses HTTPBootConfig.Status.State
directly when patching the parent condition, which can be stale if the child
HTTPBootConfig hasn't reconciled the new generation; update the logic so that
when calling or inside patchHTTPBootReadyConditionFromHTTPState you compare
httpBootConfig.ObjectMeta.Generation (or httpBootConfig.Generation) with the
parent ServerBootConfiguration.Generation (config.Generation) and treat the
child's state as Unknown when they differ. Concretely: modify
patchHTTPBootReadyConditionFromHTTPState (or its call) to detect generation
mismatch and map any non-current-child state to metav1.ConditionUnknown (or the
equivalent Unknown/Unknown constant) before creating/updating the
HTTPBootReadyCondition (HTTPBootReadyConditionType), ensuring the parent
condition is set to Unknown until the child reconciles.

In `@internal/controller/serverbootconfiguration_pxe_controller.go`:
- Around line 163-166: The code patches IPXEBootReadyCondition from the child
IPXEBootConfig immediately after Apply, but may read a stale
IPXEBootConfig.Status.State and incorrectly stamp ObservedGeneration for the new
ServerBootConfiguration; update patchIPXEBootReadyConditionFromIPXEState (and
its call site) to avoid promoting Ready/Error from a stale child status by
either (A) adding an ObservedGeneration field to IPXEBootConfig.Status and only
applying the child's Ready/Error when IPXEBootConfig.Status.ObservedGeneration
== ipxe.Spec.Generation (or otherwise indicates the child has observed the new
spec), or (B) skip setting ObservedGeneration/Ready/Error in
ServerBootConfiguration until a subsequent reconcile when the child status shows
it has processed the spec; reference the functions/methods
patchIPXEBootReadyConditionFromIPXEState, IPXEBootConfig.Status and the
ServerBootConfiguration reconciliation path where Apply is called to locate and
implement the change.

In `@internal/controller/serverbootconfiguration_readiness_controller.go`:
- Around line 55-76: When evaluating HTTPBootReadyConditionType and
IPXEBootReadyConditionType (in the RequireHTTPBoot / RequireIPXEBoot blocks)
treat conditions whose ObservedGeneration != cfg.Generation as if they were
missing: after calling apimeta.FindStatusCondition(cfg.Status.Conditions, ...),
consider the condition nil if c == nil OR c.ObservedGeneration !=
cfg.Generation, and then set allReady = false for that case; keep the existing
handling for c.Status == metav1.ConditionFalse (set hasError = true) and
c.Status != metav1.ConditionTrue (set allReady = false). This ensures stale
conditions are treated as pending until the mode controllers update them.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26a68016-47ce-46c2-ac59-401c729d2609

📥 Commits

Reviewing files that changed from the base of the PR and between f5bd6b8 and 2e13bdc.

📒 Files selected for processing (6)
  • cmd/main.go
  • internal/controller/serverbootconfig_helpers.go
  • internal/controller/serverbootconfiguration_http_controller.go
  • internal/controller/serverbootconfiguration_pxe_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller.go
  • internal/controller/suite_test.go

cmd/main.go Outdated
serverBootConfigControllerPxe,
serverBootConfigControllerHttp,
httpBootConfigController,
serverBootConfigReadiness,
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.

⚠️ Potential issue | 🟠 Major

Don't make readiness independently disable-able.

ServerBootConfigurationReadinessReconciler is now the only writer of ServerBootConfiguration.Status.State. Any deployment that passes an explicit --controllers= allow-list and doesn't include this new switch will keep running the HTTP/PXE reconcilers, but Status.State will stop changing entirely. Please auto-enable readiness whenever either mode controller is enabled, or fail fast on that invalid combination.

Also applies to: 289-299

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/main.go` at line 112, The readiness controller (serverBootConfigReadiness
/ ServerBootConfigurationReadinessReconciler) must not be independently
disable-able because it's the sole writer of
ServerBootConfiguration.Status.State; update cmd/main.go so that when either
mode controller (HTTP or PXE reconcilers) is enabled the readiness controller is
automatically enabled, or else validate the --controllers allow-list at startup
and exit with an error if a mode controller is enabled while
serverBootConfigReadiness is disabled; modify the controller initialization or
add a startup check that references serverBootConfigReadiness and the mode
controller flags and either force-enable readiness or fail fast on the invalid
combination.

Comment on lines +55 to +76
if r.RequireHTTPBoot {
c := apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
switch {
case c == nil:
allReady = false
case c.Status == metav1.ConditionFalse:
hasError = true
case c.Status != metav1.ConditionTrue:
allReady = false
}
}

if r.RequireIPXEBoot {
c := apimeta.FindStatusCondition(cfg.Status.Conditions, IPXEBootReadyConditionType)
switch {
case c == nil:
allReady = false
case c.Status == metav1.ConditionFalse:
hasError = true
case c.Status != metav1.ConditionTrue:
allReady = false
}
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n internal/controller/serverbootconfiguration_readiness_controller.go

Repository: ironcore-dev/boot-operator

Length of output: 3972


🏁 Script executed:

# Find the ServerBootConfiguration type definition
find . -name "*.go" -type f | xargs grep -l "type ServerBootConfiguration struct" | head -5

Repository: ironcore-dev/boot-operator

Length of output: 52


🏁 Script executed:

# Search for HTTPBootReadyConditionType and IPXEBootReadyConditionType usage
rg "HTTPBootReadyConditionType|IPXEBootReadyConditionType" -A 3 -B 3

Repository: ironcore-dev/boot-operator

Length of output: 8224


🏁 Script executed:

# Search for other uses of FindStatusCondition to see if there's a pattern
rg "FindStatusCondition" -B 2 -A 5

Repository: ironcore-dev/boot-operator

Length of output: 2501


🏁 Script executed:

# Search for .Generation usage in the codebase
rg "\.Generation" -B 2 -A 2 | head -50

Repository: ironcore-dev/boot-operator

Length of output: 3489


Treat stale mode conditions as pending.

The aggregator ignores Condition.ObservedGeneration. After a ServerBootConfiguration spec change, old HTTPBootReady / IPXEBootReady values can keep Status.State at Ready or Error until the mode controllers rewrite them. ObservedGeneration != cfg.Generation should be handled the same as a missing condition.

Suggested guard
 	if r.RequireHTTPBoot {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}

 	if r.RequireIPXEBoot {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, IPXEBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if r.RequireHTTPBoot {
c := apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
switch {
case c == nil:
allReady = false
case c.Status == metav1.ConditionFalse:
hasError = true
case c.Status != metav1.ConditionTrue:
allReady = false
}
}
if r.RequireIPXEBoot {
c := apimeta.FindStatusCondition(cfg.Status.Conditions, IPXEBootReadyConditionType)
switch {
case c == nil:
allReady = false
case c.Status == metav1.ConditionFalse:
hasError = true
case c.Status != metav1.ConditionTrue:
allReady = false
}
if r.RequireHTTPBoot {
c := apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
switch {
case c == nil || c.ObservedGeneration != cfg.Generation:
allReady = false
case c.Status == metav1.ConditionFalse:
hasError = true
case c.Status != metav1.ConditionTrue:
allReady = false
}
}
if r.RequireIPXEBoot {
c := apimeta.FindStatusCondition(cfg.Status.Conditions, IPXEBootReadyConditionType)
switch {
case c == nil || c.ObservedGeneration != cfg.Generation:
allReady = false
case c.Status == metav1.ConditionFalse:
hasError = true
case c.Status != metav1.ConditionTrue:
allReady = false
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller.go` around
lines 55 - 76, When evaluating HTTPBootReadyConditionType and
IPXEBootReadyConditionType (in the RequireHTTPBoot / RequireIPXEBoot blocks)
treat conditions whose ObservedGeneration != cfg.Generation as if they were
missing: after calling apimeta.FindStatusCondition(cfg.Status.Conditions, ...),
consider the condition nil if c == nil OR c.ObservedGeneration !=
cfg.Generation, and then set allReady = false for that case; keep the existing
handling for c.Status == metav1.ConditionFalse (set hasError = true) and
c.Status != metav1.ConditionTrue (set allReady = false). This ensures stale
conditions are treated as pending until the mode controllers update them.

@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from 2e13bdc to 97f67dd Compare March 20, 2026 13:52
@hardikdr hardikdr removed the do-not-merge Do Not Merge label Mar 30, 2026
@atd9876
Copy link
Copy Markdown
Contributor

atd9876 commented Mar 30, 2026

Wouldn't the implementation of ironcore-dev/metal-operator#654, and then producing either an httpBootConfig or an ipxeBootConfig according to the BootMethod, eliminate the race condition without the need for another controller?

@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from 97f67dd to 72da76e Compare April 1, 2026 13:44
@github-actions github-actions bot added size/XL and removed size/L labels Apr 1, 2026
@hardikdr hardikdr added ok-to-publish Run github action to publish the image ok-to-image labels Apr 1, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
internal/controller/serverbootconfig_helpers.go (1)

149-170: ⚠️ Potential issue | 🟠 Major

RetryOnConflict still isn't conflict-safe with plain MergeFrom.

client.MergeFrom(base) builds a merge patch that replaces modified list fields, and controller-runtime only adds metadata.resourceVersion preconditions when you opt into MergeFromWithOptimisticLock. With the current code, concurrent HTTP/PXE writers can still silently clobber each other's status.conditions, so this helper does not actually close the dual-mode lost-update race. Please switch this helper to client.MergeFromWithOptions(base, client.MergeFromWithOptimisticLock{}) and have the inline HTTP/PXE patchers delegate here so the same fix applies everywhere. (pkg.go.dev)

🔧 Suggested direction
-		return c.Status().Patch(ctx, &cur, client.MergeFrom(base))
+		return c.Status().Patch(
+			ctx,
+			&cur,
+			client.MergeFromWithOptions(base, client.MergeFromWithOptimisticLock{}),
+		)
Does controller-runtime's client.MergeFrom include conflict detection by default, and what does MergeFromWithOptimisticLock add?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfig_helpers.go` around lines 149 - 170,
PatchServerBootConfigCondition currently uses client.MergeFrom(base) which
creates a merge patch without optimistic-lock preconditions, so concurrent
status.conditions updates can be lost; change the final Patch call to use
client.MergeFromWithOptions(base, client.MergeFromWithOptimisticLock{}) so
controller-runtime will include metadata.resourceVersion preconditions, and
update any inline HTTP/PXE patchers to delegate to this helper (or call the same
MergeFromWithOptions) to ensure both writers use optimistic locking with
RetryOnConflict.
cmd/main.go (1)

109-116: ⚠️ Potential issue | 🟠 Major

Don't let mode controllers run without readiness.

ServerBootConfigurationReadinessReconciler is now the only writer of ServerBootConfiguration.Status.State, but --controllers can still enable serverbootconfighttp and/or serverbootconfigpxe while omitting serverbootconfigreadiness. In that deployment shape, per-mode conditions keep changing and Status.State freezes. Please either auto-enable readiness whenever a mode controller is enabled, or fail fast on that invalid combination.

🚫 Example fail-fast guard
  flag.Parse()
+
+	if (controllers.Enabled(serverBootConfigControllerHttp) || controllers.Enabled(serverBootConfigControllerPxe)) &&
+		!controllers.Enabled(serverBootConfigReadiness) {
+		setupLog.Error(nil, "invalid --controllers selection: serverbootconfigreadiness is required when enabling serverbootconfighttp or serverbootconfigpxe")
+		os.Exit(1)
+	}

Also applies to: 299-309

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/main.go` around lines 109 - 116, When constructing the controllers via
switches.New (the controllers variable) ensure that serverbootconfigreadiness is
present whenever serverbootconfigpxe (serverBootConfigControllerPxe) or
serverbootconfighttp (serverBootConfigControllerHttp) is being enabled by
--controllers: either automatically add serverBootConfigReadiness into the
controllers slice before calling switches.New, or validate the parsed controller
set and fail fast (log.Fatal / return error) if PXE or HTTP mode is enabled
without serverBootConfigReadiness; update the construction logic that currently
lists ipxeBootConfigController, serverBootConfigControllerPxe,
serverBootConfigControllerHttp, httpBootConfigController,
serverBootConfigReadiness to perform this check and enforce the invariant.
internal/controller/serverbootconfiguration_pxe_controller.go (1)

165-205: ⚠️ Potential issue | 🟠 Major

Don't publish current-generation IPXEBootReady from a just-applied child.

The post-apply read feeding this method can still return the previous IPXEBootConfig.Status.State, but Line 185 stamps that result onto the current ServerBootConfiguration generation. A stale Ready/Error here makes the parent look current before the child has actually processed the new spec. Please only promote True/False once the child can prove it has observed the latest spec (for example via a child status.observedGeneration); otherwise keep IPXEBootReady as Unknown.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_pxe_controller.go` around lines
165 - 205, The current patchIPXEBootReadyConditionFromIPXEState function uses
config.Status.State directly to stamp True/False on the parent even if the child
hasn't observed the latest spec; change the logic to only promote
ConditionTrue/ConditionFalse when the child has proven it observed the config
generation (e.g., compare config.Status.ObservedGeneration >=
config.Generation), otherwise set the IPXEBootReady condition to
Unknown/BootConfigPending; update the switch in
patchIPXEBootReadyConditionFromIPXEState to check
config.Status.ObservedGeneration first (and only set metav1.ConditionTrue/False
for v1alpha1.IPXEBootConfigStateReady/Error when the observedGeneration is
up-to-date), leaving the default/awaiting case as Unknown and keep
Reason/Message accordingly.
internal/controller/serverbootconfiguration_http_controller.go (1)

146-185: ⚠️ Potential issue | 🟠 Major

Don't promote stale HTTPBootConfig status to the current parent generation.

This path reads httpBootConfig.Status.State immediately after applying the child, so it can still be carrying the previous child reconcile result, while Line 166 marks it as observed for the current ServerBootConfiguration generation. That can make HTTPBootReady flip to True/False too early. Please gate True/False on proof that the child has observed the latest spec (for example a child status.observedGeneration); otherwise keep the parent condition Unknown.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_http_controller.go` around lines
146 - 185, In patchHTTPBootReadyConditionFromHTTPState, don't set cond.Status to
True/False based solely on httpBootConfig.Status.State; first verify the child
has observed the parent generation (check
httpBootConfig.Status.ObservedGeneration or similar) against the parent
generation used for cond.ObservedGeneration (cur.Generation or cfg.Generation).
If the child's observedGeneration is less than the parent's current generation,
set cond.Status = metav1.ConditionUnknown with a Pending reason/message; only
when observedGeneration >= parent generation map HTTPBootConfigStateReady ->
ConditionTrue and HTTPBootConfigStateError -> ConditionFalse; update the logic
around httpBootConfig.Status.State and the metav1.Condition construction in
patchHTTPBootReadyConditionFromHTTPState (referencing
HTTPBootReadyConditionType, cond, httpBootConfig.Status.State, and
cond.ObservedGeneration).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/controller/serverbootconfiguration_readiness_controller_test.go`:
- Around line 1-2: Update the SPDX header in
internal/controller/serverbootconfiguration_readiness_controller_test.go to the
repo-standard exact two-line header: replace the current year/token with the
canonical lines "// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate
company and IronCore contributors" and "// SPDX-License-Identifier: Apache-2.0"
at the top of the file so the test file matches other Go files in the
repository.

---

Duplicate comments:
In `@cmd/main.go`:
- Around line 109-116: When constructing the controllers via switches.New (the
controllers variable) ensure that serverbootconfigreadiness is present whenever
serverbootconfigpxe (serverBootConfigControllerPxe) or serverbootconfighttp
(serverBootConfigControllerHttp) is being enabled by --controllers: either
automatically add serverBootConfigReadiness into the controllers slice before
calling switches.New, or validate the parsed controller set and fail fast
(log.Fatal / return error) if PXE or HTTP mode is enabled without
serverBootConfigReadiness; update the construction logic that currently lists
ipxeBootConfigController, serverBootConfigControllerPxe,
serverBootConfigControllerHttp, httpBootConfigController,
serverBootConfigReadiness to perform this check and enforce the invariant.

In `@internal/controller/serverbootconfig_helpers.go`:
- Around line 149-170: PatchServerBootConfigCondition currently uses
client.MergeFrom(base) which creates a merge patch without optimistic-lock
preconditions, so concurrent status.conditions updates can be lost; change the
final Patch call to use client.MergeFromWithOptions(base,
client.MergeFromWithOptimisticLock{}) so controller-runtime will include
metadata.resourceVersion preconditions, and update any inline HTTP/PXE patchers
to delegate to this helper (or call the same MergeFromWithOptions) to ensure
both writers use optimistic locking with RetryOnConflict.

In `@internal/controller/serverbootconfiguration_http_controller.go`:
- Around line 146-185: In patchHTTPBootReadyConditionFromHTTPState, don't set
cond.Status to True/False based solely on httpBootConfig.Status.State; first
verify the child has observed the parent generation (check
httpBootConfig.Status.ObservedGeneration or similar) against the parent
generation used for cond.ObservedGeneration (cur.Generation or cfg.Generation).
If the child's observedGeneration is less than the parent's current generation,
set cond.Status = metav1.ConditionUnknown with a Pending reason/message; only
when observedGeneration >= parent generation map HTTPBootConfigStateReady ->
ConditionTrue and HTTPBootConfigStateError -> ConditionFalse; update the logic
around httpBootConfig.Status.State and the metav1.Condition construction in
patchHTTPBootReadyConditionFromHTTPState (referencing
HTTPBootReadyConditionType, cond, httpBootConfig.Status.State, and
cond.ObservedGeneration).

In `@internal/controller/serverbootconfiguration_pxe_controller.go`:
- Around line 165-205: The current patchIPXEBootReadyConditionFromIPXEState
function uses config.Status.State directly to stamp True/False on the parent
even if the child hasn't observed the latest spec; change the logic to only
promote ConditionTrue/ConditionFalse when the child has proven it observed the
config generation (e.g., compare config.Status.ObservedGeneration >=
config.Generation), otherwise set the IPXEBootReady condition to
Unknown/BootConfigPending; update the switch in
patchIPXEBootReadyConditionFromIPXEState to check
config.Status.ObservedGeneration first (and only set metav1.ConditionTrue/False
for v1alpha1.IPXEBootConfigStateReady/Error when the observedGeneration is
up-to-date), leaving the default/awaiting case as Unknown and keep
Reason/Message accordingly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c39714dd-e103-4cae-9eb4-0250113cdeb4

📥 Commits

Reviewing files that changed from the base of the PR and between 2e13bdc and 72da76e.

📒 Files selected for processing (7)
  • cmd/main.go
  • internal/controller/serverbootconfig_helpers.go
  • internal/controller/serverbootconfiguration_http_controller.go
  • internal/controller/serverbootconfiguration_pxe_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller_test.go
  • internal/controller/suite_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/controller/suite_test.go
  • internal/controller/serverbootconfiguration_readiness_controller.go

Comment on lines +1 to +2
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0
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.

⚠️ Potential issue | 🟡 Minor

Use the repo-standard SPDX header.

Line 1 uses 2026, but this repository requires the exact standard header text for Go files. Please align this new test file with the existing SPDX template.

As per coding guidelines, **/*.go: All Go files require an SPDX header: // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors and // SPDX-License-Identifier: Apache-2.0

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller_test.go`
around lines 1 - 2, Update the SPDX header in
internal/controller/serverbootconfiguration_readiness_controller_test.go to the
repo-standard exact two-line header: replace the current year/token with the
canonical lines "// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate
company and IronCore contributors" and "// SPDX-License-Identifier: Apache-2.0"
at the top of the file so the test file matches other Go files in the
repository.

@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from 72da76e to a6baf8d Compare April 1, 2026 14:02
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
internal/controller/serverbootconfiguration_readiness_controller.go (1)

86-107: ⚠️ Potential issue | 🟠 Major

Ignore stale per-mode conditions when aggregating readiness.

computeDesiredState only checks whether the condition exists and what its status is. After a ServerBootConfiguration spec change, an old HTTPBootReady / IPXEBootReady condition can keep Status.State at Ready or Error until the mode controller rewrites it. Treat ObservedGeneration != cfg.Generation the same as a missing condition so stale data remains pending.

🛠️ Proposed fix
 	if requireHTTP {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}

 	if requireIPXE {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, IPXEBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller.go` around
lines 86 - 107, The readiness aggregation should ignore stale per-mode
conditions: in computeDesiredState when inspecting HTTPBootReadyConditionType
and IPXEBootReadyConditionType (via apimeta.FindStatusCondition on
cfg.Status.Conditions), treat a condition whose ObservedGeneration !=
cfg.Generation the same as a missing condition (i.e., set allReady = false) so
stale Ready/Error values do not affect aggregation; keep the existing behavior
that a present condition with Status == metav1.ConditionFalse still sets
hasError = true, but ensure the ObservedGeneration check is applied before
trusting Status for both HTTPBootReadyConditionType and
IPXEBootReadyConditionType.
internal/controller/serverbootconfiguration_http_controller.go (1)

146-147: ⚠️ Potential issue | 🟠 Major

Don't publish the child's old state as current readiness.

This path reads httpBootConfig.Status.State immediately after applying the child spec. At that point the status can still belong to the previous HTTPBootConfig generation, but the parent condition is stamped with ObservedGeneration=cur.Generation, so stale Ready/Error can be treated as current. Please keep HTTPBootReady at Unknown until the child has reconciled the new spec, or gate the Ready/Error mapping on a child freshness signal.

Also applies to: 155-185

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_http_controller.go` around lines
146 - 147, The code is publishing the child's previous Status.State as current
readiness; instead gate mapping on child freshness: in
r.patchHTTPBootReadyConditionFromHTTPState (and the other similar call sites
155-185) detect if httpBootConfig.Status.ObservedGeneration <
httpBootConfig.Generation (or otherwise indicates the child hasn't reconciled
the new spec) and in that case set HTTPBootReadyConditionType to Unknown (do not
map Ready/Error from httpBootConfig.Status.State); only map Ready/Error when
ObservedGeneration is up-to-date. Also avoid reading httpBootConfig.Status.State
for error messages when the child is stale—use a clear "stale/unknown" message
or include the freshness check result instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/controller/serverbootconfiguration_readiness_controller.go`:
- Around line 1-2: The SPDX header in the new file uses year 2026 instead of the
repo-standard 2024; update the top two lines so they match the repository
requirement by changing "// SPDX-FileCopyrightText: 2026 SAP SE or an SAP
affiliate company and IronCore contributors" to use 2024 and keep the existing
"// SPDX-License-Identifier: Apache-2.0" line unchanged so the file header
matches the repository standard.

---

Duplicate comments:
In `@internal/controller/serverbootconfiguration_http_controller.go`:
- Around line 146-147: The code is publishing the child's previous Status.State
as current readiness; instead gate mapping on child freshness: in
r.patchHTTPBootReadyConditionFromHTTPState (and the other similar call sites
155-185) detect if httpBootConfig.Status.ObservedGeneration <
httpBootConfig.Generation (or otherwise indicates the child hasn't reconciled
the new spec) and in that case set HTTPBootReadyConditionType to Unknown (do not
map Ready/Error from httpBootConfig.Status.State); only map Ready/Error when
ObservedGeneration is up-to-date. Also avoid reading httpBootConfig.Status.State
for error messages when the child is stale—use a clear "stale/unknown" message
or include the freshness check result instead.

In `@internal/controller/serverbootconfiguration_readiness_controller.go`:
- Around line 86-107: The readiness aggregation should ignore stale per-mode
conditions: in computeDesiredState when inspecting HTTPBootReadyConditionType
and IPXEBootReadyConditionType (via apimeta.FindStatusCondition on
cfg.Status.Conditions), treat a condition whose ObservedGeneration !=
cfg.Generation the same as a missing condition (i.e., set allReady = false) so
stale Ready/Error values do not affect aggregation; keep the existing behavior
that a present condition with Status == metav1.ConditionFalse still sets
hasError = true, but ensure the ObservedGeneration check is applied before
trusting Status for both HTTPBootReadyConditionType and
IPXEBootReadyConditionType.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 365b6f38-da3a-483d-bbb8-ec248ce5a591

📥 Commits

Reviewing files that changed from the base of the PR and between 72da76e and a6baf8d.

📒 Files selected for processing (6)
  • api/v1alpha1/zz_generated.deepcopy.go
  • internal/controller/serverbootconfig_helpers.go
  • internal/controller/serverbootconfiguration_http_controller.go
  • internal/controller/serverbootconfiguration_pxe_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller_test.go
✅ Files skipped from review due to trivial changes (2)
  • api/v1alpha1/zz_generated.deepcopy.go
  • internal/controller/serverbootconfiguration_readiness_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/controller/serverbootconfig_helpers.go
  • internal/controller/serverbootconfiguration_pxe_controller.go

Comment on lines +1 to +2
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0
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.

⚠️ Potential issue | 🟡 Minor

Use the repository-standard SPDX header text.

This new file uses 2026 in the copyright line, but the repository standard here is 2024.

🪪 Proposed fix
-// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
+// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors
 // SPDX-License-Identifier: Apache-2.0

As per coding guidelines, **/*.go: All Go files require an SPDX header: // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors and // SPDX-License-Identifier: Apache-2.0

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller.go` around
lines 1 - 2, The SPDX header in the new file uses year 2026 instead of the
repo-standard 2024; update the top two lines so they match the repository
requirement by changing "// SPDX-FileCopyrightText: 2026 SAP SE or an SAP
affiliate company and IronCore contributors" to use 2024 and keep the existing
"// SPDX-License-Identifier: Apache-2.0" line unchanged so the file header
matches the repository standard.

@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from a6baf8d to 0edbc58 Compare April 1, 2026 14:10
@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from 0edbc58 to 6f510fd Compare April 2, 2026 09:57
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (4)
internal/controller/serverbootconfiguration_readiness_controller_test.go (1)

1-2: ⚠️ Potential issue | 🟡 Minor

Use the repository SPDX header verbatim.

Line 1 uses 2026, but this repo requires the canonical 2024 header text on Go files.

As per coding guidelines, **/*.go: All Go files require an SPDX header: // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors and // SPDX-License-Identifier: Apache-2.0

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller_test.go`
around lines 1 - 2, Replace the incorrect SPDX year header: update the two
top-of-file SPDX comment lines in
serverbootconfiguration_readiness_controller_test.go so they exactly match the
repository canonical header text, i.e. change the year from 2026 to 2024 and
ensure both lines are present as "// SPDX-FileCopyrightText: 2024 SAP SE or an
SAP affiliate company and IronCore contributors" and "//
SPDX-License-Identifier: Apache-2.0".
internal/controller/serverbootconfiguration_pxe_controller.go (1)

165-204: ⚠️ Potential issue | 🟠 Major

Don't mark the new SBC generation from a stale IPXEBootConfig status.

config.Status.State is read immediately after the Apply, before the IPXEBootConfig controller has necessarily observed the new spec. This helper then stamps ObservedGeneration: cur.Generation, so an old child Ready / Error can be promoted as current for the new ServerBootConfiguration generation. Please keep IPXEBootReady in Unknown until the child can prove it processed the latest spec, or add generation tracking to IPXEBootConfig.Status.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_pxe_controller.go` around lines
165 - 204, The helper patchIPXEBootReadyConditionFromIPXEState must not promote
a stale child status: instead of using config.Status.State immediately, set
IPXEBootReady condition to Unknown unless the IPXEBootConfig has actually
observed the new spec; change the logic in
patchIPXEBootReadyConditionFromIPXEState to check generation tracking (e.g.
require config.Status.ObservedGeneration >= config.Generation or a dedicated
Status.ObservedSpecGeneration field) before mapping config.Status.State to
True/False, and default to Unknown (Reason "BootConfigPending") when the child's
observed generation is older; update any references to config.Status.State
accordingly and add generation tracking to IPXEBootConfig.Status if it doesn't
exist.
internal/controller/serverbootconfiguration_readiness_controller.go (2)

1-2: ⚠️ Potential issue | 🟡 Minor

Use the repository SPDX header verbatim.

Line 1 uses 2026, but this repo requires the canonical 2024 header text on Go files.

As per coding guidelines, **/*.go: All Go files require an SPDX header: // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors and // SPDX-License-Identifier: Apache-2.0

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller.go` around
lines 1 - 2, Replace the incorrect SPDX header year in the file's top comment:
change the two header lines that currently contain "2026" to the canonical
repository header "2024" so they read "// SPDX-FileCopyrightText: 2024 SAP SE or
an SAP affiliate company and IronCore contributors" and "//
SPDX-License-Identifier: Apache-2.0"; update the header in
serverbootconfiguration_readiness_controller.go wherever the SPDX lines appear.

86-108: ⚠️ Potential issue | 🟠 Major

Treat stale mode conditions as pending.

After a ServerBootConfiguration spec change, the previous HTTPBootReady / IPXEBootReady conditions remain on the object until the mode controllers rewrite them. Because computeDesiredState ignores ObservedGeneration, those stale values can keep Status.State at Ready or Error for the new generation instead of falling back to Pending.

🛠️ Minimal guard
 	if requireHTTP {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}

 	if requireIPXE {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, IPXEBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller.go` around
lines 86 - 108, The mode-ready checks in computeDesiredState treat stale
HTTPBootReady/IPXEBootReady conditions as current; change the logic that reads
apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
and IPXEBootReadyConditionType to treat a condition whose ObservedGeneration !=
cfg.Generation as "pending" (i.e., act like c == nil -> set allReady = false) so
old-generation False/True values don't keep Status.State at Ready/Error; only
set hasError = true when c is present, c.ObservedGeneration == cfg.Generation,
and c.Status == metav1.ConditionFalse, otherwise set allReady = false for
nil/stale/non-True conditions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/controller/serverbootconfiguration_pxe_controller.go`:
- Around line 165-204: The helper patchIPXEBootReadyConditionFromIPXEState must
not promote a stale child status: instead of using config.Status.State
immediately, set IPXEBootReady condition to Unknown unless the IPXEBootConfig
has actually observed the new spec; change the logic in
patchIPXEBootReadyConditionFromIPXEState to check generation tracking (e.g.
require config.Status.ObservedGeneration >= config.Generation or a dedicated
Status.ObservedSpecGeneration field) before mapping config.Status.State to
True/False, and default to Unknown (Reason "BootConfigPending") when the child's
observed generation is older; update any references to config.Status.State
accordingly and add generation tracking to IPXEBootConfig.Status if it doesn't
exist.

In `@internal/controller/serverbootconfiguration_readiness_controller_test.go`:
- Around line 1-2: Replace the incorrect SPDX year header: update the two
top-of-file SPDX comment lines in
serverbootconfiguration_readiness_controller_test.go so they exactly match the
repository canonical header text, i.e. change the year from 2026 to 2024 and
ensure both lines are present as "// SPDX-FileCopyrightText: 2024 SAP SE or an
SAP affiliate company and IronCore contributors" and "//
SPDX-License-Identifier: Apache-2.0".

In `@internal/controller/serverbootconfiguration_readiness_controller.go`:
- Around line 1-2: Replace the incorrect SPDX header year in the file's top
comment: change the two header lines that currently contain "2026" to the
canonical repository header "2024" so they read "// SPDX-FileCopyrightText: 2024
SAP SE or an SAP affiliate company and IronCore contributors" and "//
SPDX-License-Identifier: Apache-2.0"; update the header in
serverbootconfiguration_readiness_controller.go wherever the SPDX lines appear.
- Around line 86-108: The mode-ready checks in computeDesiredState treat stale
HTTPBootReady/IPXEBootReady conditions as current; change the logic that reads
apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
and IPXEBootReadyConditionType to treat a condition whose ObservedGeneration !=
cfg.Generation as "pending" (i.e., act like c == nil -> set allReady = false) so
old-generation False/True values don't keep Status.State at Ready/Error; only
set hasError = true when c is present, c.ObservedGeneration == cfg.Generation,
and c.Status == metav1.ConditionFalse, otherwise set allReady = false for
nil/stale/non-True conditions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 302da5dd-e265-4b5a-b345-eb1d05949343

📥 Commits

Reviewing files that changed from the base of the PR and between a6baf8d and 6f510fd.

📒 Files selected for processing (7)
  • cmd/main.go
  • internal/controller/serverbootconfig_helpers.go
  • internal/controller/serverbootconfiguration_http_controller.go
  • internal/controller/serverbootconfiguration_pxe_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller_test.go
  • internal/controller/suite_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/controller/serverbootconfig_helpers.go
  • internal/controller/serverbootconfiguration_http_controller.go

@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch 3 times, most recently from 0b4afa4 to 43874a6 Compare April 2, 2026 11:08
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/controller/serverbootconfiguration_http_controller.go (1)

76-149: ⚠️ Potential issue | 🟠 Major

Clear stale HTTPBootReady on every owned error path.

Now that readiness is driven from HTTPBootReadyConditionType, only the UKI-construction failure updates the condition. If either server lookup, SetControllerReference, the SSA patch, or the post-apply Get fails, the previous condition is left untouched. computeDesiredState only looks at condition status, so an older True can keep the parent Ready after this reconcile has already failed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_http_controller.go` around lines
76 - 149, The reconcile path leaves an older HTTPBootReady condition unchanged
on many error branches; ensure we clear/patch the HTTPBootReadyConditionType to
False on every owned error path (not just UKI construction). For each failure in
reconcile (errors from r.getSystemUUIDFromServer,
r.getSystemNetworkIDsFromServer, controllerutil.SetControllerReference,
json.Marshal of httpBootConfig, r.Patch (SSA apply), and r.Get of
httpBootConfig) call PatchServerBootConfigCondition with a metav1.Condition
having Type HTTPBootReadyConditionType, Status False, an appropriate Reason
(e.g.,
"LookupFailed","NetworkIDsFailed","SetControllerRefFailed","MarshalFailed","ApplyFailed","GetFailed"),
Message from err.Error(), and ObservedGeneration config.Generation before
returning the original error so the parent readiness is cleared consistently;
keep existing UKI-construction patch logic as-is and reuse
PatchServerBootConfigCondition to implement this.
internal/controller/serverbootconfiguration_pxe_controller.go (1)

94-168: ⚠️ Potential issue | 🟠 Major

Clear stale IPXEBootReady on every owned error path.

Now that readiness is driven from IPXEBootReadyConditionType, only the image-details failure updates the condition. If either server lookup, SetControllerReference, the SSA patch, or the post-apply Get fails, the previous condition is left untouched. computeDesiredState only looks at condition status, so an older True can keep the parent Ready after this reconcile has already failed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_pxe_controller.go` around lines
94 - 168, The reconcile function leaves a stale IPXEBootReady condition on
several error paths (server lookup via r.getSystemUUIDFromBootConfig /
r.getSystemIPFromBootConfig, controllerutil.SetControllerReference, the SSA
apply via r.Patch, and the post-apply r.Get), so update each of those error
return paths to call PatchServerBootConfigCondition with
IPXEBootReadyConditionType set to metav1.ConditionFalse (Reason like
"ReconcileFailed" and Message set to the error message) before returning; ensure
you call PatchServerBootConfigCondition with the same NamespacedName used
elsewhere and propagate any patch errors by wrapping them (similar to the
existing image-details error handling) so failures still return useful context.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/controller/serverbootconfiguration_http_controller.go`:
- Around line 155-189: When patching the HTTPBootReady condition in
patchHTTPBootReadyConditionFromHTTPState, guard against stamping a newer
ServerBootConfiguration generation from an older reconcile: after re-fetching
cur (the fetched metalv1alpha1.ServerBootConfiguration) compare cur.Generation
to cfg.Generation (the generation this reconcile processed); if they differ, set
cond.ObservedGeneration = cfg.Generation and force cond.Status =
metav1.ConditionUnknown with a Reason like "StaleReconcile" and a message such
as "ServerBootConfiguration generation changed during reconcile; deferring
update." (alternatively return nil to bail out), then continue to
apimeta.SetStatusCondition and Patch; otherwise keep the current logic but
ensure ObservedGeneration is tied to cfg.Generation so we never claim readiness
for a generation we didn't process.

In `@internal/controller/serverbootconfiguration_pxe_controller.go`:
- Around line 174-208: In patchIPXEBootReadyConditionFromIPXEState, avoid
stamping a newer ServerBootConfiguration generation by comparing cur.Generation
(fresh from the retry fetch) with the bootConfig.Generation (the generation this
reconcile processed): if they differ, either return without patching or set
cond.Status = metav1.ConditionUnknown and cond.Reason = "StaleReconcile" before
calling SetStatusCondition so you don't mark the newer generation based on an
older IPXEBootConfig; also ensure ObservedGeneration on the cond is set to the
generation actually processed (use bootConfig.Generation) rather than
cur.Generation.

---

Outside diff comments:
In `@internal/controller/serverbootconfiguration_http_controller.go`:
- Around line 76-149: The reconcile path leaves an older HTTPBootReady condition
unchanged on many error branches; ensure we clear/patch the
HTTPBootReadyConditionType to False on every owned error path (not just UKI
construction). For each failure in reconcile (errors from
r.getSystemUUIDFromServer, r.getSystemNetworkIDsFromServer,
controllerutil.SetControllerReference, json.Marshal of httpBootConfig, r.Patch
(SSA apply), and r.Get of httpBootConfig) call PatchServerBootConfigCondition
with a metav1.Condition having Type HTTPBootReadyConditionType, Status False, an
appropriate Reason (e.g.,
"LookupFailed","NetworkIDsFailed","SetControllerRefFailed","MarshalFailed","ApplyFailed","GetFailed"),
Message from err.Error(), and ObservedGeneration config.Generation before
returning the original error so the parent readiness is cleared consistently;
keep existing UKI-construction patch logic as-is and reuse
PatchServerBootConfigCondition to implement this.

In `@internal/controller/serverbootconfiguration_pxe_controller.go`:
- Around line 94-168: The reconcile function leaves a stale IPXEBootReady
condition on several error paths (server lookup via
r.getSystemUUIDFromBootConfig / r.getSystemIPFromBootConfig,
controllerutil.SetControllerReference, the SSA apply via r.Patch, and the
post-apply r.Get), so update each of those error return paths to call
PatchServerBootConfigCondition with IPXEBootReadyConditionType set to
metav1.ConditionFalse (Reason like "ReconcileFailed" and Message set to the
error message) before returning; ensure you call PatchServerBootConfigCondition
with the same NamespacedName used elsewhere and propagate any patch errors by
wrapping them (similar to the existing image-details error handling) so failures
still return useful context.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbc93ab7-80c0-4072-8429-e3fbbe669383

📥 Commits

Reviewing files that changed from the base of the PR and between 6f510fd and 4d25195.

📒 Files selected for processing (8)
  • api/v1alpha1/httpbootconfig_types.go
  • api/v1alpha1/ipxebootconfig_types.go
  • config/crd/bases/boot.ironcore.dev_httpbootconfigs.yaml
  • config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml
  • internal/controller/httpbootconfig_controller.go
  • internal/controller/ipxebootconfig_controller.go
  • internal/controller/serverbootconfiguration_http_controller.go
  • internal/controller/serverbootconfiguration_pxe_controller.go
✅ Files skipped from review due to trivial changes (1)
  • api/v1alpha1/httpbootconfig_types.go

@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from 43874a6 to cfd8ede Compare April 2, 2026 11:56
@hardikdr
Copy link
Copy Markdown
Member Author

hardikdr commented Apr 2, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
internal/controller/serverbootconfiguration_readiness_controller.go (2)

89-111: ⚠️ Potential issue | 🟠 Major

Stale conditions should be treated as pending.

The condition evaluation doesn't check ObservedGeneration. After a ServerBootConfiguration spec change, old HTTPBootReady/IPXEBootReady conditions (with stale ObservedGeneration) can incorrectly keep Status.State at Ready or Error until the mode controllers rewrite them.

🛡️ Proposed fix
 	if requireHTTP {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, HTTPBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}

 	if requireIPXE {
 		c := apimeta.FindStatusCondition(cfg.Status.Conditions, IPXEBootReadyConditionType)
 		switch {
-		case c == nil:
+		case c == nil || c.ObservedGeneration != cfg.Generation:
 			allReady = false
 		case c.Status == metav1.ConditionFalse:
 			hasError = true
 		case c.Status != metav1.ConditionTrue:
 			allReady = false
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller.go` around
lines 89 - 111, The condition checks for HTTPBootReadyConditionType and
IPXEBootReadyConditionType must treat stale conditions (where
condition.ObservedGeneration != cfg.Generation) as pending; update the logic in
the blocks using apimeta.FindStatusCondition(cfg.Status.Conditions, ...) to
first check c != nil && c.ObservedGeneration == cfg.Generation before
interpreting c.Status, and if the observed generation is stale set allReady =
false (do not set hasError). Ensure you reference ObservedGeneration and
cfg.Generation when deciding pending vs error for HTTPBootReadyConditionType and
IPXEBootReadyConditionType so stale conditions don't keep State as Ready or
Error.

1-2: ⚠️ Potential issue | 🟡 Minor

Use the repository-standard SPDX header year.

The SPDX header uses 2026 but the repository standard requires 2024.

🪪 Proposed fix
-// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
+// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors
 // SPDX-License-Identifier: Apache-2.0

As per coding guidelines, **/*.go: All Go files require an SPDX header: // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/serverbootconfiguration_readiness_controller.go` around
lines 1 - 2, Update the SPDX header year in
internal/controller/serverbootconfiguration_readiness_controller.go: change the
top comment line that currently reads "2026 SAP SE or an SAP affiliate company
and IronCore contributors" to use the repository-standard year "2024"; ensure
the exact header format remains "// SPDX-FileCopyrightText: 2024 SAP SE or an
SAP affiliate company and IronCore contributors" to match other Go files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/controller/serverbootconfiguration_readiness_controller.go`:
- Around line 89-111: The condition checks for HTTPBootReadyConditionType and
IPXEBootReadyConditionType must treat stale conditions (where
condition.ObservedGeneration != cfg.Generation) as pending; update the logic in
the blocks using apimeta.FindStatusCondition(cfg.Status.Conditions, ...) to
first check c != nil && c.ObservedGeneration == cfg.Generation before
interpreting c.Status, and if the observed generation is stale set allReady =
false (do not set hasError). Ensure you reference ObservedGeneration and
cfg.Generation when deciding pending vs error for HTTPBootReadyConditionType and
IPXEBootReadyConditionType so stale conditions don't keep State as Ready or
Error.
- Around line 1-2: Update the SPDX header year in
internal/controller/serverbootconfiguration_readiness_controller.go: change the
top comment line that currently reads "2026 SAP SE or an SAP affiliate company
and IronCore contributors" to use the repository-standard year "2024"; ensure
the exact header format remains "// SPDX-FileCopyrightText: 2024 SAP SE or an
SAP affiliate company and IronCore contributors" to match other Go files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3184fe2d-17c5-4ca5-a2dd-3cac6ff3cd98

📥 Commits

Reviewing files that changed from the base of the PR and between 6f510fd and cfd8ede.

⛔ Files ignored due to path filters (2)
  • dist/chart/templates/crd/boot.ironcore.dev_httpbootconfigs.yaml is excluded by !**/dist/**
  • dist/chart/templates/crd/boot.ironcore.dev_ipxebootconfigs.yaml is excluded by !**/dist/**
📒 Files selected for processing (10)
  • api/v1alpha1/httpbootconfig_types.go
  • api/v1alpha1/ipxebootconfig_types.go
  • config/crd/bases/boot.ironcore.dev_httpbootconfigs.yaml
  • config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml
  • docs/api-reference/api.md
  • internal/controller/httpbootconfig_controller.go
  • internal/controller/ipxebootconfig_controller.go
  • internal/controller/serverbootconfiguration_http_controller.go
  • internal/controller/serverbootconfiguration_pxe_controller.go
  • internal/controller/serverbootconfiguration_readiness_controller.go
✅ Files skipped from review due to trivial changes (2)
  • internal/controller/ipxebootconfig_controller.go
  • internal/controller/httpbootconfig_controller.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • api/v1alpha1/httpbootconfig_types.go
  • api/v1alpha1/ipxebootconfig_types.go
  • config/crd/bases/boot.ironcore.dev_ipxebootconfigs.yaml
  • internal/controller/serverbootconfiguration_http_controller.go

@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from cfd8ede to 18c7d15 Compare April 2, 2026 13:39
@hardikdr hardikdr force-pushed the enh/dual-mode-httpboot-ipxe branch from 18c7d15 to 36fd51b Compare April 2, 2026 13:49
@hardikdr hardikdr removed ok-to-publish Run github action to publish the image ok-to-image labels Apr 2, 2026
@hardikdr hardikdr requested review from a team, afritzler and atd9876 April 3, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants