Skip to content

Commit 01f27ee

Browse files
committed
feat(clean): reap leaked AMP integ-test workspaces
Integration tests create one AMP (Amazon Managed Service for Prometheus) workspace per /amp run (alias cwagent-integ-test-<id>) and destroy it via terraform. Workspaces left behind by cancelled or hard-killed jobs, whose terraform state dies with the ephemeral runner, accumulate to the per-region 75-workspace quota. Once at the cap every new run fails at CreateWorkspace with ServiceQuotaExceededException before the Go test binary runs (observed on the al2:selinux_amp_test job). The existing if:cancelled()||failure() terraform destroy step cannot reclaim them because their state is gone. Add tool/clean/clean_amp/clean_amp.go (modeled on clean_eks): list AMP workspaces and delete ACTIVE ones whose alias has the cwagent-integ-test- prefix and were created longer ago than KeepDurationOneDay. Supports -dry-run. Add a clean-amp-workspaces job to clean-aws-resources.yml using the same OIDC role and daily schedule as the other cleaners. Adding service/amp pulled an aligned aws-sdk-go-v2 core bump in the isolated tool/clean module. Verified: go build and go vet are clean for clean_amp; a dry-run against the test account matches an independent audit (targets only cwagent-integ-test-* workspaces and spares non-test workspaces). Note: the clean OIDC role (vars.TERRAFORM_AWS_ASSUME_ROLE) needs amp:ListWorkspaces and amp:DeleteWorkspace permissions for the scheduled job.
1 parent 0c715d0 commit 01f27ee

4 files changed

Lines changed: 142 additions & 15 deletions

File tree

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,22 @@ jobs:
311311
set -e
312312
go run ./clean_security_group/clean_security_group.go || { echo "Failed to clean security groups"; exit 1; }
313313
314+
clean-amp-workspaces:
315+
runs-on: ubuntu-latest
316+
permissions:
317+
id-token: write
318+
contents: read
319+
steps:
320+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
321+
- uses: actions/setup-go@7b8cf10d4e4a01d4992d18a89f4d7dc5a3e6d6f4 # v4.3.0
322+
323+
- name: Configure AWS Credentials
324+
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1
325+
with:
326+
role-to-assume: ${{ vars.TERRAFORM_AWS_ASSUME_ROLE }}
327+
aws-region: us-west-2
328+
329+
- name: Clean old amp workspaces
330+
working-directory: tool/clean
331+
run: go run ./clean_amp/clean_amp.go
332+

