Skip to content

Commit c85a473

Browse files
committed
fix: non control plane node deletion when the cluster has no control plane machines
1 parent 2a7d61d commit c85a473

File tree

3 files changed

+109
-61
lines changed

3 files changed

+109
-61
lines changed

internal/controllers/machine/machine_controller.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -784,21 +784,25 @@ func (r *Reconciler) isDeleteNodeAllowed(ctx context.Context, cluster *clusterv1
784784
}
785785
}
786786

787-
// Get all of the active machines that belong to this cluster.
788-
machines, err := collections.GetFilteredMachinesForCluster(ctx, r.Client, cluster, collections.ActiveMachines)
787+
// Get all the machines that belong to this cluster.
788+
machines, err := collections.GetFilteredMachinesForCluster(ctx, r.Client, cluster)
789789
if err != nil {
790790
return err
791791
}
792792

793793
// Whether or not it is okay to delete the NodeRef depends on the
794794
// number of remaining control plane members and whether or not this
795795
// machine is one of them.
796-
numControlPlaneMachines := len(machines.Filter(collections.ControlPlaneMachines(cluster.Name)))
797-
if numControlPlaneMachines == 0 {
798-
// Do not delete the NodeRef if there are no remaining members of
796+
controlPlaneMachines := machines.Filter(collections.ControlPlaneMachines(cluster.Name))
797+
isMachineAControlPlaneMachine := slices.Contains(controlPlaneMachines.Names(), machine.Name)
798+
numOfControlPlaneMachines := len(controlPlaneMachines)
799+
800+
if isMachineAControlPlaneMachine && numOfControlPlaneMachines == 1 {
801+
// Do not delete the NodeRef as this is the last remaining member of
799802
// the control plane.
800-
return errNoControlPlaneNodes
803+
return errLastControlPlaneNode
801804
}
805+
802806
// Otherwise it is okay to delete the NodeRef.
803807
return nil
804808
}

internal/controllers/machine/machine_controller_test.go

