Skip to content

Commit 2d5b9ae

Browse files
authored
Merge pull request #10688 from sbueringer/pr-md-rollout-log
🌱 Log reason for MachineDeployment rollouts / MachineSet creations
2 parents d1a2301 + 92a3bcb commit 2d5b9ae

File tree

3 files changed

+370
-43
lines changed

3 files changed

+370
-43
lines changed

internal/controllers/machinedeployment/machinedeployment_sync.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ func (r *Reconciler) sync(ctx context.Context, md *clusterv1.MachineDeployment,
7979
// This may lead to stale reads of machine sets, thus incorrect deployment status.
8080
func (r *Reconciler) getAllMachineSetsAndSyncRevision(ctx context.Context, md *clusterv1.MachineDeployment, msList []*clusterv1.MachineSet, createIfNotExisted bool) (*clusterv1.MachineSet, []*clusterv1.MachineSet, error) {
8181
reconciliationTime := metav1.Now()
82-
allOldMSs := mdutil.FindOldMachineSets(md, msList, &reconciliationTime)
82+
allOldMSs, err := mdutil.FindOldMachineSets(md, msList, &reconciliationTime)
83+
if err != nil {
84+
return nil, nil, err
85+
}
8386

8487
// Get new machine set with the updated revision number
8588
newMS, err := r.getNewMachineSet(ctx, md, msList, allOldMSs, createIfNotExisted, &reconciliationTime)
@@ -101,7 +104,10 @@ func (r *Reconciler) getNewMachineSet(ctx context.Context, md *clusterv1.Machine
101104
// If we don't find a matching MachineSet, we need a rollout and thus create a new MachineSet.
102105
// Note: The in-place mutable fields can be just updated inline, because they do not affect the actual machines
103106
// themselves (i.e. the infrastructure and the software running on the Machines not the Machine object).
104-
matchingMS := mdutil.FindNewMachineSet(md, msList, reconciliationTime)
107+
matchingMS, createReason, err := mdutil.FindNewMachineSet(md, msList, reconciliationTime)
108+
if err != nil {
109+
return nil, err
110+
}
105111

106112
// If there is a MachineSet that matches the intent of the MachineDeployment, update the MachineSet
107113
// to propagate all in-place mutable fields from MachineDeployment to the MachineSet.
@@ -121,7 +127,7 @@ func (r *Reconciler) getNewMachineSet(ctx context.Context, md *clusterv1.Machine
121127
}
122128

123129
// Create a new MachineSet and wait until the new MachineSet exists in the cache.
124-
newMS, err := r.createMachineSetAndWait(ctx, md, oldMSs)
130+
newMS, err := r.createMachineSetAndWait(ctx, md, oldMSs, createReason)
125131
if err != nil {
126132
return nil, err
127133
}
@@ -154,7 +160,7 @@ func (r *Reconciler) updateMachineSet(ctx context.Context, deployment *clusterv1
154160

155161
// createMachineSetAndWait creates a new MachineSet with the desired intent of the MachineDeployment.
156162
// It waits for the cache to be updated with the newly created MachineSet.
157-
func (r *Reconciler) createMachineSetAndWait(ctx context.Context, deployment *clusterv1.MachineDeployment, oldMSs []*clusterv1.MachineSet) (*clusterv1.MachineSet, error) {
163+
func (r *Reconciler) createMachineSetAndWait(ctx context.Context, deployment *clusterv1.MachineDeployment, oldMSs []*clusterv1.MachineSet, createReason string) (*clusterv1.MachineSet, error) {
158164
log := ctrl.LoggerFrom(ctx)
159165

160166
// Compute the desired MachineSet.
@@ -163,12 +169,17 @@ func (r *Reconciler) createMachineSetAndWait(ctx context.Context, deployment *cl
163169
return nil, errors.Wrap(err, "failed to create new MachineSet")
164170
}
165171

172+
log = log.WithValues("MachineSet", klog.KObj(newMS))
173+
ctx = ctrl.LoggerInto(ctx, log)
174+
175+
log.Info(fmt.Sprintf("Creating new MachineSet, %s", createReason))
176+
166177
// Create the MachineSet.
167178
if err := ssa.Patch(ctx, r.Client, machineDeploymentManagerName, newMS); err != nil {
168179
r.recorder.Eventf(deployment, corev1.EventTypeWarning, "FailedCreate", "Failed to create MachineSet %s: %v", klog.KObj(newMS), err)
169180
return nil, errors.Wrapf(err, "failed to create new MachineSet %s", klog.KObj(newMS))
170181
}
171-
log.V(4).Info("Created new MachineSet", "MachineSet", klog.KObj(newMS))
182+
log.V(4).Info("Created new MachineSet")
172183
r.recorder.Eventf(deployment, corev1.EventTypeNormal, "SuccessfulCreate", "Created MachineSet %s", klog.KObj(newMS))
173184

174185
// Keep trying to get the MachineSet. This will force the cache to update and prevent any future reconciliation of

internal/controllers/machinedeployment/mdutil/util.go

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323
"sort"
2424
"strconv"
2525
"strings"
26+
"time"
2627

2728
"github.com/go-logr/logr"
2829
"github.com/pkg/errors"
2930
corev1 "k8s.io/api/core/v1"
30-
apiequality "k8s.io/apimachinery/pkg/api/equality"
3131
"k8s.io/apimachinery/pkg/api/meta"
3232
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3333
"k8s.io/apimachinery/pkg/runtime"
@@ -37,6 +37,7 @@ import (
3737
ctrl "sigs.k8s.io/controller-runtime"
3838

3939
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
40+
"sigs.k8s.io/cluster-api/internal/util/compare"
4041
"sigs.k8s.io/cluster-api/util/conversion"
4142
)
4243

@@ -371,11 +372,11 @@ func getMachineSetFraction(ms clusterv1.MachineSet, md clusterv1.MachineDeployme
371372

372373
// EqualMachineTemplate returns true if two given machineTemplateSpec are equal,
373374
// 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) {
375376
t1Copy := MachineTemplateDeepCopyRolloutFields(template1)
376377
t2Copy := MachineTemplateDeepCopyRolloutFields(template2)
377378

378-
return apiequality.Semantic.DeepEqual(t1Copy, t2Copy)
379+
return compare.Diff(t1Copy, t2Copy)
379380
}
380381

381382
// MachineTemplateDeepCopyRolloutFields copies a MachineTemplateSpec
@@ -415,45 +416,75 @@ func MachineTemplateDeepCopyRolloutFields(template *clusterv1.MachineTemplateSpe
415416
// 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.
416417
// In fact previously not matching MS can now start matching the target. Since there could be multiple matches, lets choose the
417418
// 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.
419428
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))
428443
}
429444
}
430-
// new MachineSet does not exist.
431-
return nil
432-
}
433445

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
437448
}
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+
}
440466
}
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
442470
}
443471

444472
// FindOldMachineSets returns the old machine sets targeted by the given Deployment, within the given slice of MSes.
445473
// 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) {
447475
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+
}
449480
for _, ms := range msList {
450481
// Filter out new machine set
451482
if newMS != nil && ms.UID == newMS.UID {
452483
continue
453484
}
454485
allMSs = append(allMSs, ms)
455486
}
456-
return allMSs
487+
return allMSs, nil
457488
}
458489

459490
// GetReplicaCountForMachineSets returns the sum of Replicas of the given machine sets.

0 commit comments

Comments
 (0)