Monitor MachinePool Health#1476
Conversation
apiserver and controller components now have:
- config/<component>/default/ - namespace+namePrefix only, emits no
Namespace (used as a base)
- config/<component>/standalone/ - wraps default/ and adds the
ironcore-system Namespace; use this for
single-component deploys
Shared Namespace lives in config/namespaces/ironcore-system/. The combined
config/default and config/etcdless installs reference the bases and the
namespace kustomization directly, eliminating the previous
remove-namespace.yaml patch dance. Build output for both is byte-identical
to main (only kustomize deprecation-warning lines disappear, since the
patches that triggered them are gone).
The namespaces folder can be extended with additional namespaces, e.g. for
the pool leases.
Behavioral change for downstream consumers: users who previously ran
`kustomize build config/controller/default` for a complete deploy must
migrate to `config/controller/standalone`; same for
`config/apiserver/default` -> `config/apiserver/standalone` (or
`config/apiserver/standalone-etcdless` for the external-etcd variant).
Consumers must not apply namespace transformers in overlays, these
will fail once we have multiple namespaces.
The Makefile install/uninstall/deploy/undeploy targets are retargeted at
the standalone variants accordingly. hack/validate-kustomize.sh is also
made portable (GNU realpath --relative-to is unavailable on macOS).
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
This change mostly consists of three parts: 1. A MachinePool lifecycle controller: Implements `machinepool-lifecycle-controller` monitoring MachinePool health, with corresponding API additions for MachinePool/VolumePool/BucketPool conditions, codegen updates 2. A pool health heartbeat runnable in the machinepoollet: The poollet now actively reports pool liveness so the lifecycle controller can set pools whose poollets have gone away to Unknown. It also includes kustomizations adding the Lease namespace and RBAC adjustments. The MachinePoolHeartbeat, a ticker-driven Runnable that probes the IRI runtime via Status, renews the pool's Lease in ironcore-machinepool-lease, and patches Ready only when its value or observedGeneration actually changes. Errors on either sub-step are logged and retried on the next tick; the lifecycle controller's grace period absorbs short blips. Lease takeover from a previous holder is logged at Info. Contains app arguments that make the heartbeat intervals configurable, defaulting to the IEP-15 values. 3. Lease namespaces and RBACs Contributes to #1472 Signed-off-by: Felix Riegger <felix.riegger@sap.com>
75d6863 to
20479ee
Compare
adracus
left a comment
There was a problem hiding this comment.
What about the remaining pool health controllers / reconcilers? If we want to make this change only about machine pools, we should either remove everything else or add the rest.
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughMachine pool health monitoring: adds a MachinePoolReady condition and LastUpdateTime, poollet heartbeat that probes runtime and maintains Leases, a lifecycle reconciler that marks stale pools Unknown after a grace period, and namespace/RBAC manifests plus tests and generated updates. ChangesMachine pool health monitoring
Sequence Diagram(s)sequenceDiagram
participant Poollet as MachinePoollet
participant IRI as IRI Runtime
participant ReadyState as MachinePoolReadyState
participant Events as Event Channel
participant Lease as Lease Storage
Poollet->>IRI: Status probe (with timeout)
IRI-->>Poollet: probe result (success/error)
Poollet->>ReadyState: Set(probeErr)
alt probe error changed
ReadyState-->>Poollet: changed=true
Poollet->>Events: emit GenericEvent for MachinePool
else probe error unchanged
ReadyState-->>Poollet: changed=false
end
Poollet->>Lease: reconcileLease (create/update renewTime)
Lease-->>Poollet: Lease created/updated
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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
♻️ Duplicate comments (1)
internal/controllers/compute/machinepool_lifecycle_controller.go (1)
44-46:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winMemory leak: clean up
healthDatawhen MachinePool is deleted.When a MachinePool is deleted,
client.IgnoreNotFound(err)returns without removing the corresponding entry from ther.healthDatamap. Over time, this will cause the map to accumulate stale entries for deleted pools, leading to a memory leak.As per the past review comment from adracus, you should free the health data when the machine pool is not found.
🛠️ Proposed fix
func (r *MachinePoolLifecycleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := ctrl.LoggerFrom(ctx) machinePool := &computev1alpha1.MachinePool{} if err := r.Get(ctx, req.NamespacedName, machinePool); err != nil { + if apierrors.IsNotFound(err) { + r.healthDataMu.Lock() + delete(r.healthData, req.Name) + r.healthDataMu.Unlock() + } return ctrl.Result{}, client.IgnoreNotFound(err) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/controllers/compute/machinepool_lifecycle_controller.go` around lines 44 - 46, The current Reconcile branch returns client.IgnoreNotFound(err) after r.Get and thus never cleans up r.healthData for deleted MachinePools; modify the error handling so that when r.Get returns a NotFound error (use apierrors.IsNotFound(err) or check client.IgnoreNotFound result), you remove the corresponding entry from r.healthData using req.NamespacedName (or its string form) as the key, then return ctrl.Result{} and nil (or the ignored-not-found result). Keep the rest of the error path unchanged for non-NotFound errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@client-go/openapi/api_violations.report`:
- Line 42: The Conditions slice in BucketPoolStatus lacks explicit list
semantics; add kubebuilder markers above the Conditions field in
api/storage/v1alpha1/bucketpool_types.go (e.g., annotate
BucketPoolStatus.Conditions with +listType=map and +listMapKey=type) so the list
is treated as a map keyed by condition Type, then run the CRD/OpenAPI generation
step to regenerate the openapi/report artifacts.
In `@poollet/machinepoollet/controllers/machinepool_heartbeat_test.go`:
- Around line 56-58: The test titled "ignores LastUpdateTime and
LastTransitionTime" currently compares an unchanged copy; update it so desired
mutates the condition timestamps before asserting: create desired := base (value
copy) then modify desired.Status.Conditions[*].LastUpdateTime and
LastTransitionTime (or at least one condition) to different non-zero values
(e.g., time.Now() or a different metav1.Time) and then call
controllers.ReadyConditionsDiffer(&base, desired) and Expect(...).To(BeFalse())
to ensure timestamp differences are ignored; reference ReadyConditionsDiffer,
base, desired, LastUpdateTime and LastTransitionTime to locate the change.
---
Duplicate comments:
In `@internal/controllers/compute/machinepool_lifecycle_controller.go`:
- Around line 44-46: The current Reconcile branch returns
client.IgnoreNotFound(err) after r.Get and thus never cleans up r.healthData for
deleted MachinePools; modify the error handling so that when r.Get returns a
NotFound error (use apierrors.IsNotFound(err) or check client.IgnoreNotFound
result), you remove the corresponding entry from r.healthData using
req.NamespacedName (or its string form) as the key, then return ctrl.Result{}
and nil (or the ignored-not-found result). Keep the rest of the error path
unchanged for non-NotFound errors.
🪄 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: 5d7b923a-79b4-4d83-aee3-392f96b423a5
📒 Files selected for processing (49)
api/compute/v1alpha1/common.goapi/compute/v1alpha1/conditions.goapi/compute/v1alpha1/conditions_test.goapi/compute/v1alpha1/machinepool_types.goapi/compute/v1alpha1/v1alpha1_suite_test.goapi/compute/v1alpha1/zz_generated.deepcopy.goapi/storage/v1alpha1/bucketpool_types.goapi/storage/v1alpha1/common.goapi/storage/v1alpha1/volumepool_types.goapi/storage/v1alpha1/zz_generated.deepcopy.goapi/storage/v1alpha1/zz_generated.model_name.goclient-go/applyconfigurations/compute/v1alpha1/machinepoolcondition.goclient-go/applyconfigurations/storage/v1alpha1/bucketpoolcondition.goclient-go/applyconfigurations/storage/v1alpha1/bucketpoolstatus.goclient-go/applyconfigurations/storage/v1alpha1/volumepoolcondition.goclient-go/applyconfigurations/utils.goclient-go/openapi/api_violations.reportclient-go/openapi/zz_generated.openapi.gocmd/ironcore-controller-manager/main.goconfig/apiserver/standalone-etcdless/kustomization.yamlconfig/apiserver/standalone/kustomization.yamlconfig/controller/standalone/kustomization.yamlconfig/default/kustomization.yamlconfig/etcdless/kustomization.yamlconfig/machinepoollet-broker/poollet-rbac/role.yamlconfig/namespaces/machinepool-lease/controller_role.yamlconfig/namespaces/machinepool-lease/controller_rolebinding.yamlconfig/namespaces/machinepool-lease/kustomization.yamlconfig/namespaces/machinepool-lease/namespace.yamlconfig/namespaces/machinepool-lease/poollet_role.yamlconfig/namespaces/machinepool-lease/poollet_rolebinding.yamlgo.modhack/update-codegen.shinternal/apis/compute/machinepool_types.gointernal/apis/compute/v1alpha1/zz_generated.conversion.gointernal/apis/compute/zz_generated.deepcopy.gointernal/apis/storage/bucketpool_types.gointernal/apis/storage/v1alpha1/zz_generated.conversion.gointernal/apis/storage/volumepool_types.gointernal/apis/storage/zz_generated.deepcopy.gointernal/controllers/compute/machinepool_lifecycle_controller.gointernal/controllers/compute/machinepool_lifecycle_controller_test.gointernal/controllers/compute/suite_test.gopoollet/machinepoollet/cmd/machinepoollet/app/app.gopoollet/machinepoollet/controllers/controllers_suite_test.gopoollet/machinepoollet/controllers/machinepool_heartbeat.gopoollet/machinepoollet/controllers/machinepool_heartbeat_envtest_test.gopoollet/machinepoollet/controllers/machinepool_heartbeat_test.gopoollet/machinepoollet/controllers/rbac.go
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
The MachinePoolReconciler already owns the rest of MachinePool.Status, so it should also own the Ready condition. The MachinePoolHeartbeat keeps the IRI Status probe and the Lease, and now publishes its latest probe result through a new thread-safe MachinePoolReadyState. When the result changes, the heartbeat sends a GenericEvent on a channel that the reconciler watches via source.Channel; the reconciler reads the state in its existing updateStatus and merges the desired condition into the single status patch. Per code-review feedback on the original heartbeat implementation: the heartbeat used to write the condition itself, which made it the second poollet-side writer of MachinePool.Status. Single-writer-per-status-field is the cleaner default. Tests: the envtest now registers a MachinePoolReconciler alongside the heartbeat so the new path is exercised end-to-end. Adds unit tests for MachinePoolReadyState pinning the changed-detection behaviour the heartbeat relies on for event suppression. Signed-off-by: Felix Riegger <felix.riegger@sap.com>
These API additions were introduced together with the MachinePool health work but are not yet used by any controller. Removing them keeps the MachinePool health change shippable on its own; the VolumePool/BucketPool conditions are parked on the parked/volumepool-bucketpool-conditions branch for later. This reverts only the storage API and generated-code portions of 20479ee while keeping all MachinePool-related changes (lifecycle controller, heartbeat, MachinePoolCondition.LastUpdateTime, lease namespace, etc.). Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Pick up the ironcore-machinepool-lease Role into config/apiserver/rbac/machinepool_role.yaml. This was missed when the kubebuilder marker for the lease RBAC was added in 20479ee ("Implement Machine Pool Health checks"); make test regenerates it via the generate-rbac-aggregator rule. Signed-off-by: Felix Riegger <felix.riegger@sap.com>
849569e to
2dd3b0a
Compare
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Volume and Bucket changes have been removed from this PR. |
lukasfrank
left a comment
There was a problem hiding this comment.
Can you also please update the PR title and description.
For me it's not clear what based on standalone kustomizations means.
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
…on-standalone-ks Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
lukasfrank
left a comment
There was a problem hiding this comment.
Can you also please use komega for the tests.
Co-authored-by: Lukas Frank <lukasfrank@users.noreply.github.com> Signed-off-by: Felix Riegger <felix.riegger@sap.com>
…on-standalone-ks Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
Signed-off-by: Felix Riegger <felix.riegger@sap.com>
lukasfrank
left a comment
There was a problem hiding this comment.
Many thanks for the great work! LGTM
Implements
machinepool-lifecycle-controllermonitoring MachinePool health, with corresponding API additions for MachinePool/VolumePool/BucketPool conditions, codegen updatesThe poollet now actively reports pool liveness so the lifecycle controller can set pools whose poollets have gone away to Unknown. It also includes kustomizations adding the Lease namespace and RBAC adjustments. The MachinePoolHeartbeat, a ticker-driven Runnable that probes the IRI runtime via Status, renews the pool's Lease in ironcore-machinepool-lease, and patches Ready only when its value or observedGeneration actually changes. Errors on either sub-step are logged and retried on the next tick; the lifecycle controller's grace period absorbs short blips. Lease takeover from a previous holder is logged at Info. Contains app arguments that make the heartbeat intervals configurable, defaulting to the IEP-15 values.
Contributes to #1472
Summary by CodeRabbit
New Features
Configuration