Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Tag and Untag APIs for User resource. #45

Merged
merged 1 commit into from
Dec 23, 2022
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
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"
Comment on lines +21 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github.com/aws-controllers-k8s/runtime are external packages


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