Skip to content

Commit c25778c

Browse files
committed
refactor: cleanup functions filter resource
The the cleanup functions now accepted the full list of AWS resources and then they filter which resources they want to delete themselves. Signed-off-by: Richard Case <[email protected]>
1 parent 00729b6 commit c25778c

File tree

5 files changed

+191
-136
lines changed

5 files changed

+191
-136
lines changed

pkg/cloud/services/gc/cleanup.go

+25-25
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"strconv"
23+
"strings"
2324

2425
"github.com/aws/aws-sdk-go/aws"
2526
"github.com/aws/aws-sdk-go/aws/arn"
@@ -51,13 +52,13 @@ func (s *Service) ReconcileDelete(ctx context.Context) error {
5152
return fmt.Errorf("converting value %s of annotation %s to bool: %w", val, expinfrav1.ExternalResourceGCAnnotation, err)
5253
}
5354

54-
if shouldGC {
55-
if err := s.deleteResources(ctx); err != nil {
56-
return fmt.Errorf("deleting workload services of type load balancer: %w", err)
57-
}
55+
if !shouldGC {
56+
s.scope.Info("cluster opted-out of garbage collection")
57+
58+
return nil
5859
}
5960

60-
return nil
61+
return s.deleteResources(ctx)
6162
}
6263