tool/clean/clean_amp/clean_amp.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package main
5+
6+
import (
7+
"context"
8+
"flag"
9+
"log"
10+
"strings"
11+
"time"
12+
13+
"github.com/aws/aws-sdk-go-v2/aws"
14+
"github.com/aws/aws-sdk-go-v2/config"
15+
"github.com/aws/aws-sdk-go-v2/service/amp"
16+
"github.com/aws/aws-sdk-go-v2/service/amp/types"
17+
18+
"github.com/aws/amazon-cloudwatch-agent/tool/clean"
19+
)
20+
21+
// WorkspacesToClean lists the alias prefixes of AMP (Amazon Managed Service for
22+
// Prometheus) workspaces created by the integration-test harness. The harness sets
23+
// workspace_alias to "cwagent-integ-test-<testing_id>" in
24+
// terraform/ec2/linux/main.tf (module "amp").
25+
var WorkspacesToClean = []string{
26+
"cwagent-integ-test-",
27+
}
28+
29+
var dryRun bool
30+
31+
// Clean AMP workspaces created by the integration tests if they have been open
32+
// longer than one day. A test run creates a workspace, uses it for a few minutes,
33+
// then destroys it via terraform. Workspaces left behind by cancelled or
34+
// hard-killed jobs (whose terraform state is lost with the ephemeral runner)
35+
// accumulate to the per-region 75-workspace quota; once at the cap every new run
36+
// fails at CreateWorkspace with ServiceQuotaExceededException. The existing
37+
// terraform destroy cleanup step cannot reclaim these because their state is gone,
38+
// so this cleaner deletes them out-of-band via the AMP API.
39+
func main() {
40+
flag.BoolVar(&dryRun, "dry-run", false, "Enable dry-run mode (no actual deletion)")
41+
flag.Parse()
42+
43+
if err := cleanWorkspaces(); err != nil {
44+
log.Fatalf("errors cleaning %v", err)
45+
}
46+
}
47+
48+
func cleanWorkspaces() error {
49+
log.Print("Begin to clean AMP workspaces")
50+
ctx := context.Background()
51+
defaultConfig, err := config.LoadDefaultConfig(ctx)
52+
if err != nil {
53+
return err
54+
}
55+
client := amp.NewFromConfig(defaultConfig)
56+
return terminateWorkspaces(ctx, client)
57+
}
58+
59+
func aliasMatchesWorkspacesToClean(alias string, workspacesToClean []string) bool {
60+
for _, workspaceToClean := range workspacesToClean {
61+
if strings.HasPrefix(alias, workspaceToClean) {
62+
return true
63+
}
64+
}
65+
return false
66+
}
67+
68+
func terminateWorkspaces(ctx context.Context, client *amp.Client) error {
69+
expirationDate := time.Now().UTC().Add(clean.KeepDurationOneDay)
70+
71+
paginator := amp.NewListWorkspacesPaginator(client, &amp.ListWorkspacesInput{})
72+
for paginator.HasMorePages() {
73+
page, err := paginator.NextPage(ctx)
74+
if err != nil {
75+
return err
76+
}
77+
for _, workspace := range page.Workspaces {
78+
workspaceID := aws.ToString(workspace.WorkspaceId)
79+
alias := aws.ToString(workspace.Alias)
80+
81+
if workspace.Status == nil || workspace.Status.StatusCode != types.WorkspaceStatusCodeActive {
82+
log.Printf("Ignoring workspace %s (alias %q) since it is not ACTIVE", workspaceID, alias)
83+
continue
84+
}
85+
if workspace.CreatedAt == nil || !expirationDate.After(*workspace.CreatedAt) {
86+
log.Printf("Ignoring workspace %s (alias %q) with create-date %v since it was created in the last %s",
87+
workspaceID, alias, aws.ToTime(workspace.CreatedAt), clean.KeepDurationOneDay)
88+
continue
89+
}
90+
if !aliasMatchesWorkspacesToClean(alias, WorkspacesToClean) {
91+
log.Printf("Ignoring workspace %s since alias %q does not match any clean prefix", workspaceID, alias)
92+
continue
93+
}
94+
if dryRun {
95+
log.Printf("Dry-Run: would delete workspace %s (alias %q, create-date %v)",
96+
workspaceID, alias, aws.ToTime(workspace.CreatedAt))
97+
continue
98+
}
99+
log.Printf("Try to delete workspace %s (alias %q, create-date %v)",
100+
workspaceID, alias, aws.ToTime(workspace.CreatedAt))
101+
if _, err := client.DeleteWorkspace(ctx, &amp.DeleteWorkspaceInput{WorkspaceId: workspace.WorkspaceId}); err != nil {
102+
log.Printf("could not delete workspace %s err %v", workspaceID, err)
103+
}
104+
}
105+
}
106+
return nil
107+
}

tool/clean/go.mod

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
11
module github.com/aws/amazon-cloudwatch-agent/tool/clean
22

3-
go 1.22
4-
5-
toolchain go1.23.6
3+
go 1.24
64

75
require (
86
github.com/aws/aws-sdk-go v1.48.14
9-
github.com/aws/aws-sdk-go-v2 v1.36.2
7+
github.com/aws/aws-sdk-go-v2 v1.43.4
108
github.com/aws/aws-sdk-go-v2/config v1.25.12
9+
github.com/aws/aws-sdk-go-v2/service/amp v1.48.1
1110
github.com/aws/aws-sdk-go-v2/service/autoscaling v1.36.3
1211
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.14
1312
github.com/aws/aws-sdk-go-v2/service/ec2 v1.140.0
1413
github.com/aws/aws-sdk-go-v2/service/ecs v1.35.3
1514
github.com/aws/aws-sdk-go-v2/service/efs v1.26.3
1615
github.com/aws/aws-sdk-go-v2/service/eks v1.35.3
1716
github.com/aws/aws-sdk-go-v2/service/iam v1.28.3
18-
github.com/aws/smithy-go v1.22.2
17+
github.com/aws/smithy-go v1.27.6
1918
github.com/stretchr/testify v1.8.4
2019
)
2120

