Skip to content

Commit 2e57557

Browse files
authored
Support Tag and Untag APIs for User resource. (#45)
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.
1 parent 0cd29f3 commit 2e57557

File tree

8 files changed

+258
-7
lines changed

8 files changed

+258
-7
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
ack_generate_info:
2-
build_date: "2022-12-23T06:27:19Z"
2+
build_date: "2022-12-23T17:00:07Z"
33
build_hash: 16f0e201b37a06b535370cc69e11adb934a22d33
44
go_version: go1.19
55
version: v0.20.1-18-g16f0e20
66
api_directory_checksum: a1e396caca4bdd1612fa7d09f0ee56f3e4976ff7
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.44.93
99
generator_config_info:
10-
file_checksum: 62ca61f60f6152f6c53f31b6ebbe43b39f86d225
10+
file_checksum: f50534e33903e1ca74491595fd6c21351988eb1c
1111
original_file_name: generator.yaml
1212
last_modification:
1313
reason: API generation

apis/v1alpha1/generator.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ resources:
165165
hooks:
166166
sdk_create_post_set_output:
167167
code: "rm.setAnnotationsFields(desired, ko)"
168+
sdk_read_many_post_set_output:
169+
template_path: hooks/user/sdk_read_many_post_set_output.go.tpl
168170
sdk_update_post_set_output:
169171
code: "rm.setAnnotationsFields(desired, ko)"
170172
sdk_update_pre_build_request:

generator.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ resources:
165165
hooks:
166166
sdk_create_post_set_output:
167167
code: "rm.setAnnotationsFields(desired, ko)"
168+
sdk_read_many_post_set_output:
169+
template_path: hooks/user/sdk_read_many_post_set_output.go.tpl
168170
sdk_update_post_set_output:
169171
code: "rm.setAnnotationsFields(desired, ko)"
170172
sdk_update_pre_build_request:

pkg/resource/user/hooks.go

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@
1414
package user
1515

1616
import (
17-
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
17+
"context"
1818
"github.com/pkg/errors"
1919

20+
svcapitypes "github.com/aws-controllers-k8s/memorydb-controller/apis/v1alpha1"
21+
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
2022
"github.com/aws-controllers-k8s/runtime/pkg/requeue"
23+
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
24+
ackutil "github.com/aws-controllers-k8s/runtime/pkg/util"
25+
26+
svcsdk "github.com/aws/aws-sdk-go/service/memorydb"
2127
)
2228

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

4955
return nil, nil
5056
}
57+
58+
// getTags gets tags from given ParameterGroup.
59+
func (rm *resourceManager) getTags(
60+
ctx context.Context,
61+
resourceARN string,
62+
) ([]*svcapitypes.Tag, error) {
63+
resp, err := rm.sdkapi.ListTagsWithContext(
64+
ctx,
65+
&svcsdk.ListTagsInput{
66+
ResourceArn: &resourceARN,
67+
},
68+
)
69+
rm.metrics.RecordAPICall("GET", "ListTags", err)
70+
if err != nil {
71+
return nil, err
72+
}
73+
tags := make([]*svcapitypes.Tag, 0, len(resp.TagList))
74+
for _, tag := range resp.TagList {
75+
tags = append(tags, &svcapitypes.Tag{
76+
Key: tag.Key,
77+
Value: tag.Value,
78+
})
79+
}
80+
return tags, nil
81+
}
82+
83+
// updateTags updates tags of given ParameterGroup to desired tags.
84+
func (rm *resourceManager) updateTags(
85+
ctx context.Context,
86+
desired *resource,
87+
latest *resource,
88+
) (err error) {
89+
rlog := ackrtlog.FromContext(ctx)
90+
exit := rlog.Trace("rm.updateTags")
91+
defer func(err error) { exit(err) }(err)
92+
93+
arn := (*string)(latest.ko.Status.ACKResourceMetadata.ARN)
94+
95+
toAdd, toDelete := computeTagsDelta(
96+
desired.ko.Spec.Tags, latest.ko.Spec.Tags,
97+
)
98+
99+
if len(toDelete) > 0 {
100+
rlog.Debug("removing tags from user", "tags", toDelete)
101+
_, err = rm.sdkapi.UntagResourceWithContext(
102+
ctx,
103+
&svcsdk.UntagResourceInput{
104+
ResourceArn: arn,
105+
TagKeys: toDelete,
106+
},
107+
)
108+
rm.metrics.RecordAPICall("UPDATE", "UntagResource", err)
109+
if err != nil {
110+
return err
111+
}
112+
}
113+
114+
if len(toAdd) > 0 {
115+
rlog.Debug("adding tags to user", "tags", toAdd)
116+
_, err = rm.sdkapi.TagResourceWithContext(
117+
ctx,
118+
&svcsdk.TagResourceInput{
119+
ResourceArn: arn,
120+
Tags: sdkTagsFromResourceTags(toAdd),
121+
},
122+
)
123+
rm.metrics.RecordAPICall("UPDATE", "TagResource", err)
124+
if err != nil {
125+
return err
126+
}
127+
}
128+
129+
return nil
130+
}
131+
132+
func computeTagsDelta(
133+
desired []*svcapitypes.Tag,
134+
latest []*svcapitypes.Tag,
135+
) (addedOrUpdated []*svcapitypes.Tag, removed []*string) {
136+
var visitedIndexes []string
137+
138+
for _, latestElement := range latest {
139+
visitedIndexes = append(visitedIndexes, *latestElement.Key)
140+
for _, desiredElement := range desired {
141+
if equalStrings(latestElement.Key, desiredElement.Key) {
142+
if !equalStrings(latestElement.Value, desiredElement.Value) {
143+
addedOrUpdated = append(addedOrUpdated, desiredElement)
144+
}
145+
continue
146+
}
147+
}
148+
removed = append(removed, latestElement.Key)
149+
}
150+
for _, desiredElement := range desired {
151+
if !ackutil.InStrings(*desiredElement.Key, visitedIndexes) {
152+
addedOrUpdated = append(addedOrUpdated, desiredElement)
153+
}
154+
}
155+
return addedOrUpdated, removed
156+
}
157+
158+
func sdkTagsFromResourceTags(
159+
rTags []*svcapitypes.Tag,
160+
) []*svcsdk.Tag {
161+
tags := make([]*svcsdk.Tag, len(rTags))
162+
for i := range rTags {
163+
tags[i] = &svcsdk.Tag{
164+
Key: rTags[i].Key,
165+
Value: rTags[i].Value,
166+
}
167+
}
168+
return tags
169+
}
170+
171+
func resourceTagsFromSDKTags(
172+
sdkTags []*svcsdk.Tag,
173+
) []*svcapitypes.Tag {
174+
tags := make([]*svcapitypes.Tag, len(sdkTags))
175+
for i := range sdkTags {
176+
tags[i] = &svcapitypes.Tag{
177+
Key: sdkTags[i].Key,
178+
Value: sdkTags[i].Value,
179+
}
180+
}
181+
return tags
182+
}
183+
184+
func equalStrings(a, b *string) bool {
185+
if a == nil {
186+
return b == nil || *b == ""
187+
}
188+
return (*a == "" && b == nil) || *a == *b
189+
}

