Skip to content

Conversation

@xkonni
Copy link
Contributor

@xkonni xkonni commented Jan 6, 2026

When the metal operator tries to apply biossettings to a server with pending changes, it fails and releases the server immediately. no maintenance object is created. the server can then be claimed and integrated into production, making it hard to apply the biossettings at a later time.

This change creates a maintenance object when pending changes exist and sets the biossettings state to failed.

Changes:
- if pending settings exist, create maintenance object and fail
- added unit test and delete method for bmc mock

Summary by CodeRabbit

  • Bug Fixes

    • Fixed logging output in BIOS settings state handling
    • Added automatic maintenance requests when pending BIOS setting changes exist
  • Tests

    • Enhanced test coverage for pending BIOS settings scenarios
    • Added validation for maintenance workflow integration with BIOS operations

✏️ Tip: You can customize this high-level summary in your review settings.

@xkonni xkonni requested a review from a team as a code owner January 6, 2026 13:46
@github-actions github-actions bot added the size/L label Jan 6, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

Walkthrough

Adds HTTP DELETE endpoint to mock Redfish server for reverting resource overrides, fixes a logging typo in BIOS settings controller, and introduces test coverage for the maintenance workflow when pending BIOS settings exist, including HTTP PATCH and DELETE interactions.

Changes

Cohort / File(s) Summary
Mock Redfish Server Enhancement
bmc/mock/server/server.go
New handleRedfishDELETE method routes DELETE requests to revert resource overrides. Resolves request path, fetches current resource, rejects collections, deletes override entry under mutex lock, and returns 204 No Content. Error handling mirrors PATCH (404 if not found, 500 on corruption).
BIOS Settings Controller Update
internal/controller/biossettings_controller.go
Corrects log key typo "TaskCound" → "TaskCount" in handleSettingPendingState. Adds maintenance request call via r.requestMaintenanceForServer when pending BIOS settings exist; returns error if request fails.
BIOS Settings Test Expansion
internal/controller/biossettings_controller_test.go
Introduces mock server configuration (IP, port) replacing hard-coded values. New test scenario validates maintenance workflow when pending settings exist: patches mock server with PATCH, approves maintenance, verifies ServerMaintenance creation, clears settings via DELETE. Adds HTTP interaction imports and test scaffolding.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description lacks the required 'Proposed Changes' section with bullet points and 'Fixes #' reference from the template; it uses a different format without the standard structure. Reformat the description to match the template: add '# Proposed Changes' section with 3-4 bullet points, and include 'Fixes #581' to reference the issue number.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: transitioning a Server to Maintenance when pending BIOS settings are detected, which is the core objective of this PR.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@github-actions github-actions bot added api-change bug Something isn't working labels Jan 6, 2026
Copy link

@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: 0

🧹 Nitpick comments (1)
internal/controller/biossettings_controller_test.go (1)

1124-1128: Consider adding a timeout to the HTTP client.

While the mock server is local and should respond quickly, adding a timeout to the HTTP client provides better test hygiene and prevents potential hangs.

🔎 Suggested improvement
-		client := &http.Client{}
+		client := &http.Client{Timeout: 10 * time.Second}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37bd373 and 7b7970a.

📒 Files selected for processing (4)
  • api/v1alpha1/biossettings_types.go
  • bmc/mock/server/server.go
  • internal/controller/biossettings_controller.go
  • internal/controller/biossettings_controller_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
