Skip to content

Commit c26d579

Browse files
Update tags support for DhcpOptions resource (#97)
Description of changes: - Included `update_operation` in `generator.yaml` file. - Implemented update tags support for **DhcpOptions** resource. - Added e2e tests for the same. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 99eedf9 commit c26d579

File tree

8 files changed

+251
-5
lines changed

8 files changed

+251
-5
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-09-14T19:52:51Z"
2+
build_date: "2022-09-15T15:45:52Z"
33
build_hash: 1da44f4be322eea156ed23a86e33c29da5cede9c
44
go_version: go1.18.3
55
version: v0.20.1-2-g1da44f4
66
api_directory_checksum: 127aa0f51fbcdde596b8f42f93bcdf3b6dee8488
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.44.93
99
generator_config_info:
10-
file_checksum: 54d118008c1b41e15473e323dbc940d79301b228
10+
file_checksum: 92993035f8ed34335b87f5ece6405b3d6d8d61eb
1111
original_file_name: generator.yaml
1212
last_modification:
1313
reason: API generation

apis/v1alpha1/generator.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ resources:
183183
hooks:
184184
sdk_create_post_build_request:
185185
template_path: hooks/dhcp_options/sdk_create_post_build_request.go.tpl
186+
sdk_file_end:
187+
template_path: hooks/dhcp_options/sdk_file_end.go.tpl
188+
update_operation:
189+
custom_method_name: customUpdateDhcpOptions
186190
Instance:
187191
fields:
188192
HibernationOptions:

generator.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ resources:
183183
hooks:
184184
sdk_create_post_build_request:
185185
template_path: hooks/dhcp_options/sdk_create_post_build_request.go.tpl
186+
sdk_file_end:
187+
template_path: hooks/dhcp_options/sdk_file_end.go.tpl
188+
update_operation:
189+
custom_method_name: customUpdateDhcpOptions
186190
Instance:
187191
fields:
188192
HibernationOptions:

pkg/resource/dhcp_options/hook.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,137 @@
1414
package dhcp_options
1515

1616
import (
17+
"context"
18+
19+
svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1"
20+
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
21+
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
1722
svcsdk "github.com/aws/aws-sdk-go/service/ec2"
1823
)
1924

25+
func (rm *resourceManager) customUpdateDhcpOptions(
26+
ctx context.Context,
27+
desired *resource,
28+
latest *resource,
29+
delta *ackcompare.Delta,
30+
) (updated *resource, err error) {
31+
rlog := ackrtlog.FromContext(ctx)
32+
exit := rlog.Trace("rm.customUpdateDhcpOptions")
33+
defer exit(err)
34+
35+
// Merge in the information we read from the API call above to the copy of
36+
// the original Kubernetes object we passed to the function
37+
ko := desired.ko.DeepCopy()
38+
39+
if delta.DifferentAt("Spec.Tags") {
40+
if err := rm.syncTags(ctx, desired, latest); err != nil {
41+
return nil, err
42+
}
43+
}
44+
45+
rm.setStatusDefaults(ko)
46+
return &resource{ko}, nil
47+
}
48+
49+
// syncTags used to keep tags in sync by calling Create and Delete API's
50+
func (rm *resourceManager) syncTags(
51+
ctx context.Context,
52+
desired *resource,
53+
latest *resource,
54+
) (err error) {
55+
rlog := ackrtlog.FromContext(ctx)
56+
exit := rlog.Trace("rm.syncTags")
57+
defer func(err error) {
58+
exit(err)
59+
}(err)
60+
61+
resourceId := []*string{latest.ko.Status.DHCPOptionsID}
62+
63+
toAdd, toDelete := computeTagsDelta(
64+
desired.ko.Spec.Tags, latest.ko.Spec.Tags,
65+
)
66+
67+
if len(toDelete) > 0 {
68+
rlog.Debug("removing tags from DhcpOptions resource", "tags", toDelete)
69+
_, err = rm.sdkapi.DeleteTagsWithContext(
70+
ctx,
71+
&svcsdk.DeleteTagsInput{
72+
Resources: resourceId,
73+
Tags: rm.sdkTags(toDelete),
74+
},
75+
)
76+
rm.metrics.RecordAPICall("UPDATE", "DeleteTags", err)
77+
if err != nil {
78+
return err
79+
}
80+
81+
}
82+
83+
if len(toAdd) > 0 {
84+
rlog.Debug("adding tags to DhcpOptions resource", "tags", toAdd)
85+
_, err = rm.sdkapi.CreateTagsWithContext(
86+
ctx,
87+
&svcsdk.CreateTagsInput{
88+
Resources: resourceId,
89+
Tags: rm.sdkTags(toAdd),
90+
},
91+
)
92+
rm.metrics.RecordAPICall("UPDATE", "CreateTags", err)
93+
if err != nil {
94+
return err
95+
}
96+
}
97+
98+
return nil
99+
}
100+
101+
// sdkTags converts *svcapitypes.Tag array to a *svcsdk.Tag array
102+
func (rm *resourceManager) sdkTags(
103+
tags []*svcapitypes.Tag,
104+
) (sdktags []*svcsdk.Tag) {
105+
106+
for _, i := range tags {
107+
sdktag := rm.newTag(*i)
108+
sdktags = append(sdktags, sdktag)
109+
}
110+
111+
return sdktags
112+
}
113+
114+
// computeTagsDelta returns tags to be added and removed from the resource
115+
func computeTagsDelta(
116+
desired []*svcapitypes.Tag,
117+
latest []*svcapitypes.Tag,
118+
) (toAdd []*svcapitypes.Tag, toDelete []*svcapitypes.Tag) {
119+
120+
desiredTags := map[string]string{}
121+
for _, tag := range desired {
122+
desiredTags[*tag.Key] = *tag.Value
123+
}
124+
125+
latestTags := map[string]string{}
126+
for _, tag := range latest {
127+
latestTags[*tag.Key] = *tag.Value
128+
}
129+
130+
for _, tag := range desired {
131+
val, ok := latestTags[*tag.Key]
132+
if !ok || val != *tag.Value {
133+
toAdd = append(toAdd, tag)
134+
}
135+
}
136+
137+
for _, tag := range latest {
138+
_, ok := desiredTags[*tag.Key]
139+
if !ok {
140+
toDelete = append(toDelete, tag)
141+
}
142+
}
143+
144+
return toAdd, toDelete
145+
146+
}
147+
20148
// updateTagSpecificationsInCreateRequest adds
21149
// Tags defined in the Spec to CreateDhcpOptionsInput.TagSpecification
22150
// and ensures the ResourceType is always set to 'dhcp-options'

pkg/resource/dhcp_options/sdk.go

Lines changed: 15 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{{ $CRD := .CRD }}
2+
{{ $SDKAPI := .SDKAPI }}
3+
4+
{{/* Generate helper methods for DhcpOptions */}}
5+
{{- range $specFieldName, $specField := $CRD.Config.Resources.DhcpOptions.Fields }}
6+
{{- if $specField.From }}
7+
{{- $operationName := $specField.From.Operation }}
8+
{{- $operation := (index $SDKAPI.API.Operations $operationName) -}}
9+
{{- range $dhcpOptionsRefName, $dhcpOptionsMemberRefs := $operation.InputRef.Shape.MemberRefs -}}
10+
{{- if eq $dhcpOptionsRefName "Tags" }}
11+
{{- $dhcpOptionsRef := $dhcpOptionsMemberRefs.Shape.MemberRef }}
12+
{{- $dhcpOptionsRefName = "Tag" }}
13+
func (rm *resourceManager) new{{ $dhcpOptionsRefName }}(
14+
c svcapitypes.{{ $dhcpOptionsRefName }},
15+
) *svcsdk.{{ $dhcpOptionsRefName }} {
16+
res := &svcsdk.{{ $dhcpOptionsRefName }}{}
17+
{{ GoCodeSetSDKForStruct $CRD "" "res" $dhcpOptionsRef "" "c" 1 }}
18+
return res
19+
}
20+
{{- end }}
21+
{{- end }}
22+
{{- end }}
23+
{{- end }}

test/e2e/resources/dhcp_options.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ spec:
1111
values:
1212
- $DHCP_VAL_2_1
1313
- $DHCP_VAL_2_2
14+
tags:
15+
- key: $TAG_KEY
16+
value: $TAG_VALUE

test/e2e/tests/test_dhcp_options.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
DEFAULT_WAIT_AFTER_SECONDS = 5
3030
CREATE_WAIT_AFTER_SECONDS = 10
3131
DELETE_WAIT_AFTER_SECONDS = 10
32+
MODIFY_WAIT_AFTER_SECONDS = 5
3233

3334
@pytest.fixture
3435
def simple_dhcp_options(request):
@@ -47,6 +48,10 @@ def simple_dhcp_options(request):
4748
data = marker.args[0]
4849
if 'dhcp_key_1' in data:
4950
replacements["DHCP_KEY_1"] = data['dhcp_key_1']
51+
if 'tag_key' in data:
52+
replacements["TAG_KEY"] = data['tag_key']
53+
if 'tag_value' in data:
54+
replacements["TAG_VALUE"] = data['tag_value']
5055

5156

5257
# Load DHCP Options CR
@@ -112,4 +117,70 @@ def test_terminal_condition_invalid_parameter_value(self, simple_dhcp_options):
112117
# An error occurred (InvalidParameterValue) when calling the CreateDhcpOptions operation:
113118
# Value (InvalidValue) for parameter value is invalid.
114119
# Unknown DHCP option
115-
assert expected_msg in terminal_condition['message']
120+
assert expected_msg in terminal_condition['message']
121+
122+
@pytest.mark.resource_data({'tag_key': 'initialtagkey', 'tag_value': 'initialtagvalue'})
123+
def test_crud_tags(self, ec2_client, simple_dhcp_options):
124+
(ref, cr) = simple_dhcp_options
125+
126+
resource = k8s.get_resource(ref)
127+
resource_id = cr["status"]["dhcpOptionsID"]
128+
129+
time.sleep(CREATE_WAIT_AFTER_SECONDS)
130+
131+
# Check dhcpOptions exists in AWS
132+
ec2_validator = EC2Validator(ec2_client)
133+
ec2_validator.assert_dhcp_options(resource_id)
134+
135+
# Check tags exist for dhcpOptions resource
136+
assert resource["spec"]["tags"][0]["key"] == "initialtagkey"
137+
assert resource["spec"]["tags"][0]["value"] == "initialtagvalue"
138+
139+
# New pair of tags
140+
new_tags = [
141+
{
142+
"key": "updatedtagkey",
143+
"value": "updatedtagvalue",
144+
}
145+
146+
]
147+
148+
# Patch the dhcpOptions, updating the tags with new pair
149+
updates = {
150+
"spec": {"tags": new_tags},
151+
}
152+
153+
k8s.patch_custom_resource(ref, updates)
154+
time.sleep(MODIFY_WAIT_AFTER_SECONDS)
155+
156+
# Check resource synced successfully
157+
assert k8s.wait_on_condition(ref, "ACK.ResourceSynced", "True", wait_periods=5)
158+
159+
# Assert tags are updated for dhcpOptions resource
160+
resource = k8s.get_resource(ref)
161+
assert resource["spec"]["tags"][0]["key"] == "updatedtagkey"
162+
assert resource["spec"]["tags"][0]["value"] == "updatedtagvalue"
163+
164+
# Patch the dhcpOptions resource, deleting the tags
165+
updates = {
166+
"spec": {"tags": []},
167+
}
168+
169+
k8s.patch_custom_resource(ref, updates)
170+
time.sleep(MODIFY_WAIT_AFTER_SECONDS)
171+
172+
# Check resource synced successfully
173+
assert k8s.wait_on_condition(ref, "ACK.ResourceSynced", "True", wait_periods=5)
174+
175+
# Assert tags are deleted
176+
resource = k8s.get_resource(ref)
177+
assert len(resource['spec']['tags']) == 0
178+
179+
# Delete k8s resource
180+
_, deleted = k8s.delete_custom_resource(ref)
181+
assert deleted is True
182+
183+
time.sleep(DELETE_WAIT_AFTER_SECONDS)
184+
185+
# Check dhcpOptions no longer exists in AWS
186+
ec2_validator.assert_dhcp_options(resource_id, exists=False)

0 commit comments

Comments
 (0)