-
Notifications
You must be signed in to change notification settings - Fork 9
/
ecr.go
122 lines (98 loc) · 3.77 KB
/
ecr.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
// Copyright 2022 Namespace Labs Inc; All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package ecr
import (
"context"
"errors"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/aws/aws-sdk-go-v2/service/ecr/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
"namespacelabs.dev/foundation/internal/artifacts/oci"
"namespacelabs.dev/foundation/internal/artifacts/registry"
"namespacelabs.dev/foundation/internal/compute"
"namespacelabs.dev/foundation/internal/fnerrors"
awsprovider "namespacelabs.dev/foundation/internal/providers/aws"
"namespacelabs.dev/foundation/std/cfg"
"namespacelabs.dev/foundation/std/tasks"
)
type ecrManager struct {
sesh *awsprovider.Session
}
var _ registry.Manager = ecrManager{}
func Register() {
registry.Register("aws/ecr", func(ctx context.Context, ck cfg.Configuration) (registry.Manager, error) {
sesh, err := awsprovider.MustConfiguredSession(ctx, ck)
if err != nil {
return nil, err
}
return ecrManager{sesh: sesh}, nil
})
oci.RegisterDomainKeychain("amazonaws.com", DefaultKeychain, oci.Keychain_UseAlways)
}
func packageURL(repo, packageName string) string {
return fmt.Sprintf("%s/%s", repo, packageName)
}
func repoURL(sesh aws.Config, caller *sts.GetCallerIdentityOutput) string {
return fmt.Sprintf("%s.dkr.ecr.%s.amazonaws.com", *caller.Account, sesh.Region)
}
func (em ecrManager) Access() oci.RegistryAccess {
return oci.RegistryAccess{
InsecureRegistry: false,
Keychain: keychainSession(em),
}
}
func (em ecrManager) AllocateName(repository, tag string) compute.Computable[oci.RepositoryWithParent] {
keychain := keychainSession(em)
var repo compute.Computable[string] = &makeRepository{
sesh: em.sesh,
callerIdentity: keychain.resolveAccount(),
repository: repository,
}
return compute.Map(tasks.Action("ecr.allocate-repository").Category("aws"),
compute.Inputs().Str("repository", repository).Computable("repo", repo),
compute.Output{},
func(ctx context.Context, deps compute.Resolved) (oci.RepositoryWithParent, error) {
repository := compute.MustGetDepValue(deps, repo, "repo")
tasks.Attachments(ctx).AddResult("repository", repository)
return oci.RepositoryWithParent{
Parent: em,
RepositoryWithAccess: oci.RepositoryWithAccess{
RegistryAccess: em.Access(),
Repository: repository,
UserTag: tag,
},
}, nil
},
)
}
type makeRepository struct {
sesh *awsprovider.Session
callerIdentity compute.Computable[*sts.GetCallerIdentityOutput]
repository string
compute.DoScoped[string] // Can share results within a graph invocation.
}
func (m *makeRepository) Action() *tasks.ActionEvent {
return tasks.Action("ecr.ensure-repository").Category("aws").Arg("repository", m.repository)
}
func (m *makeRepository) Inputs() *compute.In {
return compute.Inputs().Computable("caller", m.callerIdentity).Str("packageName", m.repository).Str("cacheKey", m.sesh.CacheKey())
}
func (m *makeRepository) Compute(ctx context.Context, deps compute.Resolved) (string, error) {
caller := compute.MustGetDepValue(deps, m.callerIdentity, "caller")
req := &ecr.CreateRepositoryInput{
RepositoryName: aws.String(m.repository),
ImageTagMutability: types.ImageTagMutabilityImmutable,
}
if _, err := ecr.NewFromConfig(m.sesh.Config()).CreateRepository(ctx, req); err != nil {
var e *types.RepositoryAlreadyExistsException
if errors.As(err, &e) {
// If the repository already exists, that's all good.
} else {
return "", fnerrors.InvocationError("aws/ecr", "%s: failed to create ECR repository: %w", m.repository, err)
}
}
return packageURL(repoURL(m.sesh.Config(), caller), m.repository), nil
}