Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions apis/v1alpha1/ack-generate-metadata.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
ack_generate_info:
build_date: "2022-04-15T17:17:20Z"
build_hash: 50c64871bcaf88b9ee200eb8d6b8245fa8f675eb
go_version: go1.17.5
version: v0.18.4
api_directory_checksum: deb6d526537cf2d0a956eb58ceeb430de3eddab5
build_date: "2022-04-20T20:24:26Z"
build_hash: ec6a51dc81164f0ee81c10e403bef9c00571384a
go_version: go1.17.6
version: v0.18.4-1-gec6a51d
api_directory_checksum: eb0e738ff4033343e16d5f30accb673360632793
api_version: v1alpha1
aws_sdk_go_version: v1.42.0
generator_config_info:
file_checksum: 23214d18e53be1516c6f0a3a2cc870e647e3c305
file_checksum: 0d4da9376313edcb86631a3fdfebc2db0f795a2d
original_file_name: generator.yaml
last_modification:
reason: API generation
4 changes: 4 additions & 0 deletions apis/v1alpha1/generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ resources:
from:
operation: PutLifecyclePolicy
path: LifecyclePolicyText
Policy:
from:
operation: SetRepositoryPolicy
path: PolicyText
Tags:
compare:
is_ignored: true
Expand Down
4 changes: 4 additions & 0 deletions apis/v1alpha1/repository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions config/crd/bases/ecr.services.k8s.aws_repositories.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ spec:
prepended with a namespace to group the repository into a category
(such as project-a/nginx-web-app).
type: string
policy:
description: The JSON repository policy text to apply to the repository.
For more information, see Amazon ECR repository policies (https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policy-examples.html)
in the Amazon Elastic Container Registry User Guide.
type: string
registryID:
description: The AWS account ID associated with the registry to create
the repository. If you do not specify a registry, the default registry
Expand Down
4 changes: 4 additions & 0 deletions generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ resources:
from:
operation: PutLifecyclePolicy
path: LifecyclePolicyText
Policy:
from:
operation: SetRepositoryPolicy
path: PolicyText
Tags:
compare:
is_ignored: true
Expand Down
5 changes: 5 additions & 0 deletions helm/crds/ecr.services.k8s.aws_repositories.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ spec:
prepended with a namespace to group the repository into a category
(such as project-a/nginx-web-app).
type: string
policy:
description: The JSON repository policy text to apply to the repository.
For more information, see Amazon ECR repository policies (https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policy-examples.html)
in the Amazon Elastic Container Registry User Guide.
type: string
registryID:
description: The AWS account ID associated with the registry to create
the repository. If you do not specify a registry, the default registry
Expand Down
70 changes: 65 additions & 5 deletions pkg/resource/repository/custom_update_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ func (rm *resourceManager) customUpdateRepository(
return nil, err
}
}
if delta.DifferentAt("Spec.Policy") {
updated, err = rm.updateRepositoryPolicy(ctx, updated)
if err != nil {
return nil, err
}
}
if delta.DifferentAt("Spec.Tags") {
err = rm.syncRepositoryTags(ctx, latest, desired)
if err != nil {
Expand Down Expand Up @@ -162,9 +168,9 @@ func (rm *resourceManager) updateLifecyclePolicy(
}

input := &svcsdk.PutLifecyclePolicyInput{
RepositoryName: aws.String(*dspec.Name),
RegistryId: aws.String(*dspec.RegistryID),
LifecyclePolicyText: aws.String(*dspec.LifecyclePolicy),
RepositoryName: dspec.Name,
RegistryId: dspec.RegistryID,
LifecyclePolicyText: dspec.LifecyclePolicy,
}

_, err = rm.sdkapi.PutLifecyclePolicyWithContext(ctx, input)
Expand All @@ -188,8 +194,8 @@ func (rm *resourceManager) deleteLifecyclePolicy(

dspec := desired.ko.Spec
input := &svcsdk.DeleteLifecyclePolicyInput{
RepositoryName: aws.String(*dspec.Name),
RegistryId: aws.String(*dspec.RegistryID),
RepositoryName: dspec.Name,
RegistryId: dspec.RegistryID,
}

_, err = rm.sdkapi.DeleteLifecyclePolicyWithContext(ctx, input)
Expand Down Expand Up @@ -244,3 +250,57 @@ func (rm *resourceManager) syncRepositoryTags(
}
return nil
}

// updateRepositoryPolicy updates the policy of a repository
func (rm *resourceManager) updateRepositoryPolicy(
ctx context.Context,
desired *resource,
) (*resource, error) {
var err error
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.updateRepositoryPolicy")
defer exit(err)

dspec := desired.ko.Spec

if dspec.Policy == nil || *dspec.Policy == "" {
return rm.deleteRepositoryPolicy(ctx, desired)
}

input := &svcsdk.SetRepositoryPolicyInput{
RepositoryName: dspec.Name,
RegistryId: dspec.RegistryID,
PolicyText: dspec.Policy,
}

_, err = rm.sdkapi.SetRepositoryPolicyWithContext(ctx, input)
rm.metrics.RecordAPICall("UPDATE", "SetRepositoryPolicy", err)
if err != nil {
return nil, err
}
return desired, nil
}

// deleteRepositoryPolicy deletes a repository policy
func (rm *resourceManager) deleteRepositoryPolicy(
ctx context.Context,
desired *resource,
) (*resource, error) {
var err error
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.deleteRepositoryPolicy")
defer exit(err)

dspec := desired.ko.Spec
input := &svcsdk.DeleteRepositoryPolicyInput{
RepositoryName: dspec.Name,
RegistryId: dspec.RegistryID,
}

_, err = rm.sdkapi.DeleteRepositoryPolicyWithContext(ctx, input)
rm.metrics.RecordAPICall("DELETE", "DeleteRepositoryPolicy", err)
if err != nil {
return nil, err
}
return desired, nil
}
7 changes: 7 additions & 0 deletions pkg/resource/repository/delta.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 77 additions & 26 deletions pkg/resource/repository/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
ackutil "github.com/aws-controllers-k8s/runtime/pkg/util"
svcsdk "github.com/aws/aws-sdk-go/service/ecr"

"github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1"
svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1"
)

Expand All @@ -23,35 +22,87 @@ func (rm *resourceManager) setResourceAdditionalFields(
exit := rlog.Trace("rm.setResourceAdditionalFields")
defer exit(err)

// Set repository policy
ko.Spec.Policy, err = rm.getRepositoryPolicy(ctx, *ko.Spec.Name, *ko.Spec.RegistryID)
if err != nil {
return err
}
// Set repository lifecycle policy
ko.Spec.LifecyclePolicy, err = rm.getRepositoryLifecyclePolicy(ctx, *ko.Spec.Name, *ko.Spec.RegistryID)
if err != nil {
return err
}
// Set repository tags
ko.Spec.Tags, err = rm.getRepositoryTags(ctx, string(*ko.Status.ACKResourceMetadata.ARN))
if err != nil {
return err
}

return nil
}

// getRepositoryPolicy retrieves a repository permissions policy.
func (rm *resourceManager) getRepositoryPolicy(
ctx context.Context,
repositoryName,
registryID string,
) (*string, error) {
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.getRepositoryPolicy")
var err error
defer exit(err)

var getRepositoryPolicyResponse *svcsdk.GetRepositoryPolicyOutput
getRepositoryPolicyResponse, err = rm.sdkapi.GetRepositoryPolicyWithContext(
ctx,
&svcsdk.GetRepositoryPolicyInput{
RepositoryName: &repositoryName,
RegistryId: &registryID,
},
)
rm.metrics.RecordAPICall("GET", "GetRepositoryPolicy", err)
if err != nil {
if awsErr, ok := ackerr.AWSError(err); !ok || awsErr.Code() != svcsdk.ErrCodeRepositoryPolicyNotFoundException {
return nil, err
}
// do not return an error if the repository policy is not found. Simply return an empty policy.
return nil, nil
}
return getRepositoryPolicyResponse.PolicyText, nil
}

// getRepositoryLifecyclePolicy retrieves a repository lifecycle policy.
func (rm *resourceManager) getRepositoryLifecyclePolicy(
ctx context.Context,
repositoryName,
registryID string,
) (*string, error) {
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.getRepositoryLifecyclePolicy")
var err error
defer exit(err)

var getLifecyclePolicyResponse *svcsdk.GetLifecyclePolicyOutput
getLifecyclePolicyResponse, err = rm.sdkapi.GetLifecyclePolicyWithContext(
ctx,
&svcsdk.GetLifecyclePolicyInput{
RepositoryName: ko.Spec.Name,
RegistryId: ko.Spec.RegistryID,
RepositoryName: &repositoryName,
RegistryId: &registryID,
},
)
rm.metrics.RecordAPICall("GET", "GetLifecyclePolicy", err)
if err != nil {
if awsErr, ok := ackerr.AWSError(err); !ok || awsErr.Code() != svcsdk.ErrCodeLifecyclePolicyNotFoundException {
return err
return nil, err
}
ko.Spec.LifecyclePolicy = nil
// do not return an error if the lifecycle policy is not found. Simply return an empty lifecycle policy.
return nil, nil
}
if getLifecyclePolicyResponse.LifecyclePolicyText != nil {
ko.Spec.LifecyclePolicy = getLifecyclePolicyResponse.LifecyclePolicyText
}

ko.Spec.Tags, err = rm.getRepositoryTags(ctx, string(*ko.Status.ACKResourceMetadata.ARN))
rm.metrics.RecordAPICall("GET", "ListTagsForResource", err)
if err != nil {
return err
}

return nil
return getLifecyclePolicyResponse.LifecyclePolicyText, nil
}

func (rm *resourceManager) getRepositoryTags(ctx context.Context, resourceARN string) ([]*v1alpha1.Tag, error) {
// getRepositoryTags retrieves a resource list of tags.
func (rm *resourceManager) getRepositoryTags(ctx context.Context, resourceARN string) ([]*svcapitypes.Tag, error) {
listTagsForResourceResponse, err := rm.sdkapi.ListTagsForResourceWithContext(
ctx,
&svcsdk.ListTagsForResourceInput{
Expand All @@ -62,9 +113,9 @@ func (rm *resourceManager) getRepositoryTags(ctx context.Context, resourceARN st
if err != nil {
return nil, err
}
tags := make([]*v1alpha1.Tag, 0, len(listTagsForResourceResponse.Tags))
tags := make([]*svcapitypes.Tag, 0, len(listTagsForResourceResponse.Tags))
for _, tag := range listTagsForResourceResponse.Tags {
tags = append(tags, &v1alpha1.Tag{
tags = append(tags, &svcapitypes.Tag{
Key: tag.Key,
Value: tag.Value,
})
Expand All @@ -89,8 +140,8 @@ func customPreCompare(
// equalTags returns true if two Tag arrays are equal regardless of the order
// of their elements.
func equalTags(
a []*v1alpha1.Tag,
b []*v1alpha1.Tag,
a []*svcapitypes.Tag,
b []*svcapitypes.Tag,
) bool {
added, updated, removed := computeTagsDelta(a, b)
return len(added) == 0 && len(updated) == 0 && len(removed) == 0
Expand All @@ -100,9 +151,9 @@ func equalTags(
// containing the added, updated and removed tags.
// The removed tags only contains the Key of tags
func computeTagsDelta(
a []*v1alpha1.Tag,
b []*v1alpha1.Tag,
) (added, updated []*v1alpha1.Tag, removed []*string) {
a []*svcapitypes.Tag,
b []*svcapitypes.Tag,
) (added, updated []*svcapitypes.Tag, removed []*string) {
var visitedIndexes []string
mainLoop:
for _, aElement := range a {
Expand All @@ -125,8 +176,8 @@ mainLoop:
return added, updated, removed
}

// svcTagsFromResourceTags transforms a *v1alpha1.Tag array to a *svcsdk.Tag array.
func sdkTagsFromResourceTags(rTags []*v1alpha1.Tag) []*svcsdk.Tag {
// svcTagsFromResourceTags transforms a *svcapitypes.Tag array to a *svcsdk.Tag array.
func sdkTagsFromResourceTags(rTags []*svcapitypes.Tag) []*svcsdk.Tag {
tags := make([]*svcsdk.Tag, len(rTags))
for i := range rTags {
tags[i] = &svcsdk.Tag{
Expand Down
7 changes: 7 additions & 0 deletions test/e2e/resources/repository_policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: ecr.services.k8s.aws/v1alpha1
kind: Repository
metadata:
name: $REPOSITORY_NAME
spec:
name: $REPOSITORY_NAME
policy: '$REPOSITORY_POLICY'
Loading