Skip to content

Commit 64b31fb

Browse files
authored
Add Lambda Layer Version cleaner (#2611)
* Initial commit to add layer cleaner * Print the layer ARNs for visibility and use ListLayersInput * Use Regex, type Pair for layer and version * Iterate on versions for that layer to delete every layer version * Modify variables usage * Remove extra fmt * Add comments for context * Fix fmt and comments * Add functionality to shouldDeleteLayer * Add regex as appropriate * Make it more generalized to match more * Remove NodeProxyAgentLayerand custom-config-layer form regex list
1 parent ad9327f commit 64b31fb

File tree

1 file changed

+106
-11
lines changed

1 file changed

+106
-11
lines changed

tools/workflow/cleaner/lambda/clean.go

+106-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lambda
22

33
import (
4+
"errors"
45
"fmt"
56
"log"
67
"os"
@@ -15,48 +16,114 @@ const Type = "lambda"
1516

1617
var logger = log.New(os.Stdout, fmt.Sprintf("[%s] ", Type), log.LstdFlags)
1718

19+
type Layer struct {
20+
LayerARN *string
21+
Version *int64
22+
}
23+
1824
func Clean(sess *session.Session, expirationDate time.Time) error {
19-
logger.Printf("Begin to clean Lambda functions")
25+
return errors.Join(cleanFunctions(sess, expirationDate), cleanLayers(sess, expirationDate))
26+
}
2027

21-
lambdaclient := lambda.New(sess)
28+
func cleanFunctions(sess *session.Session, expirationDate time.Time) error {
29+
logger.Printf("Begin to clean Lambda functions")
30+
lambdaClient := lambda.New(sess)
2231
var deleteFunctionIDs []*string
2332
var nextToken *string
33+
2434
for {
25-
ListFunctionsInput := &lambda.ListFunctionsInput{Marker: nextToken}
26-
ListFunctionsOutput, err := lambdaclient.ListFunctions(ListFunctionsInput)
35+
listFunctionsInput := &lambda.ListFunctionsInput{Marker: nextToken}
36+
listFunctionsOutput, err := lambdaClient.ListFunctions(listFunctionsInput)
2737
if err != nil {
28-
return fmt.Errorf("unable to retrieve APIs: %w", err)
38+
return fmt.Errorf("unable to retrieve functions: %w", err)
2939
}
30-
for _, lf := range ListFunctionsOutput.Functions {
40+
41+
for _, lf := range listFunctionsOutput.Functions {
3142
doesNameMatch, err := shouldDelete(lf)
3243
if err != nil {
3344
return fmt.Errorf("error during pattern match: %w", err)
3445
}
3546
created, err := time.Parse("2006-01-02T15:04:05Z0700", *lf.LastModified)
3647
if err != nil {
37-
return fmt.Errorf("error parting last modified time: %w", err)
48+
return fmt.Errorf("error parsing last modified time: %w", err)
3849
}
3950
if expirationDate.After(created) && doesNameMatch {
4051
logger.Printf("Try to delete function %s created-at %s", *lf.FunctionArn, *lf.LastModified)
4152
deleteFunctionIDs = append(deleteFunctionIDs, lf.FunctionArn)
4253
}
4354
}
44-
if ListFunctionsOutput.NextMarker == nil {
55+
if listFunctionsOutput.NextMarker == nil {
4556
break
4657
}
47-
nextToken = ListFunctionsOutput.NextMarker
58+
nextToken = listFunctionsOutput.NextMarker
4859
}
4960
if len(deleteFunctionIDs) < 1 {
5061
logger.Printf("No Lambda functions to delete")
5162
return nil
5263
}
53-
5464
for _, id := range deleteFunctionIDs {
5565
terminateFunctionInput := &lambda.DeleteFunctionInput{FunctionName: id}
56-
if _, err := lambdaclient.DeleteFunction(terminateFunctionInput); err != nil {
66+
if _, err := lambdaClient.DeleteFunction(terminateFunctionInput); err != nil {
5767
return fmt.Errorf("unable to delete function: %w", err)
5868
}
69+
logger.Printf("Deleted %d Lambda functions", len(deleteFunctionIDs))
70+
}
71+
72+
return nil
73+
}
74+
75+
func cleanLayers(sess *session.Session, expirationDate time.Time) error {
76+
logger.Printf("Begin to clean Lambda layers")
77+
lambdaClient := lambda.New(sess)
78+
var deleteLayerVersions []Layer
79+
var nextToken *string
80+
81+
for {
82+
listLayerVersionsInput := &lambda.ListLayersInput{
83+
Marker: nextToken,
84+
}
85+
86+
// Retrieve layer versions from Lambda service
87+
listLayerVersionsOutput, err := lambdaClient.ListLayers(listLayerVersionsInput)
88+
if err != nil {
89+
return fmt.Errorf("unable to retrieve layer versions: %w", err)
90+
}
91+
92+
// Loop through retrieved layer versions
93+
for _, layer := range listLayerVersionsOutput.Layers {
94+
95+
if shouldDeleteLayer(layer, expirationDate) {
96+
logger.Printf("Try to delete layer version %s created-at %s", *layer.LayerArn, *layer.LatestMatchingVersion.CreatedDate)
97+
deleteLayerVersions = append(deleteLayerVersions, Layer{layer.LayerArn, layer.LatestMatchingVersion.Version})
98+
}
99+
}
100+
101+
if listLayerVersionsOutput.NextMarker == nil {
102+
break
103+
}
104+
nextToken = listLayerVersionsOutput.NextMarker
105+
}
106+
107+
if len(deleteLayerVersions) < 1 {
108+
logger.Printf("No Lambda layers to delete")
109+
return nil
110+
}
111+
for _, id := range deleteLayerVersions {
112+
for *id.Version > 0 {
113+
// Prepare input for deleting a specific layer version
114+
deleteLayerVersionInput := &lambda.DeleteLayerVersionInput{
115+
LayerName: id.LayerARN,
116+
VersionNumber: id.Version,
117+
}
118+
if _, err := lambdaClient.DeleteLayerVersion(deleteLayerVersionInput); err != nil {
119+
return fmt.Errorf("unable to delete layer version: %w", err)
120+
}
121+
// Decrement the version number for the next iteration
122+
*id.Version--
123+
}
124+
logger.Printf("Deleted %d Lambda layer versions", len(deleteLayerVersions))
59125
}
126+
60127
return nil
61128
}
62129

@@ -79,3 +146,31 @@ func shouldDelete(lf *lambda.FunctionConfiguration) (bool, error) {
79146
}
80147
return false, nil
81148
}
149+
150+
func shouldDeleteLayer(layerList *lambda.LayersListItem, expirationDate time.Time) bool {
151+
layerARN := layerList.LayerArn
152+
regexList := []string{
153+
".*:layer:aws-otel-collector.*$",
154+
".*:layer:aws-otel-lambda-python.*$",
155+
".*:layer:aws-otel-java.*$",
156+
".*:layer:aws-otel-lambda-nodejs.*$",
157+
".*:layer:aws-otel-go-wrapper.*$",
158+
".*:layer:aws-observability.*$",
159+
".*:layer:aws-distro-for-opentelemetry.*$",
160+
}
161+
162+
for _, rx := range regexList {
163+
matched, _ := regexp.MatchString(rx, *layerARN)
164+
if matched {
165+
created, err := time.Parse("2006-01-02T15:04:05Z0700", *layerList.LatestMatchingVersion.CreatedDate)
166+
if err != nil {
167+
logger.Printf("Error Parsing the created time for layer %s", err)
168+
return false
169+
}
170+
if expirationDate.After(created) {
171+
return true
172+
}
173+
}
174+
}
175+
return false
176+
}

0 commit comments

Comments
 (0)