From 7c2d0b524402810e973a57521e36c706809534f2 Mon Sep 17 00:00:00 2001 From: Vince Prignano Date: Tue, 10 Oct 2023 16:22:25 -0700 Subject: [PATCH] Allow garbage collector to delete ec2 instances Signed-off-by: Vince Prignano --- api/v1beta2/types.go | 3 +++ pkg/cloud/services/gc/cleanup.go | 1 + pkg/cloud/services/gc/ec2.go | 39 ++++++++++++++++++++++++++++++++ pkg/cloud/services/gc/service.go | 1 + 4 files changed, 44 insertions(+) diff --git a/api/v1beta2/types.go b/api/v1beta2/types.go index 9e77923bbd..b643d2dc36 100644 --- a/api/v1beta2/types.go +++ b/api/v1beta2/types.go @@ -83,6 +83,9 @@ const ( type GCTask string var ( + // GCTaskEC2Instance defines a task to cleaning up resources for AWS EC2 instances. + GCTaskEC2Instance = GCTask("instance") + // GCTaskLoadBalancer defines a task to cleaning up resources for AWS load balancers. GCTaskLoadBalancer = GCTask("load-balancer") diff --git a/pkg/cloud/services/gc/cleanup.go b/pkg/cloud/services/gc/cleanup.go index 27fe88600f..6b0af27dd1 100644 --- a/pkg/cloud/services/gc/cleanup.go +++ b/pkg/cloud/services/gc/cleanup.go @@ -72,6 +72,7 @@ func (s *Service) deleteResources(ctx context.Context) error { if val, found := annotations.Get(s.scope.InfraCluster(), infrav1.ExternalResourceGCTasksAnnotation); found { var gcTaskToFunc = map[infrav1.GCTask]ResourceCleanupFunc{ + infrav1.GCTaskEC2Instance: s.deleteEC2Instances, infrav1.GCTaskLoadBalancer: s.deleteLoadBalancers, infrav1.GCTaskTargetGroup: s.deleteTargetGroups, infrav1.GCTaskSecurityGroup: s.deleteSecurityGroups, diff --git a/pkg/cloud/services/gc/ec2.go b/pkg/cloud/services/gc/ec2.go index 92e224ff81..878b3c0df2 100644 --- a/pkg/cloud/services/gc/ec2.go +++ b/pkg/cloud/services/gc/ec2.go @@ -28,6 +28,45 @@ import ( "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/filter" ) +func (s *Service) deleteEC2Instances(ctx context.Context, resources []*AWSResource) error { + for _, resource := range resources { + if !s.isEC2InstanceToDelete(resource) { + s.scope.Debug("Resource not an EC2 instance for deletion", "arn", resource.ARN.String()) + continue + } + + instanceID := strings.ReplaceAll(resource.ARN.Resource, "instance/", "") + if err := s.deleteEC2Instance(ctx, instanceID); err != nil { + return fmt.Errorf("deleting EC2 instance %s: %w", instanceID, err) + } + } + s.scope.Debug("Finished processing resources for EC2 instance deletion") + + return nil +} + +func (s *Service) isEC2InstanceToDelete(resource *AWSResource) bool { + if !s.isMatchingResource(resource, ec2.ServiceName, "instance") { + return false + } + if eksClusterName := resource.Tags[eksClusterNameTag]; eksClusterName != "" { + s.scope.Debug("EC2 instance was created by EKS directly", "arn", resource.ARN.String(), "check", "instance", "cluster_name", eksClusterName) + return false + } + s.scope.Debug("Resource is an EC2 instance to delete", "arn", resource.ARN.String(), "check", "instance") + return true +} + +func (s *Service) deleteEC2Instance(ctx context.Context, instanceID string) error { + input := ec2.TerminateInstancesInput{ + InstanceIds: []*string{aws.String(instanceID)}, + } + if _, err := s.ec2Client.TerminateInstancesWithContext(ctx, &input); err != nil { + return fmt.Errorf("terminating EC2 instance: %w", err) + } + return nil +} + func (s *Service) deleteSecurityGroups(ctx context.Context, resources []*AWSResource) error { for _, resource := range resources { if !s.isSecurityGroupToDelete(resource) { diff --git a/pkg/cloud/services/gc/service.go b/pkg/cloud/services/gc/service.go index 9eb9f789a6..174fc827ea 100644 --- a/pkg/cloud/services/gc/service.go +++ b/pkg/cloud/services/gc/service.go @@ -62,6 +62,7 @@ func NewService(clusterScope cloud.ClusterScoper, opts ...ServiceOption) *Servic func addDefaultCleanupFuncs(s *Service) { s.cleanupFuncs = []ResourceCleanupFunc{ + s.deleteEC2Instances, s.deleteLoadBalancers, s.deleteTargetGroups, s.deleteSecurityGroups,