Skip to content

Commit aa8d7e3

Browse files
Merge pull request #5428 from djoshy/implement-skew-enforcement
MCO-1877: MCO-1879: MCO-1882: MCO-1884: Implement boot image skew enforcement MVP
2 parents 067395e + 2277bea commit aa8d7e3

12 files changed

Lines changed: 1250 additions & 90 deletions

File tree

pkg/apihelpers/apihelpers.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,31 @@ func MergeMachineManager(status *opv1.MachineConfigurationStatus, manager opv1.M
554554

555555
status.ManagedBootImagesStatus = result
556556
}
557+
558+
// GetSkewEnforcementStatusAutomaticWithOCPVersion returns a BootImageSkewEnforcementStatus with Automatic mode and the given OCP version.
559+
func GetSkewEnforcementStatusAutomaticWithOCPVersion(ocpVersion string) opv1.BootImageSkewEnforcementStatus {
560+
return opv1.BootImageSkewEnforcementStatus{
561+
Mode: opv1.BootImageSkewEnforcementModeStatusAutomatic,
562+
Automatic: opv1.ClusterBootImageAutomatic{
563+
OCPVersion: ocpVersion,
564+
},
565+
}
566+
}
567+
568+
// GetSkewEnforcementStatusManualWithOCPVersion returns a BootImageSkewEnforcementStatus with Manual mode and the given OCP version.
569+
func GetSkewEnforcementStatusManualWithOCPVersion(ocpVersion string) opv1.BootImageSkewEnforcementStatus {
570+
return opv1.BootImageSkewEnforcementStatus{
571+
Mode: opv1.BootImageSkewEnforcementModeStatusManual,
572+
Manual: opv1.ClusterBootImageManual{
573+
Mode: opv1.ClusterBootImageSpecModeOCPVersion,
574+
OCPVersion: ocpVersion,
575+
},
576+
}
577+
}
578+
579+
// GetSkewEnforcementStatusNone returns a BootImageSkewEnforcementStatus with None mode.
580+
func GetSkewEnforcementStatusNone() opv1.BootImageSkewEnforcementStatus {
581+
return opv1.BootImageSkewEnforcementStatus{
582+
Mode: opv1.BootImageSkewEnforcementModeStatusNone,
583+
}
584+
}

pkg/controller/bootimage/boot_image_controller.go

Lines changed: 97 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"reflect"
7+
"strings"
78
"time"
89