6364
func (s *Service) deleteResources(ctx context.Context) error {
@@ -79,7 +80,7 @@ func (s *Service) deleteResources(ctx context.Context) error {
7980
return fmt.Errorf("getting tagged resources: %w", err)
8081
}
8182

82-
resources := map[string][]*rgapi.ResourceTagMapping{}
83+
resources := []*AWSResource{}
8384

8485
for i := range awsOutput.ResourceTagMappingList {
8586
mapping := awsOutput.ResourceTagMappingList[i]
@@ -88,34 +89,33 @@ func (s *Service) deleteResources(ctx context.Context) error {
8889
return fmt.Errorf("parsing resource arn %s: %w", *mapping.ResourceARN, err)
8990
}
9091

91-
_, found := s.cleanupFuncs[parsedArn.Service]
92-
if !found {
93-
s.scope.V(2).Info("skipping clean-up of tagged resource for service", "service", parsedArn.Service, "arn", mapping.ResourceARN)
94-
95-
continue
92+
tags := map[string]string{}
93+
for _, rgTag := range mapping.Tags {
94+
tags[*rgTag.Key] = *rgTag.Value
9695
}
9796

98-
resources[parsedArn.Service] = append(resources[parsedArn.Service], mapping)
97+
resources = append(resources, &AWSResource{
98+
ARN: &parsedArn,
99+
Tags: tags,
100+
})
99101
}
100102

101-
for svcName, svcResources := range resources {
102-
cleanupFunc := s.cleanupFuncs[svcName]
103-
104-
s.scope.V(2).Info("Calling clean-up function for service", "service_name", svcName)
105-
if deleteErr := cleanupFunc(ctx, svcResources); deleteErr != nil {
106-
return fmt.Errorf("deleting resources for service %s: %w", svcName, deleteErr)
107-
}
103+
if deleteErr := s.cleanupFuncs.Execute(ctx, resources); deleteErr != nil {
104+
return fmt.Errorf("deleting resources: %w", deleteErr)
108105
}
109106

110107
return nil
111108
}
112109

113-
func getTagValue(tagName string, mapping *rgapi.ResourceTagMapping) string {
114-
for _, tag := range mapping.Tags {
115-
if *tag.Key == tagName {
116-
return *tag.Value
117-
}
110+
func (s *Service) isMatchingResource(resource *AWSResource, serviceName, resourceName string) bool {
111+
if resource.ARN.Service != serviceName {
112+
s.scope.V(5).Info("Resource not for service", "arn", resource.ARN.String(), "service_name", serviceName, "resource_name", resourceName)
113+
return false
114+
}
115+
if !strings.HasPrefix(resource.ARN.Resource, resourceName+"/") {
116+
s.scope.V(5).Info("Resource type does not match", "arn", resource.ARN.String(), "service_name", serviceName, "resource_name", resourceName)
117+
return false
118118
}
119119

120-
return ""
120+
return true
121121
}

pkg/cloud/services/gc/cleanup_test.go

+59-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/aws/aws-sdk-go/aws"
2424
"github.com/aws/aws-sdk-go/aws/request"
25+
"github.com/aws/aws-sdk-go/service/ec2"
2526
"github.com/aws/aws-sdk-go/service/elb"
2627
"github.com/aws/aws-sdk-go/service/elbv2"
2728
rgapi "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
@@ -471,6 +472,19 @@ func TestReconcileDelete(t *testing.T) {
471472
},
472473
},
473474
},
475+
{
476+
ResourceARN: aws.String("arn:aws:ec2:eu-west-2:1234567890:security-group/sg-123456"),
477+
Tags: []*rgapi.Tag{
478+
{
479+
Key: aws.String("kubernetes.io/cluster/cluster1"),
480+
Value: aws.String("owned"),
481+
},
482+
{
483+
Key: aws.String(serviceNameTag),
484+
Value: aws.String("default/svc1"),
485+
},
486+
},
487+
},
474488
},
475489
}, nil
476490
})
@@ -485,7 +499,11 @@ func TestReconcileDelete(t *testing.T) {
485499
TargetGroupArn: aws.String("arn:aws:elasticloadbalancing:eu-west-2:1234567890:targetgroup/k8s-default-podinfo-2c868b281a/e979fe9bd6825433"),
486500
})
487501
},
488-
ec2Mocks: func(m *mocks.MockEC2APIMockRecorder) {},
502+
ec2Mocks: func(m *mocks.MockEC2APIMockRecorder) {
503+
m.DeleteSecurityGroupWithContext(gomock.Any(), &ec2.DeleteSecurityGroupInput{
504+
GroupId: aws.String("sg-123456"),
505+
})
506+
},
489507
expectErr: false,
490508
},
491509
{
@@ -528,6 +546,46 @@ func TestReconcileDelete(t *testing.T) {
528546
ec2Mocks: func(m *mocks.MockEC2APIMockRecorder) {},
529547
expectErr: false,
530548
},
549+
{
550+
name: "eks with security group created by EKS",
551+
clusterScope: createManageScope(t, ""),
552+
rgAPIMocks: func(m *mocks.MockResourceGroupsTaggingAPIAPIMockRecorder) {
553+
m.GetResourcesWithContext(gomock.Any(), &rgapi.GetResourcesInput{
554+
TagFilters: []*rgapi.TagFilter{
555+
{
556+
Key: aws.String("kubernetes.io/cluster/eks-test-cluster"),
557+
Values: []*string{aws.String("owned")},
558+
},
559+
},
560+
}).DoAndReturn(func(awsCtx context.Context, input *rgapi.GetResourcesInput, opts ...request.Option) (*rgapi.GetResourcesOutput, error) {
561+
return &rgapi.GetResourcesOutput{
562+
ResourceTagMappingList: []*rgapi.ResourceTagMapping{
563+
{
564+
ResourceARN: aws.String("arn:aws:ec2:eu-west-2:1234567890:security-group/sg-123456"),
565+
Tags: []*rgapi.Tag{
566+
{
567+
Key: aws.String("kubernetes.io/cluster/cluster1"),
568+
Value: aws.String("owned"),
569+
},
570+
{
571+
Key: aws.String(serviceNameTag),
572+
Value: aws.String("default/svc1"),
573+
},
574+
{
575+
Key: aws.String(eksClusterNameTag),
576+
Value: aws.String("default_eks_test_cluster"),
577+
},
578+
},
579+
},
580+
},
581+
}, nil
582+
})
583+
},
584+
elbMocks: func(m *mocks.MockELBAPIMockRecorder) {},
585+
elbv2Mocks: func(m *mocks.MockELBV2APIMockRecorder) {},
586+
ec2Mocks: func(m *mocks.MockEC2APIMockRecorder) {},
587+
expectErr: false,
588+
},
531589
}
532590

533591
for _, tc := range testCases {

pkg/cloud/services/gc/ec2.go

+23-26
Original file line numberDiff line numberDiff line change
@@ -22,49 +22,46 @@ import (
2222
"strings"
2323

2424
"github.com/aws/aws-sdk-go/aws"
25-
"github.com/aws/aws-sdk-go/aws/arn"
2625
"github.com/aws/aws-sdk-go/service/ec2"
27-
rgapi "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
2826
)
2927

30-
func (s *Service) deleteEC2Resources(ctx context.Context, resources []*rgapi.ResourceTagMapping) error {
31-
for i := range resources {
32-
res := resources[i]
33-
34-
parsedARN, err := arn.Parse(*res.ResourceARN)
35-
if err != nil {
36-
return fmt.Errorf("parsing arn %s: %w", *res.ResourceARN, err)
28+
func (s *Service) deleteSecurityGroups(ctx context.Context, resources []*AWSResource) error {
29+
for _, resource := range resources {
30+
if !s.isSecurityGroupToDelete(resource) {
31+
s.scope.V(5).Info("Resource not a security group for deletion", "arn", resource.ARN.String())
32+
continue
3733
}
3834

39-
if strings.HasPrefix(parsedARN.Resource, "security-group/") {
40-
s.scope.V(2).Info("Deleting Security group", "arn", parsedARN.String())
41-
return s.deleteSecurityGroup(ctx, &parsedARN, res)
35+
groupID := strings.ReplaceAll(resource.ARN.Resource, "security-group/", "")
36+
if err := s.deleteSecurityGroup(ctx, groupID); err != nil {
37+
return fmt.Errorf("deleting security group %s: %w", groupID, err)
4238
}
4339
}
44-
45-
s.scope.V(2).Info("Finished deleting ec2 resources")
40+
s.scope.V(2).Info("Finished processing resources for security group deletion")
4641

4742
return nil
4843
}
4944

50-
func (s *Service) deleteSecurityGroup(ctx context.Context, lbARN *arn.ARN, mapping *rgapi.ResourceTagMapping) error {
51-
eksClusterName := getTagValue(eksClusterNameTag, mapping)
52-
if eksClusterName != "" {
53-
s.scope.V(2).Info("Security group created by EKS directly, skipping deletion", "cluster_name", eksClusterName)
54-
55-
return nil
45+
func (s *Service) isSecurityGroupToDelete(resource *AWSResource) bool {
46+
if !s.isMatchingResource(resource, ec2.ServiceName, "security-group") {
47+
return false
5648
}
49+
if eksClusterName := resource.Tags[eksClusterNameTag]; eksClusterName != "" {
50+
s.scope.V(5).Info("Security group was created by EKS directly", "arn", resource.ARN.String(), "check", "securitygroup", "cluster_name", eksClusterName)
51+
return false
52+
}
53+
s.scope.V(5).Info("Resource is a security group to delete", "arn", resource.ARN.String(), "check", "securitygroup")
5754

58-
//TODO: should we check for the security group name start with k8s-elb-
55+
return true
56+
}
5957

60-
groupID := strings.ReplaceAll(lbARN.Resource, "security-group/", "")
58+
func (s *Service) deleteSecurityGroup(ctx context.Context, securityGroupID string) error {
6159
input := ec2.DeleteSecurityGroupInput{
62-
GroupId: aws.String(groupID),
60+
GroupId: aws.String(securityGroupID),
6361
}
6462

65-
s.scope.V(2).Info("Deleting security group", "group_id", groupID, "arn", lbARN.String())
66-
_, err := s.ec2Client.DeleteSecurityGroupWithContext(ctx, &input)
67-
if err != nil {
63+
s.scope.V(2).Info("Deleting security group", "group_id", securityGroupID)
64+
if _, err := s.ec2Client.DeleteSecurityGroupWithContext(ctx, &input); err != nil {
6865
return fmt.Errorf("deleting security group: %w", err)
6966
}
7067

0 commit comments

Comments
 (0)