Skip to content

Commit 9849df2

Browse files
darkweaver87estahn
andauthored
feat: add custom tags to created ECR repositories (estahn#191)
This PR adds the ability to configure custom tags for created repositories. Co-authored-by: Enrico Stahn <enrico.stahn@gmail.com>
1 parent cc534c1 commit 9849df2

File tree

6 files changed

+130
-30
lines changed

6 files changed

+130
-30
lines changed

cmd/root.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ import (
2525
"context"
2626
"fmt"
2727
"net/http"
28+
"os"
2829
"os/signal"
2930
"syscall"
3031
"time"
3132

32-
"os"
33-
3433
"github.com/estahn/k8s-image-swapper/pkg/config"
3534
"github.com/estahn/k8s-image-swapper/pkg/registry"
3635
"github.com/estahn/k8s-image-swapper/pkg/secrets"
@@ -64,12 +63,14 @@ A mutating webhook for Kubernetes, pointing the images to a new location.`,
6463
//metricsRec := metrics.NewPrometheus(promReg)
6564
log.Trace().Interface("config", cfg).Msg("config")
6665

67-
rClient, err := registry.NewECRClient(cfg.Target.AWS.Region, cfg.Target.AWS.EcrDomain(), cfg.Target.AWS.AccountID, cfg.Target.AWS.Role, cfg.Target.AWS.AccessPolicy, cfg.Target.AWS.LifecyclePolicy)
66+
rClient, err := registry.NewECRClient(cfg.Target.AWS.Region, cfg.Target.AWS.EcrDomain(), cfg.Target.AWS.AccountID, cfg.Target.AWS.Role, cfg.Target.AWS.ECROptions.AccessPolicy, cfg.Target.AWS.ECROptions.LifecyclePolicy)
6867
if err != nil {
6968
log.Err(err).Msg("error connecting to registry client")
7069
os.Exit(1)
7170
}
7271

72+
rClient.SetRepositoryTags(cfg.Target.AWS.ECROptions.Tags)
73+
7374
imageSwapPolicy, err := types.ParseImageSwapPolicy(cfg.ImageSwapPolicy)
7475
if err != nil {
7576
log.Err(err)

docs/configuration.md

+18
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,21 @@ The AWS Account ID and Region is primarily used to construct the ECR domain `[AC
138138
accountId: 123456789
139139
region: ap-southeast-2
140140
```
141+
142+
### ECR Options
143+
144+
#### Tags
145+
146+
This provides a way to add custom tags to newly created repositories. This may be useful while looking at AWS costs.
147+
It's a slice of `Key` and `Value`.
148+
149+
!!! example
150+
```yaml
151+
target:
152+
type: aws
153+
aws:
154+
ecrOptions:
155+
tags:
156+
- name: cluster
157+
value: myCluster
158+
```

pkg/config/config.go

+27-5
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,33 @@ type Target struct {
5454
}
5555

5656
type AWS struct {
57-
AccountID string `yaml:"accountId"`
58-
Region string `yaml:"region"`
59-
Role string `yaml:"role"`
60-
AccessPolicy string `yaml:"accessPolicy"`
61-
LifecyclePolicy string `yaml:"lifecyclePolicy"`
57+
AccountID string `yaml:"accountId"`
58+
Region string `yaml:"region"`
59+
Role string `yaml:"role"`
60+
ECROptions ECROptions `yaml:"ecrOptions"`
61+
}
62+
63+
type ECROptions struct {
64+
AccessPolicy string `yaml:"accessPolicy"`
65+
LifecyclePolicy string `yaml:"lifecyclePolicy"`
66+
Tags []Tag `yaml:"tags"`
67+
ImageTagMutability string `yaml:"imageTagMutability"`
68+
ImageScanningConfiguration ImageScanningConfiguration `yaml:"imageScanningConfiguration"`
69+
EncryptionConfiguration EncryptionConfiguration `yaml:"encryptionConfiguration"`
70+
}
71+
72+
type Tag struct {
73+
Key string `yaml:"key"`
74+
Value string `yaml:"value"`
75+
}
76+
77+
type ImageScanningConfiguration struct {
78+
ImageScanOnPush bool `yaml:"imageScanOnPush"`
79+
}
80+
81+
type EncryptionConfiguration struct {
82+
EncryptionType string `yaml:"encryptionType"`
83+
KmsKey string `yaml:"kmsKey"`
6284
}
6385

6486
func (a *AWS) EcrDomain() string {

pkg/config/config_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,44 @@ source:
3737
},
3838
},
3939
},
40+
{
41+
name: "should render tags config",
42+
cfg: `
43+
target:
44+
type: aws
45+
aws:
46+
accountId: 123456789
47+
region: ap-southeast-2
48+
role: arn:aws:iam::123456789012:role/roleName
49+
ecrOptions:
50+
tags:
51+
- key: CreatedBy
52+
value: k8s-image-swapper
53+
- key: A
54+
value: B
55+
`,
56+
expCfg: Config{
57+
Target: Target{
58+
AWS: AWS{
59+
AccountID: "123456789",
60+
Region: "ap-southeast-2",
61+
Role: "arn:aws:iam::123456789012:role/roleName",
62+
ECROptions: ECROptions{
63+
Tags: []Tag{
64+
{
65+
Key: "CreatedBy",
66+
Value: "k8s-image-swapper",
67+
},
68+
{
69+
Key: "A",
70+
Value: "B",
71+
},
72+
},
73+
},
74+
},
75+
},
76+
},
77+
},
4078
}
4179

4280
for _, test := range tests {

pkg/registry/ecr.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/aws/aws-sdk-go/service/ecr"
1414
"github.com/aws/aws-sdk-go/service/ecr/ecriface"
1515
"github.com/dgraph-io/ristretto"
16+
"github.com/estahn/k8s-image-swapper/pkg/config"
1617
"github.com/go-co-op/gocron"
1718
"github.com/rs/zerolog/log"
1819
)
@@ -28,6 +29,7 @@ type ECRClient struct {
2829
targetAccount string
2930
accessPolicy string
3031
lifecyclePolicy string
32+
tags []config.Tag
3133
}
3234

3335
func (e *ECRClient) Credentials() string {
@@ -46,12 +48,7 @@ func (e *ECRClient) CreateRepository(name string) error {
4648
},
4749
ImageTagMutability: aws.String(ecr.ImageTagMutabilityMutable),
4850
RegistryId: &e.targetAccount,
49-
Tags: []*ecr.Tag{
50-
{
51-
Key: aws.String("CreatedBy"),
52-
Value: aws.String("k8s-image-swapper"),
53-
},
54-
},
51+
Tags: e.buildEcrTags(),
5552
})
5653

5754
if err != nil {
@@ -102,6 +99,21 @@ func (e *ECRClient) CreateRepository(name string) error {
10299
return nil
103100
}
104101

102+
func (e *ECRClient) SetRepositoryTags(tags []config.Tag) {
103+
e.tags = tags
104+
}
105+
106+
func (e *ECRClient) buildEcrTags() []*ecr.Tag {
107+
ecrTags := []*ecr.Tag{}
108+
109+
for _, t := range e.tags {
110+
tag := ecr.Tag{Key: &t.Key, Value: &t.Value}
111+
ecrTags = append(ecrTags, &tag)
112+
}
113+
114+
return ecrTags
115+
}
116+
105117
func (e *ECRClient) RepositoryExists() bool {
106118
panic("implement me")
107119
}
@@ -249,6 +261,7 @@ func NewMockECRClient(ecrClient ecriface.ECRAPI, region string, ecrDomain string
249261
scheduler: nil,
250262
targetAccount: targetAccount,
251263
authToken: []byte("mock-ecr-client-fake-auth-token"),
264+
tags: []config.Tag{{Key: "CreatedBy", Value: "k8s-image-swapper"}},
252265
}
253266

254267
return client, nil

pkg/webhook/image_swapper_test.go

+24-16
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,12 @@ func TestImageSwapper_Mutate(t *testing.T) {
246246
ImageTagMutability: aws.String("MUTABLE"),
247247
RepositoryName: aws.String("docker.io/library/init-container"),
248248
RegistryId: aws.String("123456789"),
249-
Tags: []*ecr.Tag{{
250-
Key: aws.String("CreatedBy"),
251-
Value: aws.String("k8s-image-swapper"),
252-
}},
249+
Tags: []*ecr.Tag{
250+
{
251+
Key: aws.String("CreatedBy"),
252+
Value: aws.String("k8s-image-swapper"),
253+
},
254+
},
253255
}).Return(mock.Anything)
254256
ecrClient.On(
255257
"CreateRepository",
@@ -260,10 +262,12 @@ func TestImageSwapper_Mutate(t *testing.T) {
260262
ImageTagMutability: aws.String("MUTABLE"),
261263
RepositoryName: aws.String("docker.io/library/nginx"),
262264
RegistryId: aws.String("123456789"),
263-
Tags: []*ecr.Tag{{
264-
Key: aws.String("CreatedBy"),
265-
Value: aws.String("k8s-image-swapper"),
266-
}},
265+
Tags: []*ecr.Tag{
266+
{
267+
Key: aws.String("CreatedBy"),
268+
Value: aws.String("k8s-image-swapper"),
269+
},
270+
},
267271
}).Return(mock.Anything)
268272
ecrClient.On(
269273
"CreateRepository",
@@ -274,10 +278,12 @@ func TestImageSwapper_Mutate(t *testing.T) {
274278
ImageTagMutability: aws.String("MUTABLE"),
275279
RepositoryName: aws.String("k8s.gcr.io/ingress-nginx/controller"),
276280
RegistryId: aws.String("123456789"),
277-
Tags: []*ecr.Tag{{
278-
Key: aws.String("CreatedBy"),
279-
Value: aws.String("k8s-image-swapper"),
280-
}},
281+
Tags: []*ecr.Tag{
282+
{
283+
Key: aws.String("CreatedBy"),
284+
Value: aws.String("k8s-image-swapper"),
285+
},
286+
},
281287
}).Return(mock.Anything)
282288

283289
registryClient, _ := registry.NewMockECRClient(ecrClient, "ap-southeast-2", "123456789.dkr.ecr.ap-southeast-2.amazonaws.com", "123456789", "arn:aws:iam::123456789:role/fakerole")
@@ -328,10 +334,12 @@ func TestImageSwapper_MutateWithImagePullSecrets(t *testing.T) {
328334
ImageTagMutability: aws.String("MUTABLE"),
329335
RegistryId: aws.String("123456789"),
330336
RepositoryName: aws.String("docker.io/library/nginx"),
331-
Tags: []*ecr.Tag{{
332-
Key: aws.String("CreatedBy"),
333-
Value: aws.String("k8s-image-swapper"),
334-
}},
337+
Tags: []*ecr.Tag{
338+
{
339+
Key: aws.String("CreatedBy"),
340+
Value: aws.String("k8s-image-swapper"),
341+
},
342+
},
335343
}).Return(mock.Anything)
336344

337345
registryClient, _ := registry.NewMockECRClient(ecrClient, "ap-southeast-2", "123456789.dkr.ecr.ap-southeast-2.amazonaws.com", "123456789", "arn:aws:iam::123456789:role/fakerole")

0 commit comments

Comments
 (0)