Skip to content

Commit

Permalink
CB-23235 Disable delete protection of ELBs before delete
Browse files Browse the repository at this point in the history
  • Loading branch information
Bajzathd authored and doktoric committed Sep 18, 2023
1 parent bbb8fb7 commit 5b550db
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 11 deletions.
51 changes: 41 additions & 10 deletions aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,9 +568,14 @@ func deleteNativeStack(ec2Client ec2Client, elbClient elbClient, cloudWatchClien
go func(loadBalancerArn string) {
defer wg.Done()

err := deleteLoadBalancer(elbClient, stack.ID, loadBalancerArn, region)
elbExists, err := disableElbDeleteProtection(elbClient, stack.ID, loadBalancerArn, region)
if err != nil {
errChan <- err
} else if elbExists {
err := deleteLoadBalancer(elbClient, stack.ID, loadBalancerArn, region)
if err != nil {
errChan <- err
}
}
}(lb)
}
Expand Down Expand Up @@ -643,15 +648,23 @@ func deleteCfStack(cfClient cfClient, rdsClient rdsClient, ec2Client ec2Client,
case "AWS::RDS::DBInstance":
if *stackResource.ResourceStatus != cloudformation.ResourceStatusDeleteComplete {
var dbExists bool
dbExists, resourceErr = disableDeleteProtection(rdsClient, stack.Name, *stackResource.PhysicalResourceId, region)
dbExists, resourceErr = disableDbDeleteProtection(rdsClient, stack.Name, *stackResource.PhysicalResourceId, region)
if !dbExists {
retainResources = append(retainResources, stackResource.LogicalResourceId)
}
}
case "AWS::EC2::VPC":
resourceErr = deleteVpcWithDependencies(ec2Client, stack.Name, *stackResource.PhysicalResourceId, region)
case "AWS::ElasticLoadBalancingV2::LoadBalancer":
resourceErr = deleteLoadBalancer(elbClient, stack.Name, *stackResource.PhysicalResourceId, region)
if *stackResource.ResourceStatus != cloudformation.ResourceStatusDeleteComplete {
var elbExists bool
elbExists, resourceErr = disableElbDeleteProtection(elbClient, stack.Name, *stackResource.PhysicalResourceId, region)
if !elbExists {
retainResources = append(retainResources, stackResource.LogicalResourceId)
} else if resourceErr == nil {
resourceErr = deleteLoadBalancer(elbClient, stack.Name, *stackResource.PhysicalResourceId, region)
}
}
}