910
features "github.com/openshift/api/features"
@@ -14,6 +15,7 @@ import (
1415
corev1 "k8s.io/api/core/v1"
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
18+
k8sversion "k8s.io/apimachinery/pkg/util/version"
1719
"k8s.io/apimachinery/pkg/util/wait"
1820
coreinformersv1 "k8s.io/client-go/informers/core/v1"
1921
clientset "k8s.io/client-go/kubernetes"
@@ -77,6 +79,7 @@ type Controller struct {
7779
// Stats structure for local bookkeeping of machine resources
7880
type MachineResourceStats struct {
7981
inProgress int
82+
skippedCount int
8083
erroredCount int
8184
totalCount int
8285
}
@@ -94,6 +97,17 @@ func (mrs MachineResourceStats) isFinished() bool {
9497
return mrs.totalCount == (mrs.inProgress + mrs.erroredCount)
9598
}
9699

100+
func (mrs MachineResourceStats) getProgressingStatusMessage(name string) string {
101+
if mrs.skippedCount > 0 {
102+
return fmt.Sprintf("Reconciled %d of %d %s (%d skipped)", mrs.inProgress-mrs.skippedCount, mrs.totalCount, name, mrs.skippedCount)
103+
}
104+
return fmt.Sprintf("Reconciled %d of %d %s", mrs.inProgress, mrs.totalCount, name)
105+
}
106+
107+
func (mrs MachineResourceStats) getDegradedStatusMessage(name string) string {
108+
return fmt.Sprintf("%d Degraded %s", mrs.erroredCount, name)
109+
}
110+
97111
const (
98112
// Name of machine api namespace
99113
MachineAPINamespace = "openshift-machine-api"
@@ -446,8 +460,11 @@ func (ctrl *Controller) updateMachineConfiguration(oldMC, newMC interface{}) {
446460
return
447461
}
448462

449-
// Only take action if the there is an actual change in the MachineConfiguration's ManagedBootImagesStatus
450-
if reflect.DeepEqual(oldMachineConfiguration.Status.ManagedBootImagesStatus, newMachineConfiguration.Status.ManagedBootImagesStatus) {
463+
// Skip reconciliation if neither ManagedBootImagesStatus nor BootImageSkewEnforcementStatus has changed.
464+
// BootImageSkewEnforcementStatus is only checked when the BootImageSkewEnforcement feature gate is enabled.
465+
if reflect.DeepEqual(oldMachineConfiguration.Status.ManagedBootImagesStatus, newMachineConfiguration.Status.ManagedBootImagesStatus) &&
466+
(!ctrl.fgHandler.Enabled(features.FeatureGateBootImageSkewEnforcement) ||
467+
reflect.DeepEqual(oldMachineConfiguration.Status.BootImageSkewEnforcementStatus, newMachineConfiguration.Status.BootImageSkewEnforcementStatus)) {
451468
return
452469
}
453470

@@ -493,7 +510,13 @@ func (ctrl *Controller) updateConditions(newReason string, syncError error, targ
493510
for i, condition := range newConditions {
494511
if condition.Type == targetConditionType {
495512
if condition.Type == opv1.MachineConfigurationBootImageUpdateProgressing {
496-
newConditions[i].Message = fmt.Sprintf("Reconciled %d of %d MAPI MachineSets | Reconciled %d of %d ControlPlaneMachineSets | Reconciled %d of %d CAPI MachineSets | Reconciled %d of %d CAPI MachineDeployments", ctrl.mapiStats.inProgress, ctrl.mapiStats.totalCount, ctrl.cpmsStats.inProgress, ctrl.cpmsStats.totalCount, ctrl.capiMachineSetStats.inProgress, ctrl.capiMachineSetStats.totalCount, ctrl.capiMachineDeploymentStats.inProgress, ctrl.capiMachineDeploymentStats.totalCount)
513+
messages := []string{
514+
ctrl.mapiStats.getProgressingStatusMessage("MAPI MachineSets"),
515+
ctrl.cpmsStats.getProgressingStatusMessage("ControlPlaneMachineSets"),
516+
ctrl.capiMachineSetStats.getProgressingStatusMessage("CAPI MachineSets"),
517+
ctrl.capiMachineDeploymentStats.getProgressingStatusMessage("CAPI MachineDeployments"),
518+
}
519+
newConditions[i].Message = strings.Join(messages, " | ")
497520
newConditions[i].Reason = newReason
498521
// If all machine resources have been processed, then the controller is no longer progressing.
499522
if ctrl.mapiStats.isFinished() && ctrl.cpmsStats.isFinished() && ctrl.capiMachineSetStats.isFinished() && ctrl.capiMachineDeploymentStats.isFinished() {
@@ -502,10 +525,16 @@ func (ctrl *Controller) updateConditions(newReason string, syncError error, targ
502525
newConditions[i].Status = metav1.ConditionTrue
503526
}
504527
} else if condition.Type == opv1.MachineConfigurationBootImageUpdateDegraded {
528+
messages := []string{
529+
ctrl.mapiStats.getDegradedStatusMessage("MAPI MachineSets"),
530+
ctrl.cpmsStats.getDegradedStatusMessage("ControlPlaneMachineSets"),
531+
ctrl.capiMachineSetStats.getDegradedStatusMessage("CAPI MachineSets"),
532+
ctrl.capiMachineDeploymentStats.getDegradedStatusMessage("CAPI MachineDeployments"),
533+
}
505534
if syncError == nil {
506-
newConditions[i].Message = fmt.Sprintf("%d Degraded MAPI MachineSets | %d Degraded ControlPlaneMachineSets | %d Degraded CAPI MachineSets | %d CAPI MachineDeployments", ctrl.mapiStats.erroredCount, ctrl.cpmsStats.erroredCount, ctrl.capiMachineSetStats.erroredCount, ctrl.capiMachineDeploymentStats.erroredCount)
535+
newConditions[i].Message = strings.Join(messages, " | ")
507536
} else {
508-
newConditions[i].Message = fmt.Sprintf("%d Degraded MAPI MachineSets | %d Degraded ControlPlaneMachineSets | %d Degraded CAPI MachineSets | %d CAPI MachineDeployments | Error(s): %s", ctrl.mapiStats.erroredCount, ctrl.cpmsStats.erroredCount, ctrl.capiMachineSetStats.erroredCount, ctrl.capiMachineDeploymentStats.erroredCount, syncError.Error())
537+
newConditions[i].Message = fmt.Sprintf("%s | Error(s): %s", strings.Join(messages, " | "), syncError.Error())
509538
}
510539
newConditions[i].Reason = newReason
511540
if syncError != nil {
@@ -523,33 +552,77 @@ func (ctrl *Controller) updateConditions(newReason string, syncError error, targ
523552
}
524553
// Only make an API call if there is an update to the Conditions field
525554
if !reflect.DeepEqual(newConditions, mcop.Status.Conditions) {
526-
ctrl.updateMachineConfigurationStatus(mcop, newConditions)
555+
mcop.Status.Conditions = newConditions
556+
ctrl.updateMachineConfigurationStatus(mcop.Status)
527557
}
528558
}
529559

530-
// updateMachineConfigurationStatus updates the MachineConfiguration status with new conditions
531-
// using retry logic to handle concurrent updates.
532-
func (ctrl *Controller) updateMachineConfigurationStatus(mcop *opv1.MachineConfiguration, newConditions []metav1.Condition) {
560+
// updateClusterBootImage updates the cluster boot image record if the skew enforcement is set to Automatic mode.
561+
func (ctrl *Controller) updateClusterBootImage() {
562+
563+
mcop, err := ctrl.mcopClient.OperatorV1().MachineConfigurations().Get(context.TODO(), ctrlcommon.MCOOperatorKnobsObjectName, metav1.GetOptions{})
564+
if err != nil {
565+
klog.Errorf("error updating cluster boot image record: %s", err)
566+
return
567+
}
568+
// No action to take if not in automatic mode
569+
if mcop.Status.BootImageSkewEnforcementStatus.Mode != opv1.BootImageSkewEnforcementModeStatusAutomatic {
570+
return
571+
}
572+
573+
// Get OCP version of last boot image update from configmap
574+
configMap, err := ctrl.mcoCmLister.ConfigMaps(ctrlcommon.MCONamespace).Get(ctrlcommon.BootImagesConfigMapName)
575+
if err != nil {
576+
klog.Warningf("Failed to get boot images configmap: %v, skipping cluster boot image record update", err)
577+
return
578+
}
579+
580+
releaseVersion, found := configMap.Data[ctrlcommon.OCPReleaseVersionKey]
581+
if !found {
582+
klog.Warningf("OCP release version not found in boot images configmap, skipping cluster boot image record update")
583+
return
584+
}
533585

586+
// Parse and extract semantic version (major.minor.patch) for API validation
587+
parsedVersion, err := k8sversion.ParseGeneric(releaseVersion)
588+
if err != nil {
589+
klog.Warningf("Failed to parse release version %q from configmap: %v, skipping cluster boot image record update", releaseVersion, err)
590+
return
591+
}
592+
ocpVersion := fmt.Sprintf("%d.%d.%d", parsedVersion.Major(), parsedVersion.Minor(), parsedVersion.Patch())
593+
594+
newBootImageSkewEnforcementStatus := mcop.Status.BootImageSkewEnforcementStatus.DeepCopy()
595+
newBootImageSkewEnforcementStatus.Automatic = opv1.ClusterBootImageAutomatic{
596+
OCPVersion: ocpVersion,
597+
}
598+
599+
// Only make an API call if there is an update to the skew enforcement status
600+
if !reflect.DeepEqual(mcop.Status.BootImageSkewEnforcementStatus, newBootImageSkewEnforcementStatus) {
601+
mcop.Status.BootImageSkewEnforcementStatus = *newBootImageSkewEnforcementStatus
602+
ctrl.updateMachineConfigurationStatus(mcop.Status)
603+
}
604+
}
605+
606+
// updateMachineConfigurationStatus updates the MachineConfiguration status using retry logic to handle concurrent updates.
607+
func (ctrl *Controller) updateMachineConfigurationStatus(mcopStatus opv1.MachineConfigurationStatus) {
534608
// Using a retry here as there may be concurrent reconiliation loops updating conditions for multiple
535609
// resources at the same time and their local stores may be out of date
536-
if !reflect.DeepEqual(mcop.Status.Conditions, newConditions) {
537-
klog.V(4).Infof("%v", newConditions)
538-
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
539-
mcop, err := ctrl.mcopClient.OperatorV1().MachineConfigurations().Get(context.TODO(), ctrlcommon.MCOOperatorKnobsObjectName, metav1.GetOptions{})
540-
if err != nil {
541-
return err
542-
}
543-
mcop.Status.Conditions = newConditions
544-
_, err = ctrl.mcopClient.OperatorV1().MachineConfigurations().UpdateStatus(context.TODO(), mcop, metav1.UpdateOptions{})
545-
if err != nil {
546-
return err
547-
}
548-
return nil
549-
}); err != nil {
550-
klog.Errorf("error updating MachineConfiguration status: %v", err)
610+
klog.V(4).Infof("MachineConfiguration status update: %v", mcopStatus)
611+
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
612+
mcop, err := ctrl.mcopClient.OperatorV1().MachineConfigurations().Get(context.TODO(), ctrlcommon.MCOOperatorKnobsObjectName, metav1.GetOptions{})
613+
if err != nil {
614+
return err
551615
}
616+
mcop.Status = mcopStatus
617+
_, err = ctrl.mcopClient.OperatorV1().MachineConfigurations().UpdateStatus(context.TODO(), mcop, metav1.UpdateOptions{})
618+
if err != nil {
619+
return err
620+
}
621+
return nil
622+
}); err != nil {
623+
klog.Errorf("error updating MachineConfiguration status: %v", err)
552624
}
625+
553626
}
554627

555628
// getDefaultConditions returns the default boot image update conditions when no

pkg/controller/bootimage/boot_image_controller_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ func TestReconcileAzureProviderSpec(t *testing.T) {
813813
testStreamData = tt.streamData
814814
}
815815

816-
patchRequired, updatedProviderSpec, err := reconcileAzureProviderSpec(
816+
patchRequired, _, updatedProviderSpec, err := reconcileAzureProviderSpec(
817817
testStreamData,
818818
tt.arch,
819819
infra,

pkg/controller/bootimage/cpms_helpers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ func reconcilePlatformCPMS[T any](
257257
configMap *corev1.ConfigMap,
258258
arch string,
259259
secretClient clientset.Interface,
260-
reconcileProviderSpec func(*stream.Stream, string, *osconfigv1.Infrastructure, *T, string, clientset.Interface) (bool, *T, error),
260+
reconcileProviderSpec func(*stream.Stream, string, *osconfigv1.Infrastructure, *T, string, clientset.Interface) (bool, bool, *T, error),
261261
) (patchRequired bool, newCPMS *machinev1.ControlPlaneMachineSet, err error) {
262262
klog.Infof("Reconciling controlplanemachineset %s on %s, with arch %s", cpms.Name, string(infra.Status.PlatformStatus.Type), arch)
263263

@@ -274,7 +274,7 @@ func reconcilePlatformCPMS[T any](
274274
}
275275

276276
// Reconcile the provider spec
277-
patchRequired, newProviderSpec, err := reconcileProviderSpec(streamData, arch, infra, providerSpec, cpms.Name, secretClient)
277+
patchRequired, _, newProviderSpec, err := reconcileProviderSpec(streamData, arch, infra, providerSpec, cpms.Name, secretClient)
278278
if err != nil {
279279
return false, nil, err
280280
}

pkg/controller/bootimage/ms_helpers.go

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
osconfigv1 "github.com/openshift/api/config/v1"
12+
"github.com/openshift/api/features"
1213
machinev1beta1 "github.com/openshift/api/machine/v1beta1"
1314
opv1 "github.com/openshift/api/operator/v1"
1415
ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common"
@@ -69,30 +70,40 @@ func (ctrl *Controller) syncMAPIMachineSets(reason string) {
6970
// Reset stats before initiating reconciliation loop
7071
ctrl.mapiStats.inProgress = 0
7172
ctrl.mapiStats.totalCount = len(mapiMachineSets)
73+
ctrl.mapiStats.skippedCount = 0
7274
ctrl.mapiStats.erroredCount = 0
7375

7476
// Signal start of reconciliation process, by setting progressing to true
7577
var syncErrors []error
7678
ctrl.updateConditions(reason, nil, opv1.MachineConfigurationBootImageUpdateProgressing)
7779

7880
for _, machineSet := range mapiMachineSets {
79-
err := ctrl.syncMAPIMachineSet(machineSet)
81+
patchSkipped, err := ctrl.syncMAPIMachineSet(machineSet)
8082
if err == nil {
8183
ctrl.mapiStats.inProgress++
8284
} else {
8385
klog.Errorf("Error syncing MAPI MachineSet %v", err)
8486
syncErrors = append(syncErrors, fmt.Errorf("error syncing MAPI MachineSet %s: %v", machineSet.Name, err))
8587
ctrl.mapiStats.erroredCount++
8688
}
89+
if patchSkipped {
90+
ctrl.mapiStats.skippedCount++
91+
}
8792
// Update progressing conditions every step of the loop
8893
ctrl.updateConditions(reason, nil, opv1.MachineConfigurationBootImageUpdateProgressing)
8994
}
9095
// Update/Clear degrade conditions based on errors from this loop
9196
ctrl.updateConditions(reason, kubeErrs.NewAggregate(syncErrors), opv1.MachineConfigurationBootImageUpdateDegraded)
97+
// If no machinesets were skipped, update the cluster boot image record
98+
if ctrl.fgHandler.Enabled(features.FeatureGateBootImageSkewEnforcement) {
99+
if ctrl.mapiStats.skippedCount == 0 {
100+
ctrl.updateClusterBootImage()
101+
}
102+
}
92103
}
93104

94105
// syncMAPIMachineSet will attempt to reconcile the provided machineset
95-
func (ctrl *Controller) syncMAPIMachineSet(machineSet *machinev1beta1.MachineSet) error {
106+
func (ctrl *Controller) syncMAPIMachineSet(machineSet *machinev1beta1.MachineSet) (bool, error) {
96107

97108
startTime := time.Now()
98109
klog.V(4).Infof("Started syncing MAPI machineset %q (%v)", machineSet.Name, startTime)
@@ -104,20 +115,20 @@ func (ctrl *Controller) syncMAPIMachineSet(machineSet *machinev1beta1.MachineSet
104115
// that the machineset may be managed by another workflow and should not be reconciled.
105116
if len(machineSet.GetOwnerReferences()) != 0 {
106117
klog.Infof("machineset %s has OwnerReference: %v, skipping boot image update", machineSet.GetOwnerReferences()[0].Kind+"/"+machineSet.GetOwnerReferences()[0].Name, machineSet.Name)
107-
return nil
118+
return true, nil
108119
}
109120

110121
if os, ok := machineSet.Spec.Template.Labels[OSLabelKey]; ok {
111122
if os == "Windows" {
112123
klog.Infof("machineset %s has a windows os label, skipping boot image update", machineSet.Name)
113-
return nil
124+
return false, nil
114125
}
115126
}
116127

117128
// Fetch the ClusterVersion to determine if this is a multi-arch cluster
118129
clusterVersion, err := ctrl.clusterVersionLister.Get("version")
119130
if err != nil {
120-
return fmt.Errorf("failed to fetch clusterversion during machineset sync: %v, defaulting to single-arch behavior", err)
131+
return false, fmt.Errorf("failed to fetch clusterversion during machineset sync: %v, defaulting to single-arch behavior", err)
121132
}
122133

123134
// Fetch the architecture type of this machineset
@@ -126,15 +137,15 @@ func (ctrl *Controller) syncMAPIMachineSet(machineSet *machinev1beta1.MachineSet
126137
// If no architecture annotation was found, skip this machineset without erroring
127138
// A later sync loop will pick it up once the annotation is added
128139
if strings.Contains(err.Error(), "no architecture annotation found") {
129-
return nil
140+
return true, nil
130141
}
131-
return fmt.Errorf("failed to fetch arch during machineset sync: %w", err)
142+
return false, fmt.Errorf("failed to fetch arch during machineset sync: %w", err)
132143
}
133144

134145
// Fetch the infra object to determine the platform type
135146
infra, err := ctrl.infraLister.Get("cluster")
136147
if err != nil {
137-
return fmt.Errorf("failed to fetch infra object during machineset sync: %w", err)
148+
return false, fmt.Errorf("failed to fetch infra object during machineset sync: %w", err)
138149
}
139150

140151
// Fetch the bootimage configmap & ensure it has been stamped by the operator. This is done by
@@ -143,44 +154,44 @@ func (ctrl *Controller) syncMAPIMachineSet(machineSet *machinev1beta1.MachineSet
143154
// If it hasn't been updated, exit and wait for a resync.
144155
configMap, err := ctrl.mcoCmLister.ConfigMaps(ctrlcommon.MCONamespace).Get(ctrlcommon.BootImagesConfigMapName)
145156
if err != nil {
146-
return fmt.Errorf("failed to fetch coreos-bootimages config map during machineset sync: %w", err)
157+
return false, fmt.Errorf("failed to fetch coreos-bootimages config map during machineset sync: %w", err)
147158
}
148159
versionHashFromCM, versionHashFound := configMap.Data[ctrlcommon.MCOVersionHashKey]
149160
if !versionHashFound {
150161
klog.Infof("failed to find mco version hash in %s configmap, sync will exit to wait for the MCO upgrade to complete", ctrlcommon.BootImagesConfigMapName)
151-
return nil
162+
return true, nil
152163
}
153164
if versionHashFromCM != operatorversion.Hash {
154165
klog.Infof("mismatch between MCO hash version stored in configmap and current MCO version; sync will exit to wait for the MCO upgrade to complete")
155-
return nil
166+
return true, nil
156167
}
157168
releaseVersionFromCM, releaseVersionFound := configMap.Data[ctrlcommon.OCPReleaseVersionKey]
158169
if !releaseVersionFound {
159170
klog.Infof("failed to find OCP release version in %s configmap, sync will exit to wait for the MCO upgrade to complete", ctrlcommon.BootImagesConfigMapName)
160-
return nil
171+
return true, nil
161172
}
162173
if releaseVersionFromCM != operatorversion.ReleaseVersion {
163174
klog.Infof("mismatch between OCP release version stored in configmap and current MCO release version; sync will exit to wait for the MCO upgrade to complete")
164-
return nil
175+
return true, nil
165176
}
166177

167178
// Check if the this MachineSet requires an update
168-
patchRequired, newMachineSet, err := checkMachineSet(infra, machineSet, configMap, arch, ctrl.kubeClient)
179+
patchRequired, patchSkipped, newMachineSet, err := checkMachineSet(infra, machineSet, configMap, arch, ctrl.kubeClient)
169180
if err != nil {
170-
return fmt.Errorf("failed to reconcile machineset %s, err: %w", machineSet.Name, err)
181+
return false, fmt.Errorf("failed to reconcile machineset %s, err: %w", machineSet.Name, err)
171182
}
172183

173184
// Patch the machineset if required
174185
if patchRequired {
175186
// First, check if we're hot looping
176187
if ctrl.checkMAPIMachineSetHotLoop(newMachineSet) {
177-
return fmt.Errorf("refusing to reconcile machineset %s, hot loop detected. Please opt-out of boot image updates, adjust your machine provisioning workflow to prevent hot loops and opt back in to resume boot image updates", machineSet.Name)
188+
return false, fmt.Errorf("refusing to reconcile machineset %s, hot loop detected. Please opt-out of boot image updates, adjust your machine provisioning workflow to prevent hot loops and opt back in to resume boot image updates", machineSet.Name)
178189
}
179190
klog.Infof("Patching MAPI machineset %s", machineSet.Name)
180-
return ctrl.patchMachineSet(machineSet, newMachineSet)
191+
return false, ctrl.patchMachineSet(machineSet, newMachineSet)
181192
}
182193
klog.Infof("No patching required for MAPI machineset %s", machineSet.Name)
183-
return nil
194+
return patchSkipped, nil
184195
}
185196

186197
// Checks against a local store of boot image updates to detect hot looping

0 commit comments

Comments
 (0)