Skip to content

Commit 8dc09b7

Browse files
Refactor reference resolution into Go code (#401)
Description of changes: Refactors the `resolveReferenceFor<Field>` Go template code into direct Go code. This way we can be more intelligent about nested field references and support unit testing its output. Breaking change: The previous Go template produced valid, but ultimately faulty, code for references within lists of structs. These are not supported (see aws-controllers-k8s/community#1291). As such, this update produces a panic when it detects those types of references. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent c1a516b commit 8dc09b7

File tree

8 files changed

+344
-126
lines changed

8 files changed

+344
-126
lines changed

go.mod

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/pkg/errors v0.9.1
1717
github.com/spf13/cobra v1.2.1
1818
github.com/stretchr/testify v1.7.1
19-
golang.org/x/mod v0.4.2
19+
golang.org/x/mod v0.6.0
2020
gopkg.in/src-d/go-git.v4 v4.13.1
2121
k8s.io/apimachinery v0.23.0
2222
sigs.k8s.io/controller-runtime v0.11.0
@@ -62,14 +62,13 @@ require (
6262
go.uber.org/atomic v1.7.0 // indirect
6363
go.uber.org/multierr v1.6.0 // indirect
6464
go.uber.org/zap v1.19.1 // indirect
65-
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
66-
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
65+
golang.org/x/crypto v0.1.0 // indirect
66+
golang.org/x/net v0.1.0 // indirect
6767
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
68-
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
69-
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
70-
golang.org/x/text v0.3.7 // indirect
68+
golang.org/x/sys v0.1.0 // indirect
69+
golang.org/x/term v0.1.0 // indirect
70+
golang.org/x/text v0.4.0 // indirect
7171
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
72-
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
7372
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
7473
google.golang.org/appengine v1.6.7 // indirect
7574
google.golang.org/protobuf v1.27.1 // indirect

go.sum

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -714,8 +714,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
714714
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
715715
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
716716
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
717-
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
718717
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
718+
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
719+
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
719720
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
720721
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
721722
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -750,8 +751,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
750751
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
751752
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
752753
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
753-
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
754754
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
755+
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
756+
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
755757
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
756758
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
757759
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -804,8 +806,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
804806
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
805807
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
806808
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
807-
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
808809
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
810+
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
811+
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
809812
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
810813
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
811814
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -908,12 +911,14 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
908911
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
909912
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
910913
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
911-
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
912914
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
915+
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
916+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
913917
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
914918
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
915-
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
916919
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
920+
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
921+
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
917922
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
918923
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
919924
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -923,8 +928,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
923928
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
924929
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
925930
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
926-
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
927931
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
932+
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
933+
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
928934
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
929935
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
930936
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1004,7 +1010,6 @@ golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpd
10041010
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
10051011
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
10061012
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1007-
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
10081013
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
10091014
gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
10101015
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=

pkg/generate/ack/controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ var (
183183
return code.InitializeNestedStructField(r, sourceVarName, f,
184184
apiPkgImportName, indentLevel)
185185
},
186+
"GoCodeResolveReference": func(f *ackmodel.Field, sourceVarName string, indentLevel int) string {
187+
return code.ResolveReferencesForField(f, sourceVarName, indentLevel)
188+
},
186189
}
187190
)
188191

pkg/generate/code/resource_reference.go

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func ReferenceFieldsValidation(
9898
out += fmt.Sprintf("%sif %s.%s != nil"+
9999
" && %s.%s != nil {\n", fIndent, pathVarPrefix, field.GetReferenceFieldName().Camel, pathVarPrefix, field.Names.Camel)
100100
out += fmt.Sprintf("%s\treturn "+
101-
"ackerr.ResourceReferenceAndIDNotSupportedFor(\"%s\", \"%s\")\n",
101+
"ackerr.ResourceReferenceAndIDNotSupportedFor(%q, %q)\n",
102102
fIndent, field.Path, field.ReferenceFieldPath())
103103

104104
// Close out all the curly braces with proper indentation
@@ -117,7 +117,7 @@ func ReferenceFieldsValidation(
117117
" %s.%s == nil {\n", fIndent, pathVarPrefix,
118118
field.ReferenceFieldPath(), pathVarPrefix, field.Path)
119119
out += fmt.Sprintf("%s\treturn "+
120-
"ackerr.ResourceReferenceOrIDRequiredFor(\"%s\", \"%s\")\n",
120+
"ackerr.ResourceReferenceOrIDRequiredFor(%q, %q)\n",
121121
fIndent, field.Path, field.ReferenceFieldPath())
122122
out += fmt.Sprintf("%s}\n", fIndent)
123123
}
@@ -185,6 +185,111 @@ func ReferenceFieldsPresent(
185185
return iteratorsOut + returnOut
186186
}
187187

188+
// ResolveReferencesForField produces Go code for accessing all references that
189+
// are related to the given concrete field, determining whether its in a valid
190+
// condition and updating the concrete field with the referenced value.
191+
// Sample code (resolving a nested singular reference):
192+
//
193+
// ```
194+
//
195+
// if ko.Spec.JWTConfiguration != nil {
196+
// if ko.Spec.JWTConfiguration.IssuerRef != nil && ko.Spec.JWTConfiguration.IssuerRef.From != nil {
197+
// arr := ko.Spec.JWTConfiguration.IssuerRef.From
198+
// if arr == nil || arr.Name == nil || *arr.Name == "" {
199+
// return fmt.Errorf("provided resource reference is nil or empty: \"JWTConfiguration.IssuerRef"\")
200+
// }
201+
// obj := &svcapitypes.API{}
202+
// if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
203+
// return err
204+
// }
205+
// ko.Spec.JWTConfiguration.Issuer = obj.Status.APIID
206+
// }
207+
// }
208+
//
209+
// ```
210+
func ResolveReferencesForField(field *model.Field, sourceVarName string, indentLevel int) string {
211+
r := field.CRD
212+
fp := fieldpath.FromString(field.Path)
213+
214+
outPrefix := ""
215+
outSuffix := ""
216+
217+
fieldAccessPrefix := fmt.Sprintf("%s%s", sourceVarName, r.Config().PrefixConfig.SpecField)
218+
targetVarName := fmt.Sprintf("%s.%s", fieldAccessPrefix, field.Path)
219+
220+
for idx := 0; idx < fp.Size(); idx++ {
221+
curFP := fp.CopyAt(idx).String()
222+
cur, ok := r.Fields[curFP]
223+
if !ok {
224+
panic(fmt.Sprintf("unable to find field with path %q. crd: %q", curFP, r.Kind))
225+
}
226+
227+
ref := cur.ShapeRef
228+
229+
indent := strings.Repeat("\t", indentLevel+idx)
230+
231+
switch ref.Shape.Type {
232+
case ("structure"):
233+
fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx))
234+
235+
outPrefix += fmt.Sprintf("%sif %s != nil {\n", indent, fieldAccessPrefix)
236+
outSuffix = fmt.Sprintf("%s}\n%s", indent, outSuffix)
237+
case ("list"):
238+
if (fp.Size() - idx) > 1 {
239+
// TODO(nithomso): add support for structs nested within lists
240+
// The logic for structs nested within lists needs to not only
241+
// be added here, but also in a custom patching solution since
242+
// it isn't supported by `StrategicMergePatch`
243+
// see https://github.com/aws-controllers-k8s/community/issues/1291
244+
panic(fmt.Errorf("references within lists inside lists aren't supported. crd: %q, path: %q", r.Kind, field.Path))
245+
}
246+
fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx))
247+
248+
iterVarName := fmt.Sprintf("iter%d", idx)
249+
250+
// base case for references in a list
251+
outPrefix += fmt.Sprintf("%s%s = %s{}\n", indent, targetVarName, field.GoType)
252+
outPrefix += fmt.Sprintf("%sfor _, %s := range %s {\n", indent, iterVarName, fieldAccessPrefix)
253+
254+
fieldAccessPrefix = iterVarName
255+
outPrefix += fmt.Sprintf("%s\tarr := %s.From\n", indent, fieldAccessPrefix)
256+
outPrefix += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent)
257+
outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty: \\%q\\\")\n", indent, field.ReferenceFieldPath())
258+
outPrefix += fmt.Sprintf("%s\t}\n", indent)
259+
260+
outPrefix += fmt.Sprintf("%s\tif err := getReferencedResourceState_%s(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n", indent, field.FieldConfig.References.Resource)
261+
outPrefix += fmt.Sprintf("%s\t\treturn err\n", indent)
262+
outPrefix += fmt.Sprintf("%s\t}\n", indent)
263+
outPrefix += fmt.Sprintf("%s\t%s = append(%s, obj.%s)\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path)
264+
outPrefix += fmt.Sprintf("%s}\n", indent)
265+
case ("map"):
266+
panic("references cannot be within a map")
267+
default:
268+
// base case for single references
269+
fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, cur.GetReferenceFieldName().Camel)
270+
271+
outPrefix += fmt.Sprintf("%sif %s != nil && %s.From != nil {\n", indent, fieldAccessPrefix, fieldAccessPrefix)
272+
outPrefix += fmt.Sprintf("%s\tarr := %s.From\n", indent, fieldAccessPrefix)
273+
outPrefix += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent)
274+
outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty: \\%q\\\")\n", indent, field.ReferenceFieldPath())
275+
outPrefix += fmt.Sprintf("%s\t}\n", indent)
276+
277+
if field.FieldConfig.References.ServiceName == "" {
278+
outPrefix += fmt.Sprintf("%s\tobj := &svcapitypes.%s{}\n", indent, field.FieldConfig.References.Resource)
279+
} else {
280+
outPrefix += fmt.Sprintf("%s\tobj := &%sapitypes.%s{}\n", indent, field.ReferencedServiceName(), field.FieldConfig.References.Resource)
281+
}
282+
outPrefix += fmt.Sprintf("%s\tif err := getReferencedResourceState_%s(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n", indent, field.FieldConfig.References.Resource)
283+
outPrefix += fmt.Sprintf("%s\t\treturn err\n", indent)
284+
outPrefix += fmt.Sprintf("%s\t}\n", indent)
285+
outPrefix += fmt.Sprintf("%s\t%s = obj.%s\n", indent, targetVarName, field.FieldConfig.References.Path)
286+
outPrefix += fmt.Sprintf("%s}\n", indent)
287+
}
288+
}
289+
290+
return outPrefix + outSuffix
291+
}
292+
188293
func nestedStructNilCheck(path fieldpath.Path, fieldAccessPrefix string) string {
189294
out := ""
190295
fieldNamePrefix := ""

pkg/generate/code/resource_reference_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,127 @@ if ko.Spec.Routes != nil {
198198
return false || (ko.Spec.VPCRef != nil)`
199199
assert.Equal(expected, code.ReferenceFieldsPresent(crd, "ko"))
200200
}
201+
202+
func Test_ResolveReferencesForField_SingleReference(t *testing.T) {
203+
assert := assert.New(t)
204+
require := require.New(t)
205+
206+
g := testutil.NewModelForServiceWithOptions(t, "apigatewayv2",
207+
&testutil.TestingModelOptions{
208+
GeneratorConfigFile: "generator-with-reference.yaml",
209+
})
210+
211+
crd := testutil.GetCRDByName(t, g, "Integration")
212+
require.NotNil(crd)
213+
expected :=
214+
` if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil {
215+
arr := ko.Spec.APIRef.From
216+
if arr == nil || arr.Name == nil || *arr.Name == "" {
217+
return fmt.Errorf("provided resource reference is nil or empty: \"APIRef"\")
218+
}
219+
obj := &svcapitypes.API{}
220+
if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
221+
return err
222+
}
223+
ko.Spec.APIID = obj.Status.APIID
224+
}
225+
`
226+
227+
field := crd.Fields["APIID"]
228+
assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1))
229+
}
230+
231+
func Test_ResolveReferencesForField_SliceOfReferences(t *testing.T) {
232+
assert := assert.New(t)
233+
require := require.New(t)
234+
235+
g := testutil.NewModelForServiceWithOptions(t, "apigatewayv2",
236+
&testutil.TestingModelOptions{
237+
GeneratorConfigFile: "generator-with-reference.yaml",
238+
})
239+
240+
crd := testutil.GetCRDByName(t, g, "VpcLink")
241+
require.NotNil(crd)
242+
expected :=
243+
` ko.Spec.SecurityGroupIDs = []*string{}
244+
for _, iter0 := range ko.Spec.SecurityGroupIDs {
245+
arr := iter0.From
246+
if arr == nil || arr.Name == nil || *arr.Name == "" {
247+
return fmt.Errorf("provided resource reference is nil or empty: \"SecurityGroupRefs"\")
248+
}
249+
if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
250+
return err
251+
}
252+
ko.Spec.SecurityGroupIDs = append(ko.Spec.SecurityGroupIDs, obj.Status.ID)
253+
}
254+
`
255+
256+
field := crd.Fields["SecurityGroupIDs"]
257+
assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1))
258+
}
259+
260+
func Test_ResolveReferencesForField_NestedSingleReference(t *testing.T) {
261+
assert := assert.New(t)
262+
require := require.New(t)
263+
264+
g := testutil.NewModelForServiceWithOptions(t, "apigatewayv2",
265+
&testutil.TestingModelOptions{
266+
GeneratorConfigFile: "generator-with-nested-reference.yaml",
267+
})
268+
269+
crd := testutil.GetCRDByName(t, g, "Authorizer")
270+
require.NotNil(crd)
271+
expected :=
272+
` if ko.Spec.JWTConfiguration != nil {
273+
if ko.Spec.JWTConfiguration.IssuerRef != nil && ko.Spec.JWTConfiguration.IssuerRef.From != nil {
274+
arr := ko.Spec.JWTConfiguration.IssuerRef.From
275+
if arr == nil || arr.Name == nil || *arr.Name == "" {
276+
return fmt.Errorf("provided resource reference is nil or empty: \"JWTConfiguration.IssuerRef"\")
277+
}
278+
obj := &svcapitypes.API{}
279+
if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
280+
return err
281+
}
282+
ko.Spec.JWTConfiguration.Issuer = obj.Status.APIID
283+
}
284+
}
285+
`
286+
287+
field := crd.Fields["JWTConfiguration.Issuer"]
288+
assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1))
289+
}
290+
291+
func Test_ResolveReferencesForField_SingleReference_DeeplyNested(t *testing.T) {
292+
assert := assert.New(t)
293+
require := require.New(t)
294+
295+
g := testutil.NewModelForServiceWithOptions(t, "s3",
296+
&testutil.TestingModelOptions{
297+
GeneratorConfigFile: "generator-with-nested-references.yaml",
298+
})
299+
300+
crd := testutil.GetCRDByName(t, g, "Bucket")
301+
require.NotNil(crd)
302+
303+
// the Go template has the appropriate nil checks to ensure the parent path exists
304+
expected :=
305+
` if ko.Spec.Logging != nil {
306+
if ko.Spec.Logging.LoggingEnabled != nil {
307+
if ko.Spec.Logging.LoggingEnabled.TargetBucketRef != nil && ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From != nil {
308+
arr := ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From
309+
if arr == nil || arr.Name == nil || *arr.Name == "" {
310+
return fmt.Errorf("provided resource reference is nil or empty: \"Logging.LoggingEnabled.TargetBucketRef"\")
311+
}
312+
obj := &svcapitypes.Bucket{}
313+
if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
314+
return err
315+
}
316+
ko.Spec.Logging.LoggingEnabled.TargetBucket = obj.Spec.Name
317+
}
318+
}
319+
}
320+
`
321+
322+
field := crd.Fields["Logging.LoggingEnabled.TargetBucket"]
323+
assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1))
324+
}

0 commit comments

Comments
 (0)