Repository navigation
Expand file tree
/
Copy pathlambda_capacity_role_usage_test.go
More file actions
120 lines (117 loc) · 4.84 KB
/
Copy pathlambda_capacity_role_usage_test.go
File metadata and controls
120 lines (117 loc) · 4.84 KB
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
package stackd
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"path/filepath"
"slices"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials"
sdkiam "github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/aws/smithy-go"
"stackd/internal/awsctx"
"stackd/internal/services/iam"
"stackd/internal/services/lambda"
"stackd/storage/sqlite"
sqliam "stackd/storage/sqlite/iam"
sqllambda "stackd/storage/sqlite/lambda"
)
func TestLambdaCapacityRoleDeletionUsesSharedWritableAuthority(t *testing.T) {
db, err := sqlite.Open(t.Context(), filepath.Join(t.TempDir(), "capacity-role.sqlite"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := db.Close(); err != nil {
t.Error(err)
}
})
repository := sqllambda.New(db)
functions := lambda.New(lambda.Config{Repository: repository})
roles := iam.NewWithRepository(nil, sqliam.New(db))
t.Cleanup(func() {
if err := roles.Close(); err != nil {
t.Error(err)
}
if err := functions.Close(); err != nil {
t.Error(err)
}
})
if err := registerLambdaCapacityRoleUsage(roles, functions); err != nil {
t.Fatal(err)
}
scope := lambda.Scope{Partition: "aws", Account: "111111111111", Region: "us-east-1"}
key := lambda.CapacityProviderKey{Scope: scope, Name: "retained-provider"}
if err := repository.Update(t.Context(), func(tx lambda.Transaction) error {
return tx.PutCapacityProvider(lambda.CapacityProviderRecord{Key: key, Generation: "retained-generation", State: "Active", Architecture: "x86_64", ScalingMode: "Manual", MaxVCPUs: 6, Modified: time.Now().UTC()})
}); err != nil {
t.Fatal(err)
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
metadata := awsctx.Metadata{Partition: scope.Partition, AccountID: scope.Account, Region: scope.Region, AccessKeyID: "test", RequestID: "lambda-capacity-role-usage", PrincipalARN: "arn:aws:iam::" + scope.Account + ":root", PrincipalID: scope.Account}
roles.ServeHTTP(w, r.WithContext(awsctx.WithMetadata(r.Context(), metadata)))
}))
t.Cleanup(server.Close)
client := sdkiam.New(sdkiam.Options{Region: scope.Region, BaseEndpoint: aws.String(server.URL), Credentials: credentials.NewStaticCredentialsProvider("test", "test", ""), Retryer: aws.NopRetryer{}})
created, err := client.CreateServiceLinkedRole(t.Context(), &sdkiam.CreateServiceLinkedRoleInput{AWSServiceName: aws.String("lambda.amazonaws.com")})
if err != nil {
t.Fatal(err)
}
waitStatus := func(id string, want types.DeletionTaskStatusType) *sdkiam.GetServiceLinkedRoleDeletionStatusOutput {
t.Helper()
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
tick := time.NewTicker(time.Millisecond)
defer tick.Stop()
for {
status, err := client.GetServiceLinkedRoleDeletionStatus(ctx, &sdkiam.GetServiceLinkedRoleDeletionStatusInput{DeletionTaskId: aws.String(id)})
if err != nil {
t.Fatal(err)
}
if status.Status == want {
return status
}
if status.Status == types.DeletionTaskStatusTypeFailed || status.Status == types.DeletionTaskStatusTypeSucceeded {
t.Fatalf("linked-role terminal state=%s, want %s: %+v", status.Status, want, status.Reason)
}
select {
case <-ctx.Done():
t.Fatalf("linked-role task retained %s instead of %s", status.Status, want)
case <-tick.C:
}
}
}
first, err := client.DeleteServiceLinkedRole(t.Context(), &sdkiam.DeleteServiceLinkedRoleInput{RoleName: created.Role.RoleName})
if err != nil {
t.Fatal(err)
}
failed := waitStatus(aws.ToString(first.DeletionTaskId), types.DeletionTaskStatusTypeFailed)
if failed.Reason == nil || len(failed.Reason.RoleUsageList) != 1 {
t.Fatalf("provider dependency missing: %+v", failed.Reason)
}
usage := failed.Reason.RoleUsageList[0]
if aws.ToString(usage.Region) != scope.Region || !slices.Equal(usage.Resources, []string{key.ARN()}) {
t.Fatalf("wrong provider dependency: %+v", usage)
}
retained, err := client.GetRole(t.Context(), &sdkiam.GetRoleInput{RoleName: created.Role.RoleName})
if err != nil || aws.ToString(retained.Role.RoleId) != aws.ToString(created.Role.RoleId) {
t.Fatalf("failed deletion changed linked role: %+v %v", retained, err)
}
if err := repository.Update(t.Context(), func(tx lambda.Transaction) error { return tx.DeleteCapacityProvider(key) }); err != nil {
t.Fatal(err)
}
second, err := client.DeleteServiceLinkedRole(t.Context(), &sdkiam.DeleteServiceLinkedRoleInput{RoleName: created.Role.RoleName})
if err != nil {
t.Fatal(err)
}
waitStatus(aws.ToString(second.DeletionTaskId), types.DeletionTaskStatusTypeSucceeded)
_, err = client.GetRole(t.Context(), &sdkiam.GetRoleInput{RoleName: created.Role.RoleName})
var missing smithy.APIError
if !errors.As(err, &missing) || missing.ErrorCode() != "NoSuchEntity" {
t.Fatalf("completed deletion retained linked role: %v", err)
}
}