@@ -23,11 +23,11 @@ import (
23
23
"sort"
24
24
"strconv"
25
25
"strings"
26
+ "time"
26
27
27
28
"github.com/go-logr/logr"
28
29
"github.com/pkg/errors"
29
30
corev1 "k8s.io/api/core/v1"
30
- apiequality "k8s.io/apimachinery/pkg/api/equality"
31
31
"k8s.io/apimachinery/pkg/api/meta"
32
32
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
33
"k8s.io/apimachinery/pkg/runtime"
@@ -37,6 +37,7 @@ import (
37
37
ctrl "sigs.k8s.io/controller-runtime"
38
38
39
39
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
40
+ "sigs.k8s.io/cluster-api/internal/util/compare"
40
41
"sigs.k8s.io/cluster-api/util/conversion"
41
42
)
42
43
@@ -371,11 +372,11 @@ func getMachineSetFraction(ms clusterv1.MachineSet, md clusterv1.MachineDeployme
371
372
372
373
// EqualMachineTemplate returns true if two given machineTemplateSpec are equal,
373
374
// ignoring all the in-place propagated fields, and the version from external references.
374
- func EqualMachineTemplate (template1 , template2 * clusterv1.MachineTemplateSpec ) bool {
375
+ func EqualMachineTemplate (template1 , template2 * clusterv1.MachineTemplateSpec ) ( equal bool , diff string , err error ) {
375
376
t1Copy := MachineTemplateDeepCopyRolloutFields (template1 )
376
377
t2Copy := MachineTemplateDeepCopyRolloutFields (template2 )
377
378
378
- return apiequality . Semantic . DeepEqual (t1Copy , t2Copy )
379
+ return compare . Diff (t1Copy , t2Copy )
379
380
}
380
381
381
382
// MachineTemplateDeepCopyRolloutFields copies a MachineTemplateSpec
@@ -415,45 +416,75 @@ func MachineTemplateDeepCopyRolloutFields(template *clusterv1.MachineTemplateSpe
415
416
// not face a case where there exists a machine set matching the old logic but there does not exist a machineset matching the new logic.
416
417
// In fact previously not matching MS can now start matching the target. Since there could be multiple matches, lets choose the
417
418
// MS with the most replicas so that there is minimum machine churn.
418
- func FindNewMachineSet (deployment * clusterv1.MachineDeployment , msList []* clusterv1.MachineSet , reconciliationTime * metav1.Time ) * clusterv1.MachineSet {
419
+ func FindNewMachineSet (deployment * clusterv1.MachineDeployment , msList []* clusterv1.MachineSet , reconciliationTime * metav1.Time ) (* clusterv1.MachineSet , string , error ) {
420
+ if len (msList ) == 0 {
421
+ return nil , "no MachineSets exist for the MachineDeployment" , nil
422
+ }
423
+
424
+ // In rare cases, such as after cluster upgrades, Deployment may end up with
425
+ // having more than one new MachineSets that have the same template,
426
+ // see https://github.com/kubernetes/kubernetes/issues/40415
427
+ // We deterministically choose the oldest new MachineSet with matching template hash.
419
428
sort .Sort (MachineSetsByDecreasingReplicas (msList ))
420
- for i := range msList {
421
- if EqualMachineTemplate (& msList [i ].Spec .Template , & deployment .Spec .Template ) &&
422
- ! shouldRolloutAfter (msList [i ], reconciliationTime , deployment .Spec .RolloutAfter ) {
423
- // In rare cases, such as after cluster upgrades, Deployment may end up with
424
- // having more than one new MachineSets that have the same template,
425
- // see https://github.com/kubernetes/kubernetes/issues/40415
426
- // We deterministically choose the oldest new MachineSet with matching template hash.
427
- return msList [i ]
429
+
430
+ var matchingMachineSets []* clusterv1.MachineSet
431
+ var diffs []string
432
+ for _ , ms := range msList {
433
+ ms := ms
434
+
435
+ equal , diff , err := EqualMachineTemplate (& ms .Spec .Template , & deployment .Spec .Template )
436
+ if err != nil {
437
+ return nil , "" , errors .Wrapf (err , "failed to compare MachineDeployment spec template with MachineSet %s" , ms .Name )
438
+ }
439
+ if equal {
440
+ matchingMachineSets = append (matchingMachineSets , ms )
441
+ } else {
442
+ diffs = append (diffs , fmt .Sprintf ("MachineSet %s: diff: %s" , ms .Name , diff ))
428
443
}
429
444
}
430
- // new MachineSet does not exist.
431
- return nil
432
- }
433
445
434
- func shouldRolloutAfter (ms * clusterv1.MachineSet , reconciliationTime * metav1.Time , rolloutAfter * metav1.Time ) bool {
435
- if ms == nil {
436
- return false
446
+ if len (matchingMachineSets ) == 0 {
447
+ return nil , fmt .Sprintf ("couldn't find MachineSet matching MachineDeployment spec template: %s" , strings .Join (diffs , "," )), nil
437
448
}
438
- if reconciliationTime == nil || rolloutAfter == nil {
439
- return false
449
+
450
+ // If RolloutAfter is not set, pick the first matching MachineSet.
451
+ if deployment .Spec .RolloutAfter == nil {
452
+ return matchingMachineSets [0 ], "" , nil
453
+ }
454
+
455
+ // If reconciliation time is before RolloutAfter, pick the first matching MachineSet.
456
+ if reconciliationTime .Before (deployment .Spec .RolloutAfter ) {
457
+ return matchingMachineSets [0 ], "" , nil
458
+ }
459
+
460
+ // Pick the first matching MachineSet that has been created after RolloutAfter.
461
+ for _ , ms := range matchingMachineSets {
462
+ ms := ms
463
+ if ms .CreationTimestamp .After (deployment .Spec .RolloutAfter .Time ) {
464
+ return ms , "" , nil
465
+ }
440
466
}
441
- return ms .CreationTimestamp .Before (rolloutAfter ) && rolloutAfter .Before (reconciliationTime )
467
+
468
+ // If no matching MachineSet was created after RolloutAfter, trigger creation of a new MachineSet.
469
+ return nil , fmt .Sprintf ("RolloutAfter on MachineDeployment set to %s, no MachineSet has been created afterwards" , deployment .Spec .RolloutAfter .Format (time .RFC3339 )), nil
442
470
}
443
471
444
472
// FindOldMachineSets returns the old machine sets targeted by the given Deployment, within the given slice of MSes.
445
473
// Returns a list of machine sets which contains all old machine sets.
446
- func FindOldMachineSets (deployment * clusterv1.MachineDeployment , msList []* clusterv1.MachineSet , reconciliationTime * metav1.Time ) []* clusterv1.MachineSet {
474
+ func FindOldMachineSets (deployment * clusterv1.MachineDeployment , msList []* clusterv1.MachineSet , reconciliationTime * metav1.Time ) ( []* clusterv1.MachineSet , error ) {
447
475
allMSs := make ([]* clusterv1.MachineSet , 0 , len (msList ))
448
- newMS := FindNewMachineSet (deployment , msList , reconciliationTime )
476
+ newMS , _ , err := FindNewMachineSet (deployment , msList , reconciliationTime )
477
+ if err != nil {
478
+ return nil , err
479
+ }
449
480
for _ , ms := range msList {
450
481
// Filter out new machine set
451
482
if newMS != nil && ms .UID == newMS .UID {
452
483
continue
453
484
}
454
485
allMSs = append (allMSs , ms )
455
486
}
456
- return allMSs
487
+ return allMSs , nil
457
488
}
458
489
459
490
// GetReplicaCountForMachineSets returns the sum of Replicas of the given machine sets.
0 commit comments