/
clean.go
177 lines (155 loc) · 4.96 KB
/
clean.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package lambda
import (
"errors"
"fmt"
"log"
"os"
"regexp"
"time"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/lambda"
)
const Type = "lambda"
var logger = log.New(os.Stdout, fmt.Sprintf("[%s] ", Type), log.LstdFlags)
type Layer struct {
LayerARN *string
Version *int64
}
func Clean(sess *session.Session, expirationDate time.Time) error {
return errors.Join(cleanFunctions(sess, expirationDate), cleanLayers(sess, expirationDate))
}
func cleanFunctions(sess *session.Session, expirationDate time.Time) error {
logger.Printf("Begin to clean Lambda functions")
lambdaClient := lambda.New(sess)
var deleteFunctionIDs []*string
var nextToken *string
for {
listFunctionsInput := &lambda.ListFunctionsInput{Marker: nextToken}
listFunctionsOutput, err := lambdaClient.ListFunctions(listFunctionsInput)
if err != nil {
return fmt.Errorf("unable to retrieve functions: %w", err)
}
for _, lf := range listFunctionsOutput.Functions {
doesNameMatch, err := shouldDelete(lf)
if err != nil {
return fmt.Errorf("error during pattern match: %w", err)
}
created, err := time.Parse("2006-01-02T15:04:05Z0700", *lf.LastModified)
if err != nil {
return fmt.Errorf("error parsing last modified time: %w", err)
}
if expirationDate.After(created) && doesNameMatch {
logger.Printf("Try to delete function %s created-at %s", *lf.FunctionArn, *lf.LastModified)
deleteFunctionIDs = append(deleteFunctionIDs, lf.FunctionArn)
}
}
if listFunctionsOutput.NextMarker == nil {
break
}
nextToken = listFunctionsOutput.NextMarker
}
if len(deleteFunctionIDs) < 1 {
logger.Printf("No Lambda functions to delete")
return nil
}
for _, id := range deleteFunctionIDs {
terminateFunctionInput := &lambda.DeleteFunctionInput{FunctionName: id}
if _, err := lambdaClient.DeleteFunction(terminateFunctionInput); err != nil {
return fmt.Errorf("unable to delete function: %w", err)
}
logger.Printf("Deleted %d Lambda functions", len(deleteFunctionIDs))
}
return nil
}
func cleanLayers(sess *session.Session, expirationDate time.Time) error {
logger.Printf("Begin to clean Lambda layers")
lambdaClient := lambda.New(sess)
var deleteLayerVersions []Layer
var nextToken *string
for {
listLayerVersionsInput := &lambda.ListLayersInput{
Marker: nextToken,
}
// Retrieve layer versions from Lambda service
listLayerVersionsOutput, err := lambdaClient.ListLayers(listLayerVersionsInput)
if err != nil {
return fmt.Errorf("unable to retrieve layer versions: %w", err)
}
// Loop through retrieved layer versions
for _, layer := range listLayerVersionsOutput.Layers {
if shouldDeleteLayer(layer, expirationDate) {
logger.Printf("Try to delete layer version %s created-at %s", *layer.LayerArn, *layer.LatestMatchingVersion.CreatedDate)
deleteLayerVersions = append(deleteLayerVersions, Layer{layer.LayerArn, layer.LatestMatchingVersion.Version})
}
}
if listLayerVersionsOutput.NextMarker == nil {
break
}
nextToken = listLayerVersionsOutput.NextMarker
}
if len(deleteLayerVersions) < 1 {
logger.Printf("No Lambda layers to delete")
return nil
}
for _, id := range deleteLayerVersions {
for *id.Version > 0 {
// Prepare input for deleting a specific layer version
deleteLayerVersionInput := &lambda.DeleteLayerVersionInput{
LayerName: id.LayerARN,
VersionNumber: id.Version,
}
if _, err := lambdaClient.DeleteLayerVersion(deleteLayerVersionInput); err != nil {
return fmt.Errorf("unable to delete layer version: %w", err)
}
// Decrement the version number for the next iteration
*id.Version--
}
logger.Printf("Deleted %d Lambda layer versions", len(deleteLayerVersions))
}
return nil
}
func shouldDelete(lf *lambda.FunctionConfiguration) (bool, error) {
lfName := lf.FunctionName
regexList := []string{
"\\Alambda-[a-z]+-aws-sdk-.*$",
"\\Ahello-lambda-[a-z]+-okhttp-.*",
"\\Alambda-[a-z]+-okhttp-wrapper-.*$",
}
for _, rx := range regexList {
matches, err := regexp.MatchString(rx, *lfName)
if err != nil {
return false, fmt.Errorf("error during regex match: %w", err)
}
if matches {
return true, nil
}
}
return false, nil
}
func shouldDeleteLayer(layerList *lambda.LayersListItem, expirationDate time.Time) bool {
layerARN := layerList.LayerArn
regexList := []string{
".*:layer:aws-otel-collector.*$",
".*:layer:aws-otel-java.*$",
".*:layer:aws-otel-lambda.*$",
".*:layer:aws-otel-nodejs.*$",
".*:layer:aws-otel-go-wrapper.*$",
".*:layer:opentelemetry.*$",
".*:layer:aws-observability.*$",
".*:layer:aws-distro-for-opentelemetry.*$",
}
for _, rx := range regexList {
matched, _ := regexp.MatchString(rx, *layerARN)
if matched {
created, err := time.Parse("2006-01-02T15:04:05Z0700", *layerList.LatestMatchingVersion.CreatedDate)
if err != nil {
logger.Printf("Error Parsing the created time for layer %s", err)
return false
}
if expirationDate.After(created) {
return true
}
}
}
return false
}