Skip to content

Commit becb821

Browse files
committed
Replace To/FromACKTags with convertToOrderedACKTags and fromACKTags
Besides changing just the funciton names, it also changes their signature introduced in previous PR, to maintain tag order during conversion. This change also ensures these funcitons are generated using go code instead of templating. This keeps the code cleaner and testable.
1 parent ca12303 commit becb821

File tree

7 files changed

+335
-123
lines changed

7 files changed

+335
-123
lines changed

pkg/generate/ack/controller.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ var (
203203
"GoCodeClearResolvedReferences": func(f *ackmodel.Field, targetVarName string, indentLevel int) string {
204204
return code.ClearResolvedReferencesForField(f, targetVarName, indentLevel)
205205
},
206+
"GoCodeConvertToACKTags": func(r *ackmodel.CRD, sourceVarName string, targetVarName string, keyOrderVarName string, indentLevel int) string {
207+
return code.GoCodeConvertToACKTags(r, sourceVarName, targetVarName, keyOrderVarName, indentLevel)
208+
},
209+
"GoCodeFromACKTags": func(r *ackmodel.CRD, sourceVarName string, orderVarName string, targetVarName string, indentLevel int) string {
210+
return code.GoCodeFromACKTags(r, sourceVarName, orderVarName, targetVarName, indentLevel)
211+
},
206212
}
207213
)
208214

