sigs.k8s.io/cluster-api@v1.6.3/internal/controllers/machineset/machineset_delete_policy_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package machineset
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	. "github.com/onsi/gomega"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/util/rand"
    27  
    28  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    29  	capierrors "sigs.k8s.io/cluster-api/errors"
    30  )
    31  
    32  func TestMachineToDelete(t *testing.T) {
    33  	msg := "something wrong with the machine"
    34  	now := metav1.Now()
    35  	nodeRef := &corev1.ObjectReference{Name: "some-node"}
    36  	healthyMachine := &clusterv1.Machine{Status: clusterv1.MachineStatus{NodeRef: nodeRef}}
    37  	mustDeleteMachine := &clusterv1.Machine{
    38  		ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &now},
    39  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
    40  	}
    41  	betterDeleteMachine := &clusterv1.Machine{
    42  		Status: clusterv1.MachineStatus{FailureMessage: &msg, NodeRef: nodeRef},
    43  	}
    44  	deleteMachineWithMachineAnnotation := &clusterv1.Machine{
    45  		ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{clusterv1.DeleteMachineAnnotation: ""}},
    46  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
    47  	}
    48  	deleteMachineWithoutNodeRef := &clusterv1.Machine{}
    49  	nodeHealthyConditionFalseMachine := &clusterv1.Machine{
    50  		Status: clusterv1.MachineStatus{
    51  			NodeRef: nodeRef,
    52  			Conditions: clusterv1.Conditions{
    53  				{
    54  					Type:   clusterv1.MachineNodeHealthyCondition,
    55  					Status: corev1.ConditionFalse,
    56  				},
    57  			},
    58  		},
    59  	}
    60  	nodeHealthyConditionUnknownMachine := &clusterv1.Machine{
    61  		Status: clusterv1.MachineStatus{
    62  			NodeRef: nodeRef,
    63  			Conditions: clusterv1.Conditions{
    64  				{
    65  					Type:   clusterv1.MachineNodeHealthyCondition,
    66  					Status: corev1.ConditionUnknown,
    67  				},
    68  			},
    69  		},
    70  	}
    71  
    72  	tests := []struct {
    73  		desc     string
    74  		machines []*clusterv1.Machine
    75  		diff     int
    76  		expect   []*clusterv1.Machine
    77  	}{
    78  		{
    79  			desc: "func=randomDeletePolicy, diff=0",
    80  			diff: 0,
    81  			machines: []*clusterv1.Machine{
    82  				healthyMachine,
    83  			},
    84  			expect: []*clusterv1.Machine{},
    85  		},
    86  		{
    87  			desc: "func=randomDeletePolicy, diff>len(machines)",
    88  			diff: 2,
    89  			machines: []*clusterv1.Machine{
    90  				healthyMachine,
    91  			},
    92  			expect: []*clusterv1.Machine{
    93  				healthyMachine,
    94  			},
    95  		},
    96  		{
    97  			desc: "func=randomDeletePolicy, diff>betterDelete",
    98  			diff: 2,
    99  			machines: []*clusterv1.Machine{
   100  				healthyMachine,
   101  				betterDeleteMachine,
   102  				healthyMachine,
   103  			},
   104  			expect: []*clusterv1.Machine{
   105  				betterDeleteMachine,
   106  				healthyMachine,
   107  			},
   108  		},
   109  		{
   110  			desc: "func=randomDeletePolicy, diff<betterDelete",
   111  			diff: 2,
   112  			machines: []*clusterv1.Machine{
   113  				healthyMachine,
   114  				betterDeleteMachine,
   115  				betterDeleteMachine,
   116  				betterDeleteMachine,
   117  			},
   118  			expect: []*clusterv1.Machine{
   119  				betterDeleteMachine,
   120  				betterDeleteMachine,
   121  			},
   122  		},
   123  		{
   124  			desc: "func=randomDeletePolicy, diff<=mustDelete",
   125  			diff: 2,
   126  			machines: []*clusterv1.Machine{
   127  				healthyMachine,
   128  				mustDeleteMachine,
   129  				betterDeleteMachine,
   130  				mustDeleteMachine,
   131  			},
   132  			expect: []*clusterv1.Machine{
   133  				mustDeleteMachine,
   134  				mustDeleteMachine,
   135  			},
   136  		},
   137  		{
   138  			desc: "func=randomDeletePolicy, diff<=mustDelete+betterDelete",
   139  			diff: 2,
   140  			machines: []*clusterv1.Machine{
   141  				healthyMachine,
   142  				mustDeleteMachine,
   143  				healthyMachine,
   144  				betterDeleteMachine,
   145  			},
   146  			expect: []*clusterv1.Machine{
   147  				mustDeleteMachine,
   148  				betterDeleteMachine,
   149  			},
   150  		},
   151  		{
   152  			desc: "func=randomDeletePolicy, diff<=mustDelete+betterDelete+couldDelete",
   153  			diff: 2,
   154  			machines: []*clusterv1.Machine{
   155  				healthyMachine,
   156  				mustDeleteMachine,
   157  				healthyMachine,
   158  			},
   159  			expect: []*clusterv1.Machine{
   160  				mustDeleteMachine,
   161  				healthyMachine,
   162  			},
   163  		},
   164  		{
   165  			desc: "func=randomDeletePolicy, diff>betterDelete",
   166  			diff: 2,
   167  			machines: []*clusterv1.Machine{
   168  				healthyMachine,
   169  				betterDeleteMachine,
   170  				healthyMachine,
   171  			},
   172  			expect: []*clusterv1.Machine{
   173  				betterDeleteMachine,
   174  				healthyMachine,
   175  			},
   176  		},
   177  		{
   178  			desc: "func=randomDeletePolicy, DeleteMachineAnnotation, diff=1",
   179  			diff: 1,
   180  			machines: []*clusterv1.Machine{
   181  				healthyMachine,
   182  				deleteMachineWithMachineAnnotation,
   183  				healthyMachine,
   184  			},
   185  			expect: []*clusterv1.Machine{
   186  				deleteMachineWithMachineAnnotation,
   187  			},
   188  		},
   189  		{
   190  			desc: "func=randomDeletePolicy, MachineWithNoNodeRef, diff=1",
   191  			diff: 1,
   192  			machines: []*clusterv1.Machine{
   193  				healthyMachine,
   194  				deleteMachineWithoutNodeRef,
   195  				healthyMachine,
   196  			},
   197  			expect: []*clusterv1.Machine{
   198  				deleteMachineWithoutNodeRef,
   199  			},
   200  		},
   201  		{
   202  			desc: "func=randomDeletePolicy, NodeHealthyConditionFalseMachine, diff=1",
   203  			diff: 1,
   204  			machines: []*clusterv1.Machine{
   205  				healthyMachine,
   206  				nodeHealthyConditionFalseMachine,
   207  				healthyMachine,
   208  			},
   209  			expect: []*clusterv1.Machine{
   210  				nodeHealthyConditionFalseMachine,
   211  			},
   212  		},
   213  		{
   214  			desc: "func=randomDeletePolicy, NodeHealthyConditionUnknownMachine, diff=1",
   215  			diff: 1,
   216  			machines: []*clusterv1.Machine{
   217  				healthyMachine,
   218  				nodeHealthyConditionUnknownMachine,
   219  				healthyMachine,
   220  			},
   221  			expect: []*clusterv1.Machine{
   222  				nodeHealthyConditionUnknownMachine,
   223  			},
   224  		},
   225  	}
   226  
   227  	for _, test := range tests {
   228  		t.Run(test.desc, func(t *testing.T) {
   229  			g := NewWithT(t)
   230  
   231  			result := getMachinesToDeletePrioritized(test.machines, test.diff, randomDeletePolicy)
   232  			g.Expect(result).To(BeComparableTo(test.expect))
   233  		})
   234  	}
   235  }
   236  
   237  func TestMachineNewestDelete(t *testing.T) {
   238  	currentTime := metav1.Now()
   239  	statusError := capierrors.MachineStatusError("I'm unhealthy!")
   240  	nodeRef := &corev1.ObjectReference{Name: "some-node"}
   241  	mustDeleteMachine := &clusterv1.Machine{
   242  		ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &currentTime},
   243  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   244  	}
   245  	newest := &clusterv1.Machine{
   246  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -1))},
   247  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   248  	}
   249  	secondNewest := &clusterv1.Machine{
   250  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -5))},
   251  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   252  	}
   253  	secondOldest := &clusterv1.Machine{
   254  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   255  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   256  	}
   257  	oldest := &clusterv1.Machine{
   258  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   259  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   260  	}
   261  	deleteMachineWithMachineAnnotation := &clusterv1.Machine{
   262  		ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{clusterv1.DeleteMachineAnnotation: ""}, CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   263  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   264  	}
   265  	unhealthyMachine := &clusterv1.Machine{
   266  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   267  		Status:     clusterv1.MachineStatus{FailureReason: &statusError, NodeRef: nodeRef},
   268  	}
   269  	deleteMachineWithoutNodeRef := &clusterv1.Machine{
   270  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -1))},
   271  	}
   272  	nodeHealthyConditionFalseMachine := &clusterv1.Machine{
   273  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   274  		Status: clusterv1.MachineStatus{
   275  			NodeRef: nodeRef,
   276  			Conditions: clusterv1.Conditions{
   277  				{
   278  					Type:   clusterv1.MachineNodeHealthyCondition,
   279  					Status: corev1.ConditionFalse,
   280  				},
   281  			},
   282  		},
   283  	}
   284  	nodeHealthyConditionUnknownMachine := &clusterv1.Machine{
   285  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   286  		Status: clusterv1.MachineStatus{
   287  			NodeRef: nodeRef,
   288  			Conditions: clusterv1.Conditions{
   289  				{
   290  					Type:   clusterv1.MachineNodeHealthyCondition,
   291  					Status: corev1.ConditionUnknown,
   292  				},
   293  			},
   294  		},
   295  	}
   296  
   297  	tests := []struct {
   298  		desc     string
   299  		machines []*clusterv1.Machine
   300  		diff     int
   301  		expect   []*clusterv1.Machine
   302  	}{
   303  		{
   304  			desc: "func=newestDeletePriority, diff=1",
   305  			diff: 1,
   306  			machines: []*clusterv1.Machine{
   307  				secondNewest, oldest, secondOldest, mustDeleteMachine, newest,
   308  			},
   309  			expect: []*clusterv1.Machine{mustDeleteMachine},
   310  		},
   311  		{
   312  			desc: "func=newestDeletePriority, diff=2",
   313  			diff: 2,
   314  			machines: []*clusterv1.Machine{
   315  				secondNewest, oldest, mustDeleteMachine, secondOldest, newest,
   316  			},
   317  			expect: []*clusterv1.Machine{mustDeleteMachine, newest},
   318  		},
   319  		{
   320  			desc: "func=newestDeletePriority, diff=3",
   321  			diff: 3,
   322  			machines: []*clusterv1.Machine{
   323  				secondNewest, mustDeleteMachine, oldest, secondOldest, newest,
   324  			},
   325  			expect: []*clusterv1.Machine{mustDeleteMachine, newest, secondNewest},
   326  		},
   327  		{
   328  			desc: "func=newestDeletePriority, diff=1 (DeleteMachineAnnotation)",
   329  			diff: 1,
   330  			machines: []*clusterv1.Machine{
   331  				secondNewest, oldest, secondOldest, newest, deleteMachineWithMachineAnnotation,
   332  			},
   333  			expect: []*clusterv1.Machine{deleteMachineWithMachineAnnotation},
   334  		},
   335  		{
   336  			desc: "func=newestDeletePriority, diff=1 (deleteMachineWithoutNodeRef)",
   337  			diff: 1,
   338  			machines: []*clusterv1.Machine{
   339  				secondNewest, oldest, secondOldest, newest, deleteMachineWithoutNodeRef,
   340  			},
   341  			expect: []*clusterv1.Machine{deleteMachineWithoutNodeRef},
   342  		},
   343  		{
   344  			desc: "func=newestDeletePriority, diff=1 (unhealthy)",
   345  			diff: 1,
   346  			machines: []*clusterv1.Machine{
   347  				secondNewest, oldest, secondOldest, newest, unhealthyMachine,
   348  			},
   349  			expect: []*clusterv1.Machine{unhealthyMachine},
   350  		},
   351  		{
   352  			desc: "func=newestDeletePriority, diff=1 (nodeHealthyConditionFalseMachine)",
   353  			diff: 1,
   354  			machines: []*clusterv1.Machine{
   355  				secondNewest, oldest, secondOldest, newest, nodeHealthyConditionFalseMachine,
   356  			},
   357  			expect: []*clusterv1.Machine{nodeHealthyConditionFalseMachine},
   358  		},
   359  		{
   360  			desc: "func=newestDeletePriority, diff=1 (nodeHealthyConditionUnknownMachine)",
   361  			diff: 1,
   362  			machines: []*clusterv1.Machine{
   363  				secondNewest, oldest, secondOldest, newest, nodeHealthyConditionUnknownMachine,
   364  			},
   365  			expect: []*clusterv1.Machine{nodeHealthyConditionUnknownMachine},
   366  		},
   367  	}
   368  
   369  	for _, test := range tests {
   370  		t.Run(test.desc, func(t *testing.T) {
   371  			g := NewWithT(t)
   372  
   373  			result := getMachinesToDeletePrioritized(test.machines, test.diff, newestDeletePriority)
   374  			g.Expect(result).To(BeComparableTo(test.expect))
   375  		})
   376  	}
   377  }
   378  
   379  func TestMachineOldestDelete(t *testing.T) {
   380  	currentTime := metav1.Now()
   381  	statusError := capierrors.MachineStatusError("I'm unhealthy!")
   382  	nodeRef := &corev1.ObjectReference{Name: "some-node"}
   383  	empty := &clusterv1.Machine{
   384  		Status: clusterv1.MachineStatus{NodeRef: nodeRef},
   385  	}
   386  	newest := &clusterv1.Machine{
   387  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -1))},
   388  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   389  	}
   390  	secondNewest := &clusterv1.Machine{
   391  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -5))},
   392  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   393  	}
   394  	secondOldest := &clusterv1.Machine{
   395  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   396  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   397  	}
   398  	oldest := &clusterv1.Machine{
   399  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   400  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   401  	}
   402  	deleteMachineWithMachineAnnotation := &clusterv1.Machine{
   403  		ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{clusterv1.DeleteMachineAnnotation: ""}, CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   404  		Status:     clusterv1.MachineStatus{NodeRef: nodeRef},
   405  	}
   406  	unhealthyMachine := &clusterv1.Machine{
   407  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   408  		Status:     clusterv1.MachineStatus{FailureReason: &statusError, NodeRef: nodeRef},
   409  	}
   410  	deleteMachineWithoutNodeRef := &clusterv1.Machine{
   411  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   412  	}
   413  	nodeHealthyConditionFalseMachine := &clusterv1.Machine{
   414  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   415  		Status: clusterv1.MachineStatus{
   416  			NodeRef: nodeRef,
   417  			Conditions: clusterv1.Conditions{
   418  				{
   419  					Type:   clusterv1.MachineNodeHealthyCondition,
   420  					Status: corev1.ConditionFalse,
   421  				},
   422  			},
   423  		},
   424  	}
   425  	nodeHealthyConditionUnknownMachine := &clusterv1.Machine{
   426  		ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.NewTime(currentTime.Time.AddDate(0, 0, -10))},
   427  		Status: clusterv1.MachineStatus{
   428  			NodeRef: nodeRef,
   429  			Conditions: clusterv1.Conditions{
   430  				{
   431  					Type:   clusterv1.MachineNodeHealthyCondition,
   432  					Status: corev1.ConditionUnknown,
   433  				},
   434  			},
   435  		},
   436  	}
   437  
   438  	tests := []struct {
   439  		desc     string
   440  		machines []*clusterv1.Machine
   441  		diff     int
   442  		expect   []*clusterv1.Machine
   443  	}{
   444  		{
   445  			desc: "func=oldestDeletePriority, diff=1",
   446  			diff: 1,
   447  			machines: []*clusterv1.Machine{
   448  				empty, secondNewest, oldest, secondOldest, newest,
   449  			},
   450  			expect: []*clusterv1.Machine{oldest},
   451  		},
   452  		{
   453  			desc: "func=oldestDeletePriority, diff=2",
   454  			diff: 2,
   455  			machines: []*clusterv1.Machine{
   456  				secondNewest, oldest, secondOldest, newest, empty,
   457  			},
   458  			expect: []*clusterv1.Machine{oldest, secondOldest},
   459  		},
   460  		{
   461  			desc: "func=oldestDeletePriority, diff=3",
   462  			diff: 3,
   463  			machines: []*clusterv1.Machine{
   464  				secondNewest, oldest, secondOldest, newest, empty,
   465  			},
   466  			expect: []*clusterv1.Machine{oldest, secondOldest, secondNewest},
   467  		},
   468  		{
   469  			desc: "func=oldestDeletePriority, diff=4",
   470  			diff: 4,
   471  			machines: []*clusterv1.Machine{
   472  				secondNewest, oldest, secondOldest, newest, empty,
   473  			},
   474  			expect: []*clusterv1.Machine{oldest, secondOldest, secondNewest, newest},
   475  		},
   476  		{
   477  			desc: "func=oldestDeletePriority, diff=1 (DeleteMachineAnnotation)",
   478  			diff: 1,
   479  			machines: []*clusterv1.Machine{
   480  				empty, secondNewest, oldest, secondOldest, newest, deleteMachineWithMachineAnnotation,
   481  			},
   482  			expect: []*clusterv1.Machine{deleteMachineWithMachineAnnotation},
   483  		},
   484  		{
   485  			desc: "func=oldestDeletePriority, diff=1 (deleteMachineWithoutNodeRef)",
   486  			diff: 1,
   487  			machines: []*clusterv1.Machine{
   488  				empty, secondNewest, oldest, secondOldest, newest, deleteMachineWithoutNodeRef,
   489  			},
   490  			expect: []*clusterv1.Machine{deleteMachineWithoutNodeRef},
   491  		},
   492  		{
   493  			desc: "func=oldestDeletePriority, diff=1 (unhealthy)",
   494  			diff: 1,
   495  			machines: []*clusterv1.Machine{
   496  				empty, secondNewest, oldest, secondOldest, newest, unhealthyMachine,
   497  			},
   498  			expect: []*clusterv1.Machine{unhealthyMachine},
   499  		},
   500  		{
   501  			desc: "func=oldestDeletePriority, diff=1 (nodeHealthyConditionFalseMachine)",
   502  			diff: 1,
   503  			machines: []*clusterv1.Machine{
   504  				empty, secondNewest, oldest, secondOldest, newest, nodeHealthyConditionFalseMachine,
   505  			},
   506  			expect: []*clusterv1.Machine{nodeHealthyConditionFalseMachine},
   507  		},
   508  		{
   509  			desc: "func=oldestDeletePriority, diff=1 (nodeHealthyConditionUnknownMachine)",
   510  			diff: 1,
   511  			machines: []*clusterv1.Machine{
   512  				empty, secondNewest, oldest, secondOldest, newest, nodeHealthyConditionUnknownMachine,
   513  			},
   514  			expect: []*clusterv1.Machine{nodeHealthyConditionUnknownMachine},
   515  		},
   516  	}
   517  
   518  	for _, test := range tests {
   519  		t.Run(test.desc, func(t *testing.T) {
   520  			g := NewWithT(t)
   521  
   522  			result := getMachinesToDeletePrioritized(test.machines, test.diff, oldestDeletePriority)
   523  			g.Expect(result).To(BeComparableTo(test.expect))
   524  		})
   525  	}
   526  }
   527  
   528  func TestMachineDeleteMultipleSamePriority(t *testing.T) {
   529  	machines := make([]*clusterv1.Machine, 0, 10)
   530  	// All of these machines will have the same delete priority because they all have the "must delete" annotation.
   531  	for i := 0; i < 10; i++ {
   532  		machines = append(machines, &clusterv1.Machine{
   533  			ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("machine-%d", i), Annotations: map[string]string{clusterv1.DeleteMachineAnnotation: "true"}},
   534  		})
   535  	}
   536  
   537  	tests := []struct {
   538  		desc           string
   539  		diff           int
   540  		deletePriority deletePriorityFunc
   541  	}{
   542  		{
   543  			desc:           "multiple with same priority, func=oldestDeletePriority, diff=1",
   544  			diff:           1,
   545  			deletePriority: oldestDeletePriority,
   546  		},
   547  		{
   548  			desc:           "multiple with same priority, func=oldestDeletePriority, diff=5",
   549  			diff:           5,
   550  			deletePriority: oldestDeletePriority,
   551  		},
   552  		{
   553  			desc:           "multiple with same priority, func=oldestDeletePriority, diff=rand",
   554  			diff:           rand.Intn(len(machines)),
   555  			deletePriority: oldestDeletePriority,
   556  		},
   557  		{
   558  			desc:           "multiple with same priority, func=randomDeletePolicy, diff=1",
   559  			diff:           1,
   560  			deletePriority: randomDeletePolicy,
   561  		},
   562  		{
   563  			desc:           "multiple with same priority, func=randomDeletePolicy, diff=5",
   564  			diff:           5,
   565  			deletePriority: randomDeletePolicy,
   566  		},
   567  		{
   568  			desc:           "multiple with same priority, func=randomDeletePolicy, diff=rand",
   569  			diff:           rand.Intn(len(machines)),
   570  			deletePriority: randomDeletePolicy,
   571  		},
   572  		{
   573  			desc:           "multiple with same priority, func=newestDeletePriority, diff=1",
   574  			diff:           1,
   575  			deletePriority: newestDeletePriority,
   576  		},
   577  		{
   578  			desc:           "multiple with same priority, func=newestDeletePriority, diff=5",
   579  			diff:           5,
   580  			deletePriority: newestDeletePriority,
   581  		},
   582  		{
   583  			desc:           "multiple with same priority, func=newestDeletePriority, diff=rand",
   584  			diff:           rand.Intn(len(machines)),
   585  			deletePriority: newestDeletePriority,
   586  		},
   587  		{
   588  			desc:           "multiple with same priority, func=randomDeletePolicy, diff=1",
   589  			diff:           1,
   590  			deletePriority: randomDeletePolicy,
   591  		},
   592  		{
   593  			desc:           "multiple with same priority, func=randomDeletePolicy, diff=5",
   594  			diff:           5,
   595  			deletePriority: randomDeletePolicy,
   596  		},
   597  		{
   598  			desc:           "multiple with same priority, func=randomDeletePolicy, diff=rand",
   599  			diff:           rand.Intn(len(machines)),
   600  			deletePriority: randomDeletePolicy,
   601  		},
   602  	}
   603  
   604  	for _, test := range tests {
   605  		t.Run(test.desc, func(t *testing.T) {
   606  			g := NewWithT(t)
   607  
   608  			order := rand.Perm(len(machines))
   609  			shuffledMachines := make([]*clusterv1.Machine, len(machines))
   610  			for i, j := range order {
   611  				shuffledMachines[i] = machines[j]
   612  			}
   613  
   614  			result := getMachinesToDeletePrioritized(shuffledMachines, test.diff, test.deletePriority)
   615  			g.Expect(result).To(BeComparableTo(machines[:test.diff]))
   616  		})
   617  	}
   618  }
   619  
   620  func TestIsMachineHealthy(t *testing.T) {
   621  	nodeRef := &corev1.ObjectReference{Name: "some-node"}
   622  	statusError := capierrors.MachineStatusError("I'm unhealthy!")
   623  	msg := "something wrong with the machine"
   624  
   625  	tests := []struct {
   626  		desc    string
   627  		machine *clusterv1.Machine
   628  		expect  bool
   629  	}{
   630  		{
   631  			desc:    "when it has no NodeRef",
   632  			machine: &clusterv1.Machine{},
   633  			expect:  false,
   634  		},
   635  		{
   636  			desc: "when it has a FailureReason",
   637  			machine: &clusterv1.Machine{
   638  				Status: clusterv1.MachineStatus{FailureReason: &statusError, NodeRef: nodeRef},
   639  			},
   640  			expect: false,
   641  		},
   642  		{
   643  			desc: "when it has a FailureMessage",
   644  			machine: &clusterv1.Machine{
   645  				Status: clusterv1.MachineStatus{FailureMessage: &msg, NodeRef: nodeRef},
   646  			},
   647  			expect: false,
   648  		},
   649  		{
   650  			desc: "when nodeHealthyCondition is false",
   651  			machine: &clusterv1.Machine{
   652  				Status: clusterv1.MachineStatus{
   653  					NodeRef: nodeRef,
   654  					Conditions: clusterv1.Conditions{
   655  						{
   656  							Type:   clusterv1.MachineNodeHealthyCondition,
   657  							Status: corev1.ConditionFalse,
   658  						},
   659  					},
   660  				},
   661  			},
   662  			expect: false,
   663  		},
   664  		{
   665  			desc: "when nodeHealthyCondition is unknown",
   666  			machine: &clusterv1.Machine{
   667  				Status: clusterv1.MachineStatus{
   668  					NodeRef: nodeRef,
   669  					Conditions: clusterv1.Conditions{
   670  						{
   671  							Type:   clusterv1.MachineNodeHealthyCondition,
   672  							Status: corev1.ConditionUnknown,
   673  						},
   674  					},
   675  				},
   676  			},
   677  			expect: false,
   678  		},
   679  		{
   680  			desc: "when all requirements are met for node to be healthy",
   681  			machine: &clusterv1.Machine{
   682  				Status: clusterv1.MachineStatus{NodeRef: nodeRef},
   683  			},
   684  			expect: true,
   685  		},
   686  	}
   687  
   688  	for _, test := range tests {
   689  		t.Run(test.desc, func(t *testing.T) {
   690  			g := NewWithT(t)
   691  
   692  			result := isMachineHealthy(test.machine)
   693  			g.Expect(result).To(Equal(test.expect))
   694  		})
   695  	}
   696  }