Skip to content

Commit 75a6f4b

Browse files
authored
Auto Cleanup ECS inactive TaskDefs (#1800)
Co-authored-by: Akansha Agarwal <[email protected]>
1 parent a2fc23a commit 75a6f4b

File tree

2 files changed

+80
-6
lines changed

2 files changed

+80
-6
lines changed

.github/workflows/clean-aws-resources.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ jobs:
159159
working-directory: tool/clean
160160
run: go run ./clean_host/clean_host.go cn-north-1
161161

162-
clean-ecs-clusters:
162+
clean-ecs-resources:
163163
runs-on: ubuntu-latest
164164
permissions:
165165
id-token: write
@@ -174,9 +174,9 @@ jobs:
174174
role-to-assume: ${{ secrets.TERRAFORM_AWS_ASSUME_ROLE }}
175175
aws-region: us-west-2
176176

177-
- name: Clean old ecs cluster
177+
- name: Clean old ecs resources
178178
working-directory: tool/clean
179-
run: go run ./clean_ecs/clean_ecs.go --tags=clean
179+
run: go run --tags=clean ./clean_ecs/clean_ecs.go us-west-2
180180

181181
clean-eks-clusters:
182182
runs-on: ubuntu-latest

tool/clean/clean_ecs/clean_ecs.go

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ package main
99
import (
1010
"context"
1111
"log"
12+
"os"
1213
"strings"
1314
"time"
1415

1516
"github.com/aws/aws-sdk-go-v2/aws"
1617
"github.com/aws/aws-sdk-go-v2/config"
1718
"github.com/aws/aws-sdk-go-v2/service/ecs"
19+
"github.com/aws/aws-sdk-go-v2/service/ecs/types"
1820

1921
"github.com/aws/amazon-cloudwatch-agent/tool/clean"
2022
)
@@ -23,17 +25,20 @@ import (
2325

2426
var expirationTimeOneWeek = time.Now().UTC().Add(-clean.KeepDurationOneWeek)
2527

26-
const cwaIntegTestClusterPrefix = "cwagent-integ-test-cluster-"
28+
const clusterPrefix = "cwagent-integ-test-cluster-"
29+
30+
var taskdefPrefixes = []string{"cwagent-integ-test-", "extra-apps-family-", "cwagent-task-family-"}
2731

2832
func main() {
2933
ctx := context.Background()
30-
defaultConfig, err := config.LoadDefaultConfig(ctx)
34+
defaultConfig, err := config.LoadDefaultConfig(ctx, config.WithRegion(os.Args[1]))
3135
if err != nil {
3236
log.Fatalf("Error loading AWS config for ECS cleanup: %v", err)
3337
}
3438

3539
ecsClient := ecs.NewFromConfig(defaultConfig)
3640
terminateClusters(ctx, ecsClient)
41+
deleteInactiveTaskDefinitions(ctx, ecsClient)
3742
}
3843

3944
func terminateClusters(ctx context.Context, client *ecs.Client) {
@@ -65,7 +70,7 @@ func terminateClusters(ctx context.Context, client *ecs.Client) {
6570
*/
6671

6772
for _, cluster := range describeClustersOutput.Clusters {
68-
if !strings.HasPrefix(*cluster.ClusterName, cwaIntegTestClusterPrefix) {
73+
if !strings.HasPrefix(*cluster.ClusterName, clusterPrefix) {
6974
continue
7075
}
7176
if cluster.RunningTasksCount == 0 && cluster.PendingTasksCount == 0 {
@@ -86,6 +91,10 @@ func terminateClusters(ctx context.Context, client *ecs.Client) {
8691
ecsListClusterInput.NextToken = listClusterOutput.NextToken
8792
}
8893

94+
if len(clusterIds) == 0 {
95+
log.Print("No clusters to delete.")
96+
}
97+
8998
// Deletion Logic
9099
for _, clusterId := range clusterIds {
91100
log.Printf("Cluster to terminate: %s", *clusterId)
@@ -168,3 +177,68 @@ func isClusterTasksExpired(ctx context.Context, client *ecs.Client, clusterArn *
168177
}
169178
return true
170179
}
180+
181+
func deleteInactiveTaskDefinitions(ctx context.Context, client *ecs.Client) {
182+
log.Print("Begin cleanup of inactive task definitions")
183+
184+
taskDefsToDelete := getECSTaskDefsToDelete(ctx, client)
185+
186+
if len(taskDefsToDelete) == 0 {
187+
log.Printf("No inactive task definitions to delete")
188+
return
189+
}
190+
191+
log.Printf("Found %d inactive task definitions to delete", len(taskDefsToDelete))
192+
193+
// Batch delete task definitions (API supports up to 10 at a time)
194+
const batchSize = 10
195+
totalDeleted := 0
196+
197+
for i := 0; i < len(taskDefsToDelete); i += batchSize {
198+
end := min(i+batchSize, len(taskDefsToDelete))
199+
output, err := client.DeleteTaskDefinitions(ctx, &ecs.DeleteTaskDefinitionsInput{
200+
TaskDefinitions: taskDefsToDelete[i:end],
201+
})
202+
if err != nil {
203+
log.Printf("Error batch deleting task definitions: %v", err)
204+
continue
205+
}
206+
207+
totalDeleted += len(output.TaskDefinitions)
208+
for _, failure := range output.Failures {
209+
log.Printf("Failed to delete task definition %s: %s", *failure.Arn, *failure.Reason)
210+
}
211+
}
212+
213+
log.Printf("Successfully deleted %d task definitions", totalDeleted)
214+
}
215+
216+
func getECSTaskDefsToDelete(ctx context.Context, client *ecs.Client) []string {
217+
taskDefsToDelete := make([]string, 0)
218+
219+
for _, prefix := range taskdefPrefixes {
220+
// List inactive task definitions with integration test prefix
221+
listTaskDefsInput := ecs.ListTaskDefinitionsInput{
222+
FamilyPrefix: aws.String(prefix),
223+
Status: types.TaskDefinitionStatusInactive,
224+
}
225+
226+
for {
227+
listTaskDefsOutput, err := client.ListTaskDefinitions(ctx, &listTaskDefsInput)
228+
if err != nil {
229+
log.Printf("Error listing task definitions: %v", err)
230+
break
231+
}
232+
if len(listTaskDefsOutput.TaskDefinitionArns) == 0 {
233+
break
234+
}
235+
236+
taskDefsToDelete = append(taskDefsToDelete, listTaskDefsOutput.TaskDefinitionArns...)
237+
if listTaskDefsOutput.NextToken == nil {
238+
break
239+
}
240+
listTaskDefsInput.NextToken = listTaskDefsOutput.NextToken
241+
}
242+
}
243+
return taskDefsToDelete
244+
}

0 commit comments

Comments
 (0)