Skip to content

Aggregate HTTPBoot and iPXE Conditions to Compute ServerBootConfiguration Readiness#292

Open
hardikdr wants to merge 1 commit intomainfrom
enh/dual-mode-httpboot-ipxe
Open

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

Conversation

@hardikdr
Copy link
Copy Markdown
Member

@hardikdr hardikdr commented Mar 20, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Added HTTP and IPXE-specific boot readiness status conditions for detailed boot configuration status tracking.
    • Enhanced conflict resilience through automatic retry handling for concurrent status updates.

@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

Walkthrough

This PR introduces a new ServerBootConfigurationReadinessReconciler that aggregates HTTP and PXE boot readiness conditions into a unified ServerBootConfiguration.Status.State. The HTTP and PXE controllers were refactored to patch specific readiness conditions instead of full error state, and a new helper function manages safe condition patching with conflict retry logic.

Changes

Cohort / File(s) Summary
New Readiness Controller
internal/controller/serverbootconfiguration_readiness_controller.go
Introduced ServerBootConfigurationReadinessReconciler that aggregates HTTPBootReady and IPXEBootReady conditions into Status.State with conflict-safe status updates. Defines condition type constants and implements state machine logic: Ready when all required conditions are True, Error if any required condition is False, otherwise Pending.
Controller Refactoring
internal/controller/serverbootconfiguration_http_controller.go, internal/controller/serverbootconfiguration_pxe_controller.go
Replaced patchConfigState* functions with condition-based patching via patchHTTPBootReadyConditionFromHTTPState and patchIPXEBootReadyConditionFromIPXEState. Changed error handling to set HTTPBootReady/IPXEBootReady conditions instead of global error state. Added retry.RetryOnConflict wrapping for conflict-safe updates.
Helper Functions
internal/controller/serverbootconfig_helpers.go
Added PatchServerBootConfigCondition to atomically update a single condition in ServerBootConfiguration.Status.Conditions using deep-copy merge patch pattern with automatic ObservedGeneration defaulting and retry.RetryOnConflict conflict handling.
Main Setup
cmd/main.go
Unconditionally instantiated ServerBootConfigurationReadinessReconciler during startup, wiring RequireHTTPBoot and RequireIPXEBoot fields to corresponding controller enablement flags with error handling via os.Exit(1).
Test Infrastructure
internal/controller/suite_test.go
Registered ServerBootConfigurationReadinessReconciler in test setup with both boot modes enabled (RequireHTTPBoot: true, RequireIPXEBoot: true).
Comprehensive Test Suite
internal/controller/serverbootconfiguration_readiness_controller_test.go
Added 429 lines of Ginkgo/Gomega tests covering reconciler behavior across boot mode combinations, condition state transitions (Ready/Error/Pending), and unit tests for computeDesiredState logic with various condition permutations.

Sequence Diagram(s)

sequenceDiagram
    actor K8s as Kubernetes API
    participant HC as HTTP Controller
    participant PC as PXE Controller
    participant SBC as ServerBootConfiguration<br/>Resource
    participant RC as Readiness Controller
    
    K8s->>HC: Reconcile HTTPBootConfig
    HC->>HC: Validate UKI URL
    HC->>SBC: Patch HTTPBootReady condition<br/>(True/False/Unknown)
    
    K8s->>PC: Reconcile IPXEBootConfig
    PC->>PC: Get image details
    PC->>SBC: Patch IPXEBootReady condition<br/>(True/False/Unknown)
    
    K8s->>RC: Reconcile ServerBootConfiguration<br/>(condition change detected)
    RC->>SBC: Read current state +<br/>conditions
    RC->>RC: Compute desired State from<br/>HTTPBootReady + IPXEBootReady
    RC->>SBC: Patch Status.State if changed<br/>(Ready/Error/Pending)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement, api-change

Suggested reviewers

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

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is largely incomplete, with empty bullet points in the Proposed Changes section and no issue reference, failing to meet the template requirements. Fill in the three bullet points under Proposed Changes with specific implementation details, add the related issue number to 'Fixes #', and provide rationale for the dual-mode approach and race condition fix.
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 comprehensive refactoring across multiple controller files.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 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 +144 to +147
if err := r.patchHTTPBootReadyConditionFromHTTPState(ctx, httpBootConfig, config); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to patch %s condition from HTTPBootConfig state %s: %w", HTTPBootReadyConditionType, httpBootConfig.Status.State, err)
}
log.V(1).Info("Patched server boot config state")
log.V(1).Info("Patched server boot config condition", "condition", HTTPBootReadyConditionType)
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:

# First, locate and examine the serverbootconfiguration_http_controller.go file
fd -t f "serverbootconfiguration_http_controller.go" | head -5

Repository: ironcore-dev/boot-operator

Length of output: 132


🏁 Script executed:

# Get the full file to understand the context
cat -n internal/controller/serverbootconfiguration_http_controller.go | head -200

Repository: ironcore-dev/boot-operator

Length of output: 9707


🏁 Script executed:

# Focus on the critical sections mentioned in the review
sed -n '85,105p' internal/controller/serverbootconfiguration_http_controller.go

Repository: ironcore-dev/boot-operator

Length of output: 1014


🏁 Script executed:

# Check lines around 135-181 (child apply and status handling)
sed -n '130,185p' internal/controller/serverbootconfiguration_http_controller.go

Repository: ironcore-dev/boot-operator

Length of output: 2617


🏁 Script executed:

# Find the HTTPBootConfig API definition
fd -t f ".*http.*boot.*" | grep -i "api\|types" | head -10

Repository: ironcore-dev/boot-operator

Length of output: 106


🏁 Script executed:

# Search for HTTPBootConfig definition
rg "type HTTPBootConfig" -A 20

Repository: ironcore-dev/boot-operator

Length of output: 6726


🏁 Script executed:

# Look for the status structure
rg "HTTPBootConfigStatus" -A 15

Repository: ironcore-dev/boot-operator

Length of output: 10881


🏁 Script executed:

# Examine the HTTPBootConfig controller reconciliation
cat -n internal/controller/httpbootconfig_controller.go | head -100

Repository: ironcore-dev/boot-operator

Length of output: 4489


🏁 Script executed:

# Look for where the HTTPBootConfig controller sets status
rg "Status\.State\|SetStatusCondition\|ObservedGeneration" internal/controller/httpbootconfig_controller.go -B 2 -A 2

Repository: ironcore-dev/boot-operator

Length of output: 52


🏁 Script executed:

# Continue reading the patchStatus method
sed -n '97,120p' internal/controller/httpbootconfig_controller.go

Repository: ironcore-dev/boot-operator

Length of output: 885


🏁 Script executed:

# Check if there's any ObservedGeneration tracking at all in the HTTPBootConfig controller
rg "Generation" internal/controller/httpbootconfig_controller.go

Repository: ironcore-dev/boot-operator

Length of output: 52


🏁 Script executed:

# Look at the full patchStatus implementation
sed -n '97,115p' internal/controller/httpbootconfig_controller.go

Repository: ironcore-dev/boot-operator

Length of output: 728


Don't reuse old HTTPBootConfig status for the current SBC generation.

This path executes right after applying the child spec, so HTTPBootConfig.Status.State may still reflect the previous generation's status. The parent condition is then stamped with the current ServerBootConfiguration.Generation, making stale Ready/Error data appear current after a spec change. Until the HTTPBootConfig controller has reconciled the new spec, write HTTPBootReady=Unknown instead.

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

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

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

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants