Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b00331e
feat(controlplane-controller): Cordon and drain node before removal
hue-canonical Oct 24, 2025
72f085e
Add timeout for node removal
ader1990 Mar 6, 2026
111e017
make sure the node is removed cleanly and forcefully in this order
ader1990 Jun 10, 2026
ebfd672
Fixed unit tests and removed sleeps
ader1990 Jun 10, 2026
3006eeb
Increase timeouts for eviction
ader1990 Jun 15, 2026
f8d3979
Decrease timeouts for eviction
ader1990 Jun 16, 2026
9ccddcb
Re-add annotations for skipping cleanup. The cleanup makes evicted po…
ader1990 Jun 16, 2026
a8bdd2f
Decrease timeouts for eviction retry
ader1990 Jun 16, 2026
33edd95
Fix formatting
ader1990 Jun 16, 2026
8a90bc0
Use requeueing for convergence
ader1990 Jun 16, 2026
519e1b1
Fix formatting
ader1990 Jun 16, 2026
f54596e
Revert "Re-add annotations for skipping cleanup. The cleanup makes ev…
ader1990 Jun 16, 2026
f9c33f5
Use control plane creation timestamp
ader1990 Jun 17, 2026
4b700df
Use control plane creation timestamp for 5 minutes
ader1990 Jun 17, 2026
a072a7b
Fix code checks
ader1990 Jun 17, 2026
8cfe5a6
Reapply "Re-add annotations for skipping cleanup. The cleanup makes e…
ader1990 Jun 17, 2026
279f934
Re-add apiv1_annotations.AnnotationSkipCleanupKubernetesNodeOnRemove
ader1990 Jun 17, 2026
47e5fc0
add more logs
ader1990 Jun 17, 2026
5aee380
do not skip cleanup as the md-0 gets into a Node NotReady state
ader1990 Jun 17, 2026
e79205e
install.sh: use dangerous flag
ader1990 Jun 18, 2026
134b9c4
install.sh: hardcode download of binary
ader1990 Jun 18, 2026
e9a3e15
add the out file
ader1990 Jun 18, 2026
ac8e432
install.sh: hardcode download of binary
ader1990 Jun 18, 2026
bae2a26
Move to beta1v2
ader1990 Jun 18, 2026
6e0c28d
Revert "Move to beta1v2"
ader1990 Jun 18, 2026
873fe6a
install.sh: use more debugging
ader1990 Jun 18, 2026
c448962
scale: leave capi to remove the machine and the node
ader1990 Jun 19, 2026
c09b716
Revert "scale: leave capi to remove the machine and the node"
ader1990 Jun 19, 2026
71ba3b3
scale: leave capi to remove the machine and the node
ader1990 Jun 19, 2026
3df76e9
scale: leave capi to remove the machine and the node
ader1990 Jun 19, 2026
f7e9f4a
scale: leave capi to remove the machine and the node
ader1990 Jun 19, 2026
fafd223
scale: leave capi to remove the machine and the node
ader1990 Jun 19, 2026
21fde33
scale: leave capi to remove the machine and the node
ader1990 Jun 19, 2026
a63af01
install.sh: lxd does not have /dev/ttyS0
ader1990 Jun 19, 2026
a5b5a1d
more scaling
ader1990 Jun 19, 2026
82e93a3
more scaling
ader1990 Jun 19, 2026
4327937
more scaling
ader1990 Jun 19, 2026
77eae59
more scaling
ader1990 Jun 19, 2026
e632fa3
install.sh: hardcode download of binary k8s_v1.35.3-release-1.35-v2.3…
ader1990 Jun 19, 2026
e8f8f13
more scaling
ader1990 Jun 19, 2026
bd5cbf7
canonical-k8s: remove wait
ader1990 Jun 19, 2026
b5bf1b0
fix linter
ader1990 Jun 19, 2026
c452467
install k8s_v1.35.3-release-1.35-v2.4_amd64.snap
ader1990 Sep 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions controlplane/controllers/ck8scontrolplane_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,39 @@ func (r *CK8sControlPlaneReconciler) reconcile(ctx context.Context, cluster *clu
return r.scaleDownControlPlane(ctx, cluster, kcp, controlPlane, collections.Machines{})
}

annotationsKcp := kcp.GetAnnotations()
orphanNode := annotationsKcp["orphan-node"]
controlPlane.SetOrphanNode(orphanNode)
orphanNodeReadyToBeRemoved := controlPlane.GetOrphanNodeReadyToBeRemoved()
logger.Info("Orphan node ready to be removed name", "Orphan", orphanNodeReadyToBeRemoved)
logger.Info("Orphan node name", "Orphan", orphanNode)
if orphanNodeReadyToBeRemoved == "" && orphanNode != "" {
return ctrl.Result{Requeue: true}, nil
}

if orphanNodeReadyToBeRemoved != "" {
microclusterPort := controlPlane.KCP.Spec.CK8sConfigSpec.ControlPlaneConfig.GetMicroclusterPort()
clusterObjectKey := util.ObjectKey(cluster)
workloadCluster, err := r.managementCluster.GetWorkloadCluster(ctx, clusterObjectKey, microclusterPort)
if err != nil {
logger.Error(err, "failed to create client to workload cluster")
return ctrl.Result{}, fmt.Errorf("failed to create client to workload cluster: %w", err)
}

// TODO: If the node is not part of the microcluster, this may still return an error. We should catch that case,
// and proceed with the machine removal.
logger.Info("Removing machine from cluster gracefully")
if err := workloadCluster.RemoveMachineFromCluster(ctx, orphanNodeReadyToBeRemoved, false); err != nil {
logger.Error(err, "Failed to remove machine from cluster gracefully")
logger.Info("Removing machine from cluster forcefully")
if errForce := workloadCluster.RemoveMachineFromCluster(ctx, orphanNodeReadyToBeRemoved, true); errForce != nil {
logger.Error(err, "failed to remove machine from microcluster forcefully")
}
}
annotationsKcp["orphan-node"] = ""
kcp.SetAnnotations(annotationsKcp)
}

