@@ -22,12 +22,14 @@ import (
2222 "fmt"
2323 "time"
2424
25+ "github.com/blang/semver/v4"
2526 "github.com/google/go-cmp/cmp"
2627 "github.com/google/go-cmp/cmp/cmpopts"
2728 "github.com/pkg/errors"
2829 corev1 "k8s.io/api/core/v1"
2930 apierrors "k8s.io/apimachinery/pkg/api/errors"
3031 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3133 "k8s.io/apimachinery/pkg/runtime/schema"
3234 "k8s.io/client-go/tools/record"
3335 "k8s.io/klog/v2"
@@ -53,6 +55,7 @@ import (
5355 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/s3"
5456 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
5557 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
58+ "sigs.k8s.io/cluster-api/controllers/external"
5659 expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
5760 "sigs.k8s.io/cluster-api/util"
5861 "sigs.k8s.io/cluster-api/util/annotations"
@@ -305,6 +308,12 @@ func (r *AWSMachinePoolReconciler) reconcileNormal(ctx context.Context, machineP
305308 // But we want to update the LaunchTemplate because an error in the LaunchTemplate may be blocking the ASG creation.
306309 return true , nil
307310 }
311+
312+ canProceed , err := r .isMachinePoolAllowedToUpgradeDueToControlPlaneVersionSkew (ctx , machinePoolScope )
313+ if ! canProceed || err != nil {
314+ return false , err
315+ }
316+
308317 return asgsvc .CanStartASGInstanceRefresh (machinePoolScope )
309318 }
310319 runPostLaunchTemplateUpdateOperation := func () error {
@@ -442,6 +451,35 @@ func (r *AWSMachinePoolReconciler) reconcileNormal(ctx context.Context, machineP
442451 return ctrl.Result {}, nil
443452}
444453
454+ // isMachinePoolAllowedToUpgradeDueToControlPlaneVersionSkew checks if the control plane is being upgraded, in which case we shouldn't update the launch template.
455+ func (r * AWSMachinePoolReconciler ) isMachinePoolAllowedToUpgradeDueToControlPlaneVersionSkew (ctx context.Context , machinePoolScope * scope.MachinePoolScope ) (bool , error ) {
456+ if machinePoolScope .Cluster .Spec .ControlPlaneRef == nil {
457+ return true , nil
458+ }
459+
460+ controlPlane , err := external .Get (ctx , r .Client , machinePoolScope .Cluster .Spec .ControlPlaneRef )
461+ if err != nil {
462+ return true , errors .Wrapf (err , "failed to get ControlPlane %s" , machinePoolScope .Cluster .Spec .ControlPlaneRef .Name )
463+ }
464+
465+ cpVersion , found , err := unstructured .NestedString (controlPlane .Object , "status" , "version" )
466+ if ! found || err != nil {
467+ return true , errors .Wrapf (err , "failed to get version of ControlPlane %s" , machinePoolScope .Cluster .Spec .ControlPlaneRef .Name )
468+ }
469+
470+ controlPlaneCurrentK8sVersion , err := semver .ParseTolerant (cpVersion )
471+ if err != nil {
472+ return true , errors .Wrapf (err , "failed to parse version of ControlPlane %s" , machinePoolScope .Cluster .Spec .ControlPlaneRef .Name )
473+ }
474+
475+ machinePoolDesiredK8sVersion , err := semver .ParseTolerant (* machinePoolScope .MachinePool .Spec .Template .Spec .Version )
476+ if err != nil {
477+ return true , errors .Wrap (err , "failed to parse version of MachinePool" )
478+ }
479+
480+ return controlPlaneCurrentK8sVersion .GE (machinePoolDesiredK8sVersion ), nil
481+ }
482+
445483func (r * AWSMachinePoolReconciler ) reconcileDelete (ctx context.Context , machinePoolScope * scope.MachinePoolScope , clusterScope cloud.ClusterScoper , ec2Scope scope.EC2Scope ) error {
446484 clusterScope .Info ("Handling deleted AWSMachinePool" )
447485
0 commit comments