pkg/resource/user/sdk.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
resourceARN := (*string)(ko.Status.ACKResourceMetadata.ARN)
2+
tags, err := rm.getTags(ctx, *resourceARN)
3+
if err != nil {
4+
return nil, err
5+
}
6+
ko.Spec.Tags = tags
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1-
res, err := rm.validateUserNeedsUpdate(desired, latest, delta)
1+
res, err := rm.validateUserNeedsUpdate(desired, latest, delta)
22

3-
if err != nil || res!= nil{
4-
return res, err
5-
}
3+
if err != nil || res!= nil{
4+
return res, err
5+
}
6+
7+
if delta.DifferentAt("Spec.Tags") {
8+
err = rm.updateTags(ctx, desired, latest)
9+
if err != nil {
10+
return nil, err
11+
}
12+
}
13+
14+
if !delta.DifferentExcept("Spec.Tags") {
15+
return desired, nil
16+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
id: "USER_UPDATE_WITH_TAGS"
2+
description: "In this test we create User and update its tags"
3+
#marks:
4+
# - slow
5+
# - blocked
6+
resource:
7+
apiVersion: $CRD_GROUP/$CRD_VERSION
8+
kind: User
9+
metadata:
10+
name: user$RANDOM_SUFFIX
11+
steps:
12+
- id: "USER_INITIAL_CREATE"
13+
description: "Create User"
14+
create:
15+
spec:
16+
name: user$RANDOM_SUFFIX
17+
accessString: on +get
18+
authenticationMode:
19+
type_: password
20+
passwords:
21+
- key: password
22+
name: $SECRET1
23+
wait:
24+
status:
25+
conditions:
26+
ACK.ResourceSynced:
27+
status: "True"
28+
timeout: 100
29+
- id: "USER_ADD_TAGS"
30+
description: "Add tags in User"
31+
patch:
32+
spec:
33+
tags:
34+
- key: "test_key_1"
35+
value: "test_value_1"
36+
- key: "test_key_2"
37+
- key:
38+
wait:
39+
status:
40+
conditions:
41+
ACK.ResourceSynced:
42+
status: "True"
43+
timeout: 100
44+
- id: "USER_DELETE_TAGS"
45+
description: "Delete tags in User"
46+
patch:
47+
spec:
48+
tags:
49+
- key: "test_key_1"
50+
value: "test_value_1"
51+
wait:
52+
status:
53+
conditions:
54+
ACK.ResourceSynced:
55+
status: "True"
56+
timeout: 100
57+
- id: "SG_ADD_AND_DELETE_TAGS"
58+
description: "Add some tags and delete some tags in User"
59+
patch:
60+
spec:
61+
tags:
62+
- key: "test_key_2"
63+
value: "test_value_2"
64+
wait:
65+
status:
66+
conditions:
67+
ACK.ResourceSynced:
68+
status: "True"
69+
timeout: 100
70+
- id: "DELETE_USER"
71+
description: "Delete User"
72+
delete: user$RANDOM_SUFFIX

0 commit comments

Comments
 (0)