Skip to content

Commit e906ef4

Browse files
authored
Merge pull request #5520 from alam0rt/sam.lockart/allow-preserving-addons
✨ allow preserving addons on delete
2 parents edf2e6f + 3aa0586 commit e906ef4

File tree

11 files changed

+86
-88
lines changed

11 files changed

+86
-88
lines changed

config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ spec:
9696
description: Name is the name of the addon
9797
minLength: 2
9898
type: string
99+
preserveOnDelete:
100+
description: |-
101+
PreserveOnDelete indicates that the addon resources should be
102+
preserved in the cluster on delete.
103+
type: boolean
99104
serviceAccountRoleARN:
100105
description: ServiceAccountRoleArn is the ARN of an IAM role
101106
to bind to the addons service account
@@ -2270,6 +2275,11 @@ spec:
22702275
description: Name is the name of the addon
22712276
minLength: 2
22722277
type: string
2278+
preserveOnDelete:
2279+
description: |-
2280+
PreserveOnDelete indicates that the addon resources should be
2281+
preserved in the cluster on delete.
2282+
type: boolean
22732283
serviceAccountRoleARN:
22742284
description: ServiceAccountRoleArn is the ARN of an IAM role
22752285
to bind to the addons service account

config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanetemplates.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ spec:
8383
description: Name is the name of the addon
8484
minLength: 2
8585
type: string
86+
preserveOnDelete:
87+
description: |-
88+
PreserveOnDelete indicates that the addon resources should be
89+
preserved in the cluster on delete.
90+
type: boolean
8691
serviceAccountRoleARN:
8792
description: ServiceAccountRoleArn is the ARN of an
8893
IAM role to bind to the addons service account

controlplane/eks/api/v1beta1/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ type Addon struct {
141141
// ServiceAccountRoleArn is the ARN of an IAM role to bind to the addons service account
142142
// +optional
143143
ServiceAccountRoleArn *string `json:"serviceAccountRoleARN,omitempty"`
144+
// PreserveOnDelete indicates that the addon resources should be
145+
// preserved in the cluster on delete.
146+
// +optional
147+
PreserveOnDelete bool `json:"preserveOnDelete,omitempty"`
144148
}
145149

146150
// AddonResolution defines the method for resolving parameter conflicts.

controlplane/eks/api/v1beta1/zz_generated.conversion.go

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

controlplane/eks/api/v1beta2/awsmanagedcontrolplane_webhook_test.go

