Skip to content

Force delete lb when deletion_protection is disabled #2172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion pkg/deploy/elbv2/load_balancer_synthesizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ package elbv2
import (
"context"
awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
elbv2sdk "github.com/aws/aws-sdk-go/service/elbv2"
"github.com/go-logr/logr"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
"strings"
)

const (
lbAttrsDeletionProtectionEnabled = "deletion_protection.enabled"
)

// NewLoadBalancerSynthesizer constructs loadBalancerSynthesizer
Expand Down Expand Up @@ -55,7 +62,13 @@ func (s *loadBalancerSynthesizer) Synthesize(ctx context.Context) error {
// I don't like this, but it's the easiest solution to meet our requirement :D.
for _, sdkLB := range unmatchedSDKLBs {
if err := s.lbManager.Delete(ctx, sdkLB); err != nil {
return err
errMessage := err.Error()
if strings.Contains(errMessage,"OperationNotPermitted") && strings.Contains(errMessage, "deletion protection") {
s.disableDeletionProtection(sdkLB.LoadBalancer)
if err = s.lbManager.Delete(ctx, sdkLB); err != nil {
return err
}
}
}
}
for _, resLB := range unmatchedResLBs {
Expand All @@ -75,6 +88,21 @@ func (s *loadBalancerSynthesizer) Synthesize(ctx context.Context) error {
return nil
}

func (s *loadBalancerSynthesizer) disableDeletionProtection(lb *elbv2sdk.LoadBalancer) error {
svc := elbv2sdk.New(session.Must(session.NewSession()))
input := &elbv2sdk.ModifyLoadBalancerAttributesInput{
Attributes: []*elbv2sdk.LoadBalancerAttribute{
{
Key: awssdk.String(lbAttrsDeletionProtectionEnabled),
Value: awssdk.String("false"),
},
},
LoadBalancerArn: lb.LoadBalancerArn,
}
_, err := svc.ModifyLoadBalancerAttributes(input)
return err
}

func (s *loadBalancerSynthesizer) PostSynthesize(ctx context.Context) error {
// nothing to do here.
return nil
Expand Down
33 changes: 33 additions & 0 deletions pkg/ingress/model_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/go-logr/logr"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
networking "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
Expand All @@ -20,6 +21,11 @@ import (
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
networkingpkg "sigs.k8s.io/aws-load-balancer-controller/pkg/networking"
"sigs.k8s.io/controller-runtime/pkg/client"
"strconv"
)

const (
lbAttrsDeletionProtectionEnabled = "deletion_protection.enabled"
)

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

func (t *defaultModelBuildTask) run(ctx context.Context) error {
for _, inactiveMember := range t.ingGroup.InactiveMembers {
if !inactiveMember.DeletionTimestamp.IsZero() {
deletionProtectionEnabled, err := t.getDeletionProtectionViaAnnotation(inactiveMember)
if err != nil {
return err
}
if deletionProtectionEnabled {
return errors.Errorf("deletion_protection is enabled, cannot delete the ingress: %v", inactiveMember.Name)
}
}
}
if len(t.ingGroup.Members) == 0 {
return nil
}
Expand Down Expand Up @@ -340,6 +357,22 @@ func (t *defaultModelBuildTask) buildSSLRedirectConfig(ctx context.Context, list
}, nil
}

func (t *defaultModelBuildTask) getDeletionProtectionViaAnnotation(ing *networking.Ingress) (bool, error) {
var lbAttributes map[string]string
_, err := t.annotationParser.ParseStringMapAnnotation(annotations.IngressSuffixLoadBalancerAttributes, &lbAttributes, ing.Annotations)
if err != nil {
return false, err
}
if _, deletionProtectionSpecified := lbAttributes[lbAttrsDeletionProtectionEnabled]; deletionProtectionSpecified {
deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[lbAttrsDeletionProtectionEnabled])
if err != nil {
return false, err
}
return deletionProtectionEnabled, nil
}
return false, nil
}

// the listen port config for specific Ingress's listener port.
type listenPortConfigWithIngress struct {
ingKey types.NamespacedName
Expand Down
31 changes: 31 additions & 0 deletions pkg/ingress/model_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
testclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/log"
"testing"
"time"
)

func Test_defaultModelBuilder_Build(t *testing.T) {
Expand Down Expand Up @@ -2353,6 +2354,36 @@ func Test_defaultModelBuilder_Build(t *testing.T) {
}
}`,
},
{
name: "Ingress - deletion protection enabled error",
env: env{
svcs: []*corev1.Service{ns_1_svc_1, ns_1_svc_2, ns_1_svc_3},
},
args: args{
ingGroup: Group{
ID: GroupID{Namespace: "ns-1", Name: "ing-1"},
InactiveMembers: []*networking.Ingress{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "hello-ingress",
Annotations: map[string]string{
"kubernetes.io/ingress.class": "alb",
"alb.ingress.kubernetes.io/load-balancer-attributes": "deletion_protection.enabled=true",
},
Finalizers: []string{
"ingress.k8s.aws/resources",
},
DeletionTimestamp: &metav1.Time{
Time: time.Now(),
},
},
},
},
},
},
wantErr: errors.New("deletion_protection is enabled, cannot delete the ingress: hello-ingress"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
28 changes: 27 additions & 1 deletion pkg/service/model_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package service

import (
"context"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/sets"
"strconv"
"sync"
Expand All @@ -22,6 +23,7 @@ const (
LoadBalancerTypeExternal = "external"
LoadBalancerTargetTypeIP = "ip"
LoadBalancerTargetTypeInstance = "instance"
lbAttrsDeletionProtection = "deletion_protection.enabled"
)

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

fetchExistingLoadBalancerOnce sync.Once
existingLoadBalancer *elbv2deploy.LoadBalancerWithTags
existingLoadBalancer *elbv2deploy.LoadBalancerWithTags

defaultTags map[string]string
externalManagedTags sets.String
Expand Down Expand Up @@ -156,6 +158,13 @@ type defaultModelBuildTask struct {

func (t *defaultModelBuildTask) run(ctx context.Context) error {
if !t.service.DeletionTimestamp.IsZero() {
deletionProtectionEnabled, err := t.getDeletionProtectionViaAnnotation(*t.service)
if err != nil {
return err
}
if deletionProtectionEnabled {
return errors.Errorf("deletion_protection is enabled, cannot delete the service: %v", t.service.Name)
}
return nil
}
err := t.buildModel(ctx)
Expand All @@ -181,3 +190,20 @@ func (t *defaultModelBuildTask) buildModel(ctx context.Context) error {
}
return nil
}

func (t *defaultModelBuildTask) getDeletionProtectionViaAnnotation(svc corev1.Service) (bool, error) {
var lbAttributes map[string]string
_, err := t.annotationParser.ParseStringMapAnnotation(annotations.SvcLBSuffixLoadBalancerAttributes, &lbAttributes, svc.Annotations)
if err != nil {
return false, err
}
if _, deletionProtectionSpecified := lbAttributes[lbAttrsDeletionProtection]; deletionProtectionSpecified {
deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[lbAttrsDeletionProtection])
if err != nil {
return false, err
}
return deletionProtectionEnabled, nil
}
return false, nil
}

30 changes: 30 additions & 0 deletions pkg/service/model_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,36 @@ func Test_defaultModelBuilderTask_Build(t *testing.T) {
listLoadBalancerCalls: []listLoadBalancerCall{listLoadBalancerCallForEmptyLB},
wantError: true,
},
{
testName: "deletion protection enabled error",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "hello-svc",
Namespace: "default",
UID: "bdca2bd0-bfc6-449a-88a3-03451f05f18c",
DeletionTimestamp: &metav1.Time{
Time: time.Now(),
},
Annotations: map[string]string{
"service.beta.kubernetes.io/aws-load-balancer-type": "external",
"service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip",
"service.beta.kubernetes.io/aws-load-balancer-attributes": "deletion_protection.enabled=true",
},
},
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeLoadBalancer,
Selector: map[string]string{"app": "hello"},
Ports: []corev1.ServicePort{
{
Port: 80,
TargetPort: intstr.FromInt(80),
Protocol: corev1.ProtocolTCP,
},
},
},
},
wantError: true,
},
}

for _, tt := range tests {
Expand Down