-
Notifications
You must be signed in to change notification settings - Fork 1.5k
🐛 Fix ClusterClass MachinePool rollout on BootstrapConfig changes #13110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -981,7 +981,7 @@ func (r *Reconciler) createMachinePool(ctx context.Context, s *scope.Scope, mp * | |
| // Best effort cleanup of the InfrastructureMachinePool; | ||
| // If this fails, the object will be garbage collected when the cluster is deleted. | ||
| if err := r.Client.Delete(ctx, mp.InfrastructureMachinePoolObject); err != nil { | ||
| infraLog.Info("WARNING! Failed to cleanup InfrastructureMachinePoolObject for MachinePool while handling creation error. The object will be garbage collected when the cluster is deleted.") | ||
| infraLog.Error(err, "WARNING! Failed to cleanup InfrastructureMachinePoolObject for MachinePool while handling creation error. The object will be garbage collected when the cluster is deleted.") | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1031,7 +1031,7 @@ func (r *Reconciler) createMachinePool(ctx context.Context, s *scope.Scope, mp * | |
| return clientutil.WaitForObjectsToBeAddedToTheCache(ctx, r.Client, "MachinePool creation", mp.Object) | ||
| } | ||
|
|
||
| // updateMachinePool updates a MachinePool. Also updates the corresponding objects if necessary. | ||
| // updateMachinePool updates a MachinePool. Also rotates the corresponding objects if necessary. | ||
| func (r *Reconciler) updateMachinePool(ctx context.Context, s *scope.Scope, mpTopologyName string, currentMP, desiredMP *scope.MachinePoolState) error { | ||
| log := ctrl.LoggerFrom(ctx).WithValues("MachinePool", klog.KObj(desiredMP.Object), | ||
| "machinePoolTopology", mpTopologyName) | ||
|
|
@@ -1044,27 +1044,67 @@ func (r *Reconciler) updateMachinePool(ctx context.Context, s *scope.Scope, mpTo | |
| } | ||
|
|
||
| cluster := s.Current.Cluster | ||
| infraCtx := ctrl.LoggerInto(ctx, log.WithValues(desiredMP.InfrastructureMachinePoolObject.GetKind(), klog.KObj(desiredMP.InfrastructureMachinePoolObject))) | ||
| if _, err := r.reconcileReferencedObject(infraCtx, reconcileReferencedObjectInput{ | ||
| cluster: cluster, | ||
| current: currentMP.InfrastructureMachinePoolObject, | ||
| desired: desiredMP.InfrastructureMachinePoolObject, | ||
| }); err != nil { | ||
|
|
||
| // Reconcile the InfrastructureMachinePool object, rotating it if necessary. | ||
| infraLog := log.WithValues(desiredMP.InfrastructureMachinePoolObject.GetKind(), klog.KObj(desiredMP.InfrastructureMachinePoolObject)) | ||
| infraCtx := ctrl.LoggerInto(ctx, infraLog) | ||
| infrastructureMachinePoolCleanupFunc := func() {} | ||
| createdInfra, err := r.reconcileReferencedTemplate(infraCtx, reconcileReferencedTemplateInput{ | ||
| cluster: cluster, | ||
| ref: &desiredMP.Object.Spec.Template.Spec.InfrastructureRef, | ||
| current: currentMP.InfrastructureMachinePoolObject, | ||
| desired: desiredMP.InfrastructureMachinePoolObject, | ||
| templateNamePrefix: topologynames.InfrastructureMachinePoolNamePrefix(cluster.Name, mpTopologyName), | ||
| compatibilityChecker: check.ObjectsAreCompatible, | ||
| }) | ||
| if err != nil { | ||
| return errors.Wrapf(err, "failed to reconcile MachinePool %s", klog.KObj(currentMP.Object)) | ||
| } | ||
|
|
||
| bootstrapCtx := ctrl.LoggerInto(ctx, log.WithValues(desiredMP.BootstrapObject.GetKind(), klog.KObj(desiredMP.BootstrapObject))) | ||
| if _, err := r.reconcileReferencedObject(bootstrapCtx, reconcileReferencedObjectInput{ | ||
| cluster: cluster, | ||
| current: currentMP.BootstrapObject, | ||
| desired: desiredMP.BootstrapObject, | ||
| }); err != nil { | ||
| if createdInfra { | ||
| infrastructureMachinePoolCleanupFunc = func() { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, why are we creating an inline function instead of defining it somewhere else? Not as familiar with Kubernetes code bases so disregard if there there's a good reason.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a. I'd like to stay consistent with the current style where this is used in multiple other places already. |
||
| // Best effort cleanup of the InfrastructureMachinePool; | ||
| // If this fails, the object will be garbage collected when the cluster is deleted. | ||
| if err := r.Client.Delete(ctx, desiredMP.InfrastructureMachinePoolObject); err != nil { | ||
| infraLog.Error(err, "WARNING! Failed to cleanup InfrastructureMachinePool for MachinePool while handling update error. The object will be garbage collected when the cluster is deleted.") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Reconcile the BootstrapConfig object, rotating it if necessary. | ||
| bootstrapLog := log.WithValues(desiredMP.BootstrapObject.GetKind(), klog.KObj(desiredMP.BootstrapObject)) | ||
| bootstrapCtx := ctrl.LoggerInto(ctx, bootstrapLog) | ||
| bootstrapCleanupFunc := func() {} | ||
| createdBootstrap, err := r.reconcileReferencedTemplate(bootstrapCtx, reconcileReferencedTemplateInput{ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is this standardized via the MachinePool contract?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it's not explicitly stated in the contract. It made sense to do this in CAPA as we needed some way to roll out changes to |
||
| cluster: cluster, | ||
| ref: &desiredMP.Object.Spec.Template.Spec.Bootstrap.ConfigRef, | ||
| current: currentMP.BootstrapObject, | ||
| desired: desiredMP.BootstrapObject, | ||
| templateNamePrefix: topologynames.BootstrapConfigNamePrefix(cluster.Name, mpTopologyName), | ||
| compatibilityChecker: check.ObjectsAreInTheSameNamespace, | ||
| }) | ||
| if err != nil { | ||
| // Best effort cleanup of the InfrastructureMachinePool (only on rotation). | ||
| infrastructureMachinePoolCleanupFunc() | ||
| return errors.Wrapf(err, "failed to reconcile MachinePool %s", klog.KObj(currentMP.Object)) | ||
| } | ||
|
|
||
| if createdBootstrap { | ||
| bootstrapCleanupFunc = func() { | ||
| // Best effort cleanup of the BootstrapConfig; | ||
| // If this fails, the object will be garbage collected when the cluster is deleted. | ||
| if err := r.Client.Delete(ctx, desiredMP.BootstrapObject); err != nil { | ||
| bootstrapLog.Error(err, "WARNING! Failed to cleanup BootstrapConfig for MachinePool while handling update error. The object will be garbage collected when the cluster is deleted.") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Check differences between current and desired MachinePool, and eventually patch the current object. | ||
| patchHelper, err := structuredmerge.NewServerSidePatchHelper(ctx, currentMP.Object, desiredMP.Object, r.Client, r.ssaCache) | ||
| if err != nil { | ||
| // Best effort cleanup of the InfrastructureMachinePool & BootstrapConfig (only on rotation). | ||
| infrastructureMachinePoolCleanupFunc() | ||
| bootstrapCleanupFunc() | ||
| return errors.Wrapf(err, "failed to create patch helper for MachinePool %s", klog.KObj(currentMP.Object)) | ||
| } | ||
| if !patchHelper.HasChanges() { | ||
|
|
@@ -1080,6 +1120,9 @@ func (r *Reconciler) updateMachinePool(ctx context.Context, s *scope.Scope, mpTo | |
| } | ||
| modifiedResourceVersion, err := patchHelper.Patch(ctx) | ||
| if err != nil { | ||
| // Best effort cleanup of the InfrastructureMachinePool & BootstrapConfig (only on rotation). | ||
| infrastructureMachinePoolCleanupFunc() | ||
| bootstrapCleanupFunc() | ||
| return errors.Wrapf(err, "failed to patch MachinePool %s", klog.KObj(currentMP.Object)) | ||
| } | ||
| r.recorder.Eventf(cluster, corev1.EventTypeNormal, updateEventReason, "Updated MachinePool %q%s", klog.KObj(currentMP.Object), logMachinePoolVersionChange(currentMP.Object, desiredMP.Object)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither the InfraMachinePool nor the BootstrapConfig is a template.
Will this rotate the template? If yes, is it actually supported by infra providers that we rotate the InfraMachinePool object? (& standardized in the contract)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this warrants a bigger discussion. Last time I talked about this, we were leaning towards updating these things in the controller docs and NOT in the contract (ref).
I am going to do bring this up again, discuss, and update the contract/controller docs and then get back to this. Thank you.