return reconcile.Result{}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion controlplane/controllers/remediation.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (r *CK8sControlPlaneReconciler) reconcileUnhealthyMachines(ctx context.Cont
if machineToBeRemediated.Status.NodeRef != nil {
// TODO: If the node is not part of the microcluster, this may still return an error. We should catch that case,
// and proceed with the machine removal.
if err := workloadCluster.RemoveMachineFromCluster(ctx, machineToBeRemediated); err != nil {
if err := workloadCluster.RemoveMachineFromCluster(ctx, machineToBeRemediated.Status.NodeRef.Name, true); err != nil {
log.Error(err, "failed to remove machine from microcluster")
return ctrl.Result{}, fmt.Errorf("failed to remove machine from microcluster: %w", err)
}
Expand Down
60 changes: 38 additions & 22 deletions controlplane/controllers/scale.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,49 +102,65 @@ func (r *CK8sControlPlaneReconciler) scaleDownControlPlane(
) (ctrl.Result, error) {
logger := ctrl.LoggerFrom(ctx)

annotationsKcp := kcp.GetAnnotations()
orphanNode := annotationsKcp["orphan-node"]
controlPlane.SetOrphanNode(orphanNode)
orphanNodeReadyToBeRemoved := controlPlane.GetOrphanNodeReadyToBeRemoved()
logger.Info("Orphan node ready to be removed name", "Orphan", orphanNodeReadyToBeRemoved)
logger.Info("Orphan node name", "Orphan", orphanNode)
if orphanNodeReadyToBeRemoved == "" && orphanNode != "" {
return ctrl.Result{Requeue: true}, nil
}

microclusterPort := controlPlane.KCP.Spec.CK8sConfigSpec.ControlPlaneConfig.GetMicroclusterPort()
clusterObjectKey := util.ObjectKey(cluster)
workloadCluster, err := r.managementCluster.GetWorkloadCluster(ctx, clusterObjectKey, microclusterPort)
if err != nil {
logger.Error(err, "failed to create client to workload cluster")
return ctrl.Result{}, fmt.Errorf("failed to create client to workload cluster: %w", err)
}
if orphanNodeReadyToBeRemoved != "" {
logger.Info("Removing machine from cluster gracefully")
if err := workloadCluster.RemoveMachineFromCluster(ctx, orphanNodeReadyToBeRemoved, false); err != nil {
logger.Error(err, "Failed to remove machine from cluster gracefully")
logger.Info("Removing machine from cluster forcefully")
if errForce := workloadCluster.RemoveMachineFromCluster(ctx, orphanNodeReadyToBeRemoved, true); errForce != nil {
logger.Error(err, "failed to remove machine from microcluster forcefully")
}
}
annotationsKcp["orphan-node"] = ""
kcp.SetAnnotations(annotationsKcp)
}

// Pick the Machine that we should scale down.
machineToDelete, err := selectMachineForScaleDown(ctx, controlPlane, outdatedMachines)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to select machine for scale down: %w", err)
}

// Run preflight checks ensuring the control plane is stable before proceeding with a scale up/scale down operation; if not, wait.
// Given that we're scaling down, we can exclude the machineToDelete from the preflight checks.
if result, err := r.preflightChecks(ctx, controlPlane, machineToDelete); err != nil || !result.IsZero() {
return result, err
}

if machineToDelete == nil {
logger.Info("Failed to pick control plane Machine to delete")
return ctrl.Result{}, fmt.Errorf("failed to pick control plane Machine to delete: %w", err)
}

microclusterPort := controlPlane.KCP.Spec.CK8sConfigSpec.ControlPlaneConfig.GetMicroclusterPort()
clusterObjectKey := util.ObjectKey(cluster)
workloadCluster, err := r.managementCluster.GetWorkloadCluster(ctx, clusterObjectKey, microclusterPort)
if err != nil {
logger.Error(err, "failed to create client to workload cluster")
return ctrl.Result{}, fmt.Errorf("failed to create client to workload cluster: %w", err)
// Run preflight checks ensuring the control plane is stable before proceeding with a scale up/scale down operation; if not, wait.
// Given that we're scaling down, we can exclude the machineToDelete from the preflight checks.
if result, err := r.preflightChecks(ctx, controlPlane, machineToDelete); err != nil || !result.IsZero() {
return result, err
}

if machineToDelete.Status.NodeRef != nil {
// TODO: If the node is not part of the microcluster, this may still return an error. We should catch that case,
// and proceed with the machine removal.
if err := workloadCluster.RemoveMachineFromCluster(ctx, machineToDelete); err != nil {
logger.Error(err, "failed to remove machine from microcluster")
return ctrl.Result{}, fmt.Errorf("failed to remove machine from microcluster: %w", err)
}
}
annotations := map[string]string{}
annotations["orphan-node"] = machineToDelete.Status.NodeRef.Name
kcp.SetAnnotations(annotations)

logger = logger.WithValues("machine", machineToDelete)
logger.Info("Removing control plane machine")
if err := r.Delete(ctx, machineToDelete); err != nil && !apierrors.IsNotFound(err) {
logger.Error(err, "Failed to delete control plane machine")
r.recorder.Eventf(kcp, corev1.EventTypeWarning, "FailedScaleDown",
"Failed to delete control plane Machine %s for cluster %s/%s control plane: %v", machineToDelete.Name, cluster.Namespace, cluster.Name, err)
return ctrl.Result{}, err
}

// Requeue the control plane, in case there are additional operations to perform
return ctrl.Result{Requeue: true}, nil
}

Expand Down
Loading