From bef666409899fac5bf04801069067677a302b1c3 Mon Sep 17 00:00:00 2001 From: michaelhtm <98621731+michaelhtm@users.noreply.github.com> Date: Tue, 2 Jun 2026 14:15:07 -0700 Subject: [PATCH] feat: generate secret delta detection code for CRDs with secret fields Generate acksecret.SetResourceVersionsAnnotation call in sdkFind, conditional acksecret imports, SecretReferences() method on resources, and secret field comparison logic using resourceVersion annotations. --- pkg/generate/ack/controller.go | 6 + pkg/generate/code/compare.go | 91 +++++++ pkg/generate/code/secret_references.go | 190 +++++++++++++++ pkg/generate/code/secret_references_test.go | 227 ++++++++++++++++++ pkg/model/crd.go | 23 ++ templates/pkg/resource/delta.go.tpl | 3 + templates/pkg/resource/descriptor.go.tpl | 1 + templates/pkg/resource/resource.go.tpl | 8 + templates/pkg/resource/sdk.go.tpl | 3 + .../pkg/resource/sdk_find_read_one.go.tpl | 3 + 10 files changed, 555 insertions(+) create mode 100644 pkg/generate/code/secret_references.go create mode 100644 pkg/generate/code/secret_references_test.go diff --git a/pkg/generate/ack/controller.go b/pkg/generate/ack/controller.go index 5beab739..9e6da82d 100644 --- a/pkg/generate/ack/controller.go +++ b/pkg/generate/ack/controller.go @@ -154,6 +154,12 @@ var ( "HasPreDeleteSync": func(r *ackmodel.CRD) bool { return code.HasPreDeleteSync(r.Config(), r) }, + "HasSecretFields": func(r *ackmodel.CRD) bool { + return r.HasSecretFields() + }, + "GoCodeSecretReferences": func(r *ackmodel.CRD, koVarName string, indentLevel int) string { + return code.SecretReferences(r, koVarName, indentLevel) + }, "GoCodeIsSynced": func(r *ackmodel.CRD, resVarName string, indentLevel int) (string, error) { return code.ResourceIsSynced(r.Config(), r, resVarName, indentLevel) }, diff --git a/pkg/generate/code/compare.go b/pkg/generate/code/compare.go index d5028c78..1570fc64 100644 --- a/pkg/generate/code/compare.go +++ b/pkg/generate/code/compare.go @@ -147,6 +147,12 @@ func compareResourceFields( continue } + // Use secret data comparison for fields marked as secrets + if fieldConfig != nil && fieldConfig.IsSecret { + out += compareSecret(deltaVarName, firstResVarName, secondResVarName, firstResAdaptedVarName, secondResAdaptedVarName, fieldPath, indentLevel) + continue + } + memberShapeRef := specField.ShapeRef memberShape := memberShapeRef.Shape @@ -760,6 +766,91 @@ func compareDocument( return out } +// compareSecret outputs Go code that compares two SecretKeyReference fields. +// It first checks if the reference itself changed (name/namespace/key), and +// additionally checks whether the underlying secret data changed by comparing +// the resource version stored in the annotation. +// +// Output code will look something like this: +// +// if ackcompare.HasNilDifference(a.ko.Spec.SecretString, b.ko.Spec.SecretString) { +// delta.Add("Spec.SecretString", a.ko.Spec.SecretString, b.ko.Spec.SecretString) +// } else if a.ko.Spec.SecretString != nil && b.ko.Spec.SecretString != nil { +// if !ackcompare.SecretKeyReferenceEqual(a.ko.Spec.SecretString, b.ko.Spec.SecretString) { +// delta.Add("Spec.SecretString", a.ko.Spec.SecretString, b.ko.Spec.SecretString) +// } else if ackcompare.SecretDataChanged(a.ko.GetAnnotations(), b.ko.GetAnnotations(), acksecret.IndexKey(a.ko.Spec.SecretString, a.ko.GetNamespace())) { +// delta.Add("Spec.SecretString", a.ko.Spec.SecretString, b.ko.Spec.SecretString) +// } +// } +func compareSecret( + // String representing the name of the variable that is of type + // `*ackcompare.Delta`. We will generate Go code that calls the `Add()` + // method of this variable when differences between fields are detected. + deltaVarName string, + // String representing the name of the root resource variable for the + // desired CR. This will typically be something like "a.ko". + desiredRootVarName string, + // String representing the name of the root resource variable for the + // latest CR. This will typically be something like "b.ko". + latestRootVarName string, + // String representing the name of the variable that represents the desired + // CR field. This will typically be something like "a.ko.Spec.SecretString". + desiredResVarName string, + // String representing the name of the variable that represents the latest + // CR field. This will typically be something like "b.ko.Spec.SecretString". + latestResVarName string, + // String indicating the current field path being evaluated, e.g. + // "Spec.SecretString". + fieldPath string, + // Number of levels of indentation to use + indentLevel int, +) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + + // if ackcompare.HasNilDifference(a.ko.Spec.SecretString, b.ko.Spec.SecretString) { + out += fmt.Sprintf( + "%sif ackcompare.HasNilDifference(%s, %s) {\n", + indent, desiredResVarName, latestResVarName, + ) + // delta.Add("Spec.SecretString", a.ko.Spec.SecretString, b.ko.Spec.SecretString) + out += fmt.Sprintf( + "%s\t%s.Add(\"%s\", %s, %s)\n", + indent, deltaVarName, fieldPath, desiredResVarName, latestResVarName, + ) + // } else if a.ko.Spec.SecretString != nil && b.ko.Spec.SecretString != nil { + out += fmt.Sprintf( + "%s} else if %s != nil && %s != nil {\n", + indent, desiredResVarName, latestResVarName, + ) + // if !ackcompare.SecretKeyReferenceEqual(a.ko.Spec.SecretString, b.ko.Spec.SecretString) { + out += fmt.Sprintf( + "%s\tif !ackcompare.SecretKeyReferenceEqual(%s, %s) {\n", + indent, desiredResVarName, latestResVarName, + ) + // delta.Add("Spec.SecretString", a.ko.Spec.SecretString, b.ko.Spec.SecretString) + out += fmt.Sprintf( + "%s\t\t%s.Add(\"%s\", %s, %s)\n", + indent, deltaVarName, fieldPath, desiredResVarName, latestResVarName, + ) + // } else if ackcompare.SecretDataChanged(a.ko.GetAnnotations(), b.ko.GetAnnotations(), acksecret.IndexKey(a.ko.Spec.SecretString, a.ko.GetNamespace())) { + out += fmt.Sprintf( + "%s\t} else if ackcompare.SecretDataChanged(%s.GetAnnotations(), %s.GetAnnotations(), acksecret.IndexKey(%s, %s.GetNamespace())) {\n", + indent, desiredRootVarName, latestRootVarName, desiredResVarName, desiredRootVarName, + ) + // delta.Add("Spec.SecretString", a.ko.Spec.SecretString, b.ko.Spec.SecretString) + out += fmt.Sprintf( + "%s\t\t%s.Add(\"%s\", %s, %s)\n", + indent, deltaVarName, fieldPath, desiredResVarName, latestResVarName, + ) + // } + out += fmt.Sprintf("%s\t}\n", indent) + // } + out += fmt.Sprintf("%s}\n", indent) + + return out +} + // compareTags outputs Go code that compares two slices of tags from two // resource fields by first converting them to the common ACK tag type and then // using a map comparison. If there is a difference, adds the difference to a diff --git a/pkg/generate/code/secret_references.go b/pkg/generate/code/secret_references.go new file mode 100644 index 00000000..4dcbdffc --- /dev/null +++ b/pkg/generate/code/secret_references.go @@ -0,0 +1,190 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package code + +import ( + "fmt" + "strings" + + "github.com/aws-controllers-k8s/code-generator/pkg/model" +) + +// SecretReferences returns Go code that collects all SecretKeyReferences from +// a resource's Spec and returns them as a slice. This is used to implement the +// SecretReferenceResolver interface. +// +// Sample output: +// +// var refs []*ackv1alpha1.SecretKeyReference +// if r.ko.Spec.MasterUserPassword != nil { +// refs = append(refs, r.ko.Spec.MasterUserPassword) +// } +// if r.ko.Spec.Users != nil { +// for _, elem := range r.ko.Spec.Users { +// if elem != nil && elem.Password != nil { +// refs = append(refs, elem.Password) +// } +// } +// } +// return refs +func SecretReferences( + r *model.CRD, + // koVarName is the variable name for the resource's ko object + koVarName string, + // Number of levels of indentation to use + indentLevel int, +) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + + out += fmt.Sprintf("%svar refs []*ackv1alpha1.SecretKeyReference\n", indent) + + for _, field := range r.SpecFields { + out += secretReferencesForField(field, koVarName+".Spec", indentLevel) + } + + out += fmt.Sprintf("%sreturn refs\n", indent) + return out +} + +func secretReferencesForField( + field *model.Field, + parentAccessor string, + indentLevel int, +) string { + if field.FieldConfig != nil && field.FieldConfig.IsSecret { + return secretReferenceDirectField(field, parentAccessor, indentLevel) + } + + if field.MemberFields != nil { + return secretReferencesForStruct(field, parentAccessor, indentLevel) + } + + return "" +} + +// secretReferenceDirectField handles fields that are directly a +// SecretKeyReference or a slice of SecretKeyReferences. +func secretReferenceDirectField( + field *model.Field, + parentAccessor string, + indentLevel int, +) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + accessor := fmt.Sprintf("%s.%s", parentAccessor, field.Names.Camel) + + switch { + case field.GoType == "*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%sif %s != nil {\n", indent, accessor) + out += fmt.Sprintf("%s\trefs = append(refs, %s)\n", indent, accessor) + out += fmt.Sprintf("%s}\n", indent) + + case field.GoType == "[]*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%sif %s != nil {\n", indent, accessor) + out += fmt.Sprintf("%s\trefs = append(refs, %s...)\n", indent, accessor) + out += fmt.Sprintf("%s}\n", indent) + + case field.GoType == "map[string]*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%sfor _, v := range %s {\n", indent, accessor) + out += fmt.Sprintf("%s\tif v != nil {\n", indent) + out += fmt.Sprintf("%s\t\trefs = append(refs, v)\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s}\n", indent) + } + + return out +} + +// secretReferencesForStruct handles struct fields that may contain secret +// fields as members — either the struct itself or when wrapped in a slice. +func secretReferencesForStruct( + field *model.Field, + parentAccessor string, + indentLevel int, +) string { + out := "" + indent := strings.Repeat("\t", indentLevel) + accessor := fmt.Sprintf("%s.%s", parentAccessor, field.Names.Camel) + + hasSecretMember := false + for _, mf := range field.MemberFields { + if mf.FieldConfig != nil && mf.FieldConfig.IsSecret { + hasSecretMember = true + break + } + } + if !hasSecretMember { + return "" + } + + isSlice := strings.HasPrefix(field.GoType, "[]") + + if isSlice { + out += fmt.Sprintf("%sif %s != nil {\n", indent, accessor) + out += fmt.Sprintf("%s\tfor _, elem := range %s {\n", indent, accessor) + out += fmt.Sprintf("%s\t\tif elem == nil {\n", indent) + out += fmt.Sprintf("%s\t\t\tcontinue\n", indent) + out += fmt.Sprintf("%s\t\t}\n", indent) + for _, mf := range field.MemberFields { + if mf.FieldConfig != nil && mf.FieldConfig.IsSecret { + memberAccessor := fmt.Sprintf("elem.%s", mf.Names.Camel) + switch { + case mf.GoType == "*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%s\t\tif %s != nil {\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\t\trefs = append(refs, %s)\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\t}\n", indent) + case mf.GoType == "[]*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%s\t\tif %s != nil {\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\t\trefs = append(refs, %s...)\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\t}\n", indent) + case mf.GoType == "map[string]*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%s\t\tfor _, v := range %s {\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\t\tif v != nil {\n", indent) + out += fmt.Sprintf("%s\t\t\t\trefs = append(refs, v)\n", indent) + out += fmt.Sprintf("%s\t\t\t}\n", indent) + out += fmt.Sprintf("%s\t\t}\n", indent) + } + } + } + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s}\n", indent) + } else { + out += fmt.Sprintf("%sif %s != nil {\n", indent, accessor) + for _, mf := range field.MemberFields { + if mf.FieldConfig != nil && mf.FieldConfig.IsSecret { + memberAccessor := fmt.Sprintf("%s.%s", accessor, mf.Names.Camel) + switch { + case mf.GoType == "*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%s\tif %s != nil {\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\trefs = append(refs, %s)\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t}\n", indent) + case mf.GoType == "[]*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%s\tif %s != nil {\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\trefs = append(refs, %s...)\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t}\n", indent) + case mf.GoType == "map[string]*ackv1alpha1.SecretKeyReference": + out += fmt.Sprintf("%s\tfor _, v := range %s {\n", indent, memberAccessor) + out += fmt.Sprintf("%s\t\tif v != nil {\n", indent) + out += fmt.Sprintf("%s\t\t\trefs = append(refs, v)\n", indent) + out += fmt.Sprintf("%s\t\t}\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + } + } + } + out += fmt.Sprintf("%s}\n", indent) + } + + return out +} diff --git a/pkg/generate/code/secret_references_test.go b/pkg/generate/code/secret_references_test.go new file mode 100644 index 00000000..56db19c1 --- /dev/null +++ b/pkg/generate/code/secret_references_test.go @@ -0,0 +1,227 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package code_test + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aws-controllers-k8s/code-generator/pkg/generate/code" + "github.com/aws-controllers-k8s/code-generator/pkg/testutil" +) + +func TestSecretReferences_RDS_DBInstance(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "rds") + crd := testutil.GetCRDByName(t, g, "DBInstance") + require.NotNil(crd) + + expected := ` var refs []*ackv1alpha1.SecretKeyReference + if r.ko.Spec.MasterUserPassword != nil { + refs = append(refs, r.ko.Spec.MasterUserPassword) + } + return refs +` + got := code.SecretReferences(crd, "r.ko", 1) + assert.Equal(expected, got) +} + +func TestSecretReferences_Lambda_Function_MapOfSecrets(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForServiceWithOptions(t, "lambda", &testutil.TestingModelOptions{ + GeneratorConfigFile: "generator-environment-vars-secret.yaml", + }) + crd := testutil.GetCRDByName(t, g, "Function") + require.NotNil(crd) + + expected := ` var refs []*ackv1alpha1.SecretKeyReference + if r.ko.Spec.Environment != nil { + for _, v := range r.ko.Spec.Environment.Variables { + if v != nil { + refs = append(refs, v) + } + } + } + return refs +` + got := code.SecretReferences(crd, "r.ko", 1) + assert.Equal(expected, got) +} + +func TestSecretReferences_MQ_Broker_SliceOfStructsWithSecret(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "mq") + crd := testutil.GetCRDByName(t, g, "Broker") + require.NotNil(crd) + + expected := ` var refs []*ackv1alpha1.SecretKeyReference + if r.ko.Spec.Users != nil { + for _, elem := range r.ko.Spec.Users { + if elem == nil { + continue + } + if elem.Password != nil { + refs = append(refs, elem.Password) + } + } + } + return refs +` + got := code.SecretReferences(crd, "r.ko", 1) + assert.Equal(expected, got) +} + +func TestSecretReferences_MemoryDB_User_SliceSecretInStruct(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "memorydb") + crd := testutil.GetCRDByName(t, g, "User") + require.NotNil(crd) + + expected := ` var refs []*ackv1alpha1.SecretKeyReference + if r.ko.Spec.AuthenticationMode != nil { + if r.ko.Spec.AuthenticationMode.Passwords != nil { + refs = append(refs, r.ko.Spec.AuthenticationMode.Passwords...) + } + } + return refs +` + got := code.SecretReferences(crd, "r.ko", 1) + assert.Equal(expected, got) +} + +func TestSecretReferences_Elasticache_ReplicationGroup_TopLevelSecret(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "elasticache") + crd := testutil.GetCRDByName(t, g, "ReplicationGroup") + require.NotNil(crd) + + got := code.SecretReferences(crd, "r.ko", 1) + assert.Contains(got, "r.ko.Spec.AuthToken") + assert.Contains(got, "refs = append(refs, r.ko.Spec.AuthToken)") +} + +func TestCompareResource_RDS_DBInstance_Secret(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "rds") + crd := testutil.GetCRDByName(t, g, "DBInstance") + require.NotNil(crd) + + got, err := code.CompareResource( + crd.Config(), crd, "delta", "a.ko", "b.ko", 1, + ) + require.NoError(err) + + assert.Contains(got, "ackcompare.HasNilDifference(a.ko.Spec.MasterUserPassword, b.ko.Spec.MasterUserPassword)") + assert.Contains(got, "ackcompare.SecretKeyReferenceEqual(a.ko.Spec.MasterUserPassword, b.ko.Spec.MasterUserPassword)") + assert.Contains(got, "ackcompare.SecretDataChanged(a.ko.GetAnnotations(), b.ko.GetAnnotations(), acksecret.IndexKey(a.ko.Spec.MasterUserPassword, a.ko.GetNamespace()))") +} + +func TestCompareResource_Elasticache_ReplicationGroup_Secret(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "elasticache") + crd := testutil.GetCRDByName(t, g, "ReplicationGroup") + require.NotNil(crd) + + got, err := code.CompareResource( + crd.Config(), crd, "delta", "a.ko", "b.ko", 1, + ) + require.NoError(err) + + assert.Contains(got, "ackcompare.SecretKeyReferenceEqual(a.ko.Spec.AuthToken, b.ko.Spec.AuthToken)") + assert.Contains(got, "ackcompare.SecretDataChanged(a.ko.GetAnnotations(), b.ko.GetAnnotations(), acksecret.IndexKey(a.ko.Spec.AuthToken, a.ko.GetNamespace()))") +} + +func TestCompareResource_MemoryDB_User_SecretIsCompareIgnored(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "memorydb") + crd := testutil.GetCRDByName(t, g, "User") + require.NotNil(crd) + + got, err := code.CompareResource( + crd.Config(), crd, "delta", "a.ko", "b.ko", 1, + ) + require.NoError(err) + + // Passwords is compare.is_ignored so it should NOT appear in compare output + assert.NotContains(got, "Passwords") + assert.NotContains(got, "SecretDataChanged") +} + +func TestHasSecretFields(t *testing.T) { + require := require.New(t) + + tests := []struct { + service string + resource string + options *testutil.TestingModelOptions + hasSecret bool + }{ + {"rds", "DBInstance", nil, true}, + {"memorydb", "User", nil, true}, + {"s3", "Bucket", nil, false}, + {"lambda", "Function", &testutil.TestingModelOptions{ + GeneratorConfigFile: "generator-environment-vars-secret.yaml", + }, true}, + } + for _, tt := range tests { + t.Run(tt.service+"_"+tt.resource, func(t *testing.T) { + if tt.options != nil { + g := testutil.NewModelForServiceWithOptions(t, tt.service, tt.options) + crd := testutil.GetCRDByName(t, g, tt.resource) + require.NotNil(crd) + assert.Equal(t, tt.hasSecret, crd.HasSecretFields()) + } else { + g := testutil.NewModelForService(t, tt.service) + crd := testutil.GetCRDByName(t, g, tt.resource) + require.NotNil(crd) + assert.Equal(t, tt.hasSecret, crd.HasSecretFields()) + } + }) + } +} + +func TestSdkFind_RDS_DBInstance_SetsSecretResourceVersions(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "rds") + crd := testutil.GetCRDByName(t, g, "DBInstance") + require.NotNil(crd) + + // The sdk_find_read_one template includes SetResourceVersionsAnnotation + // when HasSecretFields is true. Verify that the CRD reports having secrets + // and the SecretReferences output is non-empty. + assert.True(crd.HasSecretFields()) + refs := code.SecretReferences(crd, "r.ko", 1) + assert.True(strings.Contains(refs, "MasterUserPassword")) +} diff --git a/pkg/model/crd.go b/pkg/model/crd.go index 5eb5e072..75421247 100644 --- a/pkg/model/crd.go +++ b/pkg/model/crd.go @@ -990,6 +990,29 @@ func (r *CRD) HasReferenceFields() bool { return false } +// HasSecretFields returns true if any field in this CRD is marked as a +// SecretKeyReference. +func (r *CRD) HasSecretFields() bool { + for _, field := range r.Fields { + if field.FieldConfig != nil && field.FieldConfig.IsSecret { + return true + } + } + return false +} + +// SecretFieldPaths returns the field paths of all fields in this CRD that are +// marked as SecretKeyReference. +func (r *CRD) SecretFieldPaths() []string { + var paths []string + for path, field := range r.Fields { + if field.FieldConfig != nil && field.FieldConfig.IsSecret { + paths = append(paths, path) + } + } + return paths +} + // ReferencedServiceNames returns the set of service names for ACK controllers // whose resources are referenced inside the CRD. The service name is // the go package name for the AWS service inside aws-sdk-go. diff --git a/templates/pkg/resource/delta.go.tpl b/templates/pkg/resource/delta.go.tpl index 51e8b2c0..8c942d47 100644 --- a/templates/pkg/resource/delta.go.tpl +++ b/templates/pkg/resource/delta.go.tpl @@ -7,6 +7,9 @@ import ( "k8s.io/apimachinery/pkg/api/equality" ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" +{{- if HasSecretFields .CRD }} + acksecret "github.com/aws-controllers-k8s/runtime/pkg/secret" +{{- end }} acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" ) diff --git a/templates/pkg/resource/descriptor.go.tpl b/templates/pkg/resource/descriptor.go.tpl index 23b3c33e..09f2cb9f 100644 --- a/templates/pkg/resource/descriptor.go.tpl +++ b/templates/pkg/resource/descriptor.go.tpl @@ -153,3 +153,4 @@ func (d *resourceDescriptor) MarkAdopted( curr[ackv1alpha1.AnnotationAdopted] = "true" obj.SetAnnotations(curr) } + diff --git a/templates/pkg/resource/resource.go.tpl b/templates/pkg/resource/resource.go.tpl index 02b89ec4..9713bec2 100644 --- a/templates/pkg/resource/resource.go.tpl +++ b/templates/pkg/resource/resource.go.tpl @@ -102,3 +102,11 @@ func (r *resource) DeepCopy() acktypes.AWSResource { koCopy := r.ko.DeepCopy() return &resource{koCopy} } + +{{ if HasSecretFields .CRD -}} +// SecretReferences returns all SecretKeyReferences held by this resource. +// Implements acktypes.SecretReferenceResolver. +func (r *resource) SecretReferences() []*ackv1alpha1.SecretKeyReference { +{{ GoCodeSecretReferences .CRD "r.ko" 1 }} +} +{{- end }} diff --git a/templates/pkg/resource/sdk.go.tpl b/templates/pkg/resource/sdk.go.tpl index 30929e8f..8060a2f6 100644 --- a/templates/pkg/resource/sdk.go.tpl +++ b/templates/pkg/resource/sdk.go.tpl @@ -15,6 +15,9 @@ import ( ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" +{{- if HasSecretFields .CRD }} + acksecret "github.com/aws-controllers-k8s/runtime/pkg/secret" +{{- end }} ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" "github.com/aws/aws-sdk-go-v2/aws" svcsdk "github.com/aws/aws-sdk-go-v2/service/{{ .ServicePackageName }}" diff --git a/templates/pkg/resource/sdk_find_read_one.go.tpl b/templates/pkg/resource/sdk_find_read_one.go.tpl index eb91bb38..4a8a12ff 100644 --- a/templates/pkg/resource/sdk_find_read_one.go.tpl +++ b/templates/pkg/resource/sdk_find_read_one.go.tpl @@ -58,6 +58,9 @@ func (rm *resourceManager) sdkFind( {{- end }} {{- if $hookCode := Hook .CRD "sdk_read_one_post_set_output" }} {{ $hookCode }} +{{- end }} +{{- if HasSecretFields .CRD }} + acksecret.SetResourceVersionsAnnotation(ctx, (&resource{ko}).SecretReferences(), ko, rm.rr.SecretResourceVersion) {{- end }} return &resource{ko}, nil }