Skip to content

Commit 394743e

Browse files
committed
Refactor to consolidate into LaunchTemplateNeedsUpdate
1 parent d2a68a1 commit 394743e

File tree

5 files changed

+632
-173
lines changed

5 files changed

+632
-173
lines changed

exp/controllers/awsmachinepool_controller_test.go

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -749,60 +749,6 @@ func TestAWSMachinePoolReconciler(t *testing.T) {
749749
g.Expect(err).To(Succeed())
750750
})
751751

752-
t.Run("launch template and ASG exist and only AMI ID changed", func(t *testing.T) {
753-
g := NewWithT(t)
754-
setup(t, g)
755-
reconciler.reconcileServiceFactory = nil // use real implementation, but keep EC2 calls mocked (`ec2ServiceFactory`)
756-
reconSvc = nil // not used
757-
defer teardown(t, g)
758-
759-
// Latest ID and version already stored, no need to retrieve it
760-
ms.AWSMachinePool.Status.LaunchTemplateID = launchTemplateIDExisting
761-
ms.AWSMachinePool.Status.LaunchTemplateVersion = ptr.To[string]("1")
762-
763-
ec2Svc.EXPECT().GetLaunchTemplate(gomock.Eq("test")).Return(
764-
&expinfrav1.AWSLaunchTemplate{
765-
Name: "test",
766-
AMI: infrav1.AMIReference{
767-
ID: ptr.To[string]("ami-existing"),
768-
},
769-
},
770-
// No change to user data
771-
userdata.ComputeHash([]byte("shell-script")),
772-
&userDataSecretKey,
773-
nil)
774-
ec2Svc.EXPECT().DiscoverLaunchTemplateAMI(gomock.Any()).Return(ptr.To[string]("ami-different"), nil)
775-
ec2Svc.EXPECT().LaunchTemplateNeedsUpdate(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil)
776-
asgSvc.EXPECT().CanStartASGInstanceRefresh(gomock.Any()).Return(true, nil)
777-
ec2Svc.EXPECT().PruneLaunchTemplateVersions(gomock.Any()).Return(nil)
778-
ec2Svc.EXPECT().CreateLaunchTemplateVersion(gomock.Any(), gomock.Any(), gomock.Eq(ptr.To[string]("ami-different")), gomock.Eq(apimachinerytypes.NamespacedName{Namespace: "default", Name: "bootstrap-data"}), gomock.Any()).Return(nil)
779-
ec2Svc.EXPECT().GetLaunchTemplateLatestVersion(gomock.Any()).Return("2", nil)
780-
// AMI change should trigger rolling out new nodes
781-
asgSvc.EXPECT().StartASGInstanceRefresh(gomock.Any())
782-
783-
asgSvc.EXPECT().GetASGByName(gomock.Any()).DoAndReturn(func(scope *scope.MachinePoolScope) (*expinfrav1.AutoScalingGroup, error) {
784-
g.Expect(scope.Name()).To(Equal("test"))
785-
786-
// No difference to `AWSMachinePool.spec`
787-
return &expinfrav1.AutoScalingGroup{
788-
Name: scope.Name(),
789-
Subnets: []string{
790-
"subnet-1",
791-
},
792-
MinSize: awsMachinePool.Spec.MinSize,
793-
MaxSize: awsMachinePool.Spec.MaxSize,
794-
MixedInstancesPolicy: awsMachinePool.Spec.MixedInstancesPolicy.DeepCopy(),
795-
}, nil
796-
})
797-
asgSvc.EXPECT().DescribeLifecycleHooks(gomock.Any()).Return(nil, nil)
798-
asgSvc.EXPECT().SubnetIDs(gomock.Any()).Return([]string{"subnet-1"}, nil) // no change
799-
// No changes, so there must not be an ASG update!
800-
asgSvc.EXPECT().UpdateASG(gomock.Any()).Times(0)
801-
802-
_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs)
803-
g.Expect(err).To(Succeed())
804-
})
805-
806752
t.Run("launch template and ASG exist and only bootstrap data secret name changed", func(t *testing.T) {
807753
g := NewWithT(t)
808754
setup(t, g)

pkg/cloud/services/ec2/launchtemplate.go

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,21 @@ func (s *Service) ReconcileLaunchTemplate(
7171
bootstrapDataHash := userdata.ComputeHash(bootstrapData)
7272

7373
scope.Info("checking for existing launch template")
74-
launchTemplate, launchTemplateUserDataHash, launchTemplateUserDataSecretKey, err := ec2svc.GetLaunchTemplate(scope.LaunchTemplateName())
74+
existingLaunchTemplate, launchTemplateUserDataHash, launchTemplateUserDataSecretKey, err := ec2svc.GetLaunchTemplate(scope.LaunchTemplateName())
7575
if err != nil {
7676
conditions.MarkUnknown(scope.GetSetter(), expinfrav1.LaunchTemplateReadyCondition, expinfrav1.LaunchTemplateNotFoundReason, "%s", err.Error())
7777
return err
7878
}
7979

80-
imageID, err := ec2svc.DiscoverLaunchTemplateAMI(scope)
80+
currentlyUsedAMIID, err := ec2svc.DiscoverLaunchTemplateAMI(scope)
8181
if err != nil {
8282
conditions.MarkFalse(scope.GetSetter(), expinfrav1.LaunchTemplateReadyCondition, expinfrav1.LaunchTemplateCreateFailedReason, clusterv1.ConditionSeverityError, "%s", err.Error())
8383
return err
8484
}
8585

86-
if launchTemplate == nil {
86+
if existingLaunchTemplate == nil {
8787
scope.Info("no existing launch template found, creating")
88-
launchTemplateID, err := ec2svc.CreateLaunchTemplate(scope, imageID, *bootstrapDataSecretKey, bootstrapData)
88+
launchTemplateID, err := ec2svc.CreateLaunchTemplate(scope, currentlyUsedAMIID, *bootstrapDataSecretKey, bootstrapData)
8989
if err != nil {
9090
conditions.MarkFalse(scope.GetSetter(), expinfrav1.LaunchTemplateReadyCondition, expinfrav1.LaunchTemplateCreateFailedReason, clusterv1.ConditionSeverityError, "%s", err.Error())
9191
return err
@@ -119,21 +119,11 @@ func (s *Service) ReconcileLaunchTemplate(
119119
}
120120
}
121121

122-
annotation, err := MachinePoolAnnotationJSON(scope, TagsLastAppliedAnnotation)
123-
if err != nil {
124-
return err
125-
}
126-
127-
// Check if the instance tags were changed. If they were, create a new LaunchTemplate.
128-
tagsChanged, _, _, _ := tagsChanged(annotation, scope.AdditionalTags()) //nolint:dogsled
129-
130-
needsUpdate, err := ec2svc.LaunchTemplateNeedsUpdate(scope, scope.GetLaunchTemplate(), launchTemplate)
122+
needsUpdate, err := ec2svc.LaunchTemplateNeedsUpdate(scope, existingLaunchTemplate, currentlyUsedAMIID)
131123
if err != nil {
132124
return err
133125
}
134126

135-
amiChanged := *imageID != *launchTemplate.AMI.ID
136-
137127
// `launchTemplateUserDataSecretKey` can be nil since it comes from a tag on the launch template
138128
// which may not exist in older launch templates created by older CAPA versions.
139129
// On change, we trigger instance refresh (rollout of new nodes). Therefore, do not consider it a change if the
@@ -142,7 +132,7 @@ func (s *Service) ReconcileLaunchTemplate(
142132
userDataSecretKeyChanged := launchTemplateUserDataSecretKey != nil && bootstrapDataSecretKey.String() != launchTemplateUserDataSecretKey.String()
143133
launchTemplateNeedsUserDataSecretKeyTag := launchTemplateUserDataSecretKey == nil
144134

145-
if needsUpdate || tagsChanged || amiChanged || userDataSecretKeyChanged {
135+
if needsUpdate || userDataSecretKeyChanged {
146136
canUpdate, err := canUpdateLaunchTemplate()
147137
if err != nil {
148138
return err
@@ -157,14 +147,14 @@ func (s *Service) ReconcileLaunchTemplate(
157147

158148
// Create a new launch template version if there's a difference in configuration, tags,
159149
// userdata, OR we've discovered a new AMI ID.
160-
if needsUpdate || tagsChanged || amiChanged || userDataHashChanged || userDataSecretKeyChanged || launchTemplateNeedsUserDataSecretKeyTag {
161-
scope.Info("creating new version for launch template", "existing", launchTemplate, "incoming", scope.GetLaunchTemplate(), "needsUpdate", needsUpdate, "tagsChanged", tagsChanged, "amiChanged", amiChanged, "userDataHashChanged", userDataHashChanged, "userDataSecretKeyChanged", userDataSecretKeyChanged)
150+
if needsUpdate || userDataHashChanged || userDataSecretKeyChanged || launchTemplateNeedsUserDataSecretKeyTag {
151+
scope.Info("creating new version for launch template", "existing", existingLaunchTemplate, "incoming", scope.GetLaunchTemplate(), "needsUpdate", needsUpdate, "userDataHashChanged", userDataHashChanged, "userDataSecretKeyChanged", userDataSecretKeyChanged)
162152
// There is a limit to the number of Launch Template Versions.
163153
// We ensure that the number of versions does not grow without bound by following a simple rule: Before we create a new version, we delete one old version, if there is at least one old version that is not in use.
164154
if err := ec2svc.PruneLaunchTemplateVersions(scope.GetLaunchTemplateIDStatus()); err != nil {
165155
return err
166156
}
167-
if err := ec2svc.CreateLaunchTemplateVersion(scope.GetLaunchTemplateIDStatus(), scope, imageID, *bootstrapDataSecretKey, bootstrapData); err != nil {
157+
if err := ec2svc.CreateLaunchTemplateVersion(scope.GetLaunchTemplateIDStatus(), scope, currentlyUsedAMIID, *bootstrapDataSecretKey, bootstrapData); err != nil {
168158
return err
169159
}
170160
version, err := ec2svc.GetLaunchTemplateLatestVersion(scope.GetLaunchTemplateIDStatus())
@@ -178,7 +168,7 @@ func (s *Service) ReconcileLaunchTemplate(
178168
}
179169
}
180170

181-
if needsUpdate || tagsChanged || amiChanged || userDataSecretKeyChanged {
171+
if needsUpdate || userDataSecretKeyChanged {
182172
if err := runPostLaunchTemplateUpdateOperation(); err != nil {
183173
conditions.MarkFalse(scope.GetSetter(), expinfrav1.PostLaunchTemplateUpdateOperationCondition, expinfrav1.PostLaunchTemplateUpdateOperationFailedReason, clusterv1.ConditionSeverityError, "%s", err.Error())
184174
return err
@@ -788,39 +778,42 @@ func (s *Service) SDKToLaunchTemplate(d *ec2.LaunchTemplateVersion) (*expinfrav1
788778
}
789779

790780
// LaunchTemplateNeedsUpdate checks if a new launch template version is needed.
791-
//
792-
// FIXME(dlipovetsky): This check should account for changed userdata, but does not yet do so.
793-
// Although userdata is stored in an EC2 Launch Template, it is not a field of AWSLaunchTemplate.
794-
func (s *Service) LaunchTemplateNeedsUpdate(scope scope.LaunchTemplateScope, incoming *expinfrav1.AWSLaunchTemplate, existing *expinfrav1.AWSLaunchTemplate) (bool, error) {
795-
if incoming.IamInstanceProfile != existing.IamInstanceProfile {
781+
func (s *Service) LaunchTemplateNeedsUpdate(scope scope.LaunchTemplateScope, existingLaunchTemplate *expinfrav1.AWSLaunchTemplate, currentlyUsedAMIID *string) (bool, error) {
782+
incomingLaunchTemplate := scope.GetLaunchTemplate()
783+
784+
if incomingLaunchTemplate.IamInstanceProfile != existingLaunchTemplate.IamInstanceProfile {
796785
return true, nil
797786
}
798787

799-
if incoming.InstanceType != existing.InstanceType {
788+
if incomingLaunchTemplate.InstanceType != existingLaunchTemplate.InstanceType {
800789
return true, nil
801790
}
802791

803-
if !cmp.Equal(incoming.InstanceMetadataOptions, existing.InstanceMetadataOptions) {
792+
if !cmp.Equal(incomingLaunchTemplate.InstanceMetadataOptions, existingLaunchTemplate.InstanceMetadataOptions) {
804793
return true, nil
805794
}
806795

807-
if !cmp.Equal(incoming.SpotMarketOptions, existing.SpotMarketOptions) {
796+
if !cmp.Equal(incomingLaunchTemplate.SpotMarketOptions, existingLaunchTemplate.SpotMarketOptions) {
808797
return true, nil
809798
}
810799

811-
if !cmp.Equal(incoming.CapacityReservationID, existing.CapacityReservationID) {
800+
if incomingLaunchTemplate.AMI.ID != nil && *incomingLaunchTemplate.AMI.ID != *currentlyUsedAMIID {
812801
return true, nil
813802
}
814803

815-
if !cmp.Equal(incoming.PrivateDNSName, existing.PrivateDNSName) {
804+
if !cmp.Equal(incomingLaunchTemplate.CapacityReservationID, existingLaunchTemplate.CapacityReservationID) {
816805
return true, nil
817806
}
818807

819-
if !cmp.Equal(incoming.SSHKeyName, existing.SSHKeyName) {
808+
if !cmp.Equal(incomingLaunchTemplate.PrivateDNSName, existingLaunchTemplate.PrivateDNSName) {
820809
return true, nil
821810
}
822811

823-
incomingIDs, err := s.GetAdditionalSecurityGroupsIDs(incoming.AdditionalSecurityGroups)
812+
if !cmp.Equal(incomingLaunchTemplate.SSHKeyName, existingLaunchTemplate.SSHKeyName) {
813+
return true, nil
814+
}
815+
816+
incomingIDs, err := s.GetAdditionalSecurityGroupsIDs(incomingLaunchTemplate.AdditionalSecurityGroups)
824817
if err != nil {
825818
return false, err
826819
}
@@ -831,7 +824,7 @@ func (s *Service) LaunchTemplateNeedsUpdate(scope scope.LaunchTemplateScope, inc
831824
}
832825

833826
incomingIDs = append(incomingIDs, coreIDs...)
834-
existingIDs, err := s.GetAdditionalSecurityGroupsIDs(existing.AdditionalSecurityGroups)
827+
existingIDs, err := s.GetAdditionalSecurityGroupsIDs(existingLaunchTemplate.AdditionalSecurityGroups)
835828
if err != nil {
836829
return false, err
837830
}
@@ -842,6 +835,16 @@ func (s *Service) LaunchTemplateNeedsUpdate(scope scope.LaunchTemplateScope, inc
842835
return true, nil
843836
}
844837

838+
annotation, err := MachinePoolAnnotationJSON(scope, TagsLastAppliedAnnotation)
839+
if err != nil {
840+
return false, err
841+
}
842+
//nolint:dogsled
843+
tagsHaveChanged, _, _, _ := tagsChanged(annotation, scope.AdditionalTags())
844+
if tagsHaveChanged {
845+
return true, nil
846+
}
847+
845848
return false, nil
846849
}
847850

0 commit comments

Comments
 (0)