From 60e626882e5c9dd3dc7797bdb4d79cead82cadf8 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Fri, 3 Feb 2023 22:59:41 +0000 Subject: [PATCH 1/9] Naively refactored Go templates into Go code --- go.mod | 13 +- go.sum | 19 +- pkg/generate/ack/controller.go | 3 + pkg/generate/code/resource_reference.go | 183 ++++++++++++ pkg/generate/code/resource_reference_test.go | 265 ++++++++++++++++++ .../generator-with-nested-references.yaml | 33 +++ 6 files changed, 502 insertions(+), 14 deletions(-) create mode 100644 pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml diff --git a/go.mod b/go.mod index ab55ee9f..ff17e07d 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.2.1 github.com/stretchr/testify v1.7.1 - golang.org/x/mod v0.4.2 + golang.org/x/mod v0.6.0 gopkg.in/src-d/go-git.v4 v4.13.1 k8s.io/apimachinery v0.23.0 sigs.k8s.io/controller-runtime v0.11.0 @@ -62,14 +62,13 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/net v0.1.0 // indirect golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/term v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect diff --git a/go.sum b/go.sum index 6d7ff4da..2da5a1cf 100644 --- a/go.sum +++ b/go.sum @@ -714,8 +714,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 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 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= diff --git a/pkg/generate/ack/controller.go b/pkg/generate/ack/controller.go index 9a322ba7..539dbf97 100644 --- a/pkg/generate/ack/controller.go +++ b/pkg/generate/ack/controller.go @@ -183,6 +183,9 @@ var ( return code.InitializeNestedStructField(r, sourceVarName, f, apiPkgImportName, indentLevel) }, + "GoCodeReferenceForField": func(r *ackmodel.CRD, f *ackmodel.Field, indentLevel int) string { + return code.ResolveReferencesForField(r, f, indentLevel) + }, } ) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 539e516b..d1df6354 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -185,6 +185,189 @@ func ReferenceFieldsPresent( return iteratorsOut + returnOut } +func ResolveReferencesForField(r *model.CRD, field *model.Field, indentLevel int) string { + out := "" + + fp := fieldpath.FromString(field.Path) + fp.Pop() + + isNested := fp.Size() > 0 + isList := field.ShapeRef.Shape.Type == "list" + + if !isList && !isNested { + return resolveSingleReference(r, field, indentLevel) + } else if !isNested { + return resolveSliceOfReferences(r, field, indentLevel) + } else { + parentField, ok := r.Fields[fp.String()] + if !ok { + panic(fmt.Sprintf("unable to find parent field with path %s", fp.String())) + } + + if parentField.ShapeRef.Shape.Type == "list" { + return resolveNestedSliceOfReferences(r, field, indentLevel) + } else { + return resolveNestedSingleReference(r, field, indentLevel) + } + } + + return out +} + +func resolveSingleReference(r *model.CRD, field *model.Field, indentLevel int) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + + rfp := field.ReferenceFieldPath() + + out += fmt.Sprintf("%sif ko.Spec.%s != nil && ko.Spec.%s.From != nil {\n", indent, rfp, rfp) + out += fmt.Sprintf("%s\tarr := ko.Spec.%s.From\n", indent, rfp) + out += readReferenceAndValidate(field, indentLevel+1) + out += fmt.Sprintf("%s\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s\tko.Spec.%s = &referencedValue\n", indent, field.Path) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%sreturn nil", indent) + + return out +} + +func resolveSliceOfReferences(r *model.CRD, field *model.Field, indentLevel int) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + + rfp := field.ReferenceFieldPath() + + out += fmt.Sprintf("%sif ko.Spec.%s != nil &&\n", indent, rfp) + out += fmt.Sprintf("%s\tlen(ko.Spec.%s) > 0 {\n", indent, rfp) + out += fmt.Sprintf("%s\tresolvedReferences := []*string{}\n", indent) + out += fmt.Sprintf("%s\tfor _, arrw := range ko.Spec.%s {\n", indent, rfp) + out += fmt.Sprintf("%s\t\tarr := arrw.From\n", indent) + out += readReferenceAndValidate(field, indentLevel+2) + out += fmt.Sprintf("%s\t\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s\t\tresolvedReferences = append(resolvedReferences, &referencedValue)\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s\tko.Spec.%s = resolvedReferences\n", indent, field.Path) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%sreturn nil", indent) + + return out +} + +func resolveNestedSingleReference(r *model.CRD, field *model.Field, indentLevel int) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + + rfp := field.ReferenceFieldPath() + + out += fmt.Sprintf("%sif ko.Spec.%s != nil &&\n", indent, rfp) + out += fmt.Sprintf("%s\tlen(ko.Spec.%s) > 0 {\n", indent, rfp) + out += fmt.Sprintf("%s\tresolvedReferences := []*string{}\n", indent) + out += fmt.Sprintf("%s\tfor _, arrw := range ko.Spec.%s {\n", indent, rfp) + out += fmt.Sprintf("%s\t\tarr := arrw.From\n", indent) + out += readReferenceAndValidate(field, indentLevel+2) + out += fmt.Sprintf("%s\t\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s\t\tresolvedReferences = append(resolvedReferences, &referencedValue)\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s\tko.Spec.%s = resolvedReferences\n", indent, field.Path) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%sreturn nil", indent) + + return out +} + +func resolveNestedSliceOfReferences(r *model.CRD, field *model.Field, indentLevel int) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + + fp := fieldpath.FromString(field.Path) + fp.Pop() + + parent, ok := r.Fields[fp.String()] + if !ok { + panic("") + } + + out += fmt.Sprintf("%sif len(ko.Spec.%s) > 0 {\n", indent, parent.Path) + out += fmt.Sprintf("%s\tfor _, elem := range ko.Spec.%s {\n", indent, parent.Path) + out += fmt.Sprintf("%s\t\tarrw := elem.%s\n", indent, field.GetReferenceFieldName().Camel) + out += fmt.Sprintf("%s\n", indent) + out += fmt.Sprintf("%s\t\tif arrw == nil || arrw.From == nil {\n", indent) + out += fmt.Sprintf("%s\t\t\tcontinue\n", indent) + out += fmt.Sprintf("%s\t\t}\n", indent) + out += fmt.Sprintf("%s\n", indent) + out += fmt.Sprintf("%s\t\tarr := arrw.From\n", indent) + out += fmt.Sprintf("%s\t\tif arr.Name == nil || *arr.Name == \"\" {\n", indent) + out += fmt.Sprintf("%s\t\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) + out += fmt.Sprintf("%s\t\t}\n", indent) + out += fmt.Sprintf("%s\n", indent) + out += readReferenceAndValidate(field, indentLevel+2) + out += fmt.Sprintf("%s\t\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s\t\telem.%s = &referencedValue\n", indent, field.Names.Camel) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%sreturn nil", indent) + + return out +} + +// readReferenceAndValidate produces Go code that attempts to fetch a referenced +// object from the K8s API server and validates whether it is in synced or +// terminal conditions, returning the appropriate errors. +func readReferenceAndValidate(field *model.Field, indentLevel int) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + + out += fmt.Sprintf("%sif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) + out += fmt.Sprintf("%s\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%snamespacedName := types.NamespacedName{\n", indent) + out += fmt.Sprintf("%s\tNamespace: namespace,\n", indent) + out += fmt.Sprintf("%s\tName: *arr.Name,\n", indent) + out += fmt.Sprintf("%s}\n", indent) + if field.FieldConfig.References.ServiceName == "" { + out += fmt.Sprintf("%sobj := svcapitypes.%s{}\n", indent, field.FieldConfig.References.Resource) + } else { + out += fmt.Sprintf("%sobj := %sapitypes.%s{}\n", indent, field.ReferencedServiceName(), field.FieldConfig.References.Resource) + } + out += fmt.Sprintf("%serr := apiReader.Get(ctx, namespacedName, &obj)\n", indent) + out += fmt.Sprintf("%sif err != nil {\n", indent) + out += fmt.Sprintf("%s\treturn err\n", indent) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%svar refResourceSynced, refResourceTerminal bool\n", indent) + out += fmt.Sprintf("%sfor _, cond := range obj.Status.Conditions {\n", indent) + out += fmt.Sprintf("%s\tif cond.Type == ackv1alpha1.ConditionTypeResourceSynced &&\n", indent) + out += fmt.Sprintf("%s\t\tcond.Status == corev1.ConditionTrue {\n", indent) + out += fmt.Sprintf("%s\t\trefResourceSynced = true\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s\tif cond.Type == ackv1alpha1.ConditionTypeTerminal &&\n", indent) + out += fmt.Sprintf("%s\t\tcond.Status == corev1.ConditionTrue {\n", indent) + out += fmt.Sprintf("%s\t\trefResourceTerminal = true\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%sif refResourceTerminal {\n", indent) + out += fmt.Sprintf("%s\treturn ackerr.ResourceReferenceTerminalFor(\n", indent) + out += fmt.Sprintf("%s\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\t\tnamespace, *arr.Name)\n", indent) + out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%sif !refResourceSynced {\n", indent) + out += fmt.Sprintf("%s\treturn ackerr.ResourceReferenceNotSyncedFor(\n", indent) + out += fmt.Sprintf("%s\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\t\tnamespace, *arr.Name)\n", indent) + out += fmt.Sprintf("%s}\n", indent) + + nilCheck := CheckNilReferencesPath(field, "obj") + if nilCheck != "" { + out += fmt.Sprintf("%sif %s {\n", indent, nilCheck) + out += fmt.Sprintf("%s\treturn ackerr.ResourceReferenceMissingTargetFieldFor(\n", indent) + out += fmt.Sprintf("%s\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\t\tnamespace, *arr.Name,\n", indent) + out += fmt.Sprintf("%s\t\t\"%s\")\n", indent, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s}\n", indent) + } + + return out +} + func nestedStructNilCheck(path fieldpath.Path, fieldAccessPrefix string) string { out := "" fieldNamePrefix := "" diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index d897d89e..167a6929 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -198,3 +198,268 @@ if ko.Spec.Routes != nil { return false || (ko.Spec.VPCRef != nil)` assert.Equal(expected, code.ReferenceFieldsPresent(crd, "ko")) } + +func Test_ReferenceForField_SingleReference(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForServiceWithOptions(t, "apigatewayv2", + &testutil.TestingModelOptions{ + GeneratorConfigFile: "generator-with-reference.yaml", + }) + + crd := testutil.GetCRDByName(t, g, "Integration") + require.NotNil(crd) + expected := + ` if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil { + arr := ko.Spec.APIRef.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := svcapitypes.API{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "API", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "API", + namespace, *arr.Name) + } + if obj.Status.APIID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "API", + namespace, *arr.Name, + "Status.APIID") + } + referencedValue := string(*obj.Status.APIID) + ko.Spec.APIID = &referencedValue + } + return nil` + + field := crd.Fields["APIID"] + assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) +} + +func Test_ReferenceForField_SliceOfReferences(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForServiceWithOptions(t, "apigatewayv2", + &testutil.TestingModelOptions{ + GeneratorConfigFile: "generator-with-reference.yaml", + }) + + crd := testutil.GetCRDByName(t, g, "VpcLink") + require.NotNil(crd) + expected := + ` if ko.Spec.SecurityGroupRefs != nil && + len(ko.Spec.SecurityGroupRefs) > 0 { + resolvedReferences := []*string{} + for _, arrw := range ko.Spec.SecurityGroupRefs { + arr := arrw.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := ec2apitypes.SecurityGroup{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "SecurityGroup", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "SecurityGroup", + namespace, *arr.Name) + } + if obj.Status.ID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "SecurityGroup", + namespace, *arr.Name, + "Status.ID") + } + referencedValue := string(*obj.Status.ID) + resolvedReferences = append(resolvedReferences, &referencedValue) + } + ko.Spec.SecurityGroupIDs = resolvedReferences + } + return nil` + + field := crd.Fields["SecurityGroupIDs"] + assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) +} + +func Test_ReferenceForField_NestedSingleReference(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForServiceWithOptions(t, "apigatewayv2", + &testutil.TestingModelOptions{ + GeneratorConfigFile: "generator-with-nested-reference.yaml", + }) + + crd := testutil.GetCRDByName(t, g, "Authorizer") + require.NotNil(crd) + expected := + ` if ko.Spec.JWTConfiguration.IssuerRef != nil && + len(ko.Spec.JWTConfiguration.IssuerRef) > 0 { + resolvedReferences := []*string{} + for _, arrw := range ko.Spec.JWTConfiguration.IssuerRef { + arr := arrw.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := svcapitypes.API{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "API", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "API", + namespace, *arr.Name) + } + if obj.Status.APIID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "API", + namespace, *arr.Name, + "Status.APIID") + } + referencedValue := string(*obj.Status.APIID) + resolvedReferences = append(resolvedReferences, &referencedValue) + } + ko.Spec.JWTConfiguration.Issuer = resolvedReferences + } + return nil` + + field := crd.Fields["JWTConfiguration.Issuer"] + assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) +} + +func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForServiceWithOptions(t, "s3", + &testutil.TestingModelOptions{ + GeneratorConfigFile: "generator-with-nested-references.yaml", + }) + + crd := testutil.GetCRDByName(t, g, "Bucket") + require.NotNil(crd) + + // the Go template has the appropriate nil checks to ensure the parent path exists + expected := + ` if ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.BucketRef != nil && + len(ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.BucketRef) > 0 { + resolvedReferences := []*string{} + for _, arrw := range ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.BucketRef { + arr := arrw.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := svcapitypes.Bucket{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "Bucket", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "Bucket", + namespace, *arr.Name) + } + if obj.Spec.Name == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "Bucket", + namespace, *arr.Name, + "Spec.Name") + } + referencedValue := string(*obj.Spec.Name) + resolvedReferences = append(resolvedReferences, &referencedValue) + } + ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.Bucket = resolvedReferences + } + return nil` + + field := crd.Fields["Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.Bucket"] + assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) +} diff --git a/pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml b/pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml new file mode 100644 index 00000000..4b914637 --- /dev/null +++ b/pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml @@ -0,0 +1,33 @@ +ignore: + resource_names: + - Object + - MultipartUpload + shape_names: + # These shapes are structs with no members... + - SSES3 + field_paths: + # We cannot support MFA, so if it is set we cannot unset + - "VersioningConfiguration.MFADelete" + # This subfield struct has no members... + - "NotificationConfiguration.EventBridgeConfiguration" +resources: + Bucket: + renames: + operations: + CreateBucket: + input_fields: + Bucket: Name + DeleteBucket: + input_fields: + Bucket: Name + list_operation: + match_fields: + - Name + fields: + Analytics: + custom_field: + list_of: AnalyticsConfiguration + Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.Bucket: + references: + resource: Bucket + path: Spec.Name From 2a88d23bdf101be039fc3ef34c86e2cfa86ca361 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Sat, 4 Feb 2023 00:56:44 +0000 Subject: [PATCH 2/9] Refactored into iterative method --- pkg/generate/ack/controller.go | 4 +- pkg/generate/code/resource_reference.go | 299 +++++++++--------- pkg/generate/code/resource_reference_test.go | 140 ++++---- .../generator-with-nested-references.yaml | 9 +- templates/pkg/resource/references.go.tpl | 9 +- 5 files changed, 225 insertions(+), 236 deletions(-) diff --git a/pkg/generate/ack/controller.go b/pkg/generate/ack/controller.go index 539dbf97..c4443b85 100644 --- a/pkg/generate/ack/controller.go +++ b/pkg/generate/ack/controller.go @@ -183,8 +183,8 @@ var ( return code.InitializeNestedStructField(r, sourceVarName, f, apiPkgImportName, indentLevel) }, - "GoCodeReferenceForField": func(r *ackmodel.CRD, f *ackmodel.Field, indentLevel int) string { - return code.ResolveReferencesForField(r, f, indentLevel) + "GoCodeReferenceForField": func(r *ackmodel.CRD, f *ackmodel.Field, sourceVarName string, indentLevel int) string { + return code.ResolveReferencesForField(r, f, sourceVarName, indentLevel) }, } ) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index d1df6354..7c72f564 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -185,186 +185,185 @@ func ReferenceFieldsPresent( return iteratorsOut + returnOut } -func ResolveReferencesForField(r *model.CRD, field *model.Field, indentLevel int) string { - out := "" - +// ResolveReferencesForField produces Go code for accessing all references that +// are related to the given concrete field, determining whether its in a valid +// condition and updating the concrete field with the referenced value. +// Sample code: +// +// if ko.Spec.SecurityGroupRefs != nil && +// len(ko.Spec.SecurityGroupRefs) > 0 { +// resolvedReferences := []*string{} +// for _, arrw := range ko.Spec.SecurityGroupRefs { +// arr := arrw.From +// if arr == nil || arr.Name == nil || *arr.Name == "" { +// return fmt.Errorf("provided resource reference is nil or empty") +// } +// namespacedName := types.NamespacedName{ +// Namespace: namespace, +// Name: *arr.Name, +// } +// obj := ec2apitypes.SecurityGroup{} +// err := apiReader.Get(ctx, namespacedName, &obj) +// if err != nil { +// return err +// } +// var refResourceSynced, refResourceTerminal bool +// for _, cond := range obj.Status.Conditions { +// if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && +// cond.Status == corev1.ConditionTrue { +// refResourceSynced = true +// } +// if cond.Type == ackv1alpha1.ConditionTypeTerminal && +// cond.Status == corev1.ConditionTrue { +// refResourceTerminal = true +// } +// } +// if refResourceTerminal { +// return ackerr.ResourceReferenceTerminalFor( +// "SecurityGroup", +// namespace, *arr.Name) +// } +// if !refResourceSynced { +// return ackerr.ResourceReferenceNotSyncedFor( +// "SecurityGroup", +// namespace, *arr.Name) +// } +// if obj.Status.ID == nil { +// return ackerr.ResourceReferenceMissingTargetFieldFor( +// "SecurityGroup", +// namespace, *arr.Name, +// "Status.ID") +// } +// referencedValue := string(*obj.Status.ID) +// resolvedReferences = append(resolvedReferences, &referencedValue) +// } +// ko.Spec.SecurityGroupIDs = resolvedReferences +// } +func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName string, indentLevel int) string { fp := fieldpath.FromString(field.Path) - fp.Pop() - isNested := fp.Size() > 0 - isList := field.ShapeRef.Shape.Type == "list" + outPrefix := "" + outSuffix := "" - if !isList && !isNested { - return resolveSingleReference(r, field, indentLevel) - } else if !isNested { - return resolveSliceOfReferences(r, field, indentLevel) - } else { - parentField, ok := r.Fields[fp.String()] - if !ok { - panic(fmt.Sprintf("unable to find parent field with path %s", fp.String())) - } + fieldAccessPrefix := fmt.Sprintf("%s%s", sourceVarName, r.Config().PrefixConfig.SpecField) + targetVarName := fmt.Sprintf("%s%s.%s", sourceVarName, r.Config().PrefixConfig.SpecField, field.Path) - if parentField.ShapeRef.Shape.Type == "list" { - return resolveNestedSliceOfReferences(r, field, indentLevel) - } else { - return resolveNestedSingleReference(r, field, indentLevel) + hasSeenList := false + for idx := 0; idx < fp.Size(); idx++ { + curFP := fp.CopyAt(idx).String() + cur, ok := r.Fields[curFP] + if !ok { + panic(fmt.Sprintf("unable to find field with path %s", curFP)) } - } - - return out -} -func resolveSingleReference(r *model.CRD, field *model.Field, indentLevel int) string { - out := "" - indent := strings.Repeat("\t", indentLevel) + ref := cur.ShapeRef - rfp := field.ReferenceFieldPath() + indent := strings.Repeat("\t", indentLevel+idx) + fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx)) - out += fmt.Sprintf("%sif ko.Spec.%s != nil && ko.Spec.%s.From != nil {\n", indent, rfp, rfp) - out += fmt.Sprintf("%s\tarr := ko.Spec.%s.From\n", indent, rfp) - out += readReferenceAndValidate(field, indentLevel+1) - out += fmt.Sprintf("%s\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) - out += fmt.Sprintf("%s\tko.Spec.%s = &referencedValue\n", indent, field.Path) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%sreturn nil", indent) + if ref.Shape.Type == "structure" { + if hasSeenList { + // TODO(nithomso): add support for structs nested within lists + // The logic for structs nested within lists needs to not only + // be added here, but also in a custom patching solution since + // it isn't supported by `StrategicMergePatch` + // see community#1291 for more details + panic("references within structs inside lists aren't supported") + } - return out -} + outPrefix += fmt.Sprintf("%sif %s != nil {\n", indent, fieldAccessPrefix) + outSuffix = fmt.Sprintf("%s}\n%s", indent, outSuffix) + } else if ref.Shape.Type == "list" { + if hasSeenList { + panic("references within lists inside lists aren't supported") + } -func resolveSliceOfReferences(r *model.CRD, field *model.Field, indentLevel int) string { - out := "" - indent := strings.Repeat("\t", indentLevel) + hasSeenList = true + iterVarName := fmt.Sprintf("iter%d", idx) + refsTarget := "refVals" - rfp := field.ReferenceFieldPath() + // base case for references in a list + outPrefix += fmt.Sprintf("%s%s := []*string{}\n", indent, refsTarget) + outPrefix += fmt.Sprintf("%sfor _, %s := range %s {\n", indent, iterVarName, fieldAccessPrefix) - out += fmt.Sprintf("%sif ko.Spec.%s != nil &&\n", indent, rfp) - out += fmt.Sprintf("%s\tlen(ko.Spec.%s) > 0 {\n", indent, rfp) - out += fmt.Sprintf("%s\tresolvedReferences := []*string{}\n", indent) - out += fmt.Sprintf("%s\tfor _, arrw := range ko.Spec.%s {\n", indent, rfp) - out += fmt.Sprintf("%s\t\tarr := arrw.From\n", indent) - out += readReferenceAndValidate(field, indentLevel+2) - out += fmt.Sprintf("%s\t\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) - out += fmt.Sprintf("%s\t\tresolvedReferences = append(resolvedReferences, &referencedValue)\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tko.Spec.%s = resolvedReferences\n", indent, field.Path) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%sreturn nil", indent) + fieldAccessPrefix = iterVarName + outPrefix += resolveSingleReference(field, fieldAccessPrefix, refsTarget, true, indentLevel+idx+1) + outSuffix = fmt.Sprintf("%s%s = %s\n%s", indent, targetVarName, refsTarget, outSuffix) + outSuffix = fmt.Sprintf("%s}\n%s", indent, outSuffix) + } else if ref.Shape.Type == "map" { + panic("references cannot be within a map") + } else { + // base case for single references + refTarget := "refVal" + outPrefix += resolveSingleReference(field, fieldAccessPrefix, refTarget, false, indentLevel+idx) + outPrefix += fmt.Sprintf("%s%s = &%s\n", indent, targetVarName, refTarget) + } + } - return out + return outPrefix + outSuffix } -func resolveNestedSingleReference(r *model.CRD, field *model.Field, indentLevel int) string { +func resolveSingleReference(field *model.Field, sourceVarName string, targetVarName string, shouldAppendTarget bool, indentLevel int) string { out := "" indent := strings.Repeat("\t", indentLevel) - rfp := field.ReferenceFieldPath() - - out += fmt.Sprintf("%sif ko.Spec.%s != nil &&\n", indent, rfp) - out += fmt.Sprintf("%s\tlen(ko.Spec.%s) > 0 {\n", indent, rfp) - out += fmt.Sprintf("%s\tresolvedReferences := []*string{}\n", indent) - out += fmt.Sprintf("%s\tfor _, arrw := range ko.Spec.%s {\n", indent, rfp) - out += fmt.Sprintf("%s\t\tarr := arrw.From\n", indent) - out += readReferenceAndValidate(field, indentLevel+2) - out += fmt.Sprintf("%s\t\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) - out += fmt.Sprintf("%s\t\tresolvedReferences = append(resolvedReferences, &referencedValue)\n", indent) + out += fmt.Sprintf("%sif %s != nil && %s.From != nil {\n", indent, sourceVarName, sourceVarName) + out += fmt.Sprintf("%s\tarr := %s.From\n", indent, sourceVarName) + out += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) + out += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tko.Spec.%s = resolvedReferences\n", indent, field.Path) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%sreturn nil", indent) - - return out -} - -func resolveNestedSliceOfReferences(r *model.CRD, field *model.Field, indentLevel int) string { - out := "" - indent := strings.Repeat("\t", indentLevel) - - fp := fieldpath.FromString(field.Path) - fp.Pop() - - parent, ok := r.Fields[fp.String()] - if !ok { - panic("") - } - - out += fmt.Sprintf("%sif len(ko.Spec.%s) > 0 {\n", indent, parent.Path) - out += fmt.Sprintf("%s\tfor _, elem := range ko.Spec.%s {\n", indent, parent.Path) - out += fmt.Sprintf("%s\t\tarrw := elem.%s\n", indent, field.GetReferenceFieldName().Camel) - out += fmt.Sprintf("%s\n", indent) - out += fmt.Sprintf("%s\t\tif arrw == nil || arrw.From == nil {\n", indent) - out += fmt.Sprintf("%s\t\t\tcontinue\n", indent) - out += fmt.Sprintf("%s\t\t}\n", indent) - out += fmt.Sprintf("%s\n", indent) - out += fmt.Sprintf("%s\t\tarr := arrw.From\n", indent) - out += fmt.Sprintf("%s\t\tif arr.Name == nil || *arr.Name == \"\" {\n", indent) - out += fmt.Sprintf("%s\t\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) - out += fmt.Sprintf("%s\t\t}\n", indent) - out += fmt.Sprintf("%s\n", indent) - out += readReferenceAndValidate(field, indentLevel+2) - out += fmt.Sprintf("%s\t\treferencedValue := string(*obj.%s)\n", indent, field.FieldConfig.References.Path) - out += fmt.Sprintf("%s\t\telem.%s = &referencedValue\n", indent, field.Names.Camel) + out += fmt.Sprintf("%s\tnamespacedName := types.NamespacedName{\n", indent) + out += fmt.Sprintf("%s\t\tNamespace: namespace,\n", indent) + out += fmt.Sprintf("%s\t\tName: *arr.Name,\n", indent) out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%sreturn nil", indent) - - return out -} - -// readReferenceAndValidate produces Go code that attempts to fetch a referenced -// object from the K8s API server and validates whether it is in synced or -// terminal conditions, returning the appropriate errors. -func readReferenceAndValidate(field *model.Field, indentLevel int) string { - out := "" - indent := strings.Repeat("\t", indentLevel) - - out += fmt.Sprintf("%sif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) - out += fmt.Sprintf("%s\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%snamespacedName := types.NamespacedName{\n", indent) - out += fmt.Sprintf("%s\tNamespace: namespace,\n", indent) - out += fmt.Sprintf("%s\tName: *arr.Name,\n", indent) - out += fmt.Sprintf("%s}\n", indent) if field.FieldConfig.References.ServiceName == "" { - out += fmt.Sprintf("%sobj := svcapitypes.%s{}\n", indent, field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\tobj := svcapitypes.%s{}\n", indent, field.FieldConfig.References.Resource) } else { - out += fmt.Sprintf("%sobj := %sapitypes.%s{}\n", indent, field.ReferencedServiceName(), field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\tobj := %sapitypes.%s{}\n", indent, field.ReferencedServiceName(), field.FieldConfig.References.Resource) } - out += fmt.Sprintf("%serr := apiReader.Get(ctx, namespacedName, &obj)\n", indent) - out += fmt.Sprintf("%sif err != nil {\n", indent) - out += fmt.Sprintf("%s\treturn err\n", indent) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%svar refResourceSynced, refResourceTerminal bool\n", indent) - out += fmt.Sprintf("%sfor _, cond := range obj.Status.Conditions {\n", indent) - out += fmt.Sprintf("%s\tif cond.Type == ackv1alpha1.ConditionTypeResourceSynced &&\n", indent) - out += fmt.Sprintf("%s\t\tcond.Status == corev1.ConditionTrue {\n", indent) - out += fmt.Sprintf("%s\t\trefResourceSynced = true\n", indent) + out += fmt.Sprintf("%s\terr := apiReader.Get(ctx, namespacedName, &obj)\n", indent) + out += fmt.Sprintf("%s\tif err != nil {\n", indent) + out += fmt.Sprintf("%s\t\treturn err\n", indent) out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tif cond.Type == ackv1alpha1.ConditionTypeTerminal &&\n", indent) - out += fmt.Sprintf("%s\t\tcond.Status == corev1.ConditionTrue {\n", indent) - out += fmt.Sprintf("%s\t\trefResourceTerminal = true\n", indent) + out += fmt.Sprintf("%s\tvar refResourceSynced, refResourceTerminal bool\n", indent) + out += fmt.Sprintf("%s\tfor _, cond := range obj.Status.Conditions {\n", indent) + out += fmt.Sprintf("%s\t\tif cond.Type == ackv1alpha1.ConditionTypeResourceSynced &&\n", indent) + out += fmt.Sprintf("%s\t\t\tcond.Status == corev1.ConditionTrue {\n", indent) + out += fmt.Sprintf("%s\t\t\trefResourceSynced = true\n", indent) + out += fmt.Sprintf("%s\t\t}\n", indent) + out += fmt.Sprintf("%s\t\tif cond.Type == ackv1alpha1.ConditionTypeTerminal &&\n", indent) + out += fmt.Sprintf("%s\t\t\tcond.Status == corev1.ConditionTrue {\n", indent) + out += fmt.Sprintf("%s\t\t\trefResourceTerminal = true\n", indent) + out += fmt.Sprintf("%s\t\t}\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s\tif refResourceTerminal {\n", indent) + out += fmt.Sprintf("%s\t\treturn ackerr.ResourceReferenceTerminalFor(\n", indent) + out += fmt.Sprintf("%s\t\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\t\t\tnamespace, *arr.Name)\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s\tif !refResourceSynced {\n", indent) + out += fmt.Sprintf("%s\t\treturn ackerr.ResourceReferenceNotSyncedFor(\n", indent) + out += fmt.Sprintf("%s\t\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\t\t\tnamespace, *arr.Name)\n", indent) out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%sif refResourceTerminal {\n", indent) - out += fmt.Sprintf("%s\treturn ackerr.ResourceReferenceTerminalFor(\n", indent) - out += fmt.Sprintf("%s\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) - out += fmt.Sprintf("%s\t\tnamespace, *arr.Name)\n", indent) - out += fmt.Sprintf("%s}\n", indent) - out += fmt.Sprintf("%sif !refResourceSynced {\n", indent) - out += fmt.Sprintf("%s\treturn ackerr.ResourceReferenceNotSyncedFor(\n", indent) - out += fmt.Sprintf("%s\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) - out += fmt.Sprintf("%s\t\tnamespace, *arr.Name)\n", indent) - out += fmt.Sprintf("%s}\n", indent) nilCheck := CheckNilReferencesPath(field, "obj") if nilCheck != "" { - out += fmt.Sprintf("%sif %s {\n", indent, nilCheck) - out += fmt.Sprintf("%s\treturn ackerr.ResourceReferenceMissingTargetFieldFor(\n", indent) - out += fmt.Sprintf("%s\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) - out += fmt.Sprintf("%s\t\tnamespace, *arr.Name,\n", indent) - out += fmt.Sprintf("%s\t\t\"%s\")\n", indent, field.FieldConfig.References.Path) - out += fmt.Sprintf("%s}\n", indent) + out += fmt.Sprintf("%s\tif %s {\n", indent, nilCheck) + out += fmt.Sprintf("%s\t\treturn ackerr.ResourceReferenceMissingTargetFieldFor(\n", indent) + out += fmt.Sprintf("%s\t\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) + out += fmt.Sprintf("%s\t\t\tnamespace, *arr.Name,\n", indent) + out += fmt.Sprintf("%s\t\t\t\"%s\")\n", indent, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s\t}\n", indent) } + if shouldAppendTarget { + out += fmt.Sprintf("%s\t%s = append(%s, string(*obj.%s))\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path) + } else { + out += fmt.Sprintf("%s\t%s := string(*obj.%s)\n", indent, targetVarName, field.FieldConfig.References.Path) + } + out += fmt.Sprintf("%s}\n", indent) + return out } diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index 167a6929..98d07011 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -211,8 +211,8 @@ func Test_ReferenceForField_SingleReference(t *testing.T) { crd := testutil.GetCRDByName(t, g, "Integration") require.NotNil(crd) expected := - ` if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil { - arr := ko.Spec.APIRef.From + ` if ko.Spec.APIID != nil && ko.Spec.APIID.From != nil { + arr := ko.Spec.APIID.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } @@ -252,13 +252,13 @@ func Test_ReferenceForField_SingleReference(t *testing.T) { namespace, *arr.Name, "Status.APIID") } - referencedValue := string(*obj.Status.APIID) - ko.Spec.APIID = &referencedValue + refVal := string(*obj.Status.APIID) } - return nil` + ko.Spec.APIID = &refVal +` field := crd.Fields["APIID"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) + assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) } func Test_ReferenceForField_SliceOfReferences(t *testing.T) { @@ -273,11 +273,10 @@ func Test_ReferenceForField_SliceOfReferences(t *testing.T) { crd := testutil.GetCRDByName(t, g, "VpcLink") require.NotNil(crd) expected := - ` if ko.Spec.SecurityGroupRefs != nil && - len(ko.Spec.SecurityGroupRefs) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.SecurityGroupRefs { - arr := arrw.From + ` refVals := []*string{} + for _, iter0 := range ko.Spec.SecurityGroupIDs { + if iter0 != nil && iter0.From != nil { + arr := iter0.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } @@ -317,15 +316,14 @@ func Test_ReferenceForField_SliceOfReferences(t *testing.T) { namespace, *arr.Name, "Status.ID") } - referencedValue := string(*obj.Status.ID) - resolvedReferences = append(resolvedReferences, &referencedValue) + refVals = append(refVals, string(*obj.Status.ID)) } - ko.Spec.SecurityGroupIDs = resolvedReferences } - return nil` + ko.Spec.SecurityGroupIDs = refVals +` field := crd.Fields["SecurityGroupIDs"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) + assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) } func Test_ReferenceForField_NestedSingleReference(t *testing.T) { @@ -340,11 +338,9 @@ func Test_ReferenceForField_NestedSingleReference(t *testing.T) { crd := testutil.GetCRDByName(t, g, "Authorizer") require.NotNil(crd) expected := - ` if ko.Spec.JWTConfiguration.IssuerRef != nil && - len(ko.Spec.JWTConfiguration.IssuerRef) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.JWTConfiguration.IssuerRef { - arr := arrw.From + ` if ko.Spec.JWTConfiguration != nil { + if ko.Spec.JWTConfiguration.Issuer != nil && ko.Spec.JWTConfiguration.Issuer.From != nil { + arr := ko.Spec.JWTConfiguration.Issuer.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } @@ -384,15 +380,14 @@ func Test_ReferenceForField_NestedSingleReference(t *testing.T) { namespace, *arr.Name, "Status.APIID") } - referencedValue := string(*obj.Status.APIID) - resolvedReferences = append(resolvedReferences, &referencedValue) + refVal := string(*obj.Status.APIID) } - ko.Spec.JWTConfiguration.Issuer = resolvedReferences + ko.Spec.JWTConfiguration.Issuer = &refVal } - return nil` +` field := crd.Fields["JWTConfiguration.Issuer"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) + assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) } func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { @@ -409,57 +404,56 @@ func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { // the Go template has the appropriate nil checks to ensure the parent path exists expected := - ` if ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.BucketRef != nil && - len(ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.BucketRef) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.BucketRef { - arr := arrw.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.Bucket{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true + ` if ko.Spec.Logging != nil { + if ko.Spec.Logging.LoggingEnabled != nil { + if ko.Spec.Logging.LoggingEnabled.TargetBucket != nil && ko.Spec.Logging.LoggingEnabled.TargetBucket.From != nil { + arr := ko.Spec.Logging.LoggingEnabled.TargetBucket.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, } + obj := svcapitypes.Bucket{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "Bucket", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "Bucket", + namespace, *arr.Name) + } + if obj.Spec.Name == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "Bucket", + namespace, *arr.Name, + "Spec.Name") + } + refVal := string(*obj.Spec.Name) } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "Bucket", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "Bucket", - namespace, *arr.Name) - } - if obj.Spec.Name == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "Bucket", - namespace, *arr.Name, - "Spec.Name") - } - referencedValue := string(*obj.Spec.Name) - resolvedReferences = append(resolvedReferences, &referencedValue) + ko.Spec.Logging.LoggingEnabled.TargetBucket = &refVal } - ko.Spec.Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.Bucket = resolvedReferences } - return nil` +` - field := crd.Fields["Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.Bucket"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, 1)) + field := crd.Fields["Logging.LoggingEnabled.TargetBucket"] + assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) } diff --git a/pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml b/pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml index 4b914637..19211326 100644 --- a/pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml +++ b/pkg/testdata/models/apis/s3/0000-00-00/generator-with-nested-references.yaml @@ -24,10 +24,11 @@ resources: match_fields: - Name fields: - Analytics: - custom_field: - list_of: AnalyticsConfiguration - Analytics.StorageClassAnalysis.DataExport.Destination.S3BucketDestination.Bucket: + Logging: + from: + operation: PutBucketLogging + path: BucketLoggingStatus + Logging.LoggingEnabled.TargetBucket: references: resource: Bucket path: Spec.Name diff --git a/templates/pkg/resource/references.go.tpl b/templates/pkg/resource/references.go.tpl index 6f823de8..d398d690 100644 --- a/templates/pkg/resource/references.go.tpl +++ b/templates/pkg/resource/references.go.tpl @@ -127,8 +127,6 @@ func resolveReferenceFor{{ $field.FieldPathWithUnderscore }}( referencedValue := string(*obj.{{ $field.FieldConfig.References.Path }}) ko.Spec.{{ $field.Path }} = &referencedValue } - return nil -} {{ else if not $isNested -}} if ko.Spec.{{ $field.ReferenceFieldPath }} != nil && len(ko.Spec.{{ $field.ReferenceFieldPath }}) > 0 { @@ -141,8 +139,7 @@ func resolveReferenceFor{{ $field.FieldPathWithUnderscore }}( } ko.Spec.{{ $field.Path }} = resolvedReferences } - return nil -} + {{ else }} {{ $parentField := index .CRD.Fields $fp.String }} {{ if eq $parentField.ShapeRef.Shape.Type "list" -}} @@ -164,8 +161,6 @@ func resolveReferenceFor{{ $field.FieldPathWithUnderscore }}( elem.{{ $field.Names.Camel }} = &referencedValue } } - return nil -} {{ else -}} if ko.Spec.{{ $field.ReferenceFieldPath }} != nil && len(ko.Spec.{{ $field.ReferenceFieldPath }}) > 0 { @@ -178,9 +173,9 @@ func resolveReferenceFor{{ $field.FieldPathWithUnderscore }}( } ko.Spec.{{ $field.Path }} = resolvedReferences } +{{ end -}} return nil } {{ end -}} {{ end -}} {{ end -}} -{{ end -}} From be6c65289b7f6951786a89f1059a74df55743934 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Sat, 4 Feb 2023 01:29:20 +0000 Subject: [PATCH 3/9] Fix nested struct detection --- pkg/generate/ack/controller.go | 2 +- pkg/generate/code/resource_reference.go | 117 +++++++++--------- pkg/generate/code/resource_reference_test.go | 23 ++-- templates/pkg/resource/references.go.tpl | 63 +--------- ...references_read_referenced_resource.go.tpl | 54 -------- 5 files changed, 74 insertions(+), 185 deletions(-) delete mode 100644 templates/pkg/resource/references_read_referenced_resource.go.tpl diff --git a/pkg/generate/ack/controller.go b/pkg/generate/ack/controller.go index c4443b85..ff19a68b 100644 --- a/pkg/generate/ack/controller.go +++ b/pkg/generate/ack/controller.go @@ -183,7 +183,7 @@ var ( return code.InitializeNestedStructField(r, sourceVarName, f, apiPkgImportName, indentLevel) }, - "GoCodeReferenceForField": func(r *ackmodel.CRD, f *ackmodel.Field, sourceVarName string, indentLevel int) string { + "GoCodeResolveReference": func(r *ackmodel.CRD, f *ackmodel.Field, sourceVarName string, indentLevel int) string { return code.ResolveReferencesForField(r, f, sourceVarName, indentLevel) }, } diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 7c72f564..cb275173 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -190,55 +190,55 @@ func ReferenceFieldsPresent( // condition and updating the concrete field with the referenced value. // Sample code: // -// if ko.Spec.SecurityGroupRefs != nil && -// len(ko.Spec.SecurityGroupRefs) > 0 { -// resolvedReferences := []*string{} -// for _, arrw := range ko.Spec.SecurityGroupRefs { -// arr := arrw.From -// if arr == nil || arr.Name == nil || *arr.Name == "" { -// return fmt.Errorf("provided resource reference is nil or empty") -// } -// namespacedName := types.NamespacedName{ -// Namespace: namespace, -// Name: *arr.Name, -// } -// obj := ec2apitypes.SecurityGroup{} -// err := apiReader.Get(ctx, namespacedName, &obj) -// if err != nil { -// return err -// } -// var refResourceSynced, refResourceTerminal bool -// for _, cond := range obj.Status.Conditions { -// if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && -// cond.Status == corev1.ConditionTrue { -// refResourceSynced = true -// } -// if cond.Type == ackv1alpha1.ConditionTypeTerminal && -// cond.Status == corev1.ConditionTrue { -// refResourceTerminal = true -// } -// } -// if refResourceTerminal { -// return ackerr.ResourceReferenceTerminalFor( -// "SecurityGroup", -// namespace, *arr.Name) -// } -// if !refResourceSynced { -// return ackerr.ResourceReferenceNotSyncedFor( -// "SecurityGroup", -// namespace, *arr.Name) +// ``` +// refVal := "" +// +// if ko.Spec.TargetRef != nil && ko.Spec.TargetRef.From != nil { +// arr := ko.Spec.TargetRef.From +// if arr == nil || arr.Name == nil || *arr.Name == "" { +// return fmt.Errorf("provided resource reference is nil or empty") +// } +// namespacedName := types.NamespacedName{ +// Namespace: namespace, +// Name: *arr.Name, +// } +// obj := svcapitypes.Integration{} +// err := apiReader.Get(ctx, namespacedName, &obj) +// if err != nil { +// return err +// } +// var refResourceSynced, refResourceTerminal bool +// for _, cond := range obj.Status.Conditions { +// if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && +// cond.Status == corev1.ConditionTrue { +// refResourceSynced = true // } -// if obj.Status.ID == nil { -// return ackerr.ResourceReferenceMissingTargetFieldFor( -// "SecurityGroup", -// namespace, *arr.Name, -// "Status.ID") +// if cond.Type == ackv1alpha1.ConditionTypeTerminal && +// cond.Status == corev1.ConditionTrue { +// refResourceTerminal = true // } -// referencedValue := string(*obj.Status.ID) -// resolvedReferences = append(resolvedReferences, &referencedValue) // } -// ko.Spec.SecurityGroupIDs = resolvedReferences +// if refResourceTerminal { +// return ackerr.ResourceReferenceTerminalFor( +// "Integration", +// namespace, *arr.Name) +// } +// if !refResourceSynced { +// return ackerr.ResourceReferenceNotSyncedFor( +// "Integration", +// namespace, *arr.Name) +// } +// if obj.Status.IntegrationID == nil { +// return ackerr.ResourceReferenceMissingTargetFieldFor( +// "Integration", +// namespace, *arr.Name, +// "Status.IntegrationID") +// } +// refVal = string(*obj.Status.IntegrationID) // } +// +// ko.Spec.Target = &refVal +// ``` func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName string, indentLevel int) string { fp := fieldpath.FromString(field.Path) @@ -248,7 +248,6 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s fieldAccessPrefix := fmt.Sprintf("%s%s", sourceVarName, r.Config().PrefixConfig.SpecField) targetVarName := fmt.Sprintf("%s%s.%s", sourceVarName, r.Config().PrefixConfig.SpecField, field.Path) - hasSeenList := false for idx := 0; idx < fp.Size(); idx++ { curFP := fp.CopyAt(idx).String() cur, ok := r.Fields[curFP] @@ -259,26 +258,23 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s ref := cur.ShapeRef indent := strings.Repeat("\t", indentLevel+idx) - fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx)) if ref.Shape.Type == "structure" { - if hasSeenList { - // TODO(nithomso): add support for structs nested within lists - // The logic for structs nested within lists needs to not only - // be added here, but also in a custom patching solution since - // it isn't supported by `StrategicMergePatch` - // see community#1291 for more details - panic("references within structs inside lists aren't supported") - } + fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx)) outPrefix += fmt.Sprintf("%sif %s != nil {\n", indent, fieldAccessPrefix) outSuffix = fmt.Sprintf("%s}\n%s", indent, outSuffix) } else if ref.Shape.Type == "list" { - if hasSeenList { + if (fp.Size() - idx) > 1 { + // TODO(nithomso): add support for structs nested within lists + // The logic for structs nested within lists needs to not only + // be added here, but also in a custom patching solution since + // it isn't supported by `StrategicMergePatch` + // see https://github.com/aws-controllers-k8s/community/issues/1291 panic("references within lists inside lists aren't supported") } + fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx)) - hasSeenList = true iterVarName := fmt.Sprintf("iter%d", idx) refsTarget := "refVals" @@ -295,6 +291,9 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s } else { // base case for single references refTarget := "refVal" + fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, cur.GetReferenceFieldName().Camel) + + outPrefix += fmt.Sprintf("%s%s := \"\"\n", indent, refTarget) outPrefix += resolveSingleReference(field, fieldAccessPrefix, refTarget, false, indentLevel+idx) outPrefix += fmt.Sprintf("%s%s = &%s\n", indent, targetVarName, refTarget) } @@ -358,9 +357,9 @@ func resolveSingleReference(field *model.Field, sourceVarName string, targetVarN } if shouldAppendTarget { - out += fmt.Sprintf("%s\t%s = append(%s, string(*obj.%s))\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s\t%s = append(%s, obj.%s)\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path) } else { - out += fmt.Sprintf("%s\t%s := string(*obj.%s)\n", indent, targetVarName, field.FieldConfig.References.Path) + out += fmt.Sprintf("%s\t%s = string(*obj.%s)\n", indent, targetVarName, field.FieldConfig.References.Path) } out += fmt.Sprintf("%s}\n", indent) diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index 98d07011..a9e678ac 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -211,8 +211,9 @@ func Test_ReferenceForField_SingleReference(t *testing.T) { crd := testutil.GetCRDByName(t, g, "Integration") require.NotNil(crd) expected := - ` if ko.Spec.APIID != nil && ko.Spec.APIID.From != nil { - arr := ko.Spec.APIID.From + ` refVal := "" + if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil { + arr := ko.Spec.APIRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } @@ -252,7 +253,7 @@ func Test_ReferenceForField_SingleReference(t *testing.T) { namespace, *arr.Name, "Status.APIID") } - refVal := string(*obj.Status.APIID) + refVal = string(*obj.Status.APIID) } ko.Spec.APIID = &refVal ` @@ -316,7 +317,7 @@ func Test_ReferenceForField_SliceOfReferences(t *testing.T) { namespace, *arr.Name, "Status.ID") } - refVals = append(refVals, string(*obj.Status.ID)) + refVals = append(refVals, obj.Status.ID) } } ko.Spec.SecurityGroupIDs = refVals @@ -339,8 +340,9 @@ func Test_ReferenceForField_NestedSingleReference(t *testing.T) { require.NotNil(crd) expected := ` if ko.Spec.JWTConfiguration != nil { - if ko.Spec.JWTConfiguration.Issuer != nil && ko.Spec.JWTConfiguration.Issuer.From != nil { - arr := ko.Spec.JWTConfiguration.Issuer.From + refVal := "" + if ko.Spec.JWTConfiguration.IssuerRef != nil && ko.Spec.JWTConfiguration.IssuerRef.From != nil { + arr := ko.Spec.JWTConfiguration.IssuerRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } @@ -380,7 +382,7 @@ func Test_ReferenceForField_NestedSingleReference(t *testing.T) { namespace, *arr.Name, "Status.APIID") } - refVal := string(*obj.Status.APIID) + refVal = string(*obj.Status.APIID) } ko.Spec.JWTConfiguration.Issuer = &refVal } @@ -406,8 +408,9 @@ func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { expected := ` if ko.Spec.Logging != nil { if ko.Spec.Logging.LoggingEnabled != nil { - if ko.Spec.Logging.LoggingEnabled.TargetBucket != nil && ko.Spec.Logging.LoggingEnabled.TargetBucket.From != nil { - arr := ko.Spec.Logging.LoggingEnabled.TargetBucket.From + refVal := "" + if ko.Spec.Logging.LoggingEnabled.TargetBucketRef != nil && ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From != nil { + arr := ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } @@ -447,7 +450,7 @@ func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { namespace, *arr.Name, "Spec.Name") } - refVal := string(*obj.Spec.Name) + refVal = string(*obj.Spec.Name) } ko.Spec.Logging.LoggingEnabled.TargetBucket = &refVal } diff --git a/templates/pkg/resource/references.go.tpl b/templates/pkg/resource/references.go.tpl index d398d690..c55eb71d 100644 --- a/templates/pkg/resource/references.go.tpl +++ b/templates/pkg/resource/references.go.tpl @@ -115,67 +115,8 @@ func resolveReferenceFor{{ $field.FieldPathWithUnderscore }}( } {{ end -}} -{{- $fp := ConstructFieldPath $field.Path -}} -{{ $_ := $fp.Pop -}} -{{ $isNested := gt $fp.Size 0 -}} -{{ $isList := eq $field.ShapeRef.Shape.Type "list" -}} -{{ if and (not $isList) (not $isNested) -}} - if ko.Spec.{{ $field.ReferenceFieldPath }} != nil && - ko.Spec.{{ $field.ReferenceFieldPath }}.From != nil { - arr := ko.Spec.{{ $field.ReferenceFieldPath }}.From -{{ template "read_referenced_resource_and_validate" $field }} - referencedValue := string(*obj.{{ $field.FieldConfig.References.Path }}) - ko.Spec.{{ $field.Path }} = &referencedValue - } -{{ else if not $isNested -}} - if ko.Spec.{{ $field.ReferenceFieldPath }} != nil && - len(ko.Spec.{{ $field.ReferenceFieldPath }}) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.{{ $field.ReferenceFieldPath }} { - arr := arrw.From -{{ template "read_referenced_resource_and_validate" $field }} - referencedValue := string(*obj.{{ $field.FieldConfig.References.Path }}) - resolvedReferences = append(resolvedReferences, &referencedValue) - } - ko.Spec.{{ $field.Path }} = resolvedReferences - } - -{{ else }} -{{ $parentField := index .CRD.Fields $fp.String }} -{{ if eq $parentField.ShapeRef.Shape.Type "list" -}} - if len(ko.Spec.{{ $parentField.Path }}) > 0 { - for _, elem := range ko.Spec.{{ $parentField.Path }} { - arrw := elem.{{ $field.GetReferenceFieldName.Camel }} - - if arrw == nil || arrw.From == nil { - continue - } - - arr := arrw.From - if arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - -{{ template "read_referenced_resource_and_validate" $field }} - referencedValue := string(*obj.{{ $field.FieldConfig.References.Path }}) - elem.{{ $field.Names.Camel }} = &referencedValue - } - } -{{ else -}} - if ko.Spec.{{ $field.ReferenceFieldPath }} != nil && - len(ko.Spec.{{ $field.ReferenceFieldPath }}) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.{{ $field.ReferenceFieldPath }} { - arr := arrw.From -{{ template "read_referenced_resource_and_validate" $field }} - referencedValue := string(*obj.{{ $field.FieldConfig.References.Path }}) - resolvedReferences = append(resolvedReferences, &referencedValue) - } - ko.Spec.{{ $field.Path }} = resolvedReferences - } -{{ end -}} +{{ GoCodeResolveReference .CRD $field "ko" 1 }} return nil } {{ end -}} -{{ end -}} -{{ end -}} +{{ end -}} \ No newline at end of file diff --git a/templates/pkg/resource/references_read_referenced_resource.go.tpl b/templates/pkg/resource/references_read_referenced_resource.go.tpl deleted file mode 100644 index 72349b5d..00000000 --- a/templates/pkg/resource/references_read_referenced_resource.go.tpl +++ /dev/null @@ -1,54 +0,0 @@ -{{/* -read_referenced_resource_and_validate" template should be invoked with a field as -parameter -Ex: {{ template "read_referenced_resource_and_validate" $field }} -Where field is of type 'Field' from aws-controllers-k8s/code-generator/pkg/model - */}} -{{- define "read_referenced_resource_and_validate" -}} - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - {{ if eq .FieldConfig.References.ServiceName "" -}} - obj := svcapitypes.{{ .FieldConfig.References.Resource }}{} - {{ else -}} - obj := {{ .ReferencedServiceName }}apitypes.{{ .FieldConfig.References.Resource }}{} - {{ end -}} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "{{ .FieldConfig.References.Resource }}", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "{{ .FieldConfig.References.Resource }}", - namespace, *arr.Name) - } - {{ $nilCheck := CheckNilReferencesPath . "obj" -}} - {{ if not (eq $nilCheck "") -}} - if {{ $nilCheck }} { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "{{ .FieldConfig.References.Resource }}", - namespace, *arr.Name, - "{{ .FieldConfig.References.Path }}") - } - {{- end -}} -{{- end -}} From 798b8a5008d5516c010895156b49f4f3c677e222 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Wed, 8 Feb 2023 23:59:23 +0000 Subject: [PATCH 4/9] Use template file for get reference value --- pkg/generate/code/resource_reference.go | 104 +++-------- pkg/generate/code/resource_reference_test.go | 174 ++---------------- templates/pkg/resource/references.go.tpl | 6 +- ...references_read_referenced_resource.go.tpl | 62 +++++++ 4 files changed, 114 insertions(+), 232 deletions(-) create mode 100644 templates/pkg/resource/references_read_referenced_resource.go.tpl diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index cb275173..6a99f4f4 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -252,7 +252,7 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s curFP := fp.CopyAt(idx).String() cur, ok := r.Fields[curFP] if !ok { - panic(fmt.Sprintf("unable to find field with path %s", curFP)) + panic(fmt.Sprintf("unable to find field with path %q. crd: %q", curFP, r.Kind)) } ref := cur.ShapeRef @@ -271,101 +271,55 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s // be added here, but also in a custom patching solution since // it isn't supported by `StrategicMergePatch` // see https://github.com/aws-controllers-k8s/community/issues/1291 - panic("references within lists inside lists aren't supported") + panic(fmt.Errorf("references within lists inside lists aren't supported. crd: %q, path: %q", r.Kind, field.Path)) } fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx)) iterVarName := fmt.Sprintf("iter%d", idx) - refsTarget := "refVals" // base case for references in a list - outPrefix += fmt.Sprintf("%s%s := []*string{}\n", indent, refsTarget) + outPrefix += fmt.Sprintf("%s%s = %s{}\n", indent, targetVarName, field.GoType) outPrefix += fmt.Sprintf("%sfor _, %s := range %s {\n", indent, iterVarName, fieldAccessPrefix) fieldAccessPrefix = iterVarName - outPrefix += resolveSingleReference(field, fieldAccessPrefix, refsTarget, true, indentLevel+idx+1) - outSuffix = fmt.Sprintf("%s%s = %s\n%s", indent, targetVarName, refsTarget, outSuffix) - outSuffix = fmt.Sprintf("%s}\n%s", indent, outSuffix) + outPrefix += fmt.Sprintf("%s\tarr := %s.From\n", indent, fieldAccessPrefix) + outPrefix += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) + outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) + outPrefix += fmt.Sprintf("%s\t}\n", indent) + + outPrefix += fmt.Sprintf("%s\tif err := getReferencedResourceState_VPCLink(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n", indent) + outPrefix += fmt.Sprintf("%s\t\treturn err\n", indent) + outPrefix += fmt.Sprintf("%s\t}\n", indent) + outPrefix += fmt.Sprintf("%s\t%s = append(%s, obj.%s)\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path) + outPrefix += fmt.Sprintf("%s}\n", indent) } else if ref.Shape.Type == "map" { panic("references cannot be within a map") } else { // base case for single references - refTarget := "refVal" fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, cur.GetReferenceFieldName().Camel) - outPrefix += fmt.Sprintf("%s%s := \"\"\n", indent, refTarget) - outPrefix += resolveSingleReference(field, fieldAccessPrefix, refTarget, false, indentLevel+idx) - outPrefix += fmt.Sprintf("%s%s = &%s\n", indent, targetVarName, refTarget) + outPrefix += fmt.Sprintf("%sif %s != nil && %s.From != nil {\n", indent, fieldAccessPrefix, fieldAccessPrefix) + outPrefix += fmt.Sprintf("%s\tarr := %s.From\n", indent, fieldAccessPrefix) + outPrefix += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) + outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) + outPrefix += fmt.Sprintf("%s\t}\n", indent) + + if field.FieldConfig.References.ServiceName == "" { + outPrefix += fmt.Sprintf("%s\tobj := &svcapitypes.%s{}\n", indent, field.FieldConfig.References.Resource) + } else { + outPrefix += fmt.Sprintf("%s\tobj := &%sapitypes.%s{}\n", indent, field.ReferencedServiceName(), field.FieldConfig.References.Resource) + } + outPrefix += fmt.Sprintf("%s\tif err := getReferencedResourceState_%s(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n", indent, field.FieldConfig.References.Resource) + outPrefix += fmt.Sprintf("%s\t\treturn err\n", indent) + outPrefix += fmt.Sprintf("%s\t}\n", indent) + outPrefix += fmt.Sprintf("%s\t%s = obj.%s\n", indent, targetVarName, field.FieldConfig.References.Path) + outPrefix += fmt.Sprintf("%s}\n", indent) } } return outPrefix + outSuffix } -func resolveSingleReference(field *model.Field, sourceVarName string, targetVarName string, shouldAppendTarget bool, indentLevel int) string { - out := "" - indent := strings.Repeat("\t", indentLevel) - - out += fmt.Sprintf("%sif %s != nil && %s.From != nil {\n", indent, sourceVarName, sourceVarName) - out += fmt.Sprintf("%s\tarr := %s.From\n", indent, sourceVarName) - out += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) - out += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tnamespacedName := types.NamespacedName{\n", indent) - out += fmt.Sprintf("%s\t\tNamespace: namespace,\n", indent) - out += fmt.Sprintf("%s\t\tName: *arr.Name,\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - if field.FieldConfig.References.ServiceName == "" { - out += fmt.Sprintf("%s\tobj := svcapitypes.%s{}\n", indent, field.FieldConfig.References.Resource) - } else { - out += fmt.Sprintf("%s\tobj := %sapitypes.%s{}\n", indent, field.ReferencedServiceName(), field.FieldConfig.References.Resource) - } - out += fmt.Sprintf("%s\terr := apiReader.Get(ctx, namespacedName, &obj)\n", indent) - out += fmt.Sprintf("%s\tif err != nil {\n", indent) - out += fmt.Sprintf("%s\t\treturn err\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tvar refResourceSynced, refResourceTerminal bool\n", indent) - out += fmt.Sprintf("%s\tfor _, cond := range obj.Status.Conditions {\n", indent) - out += fmt.Sprintf("%s\t\tif cond.Type == ackv1alpha1.ConditionTypeResourceSynced &&\n", indent) - out += fmt.Sprintf("%s\t\t\tcond.Status == corev1.ConditionTrue {\n", indent) - out += fmt.Sprintf("%s\t\t\trefResourceSynced = true\n", indent) - out += fmt.Sprintf("%s\t\t}\n", indent) - out += fmt.Sprintf("%s\t\tif cond.Type == ackv1alpha1.ConditionTypeTerminal &&\n", indent) - out += fmt.Sprintf("%s\t\t\tcond.Status == corev1.ConditionTrue {\n", indent) - out += fmt.Sprintf("%s\t\t\trefResourceTerminal = true\n", indent) - out += fmt.Sprintf("%s\t\t}\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tif refResourceTerminal {\n", indent) - out += fmt.Sprintf("%s\t\treturn ackerr.ResourceReferenceTerminalFor(\n", indent) - out += fmt.Sprintf("%s\t\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) - out += fmt.Sprintf("%s\t\t\tnamespace, *arr.Name)\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tif !refResourceSynced {\n", indent) - out += fmt.Sprintf("%s\t\treturn ackerr.ResourceReferenceNotSyncedFor(\n", indent) - out += fmt.Sprintf("%s\t\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) - out += fmt.Sprintf("%s\t\t\tnamespace, *arr.Name)\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - - nilCheck := CheckNilReferencesPath(field, "obj") - if nilCheck != "" { - out += fmt.Sprintf("%s\tif %s {\n", indent, nilCheck) - out += fmt.Sprintf("%s\t\treturn ackerr.ResourceReferenceMissingTargetFieldFor(\n", indent) - out += fmt.Sprintf("%s\t\t\t\"%s\",\n", indent, field.FieldConfig.References.Resource) - out += fmt.Sprintf("%s\t\t\tnamespace, *arr.Name,\n", indent) - out += fmt.Sprintf("%s\t\t\t\"%s\")\n", indent, field.FieldConfig.References.Path) - out += fmt.Sprintf("%s\t}\n", indent) - } - - if shouldAppendTarget { - out += fmt.Sprintf("%s\t%s = append(%s, obj.%s)\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path) - } else { - out += fmt.Sprintf("%s\t%s = string(*obj.%s)\n", indent, targetVarName, field.FieldConfig.References.Path) - } - out += fmt.Sprintf("%s}\n", indent) - - return out -} - func nestedStructNilCheck(path fieldpath.Path, fieldAccessPrefix string) string { out := "" fieldNamePrefix := "" diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index a9e678ac..718b6873 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -211,51 +211,17 @@ func Test_ReferenceForField_SingleReference(t *testing.T) { crd := testutil.GetCRDByName(t, g, "Integration") require.NotNil(crd) expected := - ` refVal := "" - if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil { + ` if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil { arr := ko.Spec.APIRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.API{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.API{} + if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "API", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "API", - namespace, *arr.Name) - } - if obj.Status.APIID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "API", - namespace, *arr.Name, - "Status.APIID") - } - refVal = string(*obj.Status.APIID) + ko.Spec.APIID = obj.Status.APIID } - ko.Spec.APIID = &refVal ` field := crd.Fields["APIID"] @@ -274,53 +240,17 @@ func Test_ReferenceForField_SliceOfReferences(t *testing.T) { crd := testutil.GetCRDByName(t, g, "VpcLink") require.NotNil(crd) expected := - ` refVals := []*string{} + ` ko.Spec.SecurityGroupIDs = []*string{} for _, iter0 := range ko.Spec.SecurityGroupIDs { - if iter0 != nil && iter0.From != nil { - arr := iter0.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := ec2apitypes.SecurityGroup{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "SecurityGroup", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "SecurityGroup", - namespace, *arr.Name) - } - if obj.Status.ID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "SecurityGroup", - namespace, *arr.Name, - "Status.ID") - } - refVals = append(refVals, obj.Status.ID) + arr := iter0.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + if err := getReferencedResourceState_VPCLink(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return err } + ko.Spec.SecurityGroupIDs = append(ko.Spec.SecurityGroupIDs, obj.Status.ID) } - ko.Spec.SecurityGroupIDs = refVals ` field := crd.Fields["SecurityGroupIDs"] @@ -340,51 +270,17 @@ func Test_ReferenceForField_NestedSingleReference(t *testing.T) { require.NotNil(crd) expected := ` if ko.Spec.JWTConfiguration != nil { - refVal := "" if ko.Spec.JWTConfiguration.IssuerRef != nil && ko.Spec.JWTConfiguration.IssuerRef.From != nil { arr := ko.Spec.JWTConfiguration.IssuerRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.API{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.API{} + if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "API", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "API", - namespace, *arr.Name) - } - if obj.Status.APIID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "API", - namespace, *arr.Name, - "Status.APIID") - } - refVal = string(*obj.Status.APIID) + ko.Spec.JWTConfiguration.Issuer = obj.Status.APIID } - ko.Spec.JWTConfiguration.Issuer = &refVal } ` @@ -408,51 +304,17 @@ func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { expected := ` if ko.Spec.Logging != nil { if ko.Spec.Logging.LoggingEnabled != nil { - refVal := "" if ko.Spec.Logging.LoggingEnabled.TargetBucketRef != nil && ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From != nil { arr := ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.Bucket{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.Bucket{} + if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "Bucket", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "Bucket", - namespace, *arr.Name) - } - if obj.Spec.Name == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "Bucket", - namespace, *arr.Name, - "Spec.Name") - } - refVal = string(*obj.Spec.Name) + ko.Spec.Logging.LoggingEnabled.TargetBucket = obj.Spec.Name } - ko.Spec.Logging.LoggingEnabled.TargetBucket = &refVal } } ` diff --git a/templates/pkg/resource/references.go.tpl b/templates/pkg/resource/references.go.tpl index c55eb71d..68ca586c 100644 --- a/templates/pkg/resource/references.go.tpl +++ b/templates/pkg/resource/references.go.tpl @@ -118,5 +118,9 @@ func resolveReferenceFor{{ $field.FieldPathWithUnderscore }}( {{ GoCodeResolveReference .CRD $field "ko" 1 }} return nil } + +{{ template "read_referenced_resource_and_validate" $field }} + {{ end -}} -{{ end -}} \ No newline at end of file +{{ end -}} + diff --git a/templates/pkg/resource/references_read_referenced_resource.go.tpl b/templates/pkg/resource/references_read_referenced_resource.go.tpl new file mode 100644 index 00000000..c5131b8d --- /dev/null +++ b/templates/pkg/resource/references_read_referenced_resource.go.tpl @@ -0,0 +1,62 @@ +{{/* +"read_referenced_resource_and_validate" template should be invoked with a field as +parameter +Ex: {{ template "read_referenced_resource_and_validate" $field }} +Where field is of type 'Field' from aws-controllers-k8s/code-generator/pkg/model + */}} +{{- define "read_referenced_resource_and_validate" -}} +{{- $objType := ( printf "%sapitypes.%s" .ReferencedServiceName .FieldConfig.References.Resource ) -}} +{{- if eq .FieldConfig.References.ServiceName "" -}} +{{- $objType = ( printf "svcapitypes.%s" .FieldConfig.References.Resource) -}} +{{ end -}} +// getReferencedResourceState_{{ .FieldConfig.References.Resource }} looks up whether a referenced resource +// exists and is in a ACK.ResourceSynced=True state. If the referenced resource does exist and is +// in a Synced state, returns nil, otherwise returns `ackerr.ResourceReferenceTerminalFor` or +// `ResourceReferenceNotSyncedFor` depending on if the resource is in a Terminal state. +func getReferencedResourceState_{{ .FieldConfig.References.Resource }}( + ctx context.Context, + apiReader client.Reader, + obj *{{ $objType }}, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource +) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "{{ .FieldConfig.References.Resource }}", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "{{ .FieldConfig.References.Resource }}", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "{{ .FieldConfig.References.Resource }}", + namespace, name) + } + if {{ CheckNilReferencesPath . "obj" }} { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "{{ .FieldConfig.References.Resource }}", + namespace, name, + "{{ .FieldConfig.References.Path }}") + } + return nil +} +{{- end -}} From 52bdff9bc2cac31a98576669eec1910afb663b7a Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Wed, 8 Feb 2023 16:00:24 -0800 Subject: [PATCH 5/9] Update pkg/generate/code/resource_reference.go Co-authored-by: Amine --- pkg/generate/code/resource_reference.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 6a99f4f4..eb254406 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -246,7 +246,7 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s outSuffix := "" fieldAccessPrefix := fmt.Sprintf("%s%s", sourceVarName, r.Config().PrefixConfig.SpecField) - targetVarName := fmt.Sprintf("%s%s.%s", sourceVarName, r.Config().PrefixConfig.SpecField, field.Path) + targetVarName := fmt.Sprintf("%s.%s", fieldAccessPrefix, field.Path) for idx := 0; idx < fp.Size(); idx++ { curFP := fp.CopyAt(idx).String() From 2936bef2d7fabfd36d2cac0c65415504c362b6b4 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Thu, 9 Feb 2023 00:06:52 +0000 Subject: [PATCH 6/9] Improve error condition messages --- pkg/generate/code/resource_reference.go | 8 ++++---- pkg/generate/code/resource_reference_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index eb254406..911e8acc 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -98,7 +98,7 @@ func ReferenceFieldsValidation( out += fmt.Sprintf("%sif %s.%s != nil"+ " && %s.%s != nil {\n", fIndent, pathVarPrefix, field.GetReferenceFieldName().Camel, pathVarPrefix, field.Names.Camel) out += fmt.Sprintf("%s\treturn "+ - "ackerr.ResourceReferenceAndIDNotSupportedFor(\"%s\", \"%s\")\n", + "ackerr.ResourceReferenceAndIDNotSupportedFor(%q, %q)\n", fIndent, field.Path, field.ReferenceFieldPath()) // Close out all the curly braces with proper indentation @@ -117,7 +117,7 @@ func ReferenceFieldsValidation( " %s.%s == nil {\n", fIndent, pathVarPrefix, field.ReferenceFieldPath(), pathVarPrefix, field.Path) out += fmt.Sprintf("%s\treturn "+ - "ackerr.ResourceReferenceOrIDRequiredFor(\"%s\", \"%s\")\n", + "ackerr.ResourceReferenceOrIDRequiredFor(%q, %q)\n", fIndent, field.Path, field.ReferenceFieldPath()) out += fmt.Sprintf("%s}\n", fIndent) } @@ -284,7 +284,7 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s fieldAccessPrefix = iterVarName outPrefix += fmt.Sprintf("%s\tarr := %s.From\n", indent, fieldAccessPrefix) outPrefix += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) - outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) + outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty: \\%q\\\")\n", indent, field.ReferenceFieldPath()) outPrefix += fmt.Sprintf("%s\t}\n", indent) outPrefix += fmt.Sprintf("%s\tif err := getReferencedResourceState_VPCLink(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n", indent) @@ -301,7 +301,7 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s outPrefix += fmt.Sprintf("%sif %s != nil && %s.From != nil {\n", indent, fieldAccessPrefix, fieldAccessPrefix) outPrefix += fmt.Sprintf("%s\tarr := %s.From\n", indent, fieldAccessPrefix) outPrefix += fmt.Sprintf("%s\tif arr == nil || arr.Name == nil || *arr.Name == \"\" {\n", indent) - outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty\")\n", indent) + outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty: \\%q\\\")\n", indent, field.ReferenceFieldPath()) outPrefix += fmt.Sprintf("%s\t}\n", indent) if field.FieldConfig.References.ServiceName == "" { diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index 718b6873..ad1dc53d 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -214,7 +214,7 @@ func Test_ReferenceForField_SingleReference(t *testing.T) { ` if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil { arr := ko.Spec.APIRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: \"APIRef"\") } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -244,7 +244,7 @@ func Test_ReferenceForField_SliceOfReferences(t *testing.T) { for _, iter0 := range ko.Spec.SecurityGroupIDs { arr := iter0.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: \"SecurityGroupRefs"\") } if err := getReferencedResourceState_VPCLink(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err @@ -273,7 +273,7 @@ func Test_ReferenceForField_NestedSingleReference(t *testing.T) { if ko.Spec.JWTConfiguration.IssuerRef != nil && ko.Spec.JWTConfiguration.IssuerRef.From != nil { arr := ko.Spec.JWTConfiguration.IssuerRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: \"JWTConfiguration.IssuerRef"\") } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -307,7 +307,7 @@ func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { if ko.Spec.Logging.LoggingEnabled.TargetBucketRef != nil && ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From != nil { arr := ko.Spec.Logging.LoggingEnabled.TargetBucketRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: \"Logging.LoggingEnabled.TargetBucketRef"\") } obj := &svcapitypes.Bucket{} if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { From f9c485bc2a3dbdb6ecd91265fd03fa8100c66f13 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Thu, 9 Feb 2023 00:09:10 +0000 Subject: [PATCH 7/9] Modify conditionals to use switch cases --- pkg/generate/code/resource_reference.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 911e8acc..1f04cac0 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -259,12 +259,13 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s indent := strings.Repeat("\t", indentLevel+idx) - if ref.Shape.Type == "structure" { + switch ref.Shape.Type { + case ("structure"): fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, fp.At(idx)) outPrefix += fmt.Sprintf("%sif %s != nil {\n", indent, fieldAccessPrefix) outSuffix = fmt.Sprintf("%s}\n%s", indent, outSuffix) - } else if ref.Shape.Type == "list" { + case ("list"): if (fp.Size() - idx) > 1 { // TODO(nithomso): add support for structs nested within lists // The logic for structs nested within lists needs to not only @@ -292,9 +293,9 @@ func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName s outPrefix += fmt.Sprintf("%s\t}\n", indent) outPrefix += fmt.Sprintf("%s\t%s = append(%s, obj.%s)\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path) outPrefix += fmt.Sprintf("%s}\n", indent) - } else if ref.Shape.Type == "map" { + case ("map"): panic("references cannot be within a map") - } else { + default: // base case for single references fieldAccessPrefix = fmt.Sprintf("%s.%s", fieldAccessPrefix, cur.GetReferenceFieldName().Camel) From c314ce1deeb4350691e8bebec43941feda408153 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Thu, 9 Feb 2023 00:14:57 +0000 Subject: [PATCH 8/9] Remove CRD as method input --- pkg/generate/ack/controller.go | 4 +- pkg/generate/code/resource_reference.go | 55 +++++--------------- pkg/generate/code/resource_reference_test.go | 16 +++--- 3 files changed, 22 insertions(+), 53 deletions(-) diff --git a/pkg/generate/ack/controller.go b/pkg/generate/ack/controller.go index ff19a68b..58dc1dd3 100644 --- a/pkg/generate/ack/controller.go +++ b/pkg/generate/ack/controller.go @@ -183,8 +183,8 @@ var ( return code.InitializeNestedStructField(r, sourceVarName, f, apiPkgImportName, indentLevel) }, - "GoCodeResolveReference": func(r *ackmodel.CRD, f *ackmodel.Field, sourceVarName string, indentLevel int) string { - return code.ResolveReferencesForField(r, f, sourceVarName, indentLevel) + "GoCodeResolveReference": func(f *ackmodel.Field, sourceVarName string, indentLevel int) string { + return code.ResolveReferencesForField(f, sourceVarName, indentLevel) }, } ) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 1f04cac0..517dedc4 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -188,58 +188,27 @@ func ReferenceFieldsPresent( // ResolveReferencesForField produces Go code for accessing all references that // are related to the given concrete field, determining whether its in a valid // condition and updating the concrete field with the referenced value. -// Sample code: +// Sample code (resolving a nested singular reference): // // ``` -// refVal := "" // -// if ko.Spec.TargetRef != nil && ko.Spec.TargetRef.From != nil { -// arr := ko.Spec.TargetRef.From -// if arr == nil || arr.Name == nil || *arr.Name == "" { -// return fmt.Errorf("provided resource reference is nil or empty") -// } -// namespacedName := types.NamespacedName{ -// Namespace: namespace, -// Name: *arr.Name, -// } -// obj := svcapitypes.Integration{} -// err := apiReader.Get(ctx, namespacedName, &obj) -// if err != nil { -// return err -// } -// var refResourceSynced, refResourceTerminal bool -// for _, cond := range obj.Status.Conditions { -// if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && -// cond.Status == corev1.ConditionTrue { -// refResourceSynced = true +// if ko.Spec.JWTConfiguration != nil { +// if ko.Spec.JWTConfiguration.IssuerRef != nil && ko.Spec.JWTConfiguration.IssuerRef.From != nil { +// arr := ko.Spec.JWTConfiguration.IssuerRef.From +// if arr == nil || arr.Name == nil || *arr.Name == "" { +// return fmt.Errorf("provided resource reference is nil or empty: \"JWTConfiguration.IssuerRef"\") // } -// if cond.Type == ackv1alpha1.ConditionTypeTerminal && -// cond.Status == corev1.ConditionTrue { -// refResourceTerminal = true +// obj := &svcapitypes.API{} +// if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { +// return err // } +// ko.Spec.JWTConfiguration.Issuer = obj.Status.APIID // } -// if refResourceTerminal { -// return ackerr.ResourceReferenceTerminalFor( -// "Integration", -// namespace, *arr.Name) -// } -// if !refResourceSynced { -// return ackerr.ResourceReferenceNotSyncedFor( -// "Integration", -// namespace, *arr.Name) -// } -// if obj.Status.IntegrationID == nil { -// return ackerr.ResourceReferenceMissingTargetFieldFor( -// "Integration", -// namespace, *arr.Name, -// "Status.IntegrationID") -// } -// refVal = string(*obj.Status.IntegrationID) // } // -// ko.Spec.Target = &refVal // ``` -func ResolveReferencesForField(r *model.CRD, field *model.Field, sourceVarName string, indentLevel int) string { +func ResolveReferencesForField(field *model.Field, sourceVarName string, indentLevel int) string { + r := field.CRD fp := fieldpath.FromString(field.Path) outPrefix := "" diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index ad1dc53d..922ebb7a 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -199,7 +199,7 @@ return false || (ko.Spec.VPCRef != nil)` assert.Equal(expected, code.ReferenceFieldsPresent(crd, "ko")) } -func Test_ReferenceForField_SingleReference(t *testing.T) { +func Test_ResolveReferencesForField_SingleReference(t *testing.T) { assert := assert.New(t) require := require.New(t) @@ -225,10 +225,10 @@ func Test_ReferenceForField_SingleReference(t *testing.T) { ` field := crd.Fields["APIID"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) + assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1)) } -func Test_ReferenceForField_SliceOfReferences(t *testing.T) { +func Test_ResolveReferencesForField_SliceOfReferences(t *testing.T) { assert := assert.New(t) require := require.New(t) @@ -254,10 +254,10 @@ func Test_ReferenceForField_SliceOfReferences(t *testing.T) { ` field := crd.Fields["SecurityGroupIDs"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) + assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1)) } -func Test_ReferenceForField_NestedSingleReference(t *testing.T) { +func Test_ResolveReferencesForField_NestedSingleReference(t *testing.T) { assert := assert.New(t) require := require.New(t) @@ -285,10 +285,10 @@ func Test_ReferenceForField_NestedSingleReference(t *testing.T) { ` field := crd.Fields["JWTConfiguration.Issuer"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) + assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1)) } -func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { +func Test_ResolveReferencesForField_SingleReference_DeeplyNested(t *testing.T) { assert := assert.New(t) require := require.New(t) @@ -320,5 +320,5 @@ func Test_ReferenceForField_SingleReference_DeeplyNested(t *testing.T) { ` field := crd.Fields["Logging.LoggingEnabled.TargetBucket"] - assert.Equal(expected, code.ResolveReferencesForField(crd, field, "ko", 1)) + assert.Equal(expected, code.ResolveReferencesForField(field, "ko", 1)) } From dde4df37dbeccde4099bdf5e5a9da20a84bf45e0 Mon Sep 17 00:00:00 2001 From: Nicholas Thomson Date: Thu, 9 Feb 2023 18:10:49 +0000 Subject: [PATCH 9/9] Fix hardcoded function name --- pkg/generate/code/resource_reference.go | 2 +- pkg/generate/code/resource_reference_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 517dedc4..e5f78032 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -257,7 +257,7 @@ func ResolveReferencesForField(field *model.Field, sourceVarName string, indentL outPrefix += fmt.Sprintf("%s\t\treturn fmt.Errorf(\"provided resource reference is nil or empty: \\%q\\\")\n", indent, field.ReferenceFieldPath()) outPrefix += fmt.Sprintf("%s\t}\n", indent) - outPrefix += fmt.Sprintf("%s\tif err := getReferencedResourceState_VPCLink(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n", indent) + outPrefix += fmt.Sprintf("%s\tif err := getReferencedResourceState_%s(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n", indent, field.FieldConfig.References.Resource) outPrefix += fmt.Sprintf("%s\t\treturn err\n", indent) outPrefix += fmt.Sprintf("%s\t}\n", indent) outPrefix += fmt.Sprintf("%s\t%s = append(%s, obj.%s)\n", indent, targetVarName, targetVarName, field.FieldConfig.References.Path) diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index 922ebb7a..63da2843 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -246,7 +246,7 @@ func Test_ResolveReferencesForField_SliceOfReferences(t *testing.T) { if arr == nil || arr.Name == nil || *arr.Name == "" { return fmt.Errorf("provided resource reference is nil or empty: \"SecurityGroupRefs"\") } - if err := getReferencedResourceState_VPCLink(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } ko.Spec.SecurityGroupIDs = append(ko.Spec.SecurityGroupIDs, obj.Status.ID)