+98-55
Original file line numberDiff line numberDiff line change
@@ -2529,9 +2529,53 @@ func nodeNameIndex(o client.Object) []string {
25292529
func TestIsDeleteNodeAllowed(t *testing.T) {
25302530
deletionts := metav1.Now()
25312531

2532+
cp1 := &clusterv1.Machine{
2533+
ObjectMeta: metav1.ObjectMeta{
2534+
Name: "cp1",
2535+
Namespace: metav1.NamespaceDefault,
2536+
Labels: map[string]string{
2537+
clusterv1.ClusterNameLabel: "test-cluster",
2538+
clusterv1.MachineControlPlaneLabel: "",
2539+
},
2540+
Finalizers: []string{clusterv1.MachineFinalizer},
2541+
},
2542+
Spec: clusterv1.MachineSpec{
2543+
ClusterName: "test-cluster",
2544+
InfrastructureRef: corev1.ObjectReference{},
2545+
Bootstrap: clusterv1.Bootstrap{DataSecretName: ptr.To("data")},
2546+
},
2547+
Status: clusterv1.MachineStatus{
2548+
NodeRef: &corev1.ObjectReference{
2549+
Name: "test1",
2550+
},
2551+
},
2552+
}
2553+
cp2 := &clusterv1.Machine{
2554+
ObjectMeta: metav1.ObjectMeta{
2555+
Name: "cp2",
2556+
Namespace: metav1.NamespaceDefault,
2557+
Labels: map[string]string{
2558+
clusterv1.ClusterNameLabel: "test-cluster",
2559+
clusterv1.MachineControlPlaneLabel: "",
2560+
},
2561+
Finalizers: []string{clusterv1.MachineFinalizer},
2562+
},
2563+
Spec: clusterv1.MachineSpec{
2564+
ClusterName: "test-cluster",
2565+
InfrastructureRef: corev1.ObjectReference{},
2566+
Bootstrap: clusterv1.Bootstrap{DataSecretName: ptr.To("data")},
2567+
},
2568+
Status: clusterv1.MachineStatus{
2569+
NodeRef: &corev1.ObjectReference{
2570+
Name: "test2",
2571+
},
2572+
},
2573+
}
2574+
25322575
testCases := []struct {
25332576
name string
25342577
cluster *clusterv1.Cluster
2578+
extraMachines []*clusterv1.Machine
25352579
machine *clusterv1.Machine
25362580
expectedError error
25372581
}{
@@ -2559,10 +2603,11 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
25592603
},
25602604
Status: clusterv1.MachineStatus{},
25612605
},
2606+
extraMachines: nil,
25622607
expectedError: errNilNodeRef,
25632608
},
25642609
{
2565-
name: "no control plane members",
2610+
name: "no control plane members, deleting a worker",
25662611
cluster: &clusterv1.Cluster{
25672612
ObjectMeta: metav1.ObjectMeta{
25682613
Name: "test-cluster",
@@ -2576,7 +2621,8 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
25762621
Labels: map[string]string{
25772622
clusterv1.ClusterNameLabel: "test-cluster",
25782623
},
2579-
Finalizers: []string{clusterv1.MachineFinalizer},
2624+
Finalizers: []string{clusterv1.MachineFinalizer},
2625+
DeletionTimestamp: &metav1.Time{Time: time.Now().UTC()},
25802626
},
25812627
Spec: clusterv1.MachineSpec{
25822628
ClusterName: "test-cluster",
@@ -2589,10 +2635,11 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
25892635
},
25902636
},
25912637
},
2592-
expectedError: errNoControlPlaneNodes,
2638+
extraMachines: nil,
2639+
expectedError: nil,
25932640
},
25942641
{
2595-
name: "is last control plane member",
2642+
name: "is last control plane member, trying to delete it",
25962643
cluster: &clusterv1.Cluster{
25972644
ObjectMeta: metav1.ObjectMeta{
25982645
Name: "test-cluster",
@@ -2621,7 +2668,40 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
26212668
},
26222669
},
26232670
},
2624-
expectedError: errNoControlPlaneNodes,
2671+
extraMachines: nil,
2672+
expectedError: errLastControlPlaneNode,
2673+
},
2674+
{
2675+
name: "only one control plane member, trying to delete a worker",
2676+
cluster: &clusterv1.Cluster{
2677+
ObjectMeta: metav1.ObjectMeta{
2678+
Name: "test-cluster",
2679+
Namespace: metav1.NamespaceDefault,
2680+
},
2681+
},
2682+
machine: &clusterv1.Machine{
2683+
ObjectMeta: metav1.ObjectMeta{
2684+
Name: "created",
2685+
Namespace: metav1.NamespaceDefault,
2686+
Labels: map[string]string{
2687+
clusterv1.ClusterNameLabel: "test-cluster",
2688+
},
2689+
Finalizers: []string{clusterv1.MachineFinalizer},
2690+
DeletionTimestamp: &metav1.Time{Time: time.Now().UTC()},
2691+
},
2692+
Spec: clusterv1.MachineSpec{
2693+
ClusterName: "test-cluster",
2694+
InfrastructureRef: corev1.ObjectReference{},
2695+
Bootstrap: clusterv1.Bootstrap{DataSecretName: ptr.To("data")},
2696+
},
2697+
Status: clusterv1.MachineStatus{
2698+
NodeRef: &corev1.ObjectReference{
2699+
Name: "test",
2700+
},
2701+
},
2702+
},
2703+
extraMachines: nil,
2704+
expectedError: nil,
26252705
},
26262706
{
26272707
name: "has nodeRef and control plane is healthy",
@@ -2651,6 +2731,7 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
26512731
},
26522732
},
26532733
},
2734+
extraMachines: []*clusterv1.Machine{cp1, cp2},
26542735
expectedError: nil,
26552736
},
26562737
{
@@ -2664,6 +2745,7 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
26642745
},
26652746
},
26662747
machine: &clusterv1.Machine{},
2748+
extraMachines: nil,
26672749
expectedError: errClusterIsBeingDeleted,
26682750
},
26692751
{
@@ -2702,6 +2784,7 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
27022784
},
27032785
},
27042786
},
2787+
extraMachines: []*clusterv1.Machine{cp1, cp2},
27052788
expectedError: nil,
27062789
},
27072790
{
@@ -2740,6 +2823,7 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
27402823
},
27412824
},
27422825
},
2826+
extraMachines: nil,
27432827
expectedError: errControlPlaneIsBeingDeleted,
27442828
},
27452829
{
@@ -2778,6 +2862,7 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
27782862
},
27792863
},
27802864
},
2865+
extraMachines: nil,
27812866
expectedError: errControlPlaneIsBeingDeleted,
27822867
},
27832868
}
@@ -2822,61 +2907,19 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
28222907
t.Run(tc.name, func(t *testing.T) {
28232908
g := NewWithT(t)
28242909

2825-
m1 := &clusterv1.Machine{
2826-
ObjectMeta: metav1.ObjectMeta{
2827-
Name: "cp1",
2828-
Namespace: metav1.NamespaceDefault,
2829-
Labels: map[string]string{
2830-
clusterv1.ClusterNameLabel: "test-cluster",
2831-
},
2832-
Finalizers: []string{clusterv1.MachineFinalizer},
2833-
},
2834-
Spec: clusterv1.MachineSpec{
2835-
ClusterName: "test-cluster",
2836-
InfrastructureRef: corev1.ObjectReference{},
2837-
Bootstrap: clusterv1.Bootstrap{DataSecretName: ptr.To("data")},
2838-
},
2839-
Status: clusterv1.MachineStatus{
2840-
NodeRef: &corev1.ObjectReference{
2841-
Name: "test1",
2842-
},
2843-
},
2844-
}
2845-
m2 := &clusterv1.Machine{
2846-
ObjectMeta: metav1.ObjectMeta{
2847-
Name: "cp2",
2848-
Namespace: metav1.NamespaceDefault,
2849-
Labels: map[string]string{
2850-
clusterv1.ClusterNameLabel: "test-cluster",
2851-
},
2852-
Finalizers: []string{clusterv1.MachineFinalizer},
2853-
},
2854-
Spec: clusterv1.MachineSpec{
2855-
ClusterName: "test-cluster",
2856-
InfrastructureRef: corev1.ObjectReference{},
2857-
Bootstrap: clusterv1.Bootstrap{DataSecretName: ptr.To("data")},
2858-
},
2859-
Status: clusterv1.MachineStatus{
2860-
NodeRef: &corev1.ObjectReference{
2861-
Name: "test2",
2862-
},
2863-
},
2864-
}
2865-
// For isDeleteNodeAllowed to be true we assume a healthy control plane.
2866-
if tc.expectedError == nil {
2867-
m1.Labels[clusterv1.MachineControlPlaneLabel] = ""
2868-
m2.Labels[clusterv1.MachineControlPlaneLabel] = ""
2869-
}
2870-
2871-
c := fake.NewClientBuilder().WithObjects(
2910+
objects := []client.Object{
28722911
tc.cluster,
28732912
tc.machine,
2874-
m1,
2875-
m2,
28762913
emp,
28772914
mcpBeingDeleted,
28782915
empBeingDeleted,
2879-
).Build()
2916+
}
2917+
2918+
for _, e := range tc.extraMachines {
2919+
objects = append(objects, e)
2920+
}
2921+
2922+
c := fake.NewClientBuilder().WithObjects(objects...).Build()
28802923
mr := &Reconciler{
28812924
Client: c,
28822925
}

util/collections/machine_filters.go

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ func ControlPlaneMachines(clusterName string) func(machine *clusterv1.Machine) b
118118
if machine == nil {
119119
return false
120120
}
121+
121122
return selector.Matches(labels.Set(machine.Labels))
122123
}
123124
}

0 commit comments

Comments
 (0)