Skip to content

Commit b382a94

Browse files
authored
Force delete lb when deletion_protection is disabled (#2172)
* force delete lb when deletion_protection is disabled * check deletion_protection for ALB * check deletion_protection in model builder step * add unit test
1 parent f861a8e commit b382a94

File tree

5 files changed

+150
-2
lines changed

5 files changed

+150
-2
lines changed

pkg/deploy/elbv2/load_balancer_synthesizer.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ package elbv2
33
import (
44
"context"
55
awssdk "github.com/aws/aws-sdk-go/aws"
6+
"github.com/aws/aws-sdk-go/aws/session"
7+
elbv2sdk "github.com/aws/aws-sdk-go/service/elbv2"
68
"github.com/go-logr/logr"
79
"github.com/pkg/errors"
810
"k8s.io/apimachinery/pkg/util/sets"
911
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
1012
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
1113
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
1214
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
15+
"strings"
16+
)
17+
18+
const (
19+
lbAttrsDeletionProtectionEnabled = "deletion_protection.enabled"
1320
)
1421

1522
// NewLoadBalancerSynthesizer constructs loadBalancerSynthesizer
@@ -55,7 +62,13 @@ func (s *loadBalancerSynthesizer) Synthesize(ctx context.Context) error {
5562
// I don't like this, but it's the easiest solution to meet our requirement :D.
5663
for _, sdkLB := range unmatchedSDKLBs {
5764
if err := s.lbManager.Delete(ctx, sdkLB); err != nil {
58-
return err
65+
errMessage := err.Error()
66+
if strings.Contains(errMessage,"OperationNotPermitted") && strings.Contains(errMessage, "deletion protection") {
67+
s.disableDeletionProtection(sdkLB.LoadBalancer)
68+
if err = s.lbManager.Delete(ctx, sdkLB); err != nil {
69+
return err
70+
}
71+
}
5972
}
6073
}
6174
for _, resLB := range unmatchedResLBs {
@@ -75,6 +88,21 @@ func (s *loadBalancerSynthesizer) Synthesize(ctx context.Context) error {
7588
return nil
7689
}
7790

91+
func (s *loadBalancerSynthesizer) disableDeletionProtection(lb *elbv2sdk.LoadBalancer) error {
92+
svc := elbv2sdk.New(session.Must(session.NewSession()))
93+
input := &elbv2sdk.ModifyLoadBalancerAttributesInput{
94+
Attributes: []*elbv2sdk.LoadBalancerAttribute{
95+
{
96+
Key: awssdk.String(lbAttrsDeletionProtectionEnabled),
97+
Value: awssdk.String("false"),
98+
},
99+
},
100+
LoadBalancerArn: lb.LoadBalancerArn,
101+
}
102+
_, err := svc.ModifyLoadBalancerAttributes(input)
103+
return err
104+
}
105+
78106
func (s *loadBalancerSynthesizer) PostSynthesize(ctx context.Context) error {
79107
// nothing to do here.
80108
return nil

pkg/ingress/model_builder.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/go-logr/logr"
88
"github.com/pkg/errors"
99
corev1 "k8s.io/api/core/v1"
10+
networking "k8s.io/api/networking/v1beta1"
1011
"k8s.io/apimachinery/pkg/types"
1112
"k8s.io/apimachinery/pkg/util/sets"
1213
"k8s.io/client-go/tools/record"
@@ -20,6 +21,11 @@ import (
2021
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
2122
networkingpkg "sigs.k8s.io/aws-load-balancer-controller/pkg/networking"
2223
"sigs.k8s.io/controller-runtime/pkg/client"
24+
"strconv"
25+
)
26+
27+
const (
28+
lbAttrsDeletionProtectionEnabled = "deletion_protection.enabled"
2329
)
2430

2531
// ModelBuilder is responsible for build mode stack for a IngressGroup.
@@ -179,6 +185,17 @@ type defaultModelBuildTask struct {
179185
}
180186

181187
func (t *defaultModelBuildTask) run(ctx context.Context) error {
188+
for _, inactiveMember := range t.ingGroup.InactiveMembers {
189+
if !inactiveMember.DeletionTimestamp.IsZero() {
190+
deletionProtectionEnabled, err := t.getDeletionProtectionViaAnnotation(inactiveMember)
191+
if err != nil {
192+
return err
193+
}
194+
if deletionProtectionEnabled {
195+
return errors.Errorf("deletion_protection is enabled, cannot delete the ingress: %v", inactiveMember.Name)
196+
}
197+
}
198+
}
182199
if len(t.ingGroup.Members) == 0 {
183200
return nil
184201
}
@@ -340,6 +357,22 @@ func (t *defaultModelBuildTask) buildSSLRedirectConfig(ctx context.Context, list
340357
}, nil
341358
}
342359

360+
func (t *defaultModelBuildTask) getDeletionProtectionViaAnnotation(ing *networking.Ingress) (bool, error) {
361+
var lbAttributes map[string]string
362+
_, err := t.annotationParser.ParseStringMapAnnotation(annotations.IngressSuffixLoadBalancerAttributes, &lbAttributes, ing.Annotations)
363+
if err != nil {
364+
return false, err
365+
}
366+
if _, deletionProtectionSpecified := lbAttributes[lbAttrsDeletionProtectionEnabled]; deletionProtectionSpecified {
367+
deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[lbAttrsDeletionProtectionEnabled])
368+
if err != nil {
369+
return false, err
370+
}
371+
return deletionProtectionEnabled, nil
372+
}
373+
return false, nil
374+
}
375+
343376
// the listen port config for specific Ingress's listener port.
344377
type listenPortConfigWithIngress struct {
345378
ingKey types.NamespacedName

pkg/ingress/model_builder_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
testclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
2626
"sigs.k8s.io/controller-runtime/pkg/log"
2727
"testing"
28+
"time"
2829
)
2930

3031
func Test_defaultModelBuilder_Build(t *testing.T) {
@@ -2353,6 +2354,36 @@ func Test_defaultModelBuilder_Build(t *testing.T) {
23532354
}
23542355
}`,
23552356
},
2357+
{
2358+
name: "Ingress - deletion protection enabled error",
2359+
env: env{
2360+
svcs: []*corev1.Service{ns_1_svc_1, ns_1_svc_2, ns_1_svc_3},
2361+
},
2362+
args: args{
2363+
ingGroup: Group{
2364+
ID: GroupID{Namespace: "ns-1", Name: "ing-1"},
2365+
InactiveMembers: []*networking.Ingress{
2366+
{
2367+
ObjectMeta: metav1.ObjectMeta{
2368+
Namespace: "default",
2369+
Name: "hello-ingress",
2370+
Annotations: map[string]string{
2371+
"kubernetes.io/ingress.class": "alb",
2372+
"alb.ingress.kubernetes.io/load-balancer-attributes": "deletion_protection.enabled=true",
2373+
},
2374+
Finalizers: []string{
2375+
"ingress.k8s.aws/resources",
2376+
},
2377+
DeletionTimestamp: &metav1.Time{
2378+
Time: time.Now(),
2379+
},
2380+
},
2381+
},
2382+
},
2383+
},
2384+
},
2385+
wantErr: errors.New("deletion_protection is enabled, cannot delete the ingress: hello-ingress"),
2386+
},
23562387
}
23572388
for _, tt := range tests {
23582389
t.Run(tt.name, func(t *testing.T) {

pkg/service/model_builder.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package service
22

33
import (
44
"context"
5+
"github.com/pkg/errors"
56
"k8s.io/apimachinery/pkg/util/sets"
67
"strconv"
78
"sync"
@@ -22,6 +23,7 @@ const (
2223
LoadBalancerTypeExternal = "external"
2324
LoadBalancerTargetTypeIP = "ip"
2425
LoadBalancerTargetTypeInstance = "instance"
26+
lbAttrsDeletionProtection = "deletion_protection.enabled"
2527
)
2628

2729
// ModelBuilder builds the model stack for the service resource.
@@ -124,7 +126,7 @@ type defaultModelBuildTask struct {
124126
ec2Subnets []*ec2.Subnet
125127

126128
fetchExistingLoadBalancerOnce sync.Once
127-
existingLoadBalancer *elbv2deploy.LoadBalancerWithTags
129+
existingLoadBalancer *elbv2deploy.LoadBalancerWithTags
128130

129131
defaultTags map[string]string
130132
externalManagedTags sets.String
@@ -156,6 +158,13 @@ type defaultModelBuildTask struct {
156158

157159
func (t *defaultModelBuildTask) run(ctx context.Context) error {
158160
if !t.service.DeletionTimestamp.IsZero() {
161+
deletionProtectionEnabled, err := t.getDeletionProtectionViaAnnotation(*t.service)
162+
if err != nil {
163+
return err
164+
}
165+
if deletionProtectionEnabled {
166+
return errors.Errorf("deletion_protection is enabled, cannot delete the service: %v", t.service.Name)
167+
}
159168
return nil
160169
}
161170
err := t.buildModel(ctx)
@@ -181,3 +190,20 @@ func (t *defaultModelBuildTask) buildModel(ctx context.Context) error {
181190
}
182191
return nil
183192
}
193+
194+
func (t *defaultModelBuildTask) getDeletionProtectionViaAnnotation(svc corev1.Service) (bool, error) {
195+
var lbAttributes map[string]string
196+
_, err := t.annotationParser.ParseStringMapAnnotation(annotations.SvcLBSuffixLoadBalancerAttributes, &lbAttributes, svc.Annotations)
197+
if err != nil {
198+
return false, err
199+
}
200+
if _, deletionProtectionSpecified := lbAttributes[lbAttrsDeletionProtection]; deletionProtectionSpecified {
201+
deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[lbAttrsDeletionProtection])
202+
if err != nil {
203+
return false, err
204+
}
205+
return deletionProtectionEnabled, nil
206+
}
207+
return false, nil
208+
}
209+

pkg/service/model_builder_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,36 @@ func Test_defaultModelBuilderTask_Build(t *testing.T) {
19691969
listLoadBalancerCalls: []listLoadBalancerCall{listLoadBalancerCallForEmptyLB},
19701970
wantError: true,
19711971
},
1972+
{
1973+
testName: "deletion protection enabled error",
1974+
svc: &corev1.Service{
1975+
ObjectMeta: metav1.ObjectMeta{
1976+
Name: "hello-svc",
1977+
Namespace: "default",
1978+
UID: "bdca2bd0-bfc6-449a-88a3-03451f05f18c",
1979+
DeletionTimestamp: &metav1.Time{
1980+
Time: time.Now(),
1981+
},
1982+
Annotations: map[string]string{
1983+
"service.beta.kubernetes.io/aws-load-balancer-type": "external",
1984+
"service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip",
1985+
"service.beta.kubernetes.io/aws-load-balancer-attributes": "deletion_protection.enabled=true",
1986+
},
1987+
},
1988+
Spec: corev1.ServiceSpec{
1989+
Type: corev1.ServiceTypeLoadBalancer,
1990+
Selector: map[string]string{"app": "hello"},
1991+
Ports: []corev1.ServicePort{
1992+
{
1993+
Port: 80,
1994+
TargetPort: intstr.FromInt(80),
1995+
Protocol: corev1.ProtocolTCP,
1996+
},
1997+
},
1998+
},
1999+
},
2000+
wantError: true,
2001+
},
19722002
}
19732003

19742004
for _, tt := range tests {

0 commit comments

Comments
 (0)