Deregister control plane VM from Kubernetes_API_Server LB rule on CloudStackMachine delete - #485
Conversation
✅ Deploy Preview for kubernetes-sigs-cluster-api-cloudstack ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: metbog The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Welcome @metbog! |
|
|
|
Hi @metbog. Thanks for your PR. I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with Regular contributors should join the org to skip this step. Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
5233eae to
d0b2075
Compare
d0b2075 to
bfeaf94
Compare
…udStackMachine delete
bfeaf94 to
c7c912b
Compare
Issue #, if available:
N/A
Description of changes:
Currently, when a control plane
CloudStackMachineis deleted, CAPC only destroys the underlying CloudStack VM - it never detaches the VM from theKubernetes_API_Serverload balancer rule. VM removal can take a while, and the CloudStack LB health check only marks the member offline after some delay, so API traffic keeps hitting the about-to-be-expunged kube-apiserver throughout that window. This causes stale members and intermittent control plane reachability issues during scale-down or rolling replace (most visibly during KCP-driven control plane rollouts)This change mirrors the CAPO (
cluster-api-provider-openstack)DeleteLoadBalancerMemberpattern, with one addition: removal is attempted early - as soon as the parent CAPI Machine is marked for deletion - instead of only on theCloudStackMachinedelete path. The late removal is kept as a safety netChanges
pkg/cloud/isolated_network.go- addedRemoveVMFromLoadBalancerRuleto theIsoNetworkIface. Idempotent: returnsnilwhenLBRuleIDis empty or the VM is not currently a member of the rule. Otherwise calls CloudStackremoveFromLoadBalancerRulecontrollers/cloudstackmachine_controller.go- two LB-detach stages plus a small helper:usesAPIServerLB()- extracted predicate shared by all LB stages: non-routed isolated network + populatedIsoNet+InstanceIDsetRemoveFromLBIfMachineDeleting- new stage in the regularReconcile()pipeline, ordered beforeAddToLBIfNeeded. Fires when the parent CAPI Machine has aDeletionTimestamp, the machine is a control plane, andusesAPIServerLB()is true. Detaches the VM from the LB rule promptly, before KCP starts cordon/drain.AddToLBIfNeededhas a matching guard so a deleting Machine never gets re-added in the same loopRemoveFromLBIfNeeded- new stage called fromReconcileDeleteimmediately beforeDestroyVMInstance, acting as a safety net for the case where the early step did not run (CSMachine deleted directly, controller downtime, etc.). Two implementation notes:clusterv1.MachineControlPlaneLabelon theCloudStackMachineitself, because on the delete path the common stages skip loading the CAPI parent Machine (RunIf(... DeletionTimestamp().IsZero() ..., GetParent(...))).CloudStackIsolatedNetworkviaGetObjectByName(IsoNetMetaName(...), r.IsoNet), since the delete path does not pre-load it (unlike the regularReconcilepath)clusterv1.Machineupdate predicate only enqueued the ownedCloudStackMachinewhenBootstrap.DataSecretNamewent fromnilto set. It now also enqueues whenDeletionTimestamptransitions from zero to non-zero. Without this, the early-removal stage would only run on the next periodic resync (1m default) - too late.pkg/cloud/isolated_network_test.go- 5 new unit tests covering: emptyLBRuleIDno-op, VM not a member no-op, happy-path removal, list error, remove errorpkg/mocks/mock_client.go- regenerated viamake generate-mocks(gitignored; auto-rebuilt bymake test)docs/book/src/topics/cloudstack-permissions.md- added the requiredremoveFromLoadBalancerRulepermissionBehavior matrix
DeletionTimestampset""(non-routed)Reconcile(), before drainCloudStackMachinedeletion (ReconcileDelete)""(non-routed)IsoNetCR already gone /LBRuleIDemptyThe cluster-scoped LB rule itself continues to be cleaned up through
DisposeIsoNetResourceson cluster teardown (unchanged) - only members are removed on machine delete, matching the CAPO modelTrade-offs / why both stages
The early stage (
RemoveFromLBIfMachineDeleting) is what actually shortens the bad window during KCP rollouts. The late stage (RemoveFromLBIfNeededinReconcileDelete) is intentionally kept because:CloudStackMachinedirectly, bypassing the CAPI MachineListLoadBalancerRuleInstancescall (idempotent no-op when already detached)Testing performed:
go build ./...- cleango vet ./controllers/... ./pkg/cloud/...- cleanREPO_ROOT=$(pwd) go run github.com/onsi/ginkgo/v2/ginkgo --label-filter='!integ' ./pkg/cloud- 137 passed, 0 failed, 24 integration specs skipped as expected. All 5 new specs greenRemoveVMFromLoadBalancerRuleonMockClientBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.