if resourceErr != nil {
Expand Down Expand Up @@ -685,22 +698,20 @@ func deleteCfStack(cfClient cfClient, rdsClient rdsClient, ec2Client ec2Client,
return nil
}

func disableDeleteProtection(rdsClient rdsClient, stackName string, dbInstanceId string, region string) (bool, error) {
func disableDbDeleteProtection(rdsClient rdsClient, stackName string, dbInstanceId string, region string) (dbExists bool, err error) {
log.Infof("[AWS] Disabling DeletionProtection for DB instance: %s in stack: %s, region: %s", dbInstanceId, stackName, region)
dbExists := true

_, err := rdsClient.ModifyDBInstance(&rds.ModifyDBInstanceInput{
_, err = rdsClient.ModifyDBInstance(&rds.ModifyDBInstanceInput{
DBInstanceIdentifier: &dbInstanceId,
DeletionProtection: aws.Bool(false),
})
if err != nil {
dbExists = false
log.Errorf("[AWS] Failed to disable DeletionProtection on db instance: %s in stack: %s, err: %s", dbInstanceId, stackName, err)
if strings.Contains(err.Error(), "DB instance not found") {
return dbExists, nil
return false, nil
}
log.Errorf("[AWS] Failed to disable DeletionProtection on db instance: %s in stack: %s, err: %s", dbInstanceId, stackName, err)
}
return dbExists, err
return true, err
}

/**
Expand Down Expand Up @@ -791,6 +802,25 @@ func deleteVpcWithDependencies(ec2Client ec2Client, stackName string, vpcId stri
return nil
}

func disableElbDeleteProtection(elbClient elbClient, stackName string, elbArn string, region string) (elbExists bool, err error) {
log.Infof("[AWS] Disabling DeletionProtection for ELB: %s in stack: %s, region: %s", elbArn, stackName, region)

deletionProtection := &elb.LoadBalancerAttribute{}
deletionProtection.SetKey("deletion_protection.enabled")
deletionProtection.SetValue("false")
_, err = elbClient.ModifyLoadBalancerAttributes(&elb.ModifyLoadBalancerAttributesInput{
LoadBalancerArn: aws.String(elbArn),
Attributes: []*elb.LoadBalancerAttribute{deletionProtection},
})
if err != nil {
log.Errorf("[AWS] Failed to disable DeletionProtection on ELB: %s in stack: %s, err: %s", elbArn, stackName, err)
if strings.Contains(err.Error(), "ELB not found") {
return false, nil
}
}
return true, err
}

func deleteLoadBalancer(elbClient elbClient, stackName string, elbArn string, region string) error {
log.Infof("[AWS] Deleting load balancer: %s in stack: %s, region: %s", elbArn, stackName, region)

Expand Down Expand Up @@ -1008,6 +1038,7 @@ type elbClient interface {
DescribeLoadBalancers(input *elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error)
DescribeTags(input *elb.DescribeTagsInput) (*elb.DescribeTagsOutput, error)
DeleteLoadBalancer(input *elb.DeleteLoadBalancerInput) (*elb.DeleteLoadBalancerOutput, error)
ModifyLoadBalancerAttributes(input *elb.ModifyLoadBalancerAttributesInput) (*elb.ModifyLoadBalancerAttributesOutput, error)
}

type cloudWatchClient interface {
Expand Down
10 changes: 9 additions & 1 deletion aws/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func TestRemoveCfStack(t *testing.T) {
assert.Equal(t, "DescribeSecurityGroups", <-operationChannel)
assert.Equal(t, "DeleteSecurityGroup:custom-group-id", <-operationChannel)
assert.Equal(t, "DeleteVpc", <-operationChannel)
assert.Equal(t, "ModifyLoadBalancerAttributes:elb-arn", <-operationChannel)
assert.Equal(t, "DeleteLoadBalancer:elb-arn", <-operationChannel)
assert.Equal(t, "DeleteStack", <-operationChannel)
assert.Equal(t, "WaitUntilStackDeleteComplete", <-operationChannel)
Expand Down Expand Up @@ -260,10 +261,12 @@ func TestRemoveNativeStack(t *testing.T) {
asyncOperations = append(asyncOperations, op)
}

assert.Equal(t, 6, len(asyncOperations))
assert.Equal(t, 8, len(asyncOperations))
assert.Contains(t, asyncOperations, "DeleteVolume:vol")
assert.Contains(t, asyncOperations, "DeleteSecurityGroup:sg")
assert.Contains(t, asyncOperations, "ModifyLoadBalancerAttributes:lb-1")
assert.Contains(t, asyncOperations, "DeleteLoadBalancer:lb-1")
assert.Contains(t, asyncOperations, "ModifyLoadBalancerAttributes:lb-2")
assert.Contains(t, asyncOperations, "DeleteLoadBalancer:lb-2")
assert.Contains(t, asyncOperations, "ReleaseAddress:ip")
assert.Contains(t, asyncOperations, "DeleteAlarms:alarm")
Expand Down Expand Up @@ -640,6 +643,11 @@ func (t mockElbClient) DeleteLoadBalancer(input *elb.DeleteLoadBalancerInput) (*
return nil, nil
}

func (t mockElbClient) ModifyLoadBalancerAttributes(input *elb.ModifyLoadBalancerAttributesInput) (*elb.ModifyLoadBalancerAttributesOutput, error) {
t.operationChannel <- "ModifyLoadBalancerAttributes:" + *input.LoadBalancerArn
return nil, nil
}

type mockCwClient struct {
operationChannel chan (string)
}
Expand Down

0 comments on commit 5b550db

Please sign in to comment.