2221
require (
2322
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
2423
github.com/aws/aws-sdk-go-v2/credentials v1.16.10 // indirect
2524
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 // indirect
26-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 // indirect
27-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 // indirect
25+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.35 // indirect
26+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.35 // indirect
2827
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect
2928
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 // indirect
3029
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.8 // indirect

tool/clean/go.sum

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
github.com/aws/aws-sdk-go v1.48.14 h1:nVLrp+F84SG+xGiFMfe1TE6ZV6smF+42tuuNgYGV30s=
22
github.com/aws/aws-sdk-go v1.48.14/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
3-
github.com/aws/aws-sdk-go-v2 v1.36.2 h1:Ub6I4lq/71+tPb/atswvToaLGVMxKZvjYDVOWEExOcU=
4-
github.com/aws/aws-sdk-go-v2 v1.36.2/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
3+
github.com/aws/aws-sdk-go-v2 v1.43.4 h1:b9FTvbRwy+JCsfp2Wp6wV/KbOx3Aj7nkoFb2cRX0IhE=
4+
github.com/aws/aws-sdk-go-v2 v1.43.4/go.mod h1:70vwSy16txshwG+g55WkpgPKDIByzHI8ccBsOteo3bQ=
55
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
66
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
77
github.com/aws/aws-sdk-go-v2/config v1.25.12 h1:mF4cMuNh/2G+d19nWnm1vJ/ak0qK6SbqF0KtSX9pxu0=
@@ -10,12 +10,14 @@ github.com/aws/aws-sdk-go-v2/credentials v1.16.10 h1:VmRkuoKaGl2ZDNGkkRQgw80Hxj1
1010
github.com/aws/aws-sdk-go-v2/credentials v1.16.10/go.mod h1:WEn22lpd50buTs/TDqywytW5xQ2zPOMbYipIlqI6xXg=
1111
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 h1:FZVFahMyZle6WcogZCOxo6D/lkDA2lqKIn4/ueUmVXw=
1212
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9/go.mod h1:kjq7REMIkxdtcEC9/4BVXjOsNY5isz6jQbEgk6osRTU=
13-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 h1:knLyPMw3r3JsU8MFHWctE4/e2qWbPaxDYLlohPvnY8c=
14-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33/go.mod h1:EBp2HQ3f+XCB+5J+IoEbGhoV7CpJbnrsd4asNXmTL0A=
15-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 h1:K0+Ne08zqti8J9jwENxZ5NoUyBnaFDTu3apwQJWrwwA=
16-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33/go.mod h1:K97stwwzaWzmqxO8yLGHhClbVW1tC6VT1pDLk1pGrq4=
13+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.35 h1:kzVuGlatQtYinwBJEEyLAbggepCoavosiaHHX9+fD+c=
14+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.35/go.mod h1:0yLx0yEI+SfqeJMPvOtIEFoZbiQYXMGszBueiutQyaI=
15+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.35 h1:WK6CjihTuLisCjSKKbildJ79sGZZgbBz3iNa7VsKIhU=
16+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.35/go.mod h1:KYleN57luLoe97R7vTnx8PMcVrr9gAcRECtOjl91DNg=
1717
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw=
1818
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
19+
github.com/aws/aws-sdk-go-v2/service/amp v1.48.1 h1:TfAuwl0nIPXZGitfHDE/cDalB0F4ifkf1UIp2vFyRcY=
20+
github.com/aws/aws-sdk-go-v2/service/amp v1.48.1/go.mod h1:61MYCp543XUJM6lhxz9IBQHR5rTZFU5mAWva5IuStl4=
1921
github.com/aws/aws-sdk-go-v2/service/autoscaling v1.36.3 h1:16TRfDZhx5aX90VsvG0yJ5XNlDNHMVDj2DBpVMwDxzc=
2022
github.com/aws/aws-sdk-go-v2/service/autoscaling v1.36.3/go.mod h1:dJD5FZKnDClUVIcCwfu676Y72h4GytmMoZHzf1nTW8Q=
2123
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.14 h1:Xc90sglbEnAC1X4d4ui422Ppw0HWjyNoqGAE1Dq+Rcg=
@@ -40,8 +42,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.3 h1:CxAHBS0BWSUqI7qzXHc2ZpTe
4042
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.3/go.mod h1:7Lt5mjQ8x5rVdKqg+sKKDeuwoszDJIIPmkd8BVsEdS0=
4143
github.com/aws/aws-sdk-go-v2/service/sts v1.26.3 h1:KfREzajmHCSYjCaMRtdLr9boUMA7KPpoPApitPlbNeo=
4244
github.com/aws/aws-sdk-go-v2/service/sts v1.26.3/go.mod h1:7Ld9eTqocTvJqqJ5K/orbSDwmGcpRdlDiLjz2DO+SL8=
43-
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
44-
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
45+
github.com/aws/smithy-go v1.27.6 h1:0zjT8jgK3jbrTT7JJ3EE6JsMhX8JTrZ+f1sEndYDXrA=
46+
github.com/aws/smithy-go v1.27.6/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
4547
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4648
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4749
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

0 commit comments

Comments
 (0)