Lines changed: 10 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -85,118 +85,52 @@ func TestDefaultingWebhook(t *testing.T) {
8585
resourceName: "cluster1",
8686
resourceNS: "default",
8787
expectHash: false,
88-
expectSpec: AWSManagedControlPlaneSpec{
89-
EKSClusterName: "default_cluster1",
90-
IdentityRef: defaultIdentityRef,
91-
Bastion: defaultTestBastion,
92-
NetworkSpec: defaultNetworkSpec,
93-
TokenMethod: &EKSTokenMethodIAMAuthenticator,
94-
BootstrapSelfManagedAddons: true,
95-
},
88+
expectSpec: AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator, BootstrapSelfManagedAddons: true},
9689
},
9790
{
9891
name: "less than 100 chars, dot in name",
9992
resourceName: "team1.cluster1",
10093
resourceNS: "default",
10194
expectHash: false,
102-
expectSpec: AWSManagedControlPlaneSpec{
103-
EKSClusterName: "default_team1_cluster1",
104-
IdentityRef: defaultIdentityRef,
105-
Bastion: defaultTestBastion,
106-
NetworkSpec: defaultNetworkSpec,
107-
TokenMethod: &EKSTokenMethodIAMAuthenticator,
108-
BootstrapSelfManagedAddons: true,
109-
},
95+
expectSpec: AWSManagedControlPlaneSpec{EKSClusterName: "default_team1_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator, BootstrapSelfManagedAddons: true},
11096
},
11197
{
11298
name: "more than 100 chars",
11399
resourceName: "abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde",
114100
resourceNS: "default",
115101
expectHash: true,
116-
expectSpec: AWSManagedControlPlaneSpec{
117-
EKSClusterName: "capi_",
118-
IdentityRef: defaultIdentityRef,
119-
Bastion: defaultTestBastion,
120-
NetworkSpec: defaultNetworkSpec,
121-
TokenMethod: &EKSTokenMethodIAMAuthenticator,
122-
BootstrapSelfManagedAddons: true,
123-
},
102+
expectSpec: AWSManagedControlPlaneSpec{EKSClusterName: "capi_", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator, BootstrapSelfManagedAddons: true},
124103
},
125104
{
126105
name: "with patch",
127106
resourceName: "cluster1",
128107
resourceNS: "default",
129108
expectHash: false,
130-
spec: AWSManagedControlPlaneSpec{
131-
Version: &vV1_17_1,
132-
},
133-
expectSpec: AWSManagedControlPlaneSpec{
134-
EKSClusterName: "default_cluster1",
135-
Version: &vV1_17_1,
136-
IdentityRef: defaultIdentityRef,
137-
Bastion: defaultTestBastion,
138-
NetworkSpec: defaultNetworkSpec,
139-
TokenMethod: &EKSTokenMethodIAMAuthenticator,
140-
BootstrapSelfManagedAddons: true,
141-
},
109+
spec: AWSManagedControlPlaneSpec{Version: &vV1_17_1},
110+
expectSpec: AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", Version: &vV1_17_1, IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator, BootstrapSelfManagedAddons: true},
142111
},
143112
{
144113
name: "with allowed ip on bastion",
145114
resourceName: "cluster1",
146115
resourceNS: "default",
147116
expectHash: false,
148-
spec: AWSManagedControlPlaneSpec{
149-
Bastion: infrav1.Bastion{
150-
AllowedCIDRBlocks: []string{"100.100.100.100/0"},
151-
},
152-
},
153-
expectSpec: AWSManagedControlPlaneSpec{
154-
EKSClusterName: "default_cluster1",
155-
IdentityRef: defaultIdentityRef,
156-
Bastion: infrav1.Bastion{
157-
AllowedCIDRBlocks: []string{"100.100.100.100/0"},
158-
},
159-
NetworkSpec: defaultNetworkSpec,
160-
TokenMethod: &EKSTokenMethodIAMAuthenticator,
161-
BootstrapSelfManagedAddons: true,
162-
},
117+
spec: AWSManagedControlPlaneSpec{Bastion: infrav1.Bastion{AllowedCIDRBlocks: []string{"100.100.100.100/0"}}},
118+
expectSpec: AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: infrav1.Bastion{AllowedCIDRBlocks: []string{"100.100.100.100/0"}}, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator, BootstrapSelfManagedAddons: true},
163119
},
164120
{
165121
name: "with CNI on network",
166122
resourceName: "cluster1",
167123
resourceNS: "default",
168124
expectHash: false,
169-
spec: AWSManagedControlPlaneSpec{
170-
NetworkSpec: infrav1.NetworkSpec{
171-
CNI: &infrav1.CNISpec{},
172-
},
173-
},
174-
expectSpec: AWSManagedControlPlaneSpec{
175-
EKSClusterName: "default_cluster1",
176-
IdentityRef: defaultIdentityRef,
177-
Bastion: defaultTestBastion,
178-
NetworkSpec: infrav1.NetworkSpec{
179-
CNI: &infrav1.CNISpec{},
180-
VPC: defaultVPCSpec,
181-
},
182-
TokenMethod: &EKSTokenMethodIAMAuthenticator,
183-
BootstrapSelfManagedAddons: true,
184-
},
125+
spec: AWSManagedControlPlaneSpec{NetworkSpec: infrav1.NetworkSpec{CNI: &infrav1.CNISpec{}}},
126+
expectSpec: AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: infrav1.NetworkSpec{CNI: &infrav1.CNISpec{}, VPC: defaultVPCSpec}, TokenMethod: &EKSTokenMethodIAMAuthenticator, BootstrapSelfManagedAddons: true},
185127
},
186128
{
187129
name: "secondary CIDR",
188130
resourceName: "cluster1",
189131
resourceNS: "default",
190132
expectHash: false,
191-
expectSpec: AWSManagedControlPlaneSpec{
192-
EKSClusterName: "default_cluster1",
193-
IdentityRef: defaultIdentityRef,
194-
Bastion: defaultTestBastion,
195-
NetworkSpec: defaultNetworkSpec,
196-
SecondaryCidrBlock: nil,
197-
TokenMethod: &EKSTokenMethodIAMAuthenticator,
198-
BootstrapSelfManagedAddons: true,
199-
},
133+
expectSpec: AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, SecondaryCidrBlock: nil, TokenMethod: &EKSTokenMethodIAMAuthenticator, BootstrapSelfManagedAddons: true},
200134
},
201135
}
202136

