Skip to content

Commit 52b706a

Browse files
AndiDogfiunchinho
authored andcommitted
Try deleting machine pool user data file from S3 when pruning an old launch template version, add S3 storage tests (#601)
1 parent 2fbec99 commit 52b706a

File tree

10 files changed

+390
-100
lines changed

10 files changed

+390
-100
lines changed

api/v1beta2/tags.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ const (
195195
// of the bootstrap secret that was used to create the user data for the latest launch
196196
// template version.
197197
LaunchTemplateBootstrapDataSecret = NameAWSProviderPrefix + "bootstrap-data-secret"
198+
199+
// LaunchTemplateBootstrapDataHash is the tag we use to store the hash of the raw bootstrap data.
200+
// If bootstrap data is stored in S3, this hash relates to that data, not to the EC2 instance
201+
// user data which only references the S3 object. We store this tag on launch template versions
202+
// so that S3 bootstrap data objects can be deleted when they get outdated.
203+
LaunchTemplateBootstrapDataHash = NameAWSProviderPrefix + "bootstrap-data-hash"
198204
)
199205

200206
// ClusterTagKey generates the key for resources associated with a cluster.

exp/controllers/awsmachinepool_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ func (r *AWSMachinePoolReconciler) reconcileDelete(machinePoolScope *scope.Machi
413413
}
414414

415415
launchTemplateID := machinePoolScope.AWSMachinePool.Status.LaunchTemplateID
416-
launchTemplate, _, _, err := ec2Svc.GetLaunchTemplate(machinePoolScope.LaunchTemplateName())
416+
launchTemplate, _, _, _, err := ec2Svc.GetLaunchTemplate(machinePoolScope.LaunchTemplateName())
417417
if err != nil {
418418
return err
419419
}

exp/controllers/awsmachinepool_controller_test.go

Lines changed: 184 additions & 14 deletions
Large diffs are not rendered by default.

exp/controllers/awsmanagedmachinepool_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ func (r *AWSManagedMachinePoolReconciler) reconcileDelete(
269269

270270
if machinePoolScope.ManagedMachinePool.Spec.AWSLaunchTemplate != nil {
271271
launchTemplateID := machinePoolScope.ManagedMachinePool.Status.LaunchTemplateID
272-
launchTemplate, _, _, err := ec2Svc.GetLaunchTemplate(machinePoolScope.LaunchTemplateName())
272+
launchTemplate, _, _, _, err := ec2Svc.GetLaunchTemplate(machinePoolScope.LaunchTemplateName())
273273
if err != nil {
274274
return err
275275
}

pkg/cloud/services/ec2/launchtemplate.go

Lines changed: 79 additions & 37 deletions
Large diffs are not rendered by default.

pkg/cloud/services/ec2/launchtemplate_test.go

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,19 @@ users:
8080

8181
var testUserDataHash = userdata.ComputeHash([]byte(testUserData))
8282

83-
func defaultEC2AndUserDataSecretKeyTags(name string, clusterName string, userDataSecretKey types.NamespacedName) []*ec2.Tag {
83+
var testBootstrapData = []byte("different from testUserData since bootstrap data may be in S3 while EC2 user data points to that S3 object")
84+
var testBootstrapDataHash = userdata.ComputeHash(testBootstrapData)
85+
86+
func defaultEC2AndDataTags(name string, clusterName string, userDataSecretKey types.NamespacedName, bootstrapDataHash string) []*ec2.Tag {
8487
tags := defaultEC2Tags(name, clusterName)
8588
tags = append(tags, &ec2.Tag{
8689
Key: aws.String(infrav1.LaunchTemplateBootstrapDataSecret),
8790
Value: aws.String(userDataSecretKey.String()),
8891
})
92+
tags = append(tags, &ec2.Tag{
93+
Key: aws.String(infrav1.LaunchTemplateBootstrapDataHash),
94+
Value: aws.String(bootstrapDataHash),
95+
})
8996
sortTags(tags)
9097
return tags
9198
}
@@ -295,20 +302,21 @@ func TestGetLaunchTemplate(t *testing.T) {
295302
tc.expect(mockEC2Client.EXPECT())
296303
}
297304

298-
launchTemplate, userData, _, err := s.GetLaunchTemplate(tc.launchTemplateName)
305+
launchTemplate, userData, _, _, err := s.GetLaunchTemplate(tc.launchTemplateName)
299306
tc.check(g, launchTemplate, userData, err)
300307
})
301308
}
302309
}
303310

