Add HypervisorReadyController to centralize Ready condition logic#286
Add HypervisorReadyController to centralize Ready condition logic#286
Conversation
📝 WalkthroughWalkthroughA new ready controller centralizes computation and patching of the Hypervisor Changes
Sequence Diagram(s)sequenceDiagram
participant Manager as "Manager\n(ctrl.Manager)\nrgba(52,152,219,0.5)"
participant ReadyCtrl as "Ready Controller\nrgba(46,204,113,0.5)"
participant API as "Kubernetes API\n(Hypervisor CR)\nrgba(241,196,15,0.5)"
Manager->>ReadyCtrl: Start & register controller
ReadyCtrl->>API: Watch Hypervisor events (status/spec.maintenance)
Note over ReadyCtrl,API: On relevant event (status change or maintenance spec)
ReadyCtrl->>API: GET Hypervisor
ReadyCtrl->>ReadyCtrl: ComputeReadyCondition(hv)
ReadyCtrl->>API: PATCH hv.status (fieldOwner: "ready") if changed
API-->>ReadyCtrl: Updated status
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/ready/controller.go`:
- Around line 134-163: The onboarding vs maintenance precedence is inverted:
move the active-onboarding branch (the check onboardingCondition.Status ==
metav1.ConditionTrue that returns ConditionReasonOnboarding/"Onboarding in
progress") so it executes before the maintenance handling, then leave the
nil/aborted/non-succeeded onboarding cases after the maintenance block; update
the logic in the handler around meta.FindStatusCondition(hv.Status.Conditions,
kvmv1.ConditionTypeOnboarding) to only short-circuit for Status==True early and
then, after the maintenance checks, evaluate onboardingCondition == nil,
onboardingCondition.Reason == kvmv1.ConditionReasonAborted, and the generic
non-succeeded case and return the appropriate metav1.Condition with Reason
Onboarding/Onboarding aborted/Onboarding not started as a table of explicit
combinations; apply the same reordering and case table changes to the analogous
block referenced later (the block around lines 166-203).
- Around line 57-73: StatusChangedPredicate.Update currently only compares
oldHv.Status vs newHv.Status so changes to hv.Spec.Maintenance don't trigger
reconcile and Ready can become stale; update StatusChangedPredicate.Update to
also detect changes to hv.Spec.Maintenance (e.g., return true if
!DeepEqual(oldHv.Status, newHv.Status) || oldHv.Spec.Maintenance !=
newHv.Spec.Maintenance) so that changes affecting ComputeReadyCondition (which
reads hv.Spec.Maintenance) will enqueue a reconcile.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: deeceb2d-145c-4aa5-bd78-6018bdec6e52
📒 Files selected for processing (7)
cmd/main.gointernal/controller/hypervisor_controller.gointernal/controller/hypervisor_maintenance_controller.gointernal/controller/offboarding_controller.gointernal/controller/onboarding_controller.gointernal/controller/ready/controller.gointernal/controller/ready/controller_test.go
💤 Files with no reviewable changes (3)
- internal/controller/hypervisor_controller.go
- internal/controller/hypervisor_maintenance_controller.go
- internal/controller/onboarding_controller.go
14b9d96 to
a9e2a9f
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/controller/ready/controller.go (1)
128-128: Consider using a constant for the "Offboarded" reason.All other condition reasons use
kvmv1.ConditionReason*constants, but line 128 uses a hardcoded string"Offboarded". For consistency and maintainability, consider defining and using a constant likekvmv1.ConditionReasonOffboarded.♻️ Suggested change
if meta.IsStatusConditionTrue(hv.Status.Conditions, kvmv1.ConditionTypeOffboarded) { return metav1.Condition{ Type: kvmv1.ConditionTypeReady, Status: metav1.ConditionFalse, - Reason: "Offboarded", + Reason: kvmv1.ConditionReasonOffboarded, // define this constant in api/v1 Message: "Hypervisor has been offboarded", } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/controller/ready/controller.go` at line 128, Replace the hardcoded Reason string "Offboarded" in the Ready controller with a named constant to match existing condition reasons; specifically, change the literal at the Reason field (currently "Offboarded") to use kvmv1.ConditionReasonOffboarded (or add that constant in kvmv1 if it doesn't exist) so the Ready controller's condition reasons are consistent with other uses of kvmv1.ConditionReason*.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@internal/controller/ready/controller.go`:
- Line 128: Replace the hardcoded Reason string "Offboarded" in the Ready
controller with a named constant to match existing condition reasons;
specifically, change the literal at the Reason field (currently "Offboarded") to
use kvmv1.ConditionReasonOffboarded (or add that constant in kvmv1 if it doesn't
exist) so the Ready controller's condition reasons are consistent with other
uses of kvmv1.ConditionReason*.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3874226e-7c21-4942-9e52-c1a84b6a3d24
📒 Files selected for processing (7)
cmd/main.gointernal/controller/hypervisor_controller.gointernal/controller/hypervisor_maintenance_controller.gointernal/controller/offboarding_controller.gointernal/controller/onboarding_controller.gointernal/controller/ready/controller.gointernal/controller/ready/controller_test.go
💤 Files with no reviewable changes (3)
- internal/controller/hypervisor_controller.go
- internal/controller/onboarding_controller.go
- internal/controller/hypervisor_maintenance_controller.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/controller/offboarding_controller.go
Introduces a new ReadyController that computes the Ready condition based on other conditions (Onboarding, Offboarded, Terminating, Evicting) and the maintenance spec field. Changes: - Add internal/controller/ready package with controller and tests - Remove Ready condition setting from OnboardingController - Remove Ready condition setting from MaintenanceController - Remove Ready condition setting from OffboardingController - Remove Ready condition setting from HypervisorController - Update OffboardingController to use Offboarded condition for state tracking The ReadyController uses a predicate that triggers on: - Status changes - Maintenance field changes (since it affects Ready condition) Ready condition priority: 1. Offboarded=True -> Ready=False 2. Terminating=True -> Ready=False 3. Onboarding in progress (Status=True) -> Ready=False 4. Maintenance mode (with eviction state) -> Ready=False 5. Onboarding not started/aborted/not succeeded -> Ready=False 6. All checks pass -> Ready=True
a9e2a9f to
b5addc7
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/controller/ready/controller.go (1)
57-81: IgnoreReady-only status churn inUpdate().This compare is broader than the controller’s actual inputs, and it also self-triggers on the
Readypatch this controller writes. That guarantees one extra no-op reconcile after every successfulReadyupdate. Please compare only the fieldsComputeReadyCondition()reads, or at least excludeConditionTypeReady, so the predicate matches the controller’s real dependencies. A small unit case for aReady-only status diff would help lock that in.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/controller/ready/controller.go` around lines 57 - 81, The Update() in StatusChangedPredicate currently treats any status change as a trigger, causing self-triggering reconciles when this controller writes the Ready condition; modify StatusChangedPredicate.Update to only consider the fields used by ComputeReadyCondition() (or at minimum ignore ConditionTypeReady) when comparing oldHv.Status vs newHv.Status: e.g. create copies of oldHv.Status and newHv.Status with the Ready condition removed/filtered and use equality.Semantic.DeepEqual on those copies, while still keeping the existing check for oldHv.Spec.Maintenance != newHv.Spec.Maintenance; also add a unit test that constructs an UpdateEvent where only the Ready condition differs to assert Update() returns false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@internal/controller/ready/controller.go`:
- Around line 57-81: The Update() in StatusChangedPredicate currently treats any
status change as a trigger, causing self-triggering reconciles when this
controller writes the Ready condition; modify StatusChangedPredicate.Update to
only consider the fields used by ComputeReadyCondition() (or at minimum ignore
ConditionTypeReady) when comparing oldHv.Status vs newHv.Status: e.g. create
copies of oldHv.Status and newHv.Status with the Ready condition
removed/filtered and use equality.Semantic.DeepEqual on those copies, while
still keeping the existing check for oldHv.Spec.Maintenance !=
newHv.Spec.Maintenance; also add a unit test that constructs an UpdateEvent
where only the Ready condition differs to assert Update() returns false.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8db087cb-c18e-471a-881e-486984855603
📒 Files selected for processing (7)
cmd/main.gointernal/controller/hypervisor_controller.gointernal/controller/hypervisor_maintenance_controller.gointernal/controller/offboarding_controller.gointernal/controller/onboarding_controller.gointernal/controller/ready/controller.gointernal/controller/ready/controller_test.go
💤 Files with no reviewable changes (3)
- internal/controller/hypervisor_controller.go
- internal/controller/onboarding_controller.go
- internal/controller/hypervisor_maintenance_controller.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/controller/offboarding_controller.go
Since the ReadyController now owns the Ready condition, update tests for maintenance, offboarding, onboarding, and hypervisor controllers to only check for the conditions those controllers actually set (Evicting, Offboarded, Onboarding, Terminating) rather than the computed Ready condition. - Remove tests checking Ready condition from maintenance controller - Update offboarding tests to check Offboarded condition instead - Remove Ready condition checks from onboarding controller tests - Remove test that checked Ready condition preservation in hypervisor controller (now handled by ReadyController) - Set initial Offboarded condition in failure mode tests so reconcile hits the error paths
Merging this branch changes the coverage (1 decrease, 1 increase)
Coverage by fileChanged files (no unit tests)
Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code. Changed unit test files
|
Introduces a new ReadyController that computes the Ready condition based on other conditions (Onboarding, Offboarded, Terminating, Evicting) and the maintenance spec field. This greatly simplifies the ready logic and removes the need to update two status conditions at once in most controllers.
Also this is the first commit that moves the controller to it's own directory. The reason is that the vast amount of controllers polluting their common module namespace.
The ReadyController uses a StatusChangedPredicate to only reconcile when status changes (with exception of the maintenance spec), avoiding unnecessary reconciliation on spec changes.
Ready condition priority:
Summary by CodeRabbit
New Features
Refactor
Behavioral Changes
Tests