pkg/generate/code/compare.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,9 +523,11 @@ func compareSlice(
523523
//
524524
// Output code will look something like this:
525525
//
526-
// if !ackcompare.MapStringStringEqual(ToACKTags(a.ko.Spec.Tags), ToACKTags(b.ko.Spec.Tags)) {
527-
// delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags)
528-
// }
526+
// desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tags)
527+
// latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tags)
528+
// if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) {
529+
// delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags)
530+
// }
529531
func compareTags(
530532
// String representing the name of the variable that is of type
531533
// `*ackcompare.Delta`. We will generate Go code that calls the `Add()`
@@ -549,7 +551,9 @@ func compareTags(
549551
out := ""
550552
indent := strings.Repeat("\t", indentLevel)
551553

552-
out += fmt.Sprintf("%sif !ackcompare.MapStringStringEqual(ToACKTags(%s), ToACKTags(%s)) {\n", indent, firstResVarName, secondResVarName)
554+
out += fmt.Sprintf("%sdesiredACKTags, _ := convertToOrderedACKTags(%s)\n", indent, firstResVarName)
555+
out += fmt.Sprintf("%slatestACKTags, _ := convertToOrderedACKTags(%s)\n", indent, secondResVarName)
556+
out += fmt.Sprintf("%sif !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) {\n", indent)
553557
out += fmt.Sprintf("%s\t%s.Add(\"%s\", %s, %s)\n", indent, deltaVarName, fieldPath, firstResVarName, secondResVarName)
554558
out += fmt.Sprintf("%s}\n", indent)
555559

pkg/generate/code/compare_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ func TestCompareResource_S3_Bucket(t *testing.T) {
127127
if ackcompare.HasNilDifference(a.ko.Spec.Tagging, b.ko.Spec.Tagging) {
128128
delta.Add("Spec.Tagging", a.ko.Spec.Tagging, b.ko.Spec.Tagging)
129129
} else if a.ko.Spec.Tagging != nil && b.ko.Spec.Tagging != nil {
130-
if !ackcompare.MapStringStringEqual(ToACKTags(a.ko.Spec.Tagging.TagSet), ToACKTags(b.ko.Spec.Tagging.TagSet)) {
130+
desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tagging.TagSet)
131+
latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tagging.TagSet)
132+
if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) {
131133
delta.Add("Spec.Tagging", a.ko.Spec.Tagging.TagSet, b.ko.Spec.Tagging.TagSet)
132134
}
133135
}
@@ -364,7 +366,9 @@ func TestCompareResource_Lambda_Function(t *testing.T) {
364366
delta.Add("Spec.Runtime", a.ko.Spec.Runtime, b.ko.Spec.Runtime)
365367
}
366368
}
367-
if !ackcompare.MapStringStringEqual(ToACKTags(a.ko.Spec.Tags), ToACKTags(b.ko.Spec.Tags)) {
369+
desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tags)
370+
latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tags)
371+
if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) {
368372
delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags)
369373
}
370374
if ackcompare.HasNilDifference(a.ko.Spec.Timeout, b.ko.Spec.Timeout) {
@@ -533,7 +537,9 @@ func TestCompareResource_IAM_OIDC_URL(t *testing.T) {
533537
delta.Add("Spec.ClientIDList", a.ko.Spec.ClientIDList, b.ko.Spec.ClientIDList)
534538
}
535539
}
536-
if !ackcompare.MapStringStringEqual(ToACKTags(a.ko.Spec.Tags), ToACKTags(b.ko.Spec.Tags)) {
540+
desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tags)
541+
latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tags)
542+
if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) {
537543
delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags)
538544
}
539545
if len(a.ko.Spec.ThumbprintList) != len(b.ko.Spec.ThumbprintList) {
@@ -586,7 +592,9 @@ func TestCompareResource_MemoryDB_User(t *testing.T) {
586592
delta.Add("Spec.Name", a.ko.Spec.Name, b.ko.Spec.Name)
587593
}
588594
}
589-
if !ackcompare.MapStringStringEqual(ToACKTags(a.ko.Spec.Tags), ToACKTags(b.ko.Spec.Tags)) {
595+
desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tags)
596+
latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tags)
597+
if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) {
590598
delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags)
591599
}
592600
`

pkg/generate/code/tags.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package code
15+
16+
import (
17+
"fmt"
18+
"strings"
19+
20+
"github.com/aws-controllers-k8s/code-generator/pkg/model"
21+
)
22+
23+
// GoCodeToACKTags returns Go code that converts Resource Tags
24+
// to ACK Tags. If Resource Tags field is of type list, we
25+
// also maintain and return the order of the list as a []string
26+
//
27+
//
28+
//
29+
// Sample output:
30+
//
31+
// for _, k := range keyOrder {
32+
// v, ok := tags[k]
33+
// if ok {
34+
// tag := svcapitypes.Tag{Key: &k, Value: &v}
35+
// result = append(result, &tag)
36+
// delete(tags, k)
37+
// }
38+
// }
39+
// for k, v := range tags {
40+
// tag := svcapitypes.Tag{Key: &k, Value: &v}
41+
// result = append(result, &tag)
42+
// }
43+
func GoCodeConvertToACKTags(r *model.CRD, sourceVarName string, targetVarName string, keyOrderVarName string, indentLevel int) string {
44+
45+
out := "\n"
46+
indent := strings.Repeat("\t", indentLevel)
47+
tagField, err := r.GetTagField()
48+
if err != nil {
49+
panic("error: resource does not have tags. ignore in generator.yaml")
50+
}
51+
52+
if tagField == nil {
53+
return ""
54+
}
55+
56+
tagFieldShapeType := tagField.ShapeRef.Shape.Type
57+
keyMemberName := r.GetTagKeyMemberName()
58+
valueMemberName := r.GetTagValueMemberName()
59+
60+
out += fmt.Sprintf("%sif len(%s) == 0 {\n", indent, sourceVarName)
61+
out += fmt.Sprintf("%s\treturn %s, %s\n", indent, targetVarName, keyOrderVarName)
62+
out += fmt.Sprintf("%s}\n", indent)
63+
64+
switch tagFieldShapeType {
65+
case "list":
66+
out += fmt.Sprintf("%sfor _, t := range %s {\n", indent, sourceVarName)
67+
out += fmt.Sprintf("%s\tif t.%s != nil {\n", indent, keyMemberName)
68+
out += fmt.Sprintf("%s\t\t%s = append(%s, *t.%s)\n", indent, keyOrderVarName, keyOrderVarName, keyMemberName)
69+
out += fmt.Sprintf("%s\t\tif t.%s != nil {\n", indent, valueMemberName)
70+
out += fmt.Sprintf("%s\t\t\t%s[*t.%s] = *t.%s\n", indent, targetVarName, keyMemberName, valueMemberName)
71+
out += fmt.Sprintf("%s\t\t} else {\n", indent)
72+
out += fmt.Sprintf("%s\t\t\t%s[*t.%s] = \"\"\n", indent, targetVarName, keyMemberName)
73+
out += fmt.Sprintf("%s\t\t}\n", indent)
74+
out += fmt.Sprintf("%s\t}\n", indent)
75+
out += fmt.Sprintf("%s}\n", indent)
76+
77+
case "map":
78+
out += fmt.Sprintf("%sfor k, v := range %s {\n", indent, sourceVarName)
79+
out += fmt.Sprintf("%s\tif v == nil {\n", indent)
80+
out += fmt.Sprintf("%s\t\t%s[k] = \"\"\n", indent, targetVarName)
81+
out += fmt.Sprintf("%s\t} else {\n", indent)
82+
out += fmt.Sprintf("%s\t\t%s[k] = *v\n", indent, targetVarName)
83+
out += fmt.Sprintf("%s\t}\n", indent)
84+
out += fmt.Sprintf("%s}\n", indent)
85+
default:
86+
msg := "error: tag type can only be a list or a map"
87+
panic(msg)
88+
}
89+
90+
return out
91+
}
92+
93+
// GoCodeFromACKTags returns Go code that converts ACKTags
94+
// to the Resource Tag shape type. Tag fields can only be
95+
// maps or lists of Tag Go type. If Tag field is a list,
96+
// when converting from ACK Tags, we try to preserve the
97+
// original order
98+
//
99+
//
100+
//
101+
// Sample output:
102+
//
103+
// for _, k := range keyOrder {
104+
// v, ok := tags[k]
105+
// if ok {
106+
// tag := svcapitypes.Tag{Key: &k, Value: &v}
107+
// result = append(result, &tag)
108+
// delete(tags, k)
109+
// }
110+
// }
111+
// for k, v := range tags {
112+
// tag := svcapitypes.Tag{Key: &k, Value: &v}
113+
// result = append(result, &tag)
114+
// }
115+
func GoCodeFromACKTags(r *model.CRD, tagsSourceVarName string, orderVarName string, targetVarName string, indentLevel int) string {
116+
out := "\n"
117+
indent := strings.Repeat("\t", indentLevel)
118+
tagField, _ := r.GetTagField()
119+
120+
if tagField == nil {
121+
return ""
122+
}
123+
124+
tagFieldShapeType := tagField.ShapeRef.Shape.Type
125+
tagFieldGoType := tagField.GoTypeElem
126+
keyMemberName := r.GetTagKeyMemberName()
127+
valueMemberName := r.GetTagValueMemberName()
128+
129+
switch tagFieldShapeType {
130+
case "list":
131+
out += fmt.Sprintf("%sfor _, k := range %s {\n", indent, orderVarName)
132+
out += fmt.Sprintf("%s\tv, ok := %s[k]\n", indent, tagsSourceVarName)
133+
out += fmt.Sprintf("%s\tif ok {\n", indent)
134+
out += fmt.Sprintf("%s\t\ttag := svcapitypes.%s{%s: &k, %s: &v}\n", indent, tagFieldGoType, keyMemberName, valueMemberName)
135+
out += fmt.Sprintf("%s\t\t%s = append(%s, &tag)\n", indent, targetVarName, targetVarName)
136+
out += fmt.Sprintf("%s\t\tdelete(%s, k)\n", indent, tagsSourceVarName)
137+
out += fmt.Sprintf("%s\t}\n", indent)
138+
out += fmt.Sprintf("%s}\n", indent)
139+
case "map":
140+
out += fmt.Sprintf("%s_ = %s\n", indent, orderVarName)
141+
default:
142+
msg := "error: tag type can only be a list of a map"
143+
panic(msg)
144+
}
145+
146+
out += fmt.Sprintf("%sfor k, v := range %s {\n", indent, tagsSourceVarName)
147+
switch tagFieldShapeType {
148+
case "list":
149+
out += fmt.Sprintf("%s\ttag := svcapitypes.%s{%s: &k, %s: &v}\n", indent, tagFieldGoType, keyMemberName, valueMemberName)
150+
out += fmt.Sprintf("%s\t%s = append(%s, &tag)\n", indent, targetVarName, targetVarName)
151+
case "map":
152+
out += fmt.Sprintf("%s\t%s[k] = &v\n", indent, targetVarName)
153+
}
154+
out += fmt.Sprintf("%s}\n", indent)
155+
156+
return out
157+
}

pkg/generate/code/tags_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package code_test
15+
16+
import (
17+
"testing"
18+
19+
"github.com/stretchr/testify/assert"
20+
"github.com/stretchr/testify/require"
21+
22+
"github.com/aws-controllers-k8s/code-generator/pkg/generate/code"
23+
"github.com/aws-controllers-k8s/code-generator/pkg/testutil"
24+
)
25+
26+
func TestToACKTagsForListShape(t *testing.T) {
27+
assert := assert.New(t)
28+
require := require.New(t)
29+
30+
g := testutil.NewModelForService(t, "ec2")
31+
32+
crd := testutil.GetCRDByName(t, g, "Vpc")
33+
require.NotNil(crd)
34+
35+
expectedSyncedConditions := `
36+
if len(tags) == 0 {
37+
return result, keyOrder
38+
}
39+
for _, t := range tags {
40+
if t.Key != nil {
41+
keyOrder = append(keyOrder, *t.Key)
42+
if t.Value != nil {
43+
result[*t.Key] = *t.Value
44+
} else {
45+
result[*t.Key] = ""
46+
}
47+
}
48+
}
49+
`
50+
assert.Equal(
51+
expectedSyncedConditions,
52+
code.GoCodeConvertToACKTags(
53+
crd, "tags", "result", "keyOrder", 1,
54+
),
55+
)
56+
}
57+
58+
func TestToACKTagsForMapShape(t *testing.T) {
59+
assert := assert.New(t)
60+
require := require.New(t)
61+
62+
g := testutil.NewModelForService(t, "apigatewayv2")
63+
64+
crd := testutil.GetCRDByName(t, g, "Api")
65+
require.NotNil(crd)
66+
67+
expectedSyncedConditions := `
68+
if len(tags) == 0 {
69+
return result, keyOrder
70+
}
71+
for k, v := range tags {
72+
if v == nil {
73+
result[k] = ""
74+
} else {
75+
result[k] = *v
76+
}
77+
}
78+
`
79+
assert.Equal(
80+
expectedSyncedConditions,
81+
code.GoCodeConvertToACKTags(
82+
crd, "tags", "result", "keyOrder", 1,
83+
),
84+
)
85+
}
86+
87+
func TestFromACKTagsForListShape(t *testing.T) {
88+
assert := assert.New(t)
89+
require := require.New(t)
90+
91+
g := testutil.NewModelForService(t, "ec2")
92+
93+
crd := testutil.GetCRDByName(t, g, "Vpc")
94+
require.NotNil(crd)
95+
96+
expectedSyncedConditions := `
97+
for _, k := range keyOrder {
98+
v, ok := tags[k]
99+
if ok {
100+
tag := svcapitypes.Tag{Key: &k, Value: &v}
101+
result = append(result, &tag)
102+
delete(tags, k)
103+
}
104+
}
105+
for k, v := range tags {
106+
tag := svcapitypes.Tag{Key: &k, Value: &v}
107+
result = append(result, &tag)
108+
}
109+
`
110+
assert.Equal(
111+
expectedSyncedConditions,
112+
code.GoCodeFromACKTags(
113+
crd, "tags", "keyOrder", "result", 1,
114+
),
115+
)
116+
}
117+
118+
func TestFromACKTagsForMapShape(t *testing.T) {
119+
assert := assert.New(t)
120+
require := require.New(t)
121+
122+
g := testutil.NewModelForService(t, "apigatewayv2")
123+
124+
crd := testutil.GetCRDByName(t, g, "Api")
125+
require.NotNil(crd)
126+
127+
expectedSyncedConditions := `
128+
_ = keyOrder
129+
for k, v := range tags {
130+
result[k] = &v
131+
}
132+
`
133+
assert.Equal(
134+
expectedSyncedConditions,
135+
code.GoCodeFromACKTags(
136+
crd, "tags", "keyOrder", "result", 1,
137+
),
138+
)
139+
}

0 commit comments

Comments
 (0)