304311
func TestServiceSDKToLaunchTemplate(t *testing.T) {
305312
tests := []struct {
306-
name string
307-
input *ec2.LaunchTemplateVersion
308-
wantLT *expinfrav1.AWSLaunchTemplate
309-
wantHash string
310-
wantDataSecretKey *types.NamespacedName
311-
wantErr bool
313+
name string
314+
input *ec2.LaunchTemplateVersion
315+
wantLT *expinfrav1.AWSLaunchTemplate
316+
wantUserDataHash string
317+
wantDataSecretKey *types.NamespacedName
318+
wantBootstrapDataHash *string
319+
wantErr bool
312320
}{
313321
{
314322
name: "lots of input",
@@ -350,8 +358,9 @@ func TestServiceSDKToLaunchTemplate(t *testing.T) {
350358
SSHKeyName: aws.String("foo-keyname"),
351359
VersionNumber: aws.Int64(1),
352360
},
353-
wantHash: testUserDataHash,
354-
wantDataSecretKey: nil, // respective tag is not given
361+
wantUserDataHash: testUserDataHash,
362+
wantDataSecretKey: nil, // respective tag is not given
363+
wantBootstrapDataHash: nil, // respective tag is not given
355364
},
356365
{
357366
name: "tag of bootstrap secret",
@@ -388,6 +397,10 @@ func TestServiceSDKToLaunchTemplate(t *testing.T) {
388397
Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/bootstrap-data-secret"),
389398
Value: aws.String("bootstrap-secret-ns/bootstrap-secret"),
390399
},
400+
{
401+
Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/bootstrap-data-hash"),
402+
Value: aws.String(testBootstrapDataHash),
403+
},
391404
},
392405
},
393406
},
@@ -404,26 +417,29 @@ func TestServiceSDKToLaunchTemplate(t *testing.T) {
404417
SSHKeyName: aws.String("foo-keyname"),
405418
VersionNumber: aws.Int64(1),
406419
},
407-
wantHash: testUserDataHash,
408-
wantDataSecretKey: &types.NamespacedName{Namespace: "bootstrap-secret-ns", Name: "bootstrap-secret"},
420+
wantUserDataHash: testUserDataHash,
421+
wantDataSecretKey: &types.NamespacedName{Namespace: "bootstrap-secret-ns", Name: "bootstrap-secret"},
422+
wantBootstrapDataHash: &testBootstrapDataHash,
409423
},
410424
}
411425
for _, tt := range tests {
412426
t.Run(tt.name, func(t *testing.T) {
427+
g := NewWithT(t)
413428
s := &Service{}
414-
gotLT, gotHash, gotDataSecretKey, err := s.SDKToLaunchTemplate(tt.input)
429+
gotLT, gotUserDataHash, gotDataSecretKey, gotBootstrapDataHash, err := s.SDKToLaunchTemplate(tt.input)
415430
if (err != nil) != tt.wantErr {
416431
t.Fatalf("error mismatch: got %v, wantErr %v", err, tt.wantErr)
417432
}
418433
if !cmp.Equal(gotLT, tt.wantLT) {
419434
t.Fatalf("launchTemplate mismatch: got %v, want %v", gotLT, tt.wantLT)
420435
}
421-
if !cmp.Equal(gotHash, tt.wantHash) {
422-
t.Fatalf("userDataHash mismatch: got %v, want %v", gotHash, tt.wantHash)
436+
if !cmp.Equal(gotUserDataHash, tt.wantUserDataHash) {
437+
t.Fatalf("userDataHash mismatch: got %v, want %v", gotUserDataHash, tt.wantUserDataHash)
423438
}
424439
if !cmp.Equal(gotDataSecretKey, tt.wantDataSecretKey) {
425440
t.Fatalf("userDataSecretKey mismatch: got %v, want %v", gotDataSecretKey, tt.wantDataSecretKey)
426441
}
442+
g.Expect(gotBootstrapDataHash).To(Equal(tt.wantBootstrapDataHash))
427443
})
428444
}
429445
}
@@ -845,7 +861,7 @@ func TestCreateLaunchTemplate(t *testing.T) {
845861
TagSpecifications: []*ec2.LaunchTemplateTagSpecificationRequest{
846862
{
847863
ResourceType: aws.String(ec2.ResourceTypeInstance),
848-
Tags: defaultEC2AndUserDataSecretKeyTags("aws-mp-name", "cluster-name", userDataSecretKey),
864+
Tags: defaultEC2AndDataTags("aws-mp-name", "cluster-name", userDataSecretKey, testBootstrapDataHash),
849865
},
850866
{
851867
ResourceType: aws.String(ec2.ResourceTypeVolume),
@@ -905,7 +921,7 @@ func TestCreateLaunchTemplate(t *testing.T) {
905921
TagSpecifications: []*ec2.LaunchTemplateTagSpecificationRequest{
906922
{
907923
ResourceType: aws.String(ec2.ResourceTypeInstance),
908-
Tags: defaultEC2AndUserDataSecretKeyTags("aws-mp-name", "cluster-name", userDataSecretKey),
924+
Tags: defaultEC2AndDataTags("aws-mp-name", "cluster-name", userDataSecretKey, testBootstrapDataHash),
909925
},
910926
{
911927
ResourceType: aws.String(ec2.ResourceTypeVolume),
@@ -967,7 +983,7 @@ func TestCreateLaunchTemplate(t *testing.T) {
967983
TagSpecifications: []*ec2.LaunchTemplateTagSpecificationRequest{
968984
{
969985
ResourceType: aws.String(ec2.ResourceTypeInstance),
970-
Tags: defaultEC2AndUserDataSecretKeyTags("aws-mp-name", "cluster-name", userDataSecretKey),
986+
Tags: defaultEC2AndDataTags("aws-mp-name", "cluster-name", userDataSecretKey, testBootstrapDataHash),
971987
},
972988
{
973989
ResourceType: aws.String(ec2.ResourceTypeVolume),
@@ -1022,7 +1038,7 @@ func TestCreateLaunchTemplate(t *testing.T) {
10221038
tc.expect(g, mockEC2Client.EXPECT())
10231039
}
10241040

1025-
launchTemplate, err := s.CreateLaunchTemplate(ms, aws.String("imageID"), userDataSecretKey, userData)
1041+
launchTemplate, err := s.CreateLaunchTemplate(ms, aws.String("imageID"), userDataSecretKey, userData, testBootstrapDataHash)
10261042
tc.check(g, launchTemplate, err)
10271043
})
10281044
}
@@ -1050,7 +1066,7 @@ func TestLaunchTemplateDataCreation(t *testing.T) {
10501066
Namespace: "bootstrap-secret-ns",
10511067
Name: "bootstrap-secret",
10521068
}
1053-
launchTemplate, err := s.CreateLaunchTemplate(ms, aws.String("imageID"), userDataSecretKey, nil)
1069+
launchTemplate, err := s.CreateLaunchTemplate(ms, aws.String("imageID"), userDataSecretKey, nil, "")
10541070
g.Expect(err).To(HaveOccurred())
10551071
g.Expect(launchTemplate).Should(BeEmpty())
10561072
})
@@ -1104,7 +1120,7 @@ func TestCreateLaunchTemplateVersion(t *testing.T) {
11041120
TagSpecifications: []*ec2.LaunchTemplateTagSpecificationRequest{
11051121
{
11061122
ResourceType: aws.String(ec2.ResourceTypeInstance),
1107-
Tags: defaultEC2AndUserDataSecretKeyTags("aws-mp-name", "cluster-name", userDataSecretKey),
1123+
Tags: defaultEC2AndDataTags("aws-mp-name", "cluster-name", userDataSecretKey, testBootstrapDataHash),
11081124
},
11091125
{
11101126
ResourceType: aws.String(ec2.ResourceTypeVolume),
@@ -1155,7 +1171,7 @@ func TestCreateLaunchTemplateVersion(t *testing.T) {
11551171
TagSpecifications: []*ec2.LaunchTemplateTagSpecificationRequest{
11561172
{
11571173
ResourceType: aws.String(ec2.ResourceTypeInstance),
1158-
Tags: defaultEC2AndUserDataSecretKeyTags("aws-mp-name", "cluster-name", userDataSecretKey),
1174+
Tags: defaultEC2AndDataTags("aws-mp-name", "cluster-name", userDataSecretKey, testBootstrapDataHash),
11591175
},
11601176
{
11611177
ResourceType: aws.String(ec2.ResourceTypeVolume),
@@ -1202,10 +1218,10 @@ func TestCreateLaunchTemplateVersion(t *testing.T) {
12021218
tc.expect(mockEC2Client.EXPECT())
12031219
}
12041220
if tc.wantErr {
1205-
g.Expect(s.CreateLaunchTemplateVersion("launch-template-id", ms, aws.String("imageID"), userDataSecretKey, userData)).To(HaveOccurred())
1221+
g.Expect(s.CreateLaunchTemplateVersion("launch-template-id", ms, aws.String("imageID"), userDataSecretKey, userData, testBootstrapDataHash)).To(HaveOccurred())
12061222
return
12071223
}
1208-
g.Expect(s.CreateLaunchTemplateVersion("launch-template-id", ms, aws.String("imageID"), userDataSecretKey, userData)).NotTo(HaveOccurred())
1224+
g.Expect(s.CreateLaunchTemplateVersion("launch-template-id", ms, aws.String("imageID"), userDataSecretKey, userData, testBootstrapDataHash)).NotTo(HaveOccurred())
12091225
})
12101226
}
12111227
}
@@ -1218,6 +1234,7 @@ func TestBuildLaunchTemplateTagSpecificationRequest(t *testing.T) {
12181234
Namespace: "bootstrap-secret-ns",
12191235
Name: "bootstrap-secret",
12201236
}
1237+
bootstrapDataHash := userdata.ComputeHash([]byte("shell-script"))
12211238
testCases := []struct {
12221239
name string
12231240
check func(g *WithT, m []*ec2.LaunchTemplateTagSpecificationRequest)
@@ -1228,7 +1245,7 @@ func TestBuildLaunchTemplateTagSpecificationRequest(t *testing.T) {
12281245
expected := []*ec2.LaunchTemplateTagSpecificationRequest{
12291246
{
12301247
ResourceType: aws.String(ec2.ResourceTypeInstance),
1231-
Tags: defaultEC2AndUserDataSecretKeyTags("aws-mp-name", "cluster-name", userDataSecretKey),
1248+
Tags: defaultEC2AndDataTags("aws-mp-name", "cluster-name", userDataSecretKey, bootstrapDataHash),
12321249
},
12331250
{
12341251
ResourceType: aws.String(ec2.ResourceTypeVolume),
@@ -1258,7 +1275,7 @@ func TestBuildLaunchTemplateTagSpecificationRequest(t *testing.T) {
12581275
g.Expect(err).NotTo(HaveOccurred())
12591276

12601277
s := NewService(cs)
1261-
tc.check(g, s.buildLaunchTemplateTagSpecificationRequest(ms, userDataSecretKey))
1278+
tc.check(g, s.buildLaunchTemplateTagSpecificationRequest(ms, userDataSecretKey, bootstrapDataHash))
12621279
})
12631280
}
12641281
}

pkg/cloud/services/interfaces.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package services
2020
import (
2121
"context"
2222

23+
"github.com/aws/aws-sdk-go/service/ec2"
2324
apimachinerytypes "k8s.io/apimachinery/pkg/types"
2425
ctrl "sigs.k8s.io/controller-runtime"
2526

@@ -73,12 +74,12 @@ type EC2Interface interface {
7374
DetachSecurityGroupsFromNetworkInterface(groups []string, interfaceID string) error
7475

7576
DiscoverLaunchTemplateAMI(scope scope.LaunchTemplateScope) (*string, error)
76-
GetLaunchTemplate(id string) (lt *expinfrav1.AWSLaunchTemplate, userDataHash string, userDataSecretKey *apimachinerytypes.NamespacedName, err error)
77+
GetLaunchTemplate(id string) (lt *expinfrav1.AWSLaunchTemplate, userDataHash string, userDataSecretKey *apimachinerytypes.NamespacedName, bootstrapDataHash *string, err error)
7778
GetLaunchTemplateID(id string) (string, error)
7879
GetLaunchTemplateLatestVersion(id string) (string, error)
79-
CreateLaunchTemplate(scope scope.LaunchTemplateScope, imageID *string, userDataSecretKey apimachinerytypes.NamespacedName, userData []byte) (string, error)
80-
CreateLaunchTemplateVersion(id string, scope scope.LaunchTemplateScope, imageID *string, userDataSecretKey apimachinerytypes.NamespacedName, userData []byte) error
81-
PruneLaunchTemplateVersions(id string) error
80+
CreateLaunchTemplate(scope scope.LaunchTemplateScope, imageID *string, userDataSecretKey apimachinerytypes.NamespacedName, userData []byte, bootstrapDataHash string) (string, error)
81+
CreateLaunchTemplateVersion(id string, scope scope.LaunchTemplateScope, imageID *string, userDataSecretKey apimachinerytypes.NamespacedName, userData []byte, bootstrapDataHash string) error
82+
PruneLaunchTemplateVersions(id string) (*ec2.LaunchTemplateVersion, error)
8283
DeleteLaunchTemplate(id string) error
8384
LaunchTemplateNeedsUpdate(scope scope.LaunchTemplateScope, incoming *expinfrav1.AWSLaunchTemplate, existing *expinfrav1.AWSLaunchTemplate) (bool, error)
8485
DeleteBastion() error
@@ -135,6 +136,7 @@ type ObjectStoreInterface interface {
135136
Delete(m *scope.MachineScope) error
136137
Create(m *scope.MachineScope, data []byte) (objectURL string, err error)
137138
CreateForMachinePool(scope scope.LaunchTemplateScope, data []byte) (objectURL string, err error)
139+
DeleteForMachinePool(scope scope.LaunchTemplateScope, bootstrapDataHash string) error
138140
}
139141

140142
// AWSNodeInterface installs the CNI for EKS clusters.

pkg/cloud/services/mock_services/ec2_interface_mock.go

Lines changed: 17 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cloud/services/mock_services/objectstore_machine_interface_mock.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)