Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pkg/generate/ack/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
},
Expand Down
91 changes: 91 additions & 0 deletions pkg/generate/code/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
190 changes: 190 additions & 0 deletions pkg/generate/code/secret_references.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading