Skip to content

Commit

Permalink
Support Tag and Untag APIs for User resource. (#45)
Browse files Browse the repository at this point in the history
Issue #, if available:
Customers cannot update tags of User resource after creation.

Description of changes:
Add functions so that customers can update tags of User resource after creation.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
  • Loading branch information
kyriechen96 authored Dec 23, 2022
1 parent 0cd29f3 commit 2e57557
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 7 deletions.
4 changes: 2 additions & 2 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-12-23T06:27:19Z"
build_date: "2022-12-23T17:00:07Z"
build_hash: 16f0e201b37a06b535370cc69e11adb934a22d33
go_version: go1.19
version: v0.20.1-18-g16f0e20
api_directory_checksum: a1e396caca4bdd1612fa7d09f0ee56f3e4976ff7
api_version: v1alpha1
aws_sdk_go_version: v1.44.93
generator_config_info:
file_checksum: 62ca61f60f6152f6c53f31b6ebbe43b39f86d225
file_checksum: f50534e33903e1ca74491595fd6c21351988eb1c
original_file_name: generator.yaml
last_modification:
reason: API generation
2 changes: 2 additions & 0 deletions apis/v1alpha1/generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ resources:
hooks:
sdk_create_post_set_output:
code: "rm.setAnnotationsFields(desired, ko)"
sdk_read_many_post_set_output:
template_path: hooks/user/sdk_read_many_post_set_output.go.tpl
sdk_update_post_set_output:
code: "rm.setAnnotationsFields(desired, ko)"
sdk_update_pre_build_request:
Expand Down
2 changes: 2 additions & 0 deletions generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ resources:
hooks:
sdk_create_post_set_output:
code: "rm.setAnnotationsFields(desired, ko)"
sdk_read_many_post_set_output:
template_path: hooks/user/sdk_read_many_post_set_output.go.tpl
sdk_update_post_set_output:
code: "rm.setAnnotationsFields(desired, ko)"
sdk_update_pre_build_request:
Expand Down
141 changes: 140 additions & 1 deletion pkg/resource/user/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@
package user

import (
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
"context"
"github.com/pkg/errors"

svcapitypes "github.com/aws-controllers-k8s/memorydb-controller/apis/v1alpha1"
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
"github.com/aws-controllers-k8s/runtime/pkg/requeue"
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
ackutil "github.com/aws-controllers-k8s/runtime/pkg/util"

svcsdk "github.com/aws/aws-sdk-go/service/memorydb"
)

// validateUserNeedsUpdate this function's purpose is to requeue if the resource is currently unavailable and
Expand Down Expand Up @@ -48,3 +54,136 @@ func (rm *resourceManager) validateUserNeedsUpdate(

return nil, nil
}

// getTags gets tags from given ParameterGroup.
func (rm *resourceManager) getTags(
ctx context.Context,
resourceARN string,
) ([]*svcapitypes.Tag, error) {
resp, err := rm.sdkapi.ListTagsWithContext(
ctx,
&svcsdk.ListTagsInput{
ResourceArn: &resourceARN,
},
)
rm.metrics.RecordAPICall("GET", "ListTags", err)
if err != nil {
return nil, err
}
tags := make([]*svcapitypes.Tag, 0, len(resp.TagList))
for _, tag := range resp.TagList {
tags = append(tags, &svcapitypes.Tag{
Key: tag.Key,
Value: tag.Value,
})
}
return tags, nil
}

// updateTags updates tags of given ParameterGroup to desired tags.
func (rm *resourceManager) updateTags(
ctx context.Context,
desired *resource,
latest *resource,
) (err error) {
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.updateTags")
defer func(err error) { exit(err) }(err)

arn := (*string)(latest.ko.Status.ACKResourceMetadata.ARN)

toAdd, toDelete := computeTagsDelta(
desired.ko.Spec.Tags, latest.ko.Spec.Tags,
)

if len(toDelete) > 0 {
rlog.Debug("removing tags from user", "tags", toDelete)
_, err = rm.sdkapi.UntagResourceWithContext(
ctx,
&svcsdk.UntagResourceInput{
ResourceArn: arn,
TagKeys: toDelete,
},
)
rm.metrics.RecordAPICall("UPDATE", "UntagResource", err)
if err != nil {
return err
}
}

if len(toAdd) > 0 {
rlog.Debug("adding tags to user", "tags", toAdd)
_, err = rm.sdkapi.TagResourceWithContext(
ctx,
&svcsdk.TagResourceInput{
ResourceArn: arn,
Tags: sdkTagsFromResourceTags(toAdd),
},
)
rm.metrics.RecordAPICall("UPDATE", "TagResource", err)
if err != nil {
return err
}
}

return nil
}

func computeTagsDelta(
desired []*svcapitypes.Tag,
latest []*svcapitypes.Tag,
) (addedOrUpdated []*svcapitypes.Tag, removed []*string) {
var visitedIndexes []string

for _, latestElement := range latest {
visitedIndexes = append(visitedIndexes, *latestElement.Key)
for _, desiredElement := range desired {
if equalStrings(latestElement.Key, desiredElement.Key) {
if !equalStrings(latestElement.Value, desiredElement.Value) {
addedOrUpdated = append(addedOrUpdated, desiredElement)
}
continue
}
}
removed = append(removed, latestElement.Key)
}
for _, desiredElement := range desired {
if !ackutil.InStrings(*desiredElement.Key, visitedIndexes) {
addedOrUpdated = append(addedOrUpdated, desiredElement)
}
}
return addedOrUpdated, removed
}

func sdkTagsFromResourceTags(
rTags []*svcapitypes.Tag,
) []*svcsdk.Tag {
tags := make([]*svcsdk.Tag, len(rTags))
for i := range rTags {
tags[i] = &svcsdk.Tag{
Key: rTags[i].Key,
Value: rTags[i].Value,
}
}
return tags
}

func resourceTagsFromSDKTags(
sdkTags []*svcsdk.Tag,
) []*svcapitypes.Tag {
tags := make([]*svcapitypes.Tag, len(sdkTags))
for i := range sdkTags {
tags[i] = &svcapitypes.Tag{
Key: sdkTags[i].Key,
Value: sdkTags[i].Value,
}
}
return tags
}

func equalStrings(a, b *string) bool {
if a == nil {
return b == nil || *b == ""
}
return (*a == "" && b == nil) || *a == *b
}
19 changes: 19 additions & 0 deletions pkg/resource/user/sdk.go

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

6 changes: 6 additions & 0 deletions templates/hooks/user/sdk_read_many_post_set_output.go.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resourceARN := (*string)(ko.Status.ACKResourceMetadata.ARN)
tags, err := rm.getTags(ctx, *resourceARN)
if err != nil {
return nil, err
}
ko.Spec.Tags = tags
19 changes: 15 additions & 4 deletions templates/hooks/user/sdk_update_pre_build_request.go.tpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
res, err := rm.validateUserNeedsUpdate(desired, latest, delta)
res, err := rm.validateUserNeedsUpdate(desired, latest, delta)

if err != nil || res!= nil{
return res, err
}
if err != nil || res!= nil{
return res, err
}

if delta.DifferentAt("Spec.Tags") {
err = rm.updateTags(ctx, desired, latest)
if err != nil {
return nil, err
}
}

if !delta.DifferentExcept("Spec.Tags") {
return desired, nil
}
72 changes: 72 additions & 0 deletions test/e2e/scenarios/User/user_update_with_tags.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
id: "USER_UPDATE_WITH_TAGS"
description: "In this test we create User and update its tags"
#marks:
# - slow
# - blocked
resource:
apiVersion: $CRD_GROUP/$CRD_VERSION
kind: User
metadata:
name: user$RANDOM_SUFFIX
steps:
- id: "USER_INITIAL_CREATE"
description: "Create User"
create:
spec:
name: user$RANDOM_SUFFIX
accessString: on +get
authenticationMode:
type_: password
passwords:
- key: password
name: $SECRET1
wait:
status:
conditions:
ACK.ResourceSynced:
status: "True"
timeout: 100
- id: "USER_ADD_TAGS"
description: "Add tags in User"
patch:
spec:
tags:
- key: "test_key_1"
value: "test_value_1"
- key: "test_key_2"
- key:
wait:
status:
conditions:
ACK.ResourceSynced:
status: "True"
timeout: 100
- id: "USER_DELETE_TAGS"
description: "Delete tags in User"
patch:
spec:
tags:
- key: "test_key_1"
value: "test_value_1"
wait:
status:
conditions:
ACK.ResourceSynced:
status: "True"
timeout: 100
- id: "SG_ADD_AND_DELETE_TAGS"
description: "Add some tags and delete some tags in User"
patch:
spec:
tags:
- key: "test_key_2"
value: "test_value_2"
wait:
status:
conditions:
ACK.ResourceSynced:
status: "True"
timeout: 100
- id: "DELETE_USER"
description: "Delete User"
delete: user$RANDOM_SUFFIX

0 comments on commit 2e57557

Please sign in to comment.