controlplane/eks/api/v1beta2/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ type Addon struct {
141141
// ServiceAccountRoleArn is the ARN of an IAM role to bind to the addons service account
142142
// +optional
143143
ServiceAccountRoleArn *string `json:"serviceAccountRoleARN,omitempty"`
144+
// PreserveOnDelete indicates that the addon resources should be
145+
// preserved in the cluster on delete.
146+
// +optional
147+
PreserveOnDelete bool `json:"preserveOnDelete,omitempty"`
144148
}
145149

146150
// AddonResolution defines the method for resolving parameter conflicts.

pkg/cloud/services/eks/addons.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ func (s *Service) translateAPIToAddon(addons []ekscontrolplanev1.Addon) []*eksad
207207
Tags: ngTags(s.scope.Cluster.Name, s.scope.AdditionalTags()),
208208
ResolveConflict: conflict,
209209
ServiceAccountRoleARN: addon.ServiceAccountRoleArn,
210+
Preserve: addon.PreserveOnDelete,
210211
}
211212

212213
converted = append(converted, convertedAddon)

pkg/eks/addons/plan.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func (a *plan) Create(_ context.Context) ([]planner.Procedure, error) {
8686
desired := a.getDesired(*installed.Name)
8787
if desired == nil {
8888
if *installed.Status != string(ekstypes.AddonStatusDeleting) {
89-
procedures = append(procedures, &DeleteAddonProcedure{plan: a, name: *installed.Name})
89+
procedures = append(procedures, &DeleteAddonProcedure{plan: a, name: *installed.Name, preserve: installed.Preserve})
9090
}
9191
procedures = append(procedures, &WaitAddonDeleteProcedure{plan: a, name: *installed.Name})
9292
}

pkg/eks/addons/plan_test.go

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func TestEKSAddonPlan(t *testing.T) {
4141
addonStatusUpdating := string(ekstypes.AddonStatusUpdating)
4242
addonStatusDeleting := string(ekstypes.AddonStatusDeleting)
4343
addonStatusCreating := string(ekstypes.AddonStatusCreating)
44+
addonPreserve := false
4445
created := time.Now()
4546
maxActiveUpdateDeleteWait := 30 * time.Minute
4647

@@ -176,7 +177,7 @@ func TestEKSAddonPlan(t *testing.T) {
176177
createDesiredAddon(addon1Name, addon1version),
177178
},
178179
installedAddons: []*EKSAddon{
179-
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive),
180+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive, false),
180181
},
181182
expectCreateError: false,
182183
expectDoError: false,
@@ -198,7 +199,7 @@ func TestEKSAddonPlan(t *testing.T) {
198199
createDesiredAddon(addon1Name, addon1version),
199200
},
200201
installedAddons: []*EKSAddon{
201-
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusCreating),
202+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusCreating, addonPreserve),
202203
},
203204
expectCreateError: false,
204205
expectDoError: false,
@@ -236,7 +237,7 @@ func TestEKSAddonPlan(t *testing.T) {
236237
createDesiredAddon(addon1Name, addon1Upgrade),
237238
},
238239
installedAddons: []*EKSAddon{
239-
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive),
240+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive, addonPreserve),
240241
},
241242
expectCreateError: false,
242243
expectDoError: false,
@@ -258,7 +259,7 @@ func TestEKSAddonPlan(t *testing.T) {
258259
createDesiredAddon(addon1Name, addon1Upgrade),
259260
},
260261
installedAddons: []*EKSAddon{
261-
createInstalledAddon(addon1Name, addon1Upgrade, addonARN, addonStatusUpdating),
262+
createInstalledAddon(addon1Name, addon1Upgrade, addonARN, addonStatusUpdating, addonPreserve),
262263
},
263264
expectCreateError: false,
264265
expectDoError: false,
@@ -277,7 +278,7 @@ func TestEKSAddonPlan(t *testing.T) {
277278
createDesiredAddonExtraTag(addon1Name, addon1version),
278279
},
279280
installedAddons: []*EKSAddon{
280-
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive),
281+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive, addonPreserve),
281282
},
282283
expectCreateError: false,
283284
expectDoError: false,
@@ -321,7 +322,7 @@ func TestEKSAddonPlan(t *testing.T) {
321322
createDesiredAddonExtraTag(addon1Name, addon1Upgrade),
322323
},
323324
installedAddons: []*EKSAddon{
324-
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive),
325+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive, addonPreserve),
325326
},
326327
expectCreateError: false,
327328
expectDoError: false,
@@ -333,6 +334,7 @@ func TestEKSAddonPlan(t *testing.T) {
333334
DeleteAddon(gomock.Eq(context.TODO()), gomock.Eq(&eks.DeleteAddonInput{
334335
AddonName: &addon1Name,
335336
ClusterName: &clusterName,
337+
Preserve: false,
336338
})).
337339
Return(&eks.DeleteAddonOutput{
338340
Addon: &ekstypes.Addon{
@@ -352,7 +354,39 @@ func TestEKSAddonPlan(t *testing.T) {
352354
}), maxActiveUpdateDeleteWait).Return(nil)
353355
},
354356
installedAddons: []*EKSAddon{
355-
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive),
357+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive, addonPreserve),
358+
},
359+
expectCreateError: false,
360+
expectDoError: false,
361+
},
362+
{
363+
name: "1 installed and 0 desired - delete addon & preserve",
364+
expect: func(m *mock_eksiface.MockEKSAPIMockRecorder) {
365+
m.
366+
DeleteAddon(gomock.Eq(context.TODO()), gomock.Eq(&eks.DeleteAddonInput{
367+
AddonName: &addon1Name,
368+
ClusterName: &clusterName,
369+
Preserve: true,
370+
})).
371+
Return(&eks.DeleteAddonOutput{
372+
Addon: &ekstypes.Addon{
373+
AddonArn: aws.String(addonARN),
374+
AddonName: aws.String(addon1Name),
375+
AddonVersion: aws.String(addon1version),
376+
ClusterName: aws.String(clusterName),
377+
CreatedAt: &created,
378+
ModifiedAt: &created,
379+
Status: ekstypes.AddonStatusDeleting,
380+
Tags: createTags(),
381+
},
382+
}, nil)
383+
m.WaitUntilAddonDeleted(gomock.Eq(context.TODO()), gomock.Eq(&eks.DescribeAddonInput{
384+
AddonName: aws.String(addon1Name),
385+
ClusterName: aws.String(clusterName),
386+
}), maxActiveUpdateDeleteWait).Return(nil)
387+
},
388+
installedAddons: []*EKSAddon{
389+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusActive, true),
356390
},
357391
expectCreateError: false,
358392
expectDoError: false,
@@ -366,7 +400,7 @@ func TestEKSAddonPlan(t *testing.T) {
366400
}), maxActiveUpdateDeleteWait).Return(nil)
367401
},
368402
installedAddons: []*EKSAddon{
369-
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusDeleting),
403+
createInstalledAddon(addon1Name, addon1version, addonARN, addonStatusDeleting, false),
370404
},
371405
expectCreateError: false,
372406
expectDoError: false,
@@ -442,10 +476,11 @@ func createDesiredAddonExtraTag(name, version string) *EKSAddon {
442476
}
443477
}
444478

445-
func createInstalledAddon(name, version, arn, status string) *EKSAddon {
479+
func createInstalledAddon(name, version, arn, status string, preserve bool) *EKSAddon {
446480
desired := createDesiredAddon(name, version)
447481
desired.ARN = &arn
448482
desired.Status = &status
483+
desired.Preserve = preserve
449484

450485
return desired
451486
}

pkg/eks/addons/procedures.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,17 @@ var (
4040

4141
// DeleteAddonProcedure is a procedure that will delete an EKS addon.
4242
type DeleteAddonProcedure struct {
43-
plan *plan
44-
name string
43+
plan *plan
44+
name string
45+
preserve bool
4546
}
4647

4748
// Do implements the logic for the procedure.
4849
func (p *DeleteAddonProcedure) Do(ctx context.Context) error {
4950
input := &eks.DeleteAddonInput{
5051
AddonName: aws.String(p.name),
5152
ClusterName: aws.String(p.plan.clusterName),
53+
Preserve: p.preserve,
5254
}
5355

5456
if _, err := p.plan.eksClient.DeleteAddon(ctx, input); err != nil {

0 commit comments

Comments
 (0)