internal/controller/biossettings_controller.go (1)
api/v1alpha1/biossettings_types.go (3)
  • BIOSSettingsStateBlocked (65-65)
  • BIOSSettings (142-148)
  • BIOSSettingsStatePending (63-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run test e2e
  • GitHub Check: Run linter
  • GitHub Check: Run tests
  • GitHub Check: Run test chart
🔇 Additional comments (7)
bmc/mock/server/server.go (1)

160-172: LGTM! Clean DELETE handler implementation.

The implementation correctly handles DELETE requests by removing overrides and returning 204 No Content. The mutex locking ensures thread safety, and the idempotent behavior (no error if override doesn't exist) aligns with HTTP DELETE semantics.

api/v1alpha1/biossettings_types.go (1)

64-65: LGTM! Well-documented new state.

The BIOSSettingsStateBlocked state is appropriately placed in the state machine and clearly documented. This state enables proper handling of servers with pre-existing pending BIOS changes.

internal/controller/biossettings_controller_test.go (2)

28-31: LGTM! Constants for mock server configuration.

Extracting the mock server IP and port into constants improves maintainability and consistency across tests.


1104-1227: Good test coverage for the blocked state flow.

The test comprehensively validates the new blocked state behavior:

  1. Sets up pending BIOS settings via HTTP PATCH
  2. Verifies transition to Blocked state
  3. Confirms state persists while pending settings exist
  4. Clears pending settings and verifies transition to InProgress/Applied

The test follows existing patterns in the file and provides confidence in the new functionality.

internal/controller/biossettings_controller.go (3)

275-276: LGTM! Blocked state case added to state machine.

The new case correctly delegates to handleBlockedState for processing.


299-316: Core fix: Transition to Blocked instead of failing.

This is the key fix for the PR. When pending BIOS settings are detected, the controller now transitions to Blocked state with a requeue interval, allowing it to wait for the pending changes to be cleared rather than failing immediately. The condition is properly updated to track the reason for blocking.


403-419: Well-structured blocked state handler.

The handleBlockedState function follows the established patterns in the controller:

  1. Checks if pending settings still exist
  2. Stays in Blocked state with requeue if pending settings persist
  3. Transitions back to Pending when cleared, which will reset conditions and restart the flow

The transition to Pending correctly leverages the existing updateStatus behavior that clears conditions when entering Pending state, ensuring a fresh start for the settings application.

@xkonni xkonni force-pushed the bug/fix-pending-biossettings branch from 7b7970a to f795f1a Compare January 7, 2026 08:37
Copy link

@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: 0

🧹 Nitpick comments (2)
internal/controller/biossettings_controller_test.go (1)

1146-1158: Variable shadowing: serverClaim shadows outer scope variable.

At line 1146, serverClaim := creates a new variable that shadows the serverClaim declared at line 899 in the outer BeforeEach scope. While this works correctly since the outer variable isn't used in this test, it's cleaner to use assignment (=) instead to avoid confusion.

♻️ Suggested fix
-		serverClaim := &metalv1alpha1.ServerClaim{
+		serverClaim = &metalv1alpha1.ServerClaim{
internal/controller/biossettings_controller.go (1)

403-419: LGTM - Clean implementation of the Blocked state handler.

The handler correctly:

  1. Rechecks pending BIOS settings on each reconciliation
  2. Stays in Blocked state with requeue if settings are still pending
  3. Transitions back to Pending when cleared, allowing the normal flow to resume

The transition to Pending (rather than InProgress) is the right choice as it ensures the full validation flow runs again after the blockage clears.

Consider adding a condition update when transitioning out of Blocked state to provide better observability/traceability. For example:

// When transitioning out of Blocked:
log.V(1).Info("Pending settings cleared - transitioning back to Pending state")
// Optionally update the existing BIOSPendingSettingConditionCheck condition
// to indicate the blockage was resolved

This is optional since the existing BIOSPendingSettingConditionCheck condition already captures that pending settings were found.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b7970a and f795f1a.

📒 Files selected for processing (4)
  • api/v1alpha1/biossettings_types.go
  • bmc/mock/server/server.go
  • internal/controller/biossettings_controller.go
  • internal/controller/biossettings_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/v1alpha1/biossettings_types.go
🧰 Additional context used
🧬 Code graph analysis (1)
internal/controller/biossettings_controller.go (1)
api/v1alpha1/biossettings_types.go (3)
  • BIOSSettingsStateBlocked (65-65)
  • BIOSSettings (142-148)
  • BIOSSettingsStatePending (63-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run test e2e
  • GitHub Check: Run linter
  • GitHub Check: Run test chart
  • GitHub Check: Run tests
🔇 Additional comments (6)
bmc/mock/server/server.go (2)

84-85: LGTM - DELETE method routing added correctly.

The routing to handleRedfishDELETE follows the existing pattern for other HTTP methods.


160-194: LGTM - DELETE handler implementation is correct.

The handler correctly:

  1. Validates the resource exists (via fetchCurrentDataForPath)
  2. Rejects collection resources
  3. Removes the override to revert to embedded JSON

Minor note: fetchCurrentDataForPath stores data in s.overrides (line 576) before DELETE removes it, which is a slight inefficiency but acceptable for a test mock server.

internal/controller/biossettings_controller_test.go (2)

28-31: LGTM - Constants extracted for test configuration.

Extracting mockServerIP and mockServerPort as constants improves maintainability and reduces magic values throughout the test file.


1104-1231: Good test coverage for the new Blocked state workflow.

The test comprehensively covers:

  1. Setting up pending BIOS settings via HTTP PATCH
  2. Verifying transition to Blocked state
  3. Confirming the state remains Blocked during retry cycles
  4. Clearing pending settings via HTTP DELETE
  5. Verifying successful transition to InProgress/Applied

The use of explicit HTTP calls to the mock server effectively simulates real-world scenarios where pending BIOS changes exist on the BMC.

internal/controller/biossettings_controller.go (2)

275-276: LGTM - Blocked state routing added to state machine.

The new case properly routes BIOSSettingsStateBlocked to the new handleBlockedState handler, maintaining the existing pattern.


299-315: LGTM - Pending settings handling improved.

Good changes:

  1. Fixed log key typo: "TaskCount" (was likely "TaskCound")
  2. Transitions to Blocked state instead of failing when pending BIOS settings are detected

The RequeueAfter: r.ResyncInterval ensures the controller will retry after the configured interval, giving external processes time to clear pending settings.

- if pending settings exist, create maintenance object and fail
- added unit test and `delete` method for bmc mock
@xkonni xkonni force-pushed the bug/fix-pending-biossettings branch from f795f1a to 8a5414e Compare January 7, 2026 12:36
Copy link

@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: 0

🧹 Nitpick comments (1)
bmc/mock/server/server.go (1)

160-194: DELETE handler correctly implements override removal.

The implementation properly reverts overrides to restore embedded JSON data. The logic flow is sound:

  • Validates the resource exists and isn't a collection
  • Removes the override entry under mutex protection
  • Returns 204 No Content (correct status for successful DELETE)

The DELETE behavior is idempotent, succeeding even when no override exists, which aligns with REST best practices.

Optional: Consider extracting duplicate error handling.

The error handling block (lines 168-179) is identical to the PATCH handler (lines 117-128). While not critical for a mock server, extracting this into a helper function would improve maintainability:

func handleFetchError(w http.ResponseWriter, r *http.Request, err error) bool {
    switch {
    case strings.Contains(err.Error(), "resource not found"):
        http.NotFound(w, r)
        return true
    case strings.Contains(err.Error(), "corrupt embedded JSON"):
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return true
    default:
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return true
    }
}

Then both handlers could use:

base, err := fetchCurrentDataForPath(s, urlPath)
if err != nil {
    handleFetchError(w, r, err)
    return
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f795f1a and 8a5414e.

📒 Files selected for processing (3)
  • bmc/mock/server/server.go
  • internal/controller/biossettings_controller.go
  • internal/controller/biossettings_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/controller/biossettings_controller.go
  • internal/controller/biossettings_controller_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run test e2e
  • GitHub Check: Run linter
  • GitHub Check: Run tests
  • GitHub Check: Run test chart
🔇 Additional comments (1)
bmc/mock/server/server.go (1)

84-85: LGTM! DELETE routing properly integrated.

The DELETE method routing follows the established pattern and correctly dispatches to the new handler.

Comment on lines +28 to +31
const (
mockServerIP = "127.0.0.1"
mockServerPort = 8000
)
Copy link
Member

Choose a reason for hiding this comment

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

Can we move this to the suite setup?

})

It("Should fail and create maintenance when pending settings exist", func(ctx SpecContext) {
// Ensure BMC is in Enabled state
Copy link
Member

Choose a reason for hiding this comment

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

Please use a By(...) statement instead of commenting the code.

}
resp, err := client.Do(req)
Expect(err).NotTo(HaveOccurred())
defer resp.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

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

Use a DeferCleanup() function here to close the body.


clearResp, err := client.Do(clearReq)
Expect(err).NotTo(HaveOccurred())
defer clearResp.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

@afritzler afritzler changed the title fix apply biossettings to a server with pending changes Transition Server to Maintenance in case of pending BIOS settings exist Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-change bug Something isn't working size/L

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants