From 65b7ee65b27faf53b6f48fbe57fcd0e1f77e65a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=BCller?= Date: Wed, 4 Dec 2024 12:32:13 +0100 Subject: [PATCH] osbuild-service-maintenance: implement removal of launch templates Launch templates of instances that are terminated should be removed. HMS-3632 --- cmd/osbuild-service-maintenance/aws.go | 44 ++++++++++++++++++++++++++ internal/cloud/awscloud/maintenance.go | 30 ++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/cmd/osbuild-service-maintenance/aws.go b/cmd/osbuild-service-maintenance/aws.go index d5824a26c..25d6e4c8f 100644 --- a/cmd/osbuild-service-maintenance/aws.go +++ b/cmd/osbuild-service-maintenance/aws.go @@ -122,6 +122,14 @@ func AWSCleanup(maxConcurrentRequests int, dryRun bool, accessKeyID, accessKey s } } + errLaunchTemplates := searchLTAndCleanup(ctx, a, dryRun) + if errLaunchTemplates != nil { + logrus.Errorf("Error in cleaning up launch templates: %v", errLaunchTemplates) + if err != nil { + err = fmt.Errorf("Multiple errors while processing AWSCleanup: %w and %w.", err, errLaunchTemplates) + } + } + return err } @@ -268,3 +276,39 @@ func allTerminated(reservations []ec2types.Reservation) bool { return true } +func searchLTAndCleanup(ctx context.Context, a *awscloud.AWS, dryRun bool) error { + launchTemplates, err := a.DescribeLaunchTemplatesByPrefix(ctx, "launch-template-for-i-") + if err != nil { + return err + } + + for _, lt := range launchTemplates { + if lt.LaunchTemplateName == nil || lt.LaunchTemplateId == nil { + logrus.Errorf( + "Launch template needs to have a LaunchTemplateName (%v) and a LaunchTemplateId (%v).", + lt.LaunchTemplateName, + lt.LaunchTemplateId) + continue + } + + reservations, err := a.DescribeInstancesByLaunchTemplateID(*lt.LaunchTemplateId) + if err != nil { + logrus.Errorf("Failed to describe launch template %s: %v", *lt.LaunchTemplateId, err) + continue + } + + if allTerminated(reservations) { + logrus.Infof("Deleting launch template: %s (%s)\n", *lt.LaunchTemplateName, *lt.LaunchTemplateId) + if !dryRun { + err := a.DeleteLaunchTemplateById(ctx, lt.LaunchTemplateId) + + if err != nil { + logrus.Errorf("Failed to delete launch template %s: %v", *lt.LaunchTemplateId, err) + } + } + } else { + fmt.Printf("Launch template %s has non terminated instances associated with it.\n", *lt.LaunchTemplateId) + } + } + return nil +} diff --git a/internal/cloud/awscloud/maintenance.go b/internal/cloud/awscloud/maintenance.go index 3e36f0181..885d5d77b 100644 --- a/internal/cloud/awscloud/maintenance.go +++ b/internal/cloud/awscloud/maintenance.go @@ -93,6 +93,10 @@ func (a *AWS) DescribeInstancesBySecurityGroupID(securityGroupID string) ([]ec2t return a.describeInstancesByKeyValue("instance.group-id", securityGroupID) } +func (a *AWS) DescribeInstancesByLaunchTemplateID(launchTemplateID string) ([]ec2types.Reservation, error) { + return a.describeInstancesByKeyValue("tag:aws:ec2launchtemplate:id", launchTemplateID) +} + func (a *AWS) DescribeInstancesByInstanceID(instanceID string) ([]ec2types.Reservation, error) { res, err := a.ec2.DescribeInstances( context.Background(), @@ -132,6 +136,22 @@ func (a *AWS) DescribeSecurityGroupsByPrefix(ctx context.Context, prefix string) return securityGroups, nil } +func (a *AWS) DescribeLaunchTemplatesByPrefix(ctx context.Context, prefix string) ([]ec2types.LaunchTemplate, error) { + var launchTemplates []ec2types.LaunchTemplate + + ltOutput, err := a.ec2.DescribeLaunchTemplates(ctx, &ec2.DescribeLaunchTemplatesInput{}) + if err != nil { + return launchTemplates, fmt.Errorf("failed to describe security groups: %w", err) + } + + for _, lt := range ltOutput.LaunchTemplates { + if lt.LaunchTemplateName != nil && strings.HasPrefix(*lt.LaunchTemplateName, prefix) { + launchTemplates = append(launchTemplates, lt) + } + } + return launchTemplates, nil +} + func (a *AWS) DeleteSecurityGroupById(ctx context.Context, sgID *string) error { _, err := a.ec2.DeleteSecurityGroup( ctx, @@ -141,3 +161,13 @@ func (a *AWS) DeleteSecurityGroupById(ctx context.Context, sgID *string) error { ) return err } + +func (a *AWS) DeleteLaunchTemplateById(ctx context.Context, ltID *string) error { + _, err := a.ec2.DeleteLaunchTemplate( + ctx, + &ec2.DeleteLaunchTemplateInput{ + LaunchTemplateId: ltID, + }, + ) + return err +}