use custom id as key of the mapping and make kio.Pipeline behave the same as framework.Execute

This commit is contained in:
Mengqi Yu
2021-11-18 19:53:46 -08:00
parent 81edfb7ee8
commit 2ee2d3e389
4 changed files with 502 additions and 96 deletions

View File

@@ -117,7 +117,7 @@ func Execute(p ResourceListProcessor, rlSource *kio.ByteReadWriter) error {
rl.FunctionConfig = rlSource.FunctionConfig rl.FunctionConfig = rlSource.FunctionConfig
// We store the original // We store the original
nodeAnnos, err := kio.GetInternalAnnotationsFromResourceList(rl.Items) nodeAnnos, err := kio.PreprocessResourcesForInternalAnnotationMigration(rl.Items)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,12 +7,17 @@ package kio
import ( import (
"fmt" "fmt"
"strconv"
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil" "sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
// resourceIDAnnotation is used to uniquely identify the resource during round trip
// to and from a function execution.
const resourceIDAnnotation = "internal.config.k8s.io/annotations-migration-resource-id"
// Reader reads ResourceNodes. Analogous to io.Reader. // Reader reads ResourceNodes. Analogous to io.Reader.
type Reader interface { type Reader interface {
Read() ([]*yaml.RNode, error) Read() ([]*yaml.RNode, error)
@@ -117,7 +122,7 @@ func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) erro
for i := range p.Filters { for i := range p.Filters {
// Not all RNodes passed through kio.Pipeline have metadata nor should // Not all RNodes passed through kio.Pipeline have metadata nor should
// they all be required to. // they all be required to.
nodeAnnos, err := GetInternalAnnotationsFromResourceList(result) nodeAnnos, err := PreprocessResourcesForInternalAnnotationMigration(result)
if err != nil { if err != nil {
return err return err
} }
@@ -136,7 +141,7 @@ func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) erro
// If either the internal annotations for path, index, and id OR the legacy // If either the internal annotations for path, index, and id OR the legacy
// annotations for path, index, and id are changed, we have to update the other. // annotations for path, index, and id are changed, we have to update the other.
err = reconcileInternalAnnotations(result, nodeAnnos, false) err = ReconcileInternalAnnotations(result, nodeAnnos)
if err != nil { if err != nil {
return err return err
} }
@@ -164,31 +169,29 @@ func FilterAll(filter yaml.Filter) Filter {
}) })
} }
// GetInternalAnnotationsFromResourceList stores the original path, index, and id annotations so that we can reconcile // PreprocessResourcesForInternalAnnotationMigration returns a mapping from id to all
// it later. This is necessary because currently both internal-prefixed annotations // internal annotations, so that we can use it to reconcile the annotations
// later. This is necessary because currently both internal-prefixed annotations
// and legacy annotations are currently supported, and a change to one must be // and legacy annotations are currently supported, and a change to one must be
// reflected in the other. // reflected in the other if needed.
func GetInternalAnnotationsFromResourceList(result []*yaml.RNode) (map[nodeAnnotations]map[string]string, error) { func PreprocessResourcesForInternalAnnotationMigration(result []*yaml.RNode) (map[string]map[string]string, error) {
nodeAnnosMap := make(map[nodeAnnotations]map[string]string) idToAnnosMap := make(map[string]map[string]string)
for i := range result { for i := range result {
id := kioutil.GetIdAnnotation(result[i]) idStr := strconv.Itoa(i)
path, index, _ := kioutil.GetFileAnnotations(result[i]) err := result[i].PipeE(yaml.SetAnnotation(resourceIDAnnotation, idStr))
annoKey := nodeAnnotations{ if err != nil {
path: path,
index: index,
id: id,
}
nodeAnnosMap[annoKey] = kioutil.GetInternalAnnotations(result[i])
if err := kioutil.CopyLegacyAnnotations(result[i]); err != nil {
return nil, err return nil, err
} }
idToAnnosMap[idStr] = kioutil.GetInternalAnnotations(result[i])
if err := checkMismatchedAnnos(result[i].GetAnnotations()); err != nil { if err = kioutil.CopyLegacyAnnotations(result[i]); err != nil {
return nil, err
}
meta, _ := result[i].GetMeta()
if err = checkMismatchedAnnos(meta.Annotations); err != nil {
return nil, err return nil, err
} }
} }
return nodeAnnosMap, nil return idToAnnosMap, nil
} }
func checkMismatchedAnnos(annotations map[string]string) error { func checkMismatchedAnnos(annotations map[string]string) error {
@@ -221,26 +224,17 @@ type nodeAnnotations struct {
} }
// ReconcileInternalAnnotations reconciles the annotation format for path, index and id annotations. // ReconcileInternalAnnotations reconciles the annotation format for path, index and id annotations.
func ReconcileInternalAnnotations(result []*yaml.RNode, nodeAnnosMap map[nodeAnnotations]map[string]string) error { // It will ensure the output annotation format matches the format in the input. e.g. if the input
return reconcileInternalAnnotations(result, nodeAnnosMap, true) // format uses the legacy format and the output will be converted to the legacy format if it's not.
} func ReconcileInternalAnnotations(result []*yaml.RNode, nodeAnnosMap map[string]map[string]string) error {
useInternal, useLegacy, err := determineAnnotationsFormat(nodeAnnosMap)
// reconcileInternalAnnotations reconciles the annotation format for path, index and id annotations. if err != nil {
// If formatAnnotations is true, we will ensure the output annotation format matches the format return err
// in the input. e.g. if the input format uses the legacy format and the output will be converted to
// the legacy format if it's not.
func reconcileInternalAnnotations(result []*yaml.RNode, nodeAnnosMap map[nodeAnnotations]map[string]string, formatAnnotations bool) error {
var useInternal, useLegacy bool
var err error
if formatAnnotations {
if useInternal, useLegacy, err = determineAnnotationsFormat(nodeAnnosMap); err != nil {
return err
}
} }
for i := range result { for i := range result {
// if only one annotation is set, set the other. // if only one annotation is set, set the other.
err := missingInternalOrLegacyAnnotations(result[i]) err = missingInternalOrLegacyAnnotations(result[i])
if err != nil { if err != nil {
return err return err
} }
@@ -251,26 +245,29 @@ func reconcileInternalAnnotations(result []*yaml.RNode, nodeAnnosMap map[nodeAnn
if err != nil { if err != nil {
return err return err
} }
if formatAnnotations { // We invoke determineAnnotationsFormat to find out if the original annotations
// We invoke determineAnnotationsFormat to find out if the original annotations // use the internal or (and) the legacy format. We format the resources to
// use the internal or (and) the legacy format. We format the resources to // make them consistent with original format.
// make them consistent with original format. err = formatInternalAnnotations(result[i], useInternal, useLegacy)
err = formatInternalAnnotations(result[i], useInternal, useLegacy) if err != nil {
if err != nil { return err
return err
}
} }
// if the annotations are still somehow out of sync, throw an error // if the annotations are still somehow out of sync, throw an error
err = checkMismatchedAnnos(result[i].GetAnnotations()) meta, _ := result[i].GetMeta()
err = checkMismatchedAnnos(meta.Annotations)
if err != nil { if err != nil {
return err return err
} }
if _, err = result[i].Pipe(yaml.ClearAnnotation(resourceIDAnnotation)); err != nil {
return err
}
} }
return nil return nil
} }
// determineAnnotationsFormat determines if the resources are using one of the internal and legacy annotation format or both of them. // determineAnnotationsFormat determines if the resources are using one of the internal and legacy annotation format or both of them.
func determineAnnotationsFormat(nodeAnnosMap map[nodeAnnotations]map[string]string) (useInternal, useLegacy bool, err error) { func determineAnnotationsFormat(nodeAnnosMap map[string]map[string]string) (useInternal, useLegacy bool, err error) {
if len(nodeAnnosMap) == 0 { if len(nodeAnnosMap) == 0 {
return true, true, nil return true, true, nil
} }
@@ -280,24 +277,31 @@ func determineAnnotationsFormat(nodeAnnosMap map[nodeAnnotations]map[string]stri
_, foundPath := annos[kioutil.PathAnnotation] _, foundPath := annos[kioutil.PathAnnotation]
_, foundIndex := annos[kioutil.IndexAnnotation] _, foundIndex := annos[kioutil.IndexAnnotation]
_, foundId := annos[kioutil.IdAnnotation] _, foundId := annos[kioutil.IdAnnotation]
_, foundLegacyPath := annos[kioutil.LegacyPathAnnotation]
_, foundLegacyIndex := annos[kioutil.LegacyIndexAnnotation]
_, foundLegacyId := annos[kioutil.LegacyIdAnnotation]
if !(foundPath || foundIndex || foundId || foundLegacyPath || foundLegacyIndex || foundLegacyId) {
continue
}
foundOneOf := foundPath || foundIndex || foundId foundOneOf := foundPath || foundIndex || foundId
if internal == nil { if internal == nil {
internal = &foundOneOf f := foundOneOf
internal = &f
} }
if (foundOneOf && !*internal) || (!foundOneOf && *internal) { if (foundOneOf && !*internal) || (!foundOneOf && *internal) {
err = fmt.Errorf("the formatting in the input resources is not consistent") err = fmt.Errorf("the annotation formatting in the input resources is not consistent")
return return
} }
_, foundPath = annos[kioutil.LegacyPathAnnotation] foundOneOf = foundLegacyPath || foundLegacyIndex || foundLegacyId
_, foundIndex = annos[kioutil.LegacyIndexAnnotation]
_, foundId = annos[kioutil.LegacyIdAnnotation]
foundOneOf = foundPath || foundIndex || foundId
if legacy == nil { if legacy == nil {
legacy = &foundOneOf f := foundOneOf
legacy = &f
} }
if (foundOneOf && !*legacy) || (!foundOneOf && *legacy) { if (foundOneOf && !*legacy) || (!foundOneOf && *legacy) {
err = fmt.Errorf("the formatting in the input resources is not consistent") err = fmt.Errorf("the annotation formatting in the input resources is not consistent")
return return
} }
} }
@@ -324,8 +328,10 @@ func missingInternalOrLegacyAnnotations(rn *yaml.RNode) error {
} }
func missingInternalOrLegacyAnnotation(rn *yaml.RNode, newKey string, legacyKey string) error { func missingInternalOrLegacyAnnotation(rn *yaml.RNode, newKey string, legacyKey string) error {
value := rn.GetAnnotations()[newKey] meta, _ := rn.GetMeta()
legacyValue := rn.GetAnnotations()[legacyKey] annotations := meta.Annotations
value := annotations[newKey]
legacyValue := annotations[legacyKey]
if value == "" && legacyValue == "" { if value == "" && legacyValue == "" {
// do nothing // do nothing
@@ -346,8 +352,9 @@ func missingInternalOrLegacyAnnotation(rn *yaml.RNode, newKey string, legacyKey
return nil return nil
} }
func checkAnnotationsAltered(rn *yaml.RNode, nodeAnnosMap map[nodeAnnotations]map[string]string) error { func checkAnnotationsAltered(rn *yaml.RNode, nodeAnnosMap map[string]map[string]string) error {
annotations := rn.GetAnnotations() meta, _ := rn.GetMeta()
annotations := meta.Annotations
// get the resource's current path, index, and ids from the new annotations // get the resource's current path, index, and ids from the new annotations
internal := nodeAnnotations{ internal := nodeAnnotations{
path: annotations[kioutil.PathAnnotation], path: annotations[kioutil.PathAnnotation],
@@ -362,16 +369,19 @@ func checkAnnotationsAltered(rn *yaml.RNode, nodeAnnosMap map[nodeAnnotations]ma
id: annotations[kioutil.LegacyIdAnnotation], id: annotations[kioutil.LegacyIdAnnotation],
} }
originalAnnotations, found := nodeAnnosMap[internal] rid := annotations[resourceIDAnnotation]
originalAnnotations, found := nodeAnnosMap[rid]
if !found { if !found {
originalAnnotations, found = nodeAnnosMap[legacy] return nil
} }
originalPath, found := originalAnnotations[kioutil.PathAnnotation] originalPath, found := originalAnnotations[kioutil.PathAnnotation]
if !found { if !found {
originalPath = originalAnnotations[kioutil.LegacyPathAnnotation] originalPath = originalAnnotations[kioutil.LegacyPathAnnotation]
} }
if originalPath != "" { if originalPath != "" {
if originalPath != internal.path { if originalPath != internal.path && originalPath != legacy.path && internal.path != legacy.path {
return fmt.Errorf("resource input to function has mismatched legacy and internal path annotations")
} else if originalPath != internal.path {
if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, internal.path)); err != nil { if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, internal.path)); err != nil {
return err return err
} }
@@ -387,7 +397,9 @@ func checkAnnotationsAltered(rn *yaml.RNode, nodeAnnosMap map[nodeAnnotations]ma
originalIndex = originalAnnotations[kioutil.LegacyIndexAnnotation] originalIndex = originalAnnotations[kioutil.LegacyIndexAnnotation]
} }
if originalIndex != "" { if originalIndex != "" {
if originalIndex != internal.index { if originalIndex != internal.index && originalIndex != legacy.index && internal.index != legacy.index {
return fmt.Errorf("resource input to function has mismatched legacy and internal index annotations")
} else if originalIndex != internal.index {
if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.LegacyIndexAnnotation, internal.index)); err != nil { if _, err := rn.Pipe(yaml.SetAnnotation(kioutil.LegacyIndexAnnotation, internal.index)); err != nil {
return err return err
} }

View File

@@ -217,9 +217,9 @@ func TestLegacyAnnotationReconciliation(t *testing.T) {
} }
return nodes, nil return nodes, nil
} }
changeBothPathAnnos := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) { changeBothPathAnnosMatch := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for _, rn := range nodes { for _, rn := range nodes {
if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "legacy")); err != nil { if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "new")); err != nil {
return nil, err return nil, err
} }
if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "new")); err != nil { if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "new")); err != nil {
@@ -228,6 +228,17 @@ func TestLegacyAnnotationReconciliation(t *testing.T) {
} }
return nodes, nil return nodes, nil
} }
changeBothPathAnnosMismatch := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for _, rn := range nodes {
if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "foo")); err != nil {
return nil, err
}
if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "bar")); err != nil {
return nil, err
}
}
return nodes, nil
}
noops := []Filter{ noops := []Filter{
FilterFunc(noopFilter1), FilterFunc(noopFilter1),
@@ -235,7 +246,8 @@ func TestLegacyAnnotationReconciliation(t *testing.T) {
} }
internal := []Filter{FilterFunc(changeInternalAnnos)} internal := []Filter{FilterFunc(changeInternalAnnos)}
legacy := []Filter{FilterFunc(changeLegacyAnnos)} legacy := []Filter{FilterFunc(changeLegacyAnnos)}
changeBoth := []Filter{FilterFunc(changeBothPathAnnos), FilterFunc(noopFilter1)} changeBothMatch := []Filter{FilterFunc(changeBothPathAnnosMatch), FilterFunc(noopFilter1)}
changeBothMismatch := []Filter{FilterFunc(changeBothPathAnnosMismatch), FilterFunc(noopFilter1)}
testCases := map[string]struct { testCases := map[string]struct {
input string input string
@@ -274,8 +286,6 @@ metadata:
annotations: annotations:
config.kubernetes.io/path: 'configmap.yaml' config.kubernetes.io/path: 'configmap.yaml'
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0'
data: data:
grpcPort: 8080 grpcPort: 8080
--- ---
@@ -286,8 +296,6 @@ metadata:
annotations: annotations:
config.kubernetes.io/path: "configmap.yaml" config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '1'
data: data:
grpcPort: 8081 grpcPort: 8081
`, `,
@@ -323,8 +331,6 @@ metadata:
annotations: annotations:
internal.config.kubernetes.io/path: 'configmap.yaml' internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0' internal.config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'configmap.yaml'
config.kubernetes.io/index: '0'
data: data:
grpcPort: 8080 grpcPort: 8080
--- ---
@@ -335,15 +341,12 @@ metadata:
annotations: annotations:
internal.config.kubernetes.io/path: "configmap.yaml" internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '1' internal.config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'configmap.yaml'
config.kubernetes.io/index: '1'
data: data:
grpcPort: 8081 grpcPort: 8081
`, `,
}, },
// the orchestrator should detect that the legacy annotations // the orchestrator should detect that the legacy annotations
// have been changed by the function, and should update the // have been changed by the function
// new internal annotations to reflect the same change
"change only legacy annotations": { "change only legacy annotations": {
input: `apiVersion: v1 input: `apiVersion: v1
kind: ConfigMap kind: ConfigMap
@@ -368,6 +371,190 @@ data:
filters: legacy, filters: legacy,
expected: `apiVersion: v1 expected: `apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: 'new'
config.kubernetes.io/index: 'new'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: 'new'
data:
grpcPort: 8081
`,
},
// the orchestrator should detect that the new internal annotations
// have been changed by the function
"change only internal annotations": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: internal,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
internal.config.kubernetes.io/path: 'new'
internal.config.kubernetes.io/index: 'new'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
internal.config.kubernetes.io/path: "new"
internal.config.kubernetes.io/index: 'new'
data:
grpcPort: 8081
`,
},
// the orchestrator should detect that the legacy annotations
// have been changed by the function
"change only internal annotations while input is legacy annotations": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: 'configmap.yaml'
config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: internal,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: 'new'
config.kubernetes.io/index: 'new'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: 'new'
data:
grpcPort: 8081
`,
},
// the orchestrator should detect that the new internal annotations
// have been changed by the function
"change only legacy annotations while input is internal annotations": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: legacy,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
internal.config.kubernetes.io/path: 'new'
internal.config.kubernetes.io/index: 'new'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
internal.config.kubernetes.io/path: "new"
internal.config.kubernetes.io/index: 'new'
data:
grpcPort: 8081
`,
},
// the orchestrator should detect that the legacy annotations
// have been changed by the function
"change only legacy annotations while input has both": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: 'configmap.yaml'
config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: legacy,
expected: `apiVersion: v1
kind: ConfigMap
metadata: metadata:
name: ports-from name: ports-from
annotations: annotations:
@@ -392,9 +579,204 @@ data:
`, `,
}, },
// the orchestrator should detect that the new internal annotations // the orchestrator should detect that the new internal annotations
// have been changed by the function, and should update the // have been changed by the function
// legacy annotations to reflect the same change "change only internal annotations while input has both": {
"change only internal annotations": { input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: internal,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: 'new'
internal.config.kubernetes.io/path: 'new'
internal.config.kubernetes.io/index: 'new'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: 'new'
internal.config.kubernetes.io/path: "new"
internal.config.kubernetes.io/index: 'new'
data:
grpcPort: 8081
`,
},
// the orchestrator should detect that the new internal annotations
// have been changed by the function
"change both to matching value while input has both": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: changeBothMatch,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'new'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: "new"
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
},
// the orchestrator should detect that the new internal annotations
// have been changed by the function
"change both to matching value while input is legacy": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: changeBothMatch,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "new"
config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
},
// the orchestrator should detect that the new internal annotations
// have been changed by the function
"change both to matching value while input is internal": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
internal.config.kubernetes.io/path: 'configmap.yaml'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: changeBothMatch,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
internal.config.kubernetes.io/path: 'new'
internal.config.kubernetes.io/index: '0'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
internal.config.kubernetes.io/path: "new"
internal.config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
},
// the function changes both the legacy and new path annotation, and they mismatch,
// so we should get an error
"change both but mismatch while input is legacy": {
input: `apiVersion: v1 input: `apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
@@ -415,16 +797,19 @@ metadata:
data: data:
grpcPort: 8081 grpcPort: 8081
`, `,
filters: internal, filters: changeBothMismatch,
expected: `apiVersion: v1 expectedErr: "resource input to function has mismatched legacy and internal path annotations",
},
// the function changes both the legacy and new path annotation, and they mismatch,
// so we should get an error
"change both but mismatch while input is internal": {
input: `apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: ports-from name: ports-from
annotations: annotations:
config.kubernetes.io/path: 'new' internal.config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: 'new' internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'new'
internal.config.kubernetes.io/index: 'new'
data: data:
grpcPort: 8080 grpcPort: 8080
--- ---
@@ -433,24 +818,27 @@ kind: ConfigMap
metadata: metadata:
name: ports-to name: ports-to
annotations: annotations:
config.kubernetes.io/path: "new" internal.config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: 'new' internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'new'
internal.config.kubernetes.io/index: 'new'
data: data:
grpcPort: 8081 grpcPort: 8081
`, `,
filters: changeBothMismatch,
expectedErr: "resource input to function has mismatched legacy and internal path annotations",
}, },
// the function changes both the legacy and new path annotation, // the function changes both the legacy and new path annotation, and they mismatch,
// so we should get an error // so we should get an error
"change both": { "change both but mismatch while input has both": {
input: `apiVersion: v1 input: `apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: ports-from name: ports-from
annotations: annotations:
config.kubernetes.io/path: 'configmap.yaml' config.kubernetes.io/path: 'configmap.yaml'
internal.kubernetes.io/path: 'configmap.yaml' config.kubernetes.io/index: '0'
config.k8s.io/id: '1'
internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '0'
data: data:
grpcPort: 8080 grpcPort: 8080
--- ---
@@ -461,10 +849,13 @@ metadata:
annotations: annotations:
config.kubernetes.io/path: "configmap.yaml" config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.k8s.io/id: '2'
internal.config.kubernetes.io/path: "configmap.yaml"
internal.config.kubernetes.io/index: '1'
data: data:
grpcPort: 8081 grpcPort: 8081
`, `,
filters: changeBoth, filters: changeBothMismatch,
expectedErr: "resource input to function has mismatched legacy and internal path annotations", expectedErr: "resource input to function has mismatched legacy and internal path annotations",
}, },
} }

View File

@@ -44,7 +44,8 @@ const (
) )
func GetFileAnnotations(rn *yaml.RNode) (string, string, error) { func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
annotations := rn.GetAnnotations() rm, _ := rn.GetMeta()
annotations := rm.Annotations
path, found := annotations[PathAnnotation] path, found := annotations[PathAnnotation]
if !found { if !found {
path = annotations[LegacyPathAnnotation] path = annotations[LegacyPathAnnotation]
@@ -57,7 +58,8 @@ func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
} }
func GetIdAnnotation(rn *yaml.RNode) string { func GetIdAnnotation(rn *yaml.RNode) string {
annotations := rn.GetAnnotations() rm, _ := rn.GetMeta()
annotations := rm.Annotations
id, found := annotations[IdAnnotation] id, found := annotations[IdAnnotation]
if !found { if !found {
id = annotations[LegacyIdAnnotation] id = annotations[LegacyIdAnnotation]
@@ -391,7 +393,8 @@ func ConfirmInternalAnnotationUnchanged(r1 *yaml.RNode, r2 *yaml.RNode, exclusio
// `internal.config.kubernetes.io` 2) is one of `config.kubernetes.io/path`, // `internal.config.kubernetes.io` 2) is one of `config.kubernetes.io/path`,
// `config.kubernetes.io/index` and `config.k8s.io/id`. // `config.kubernetes.io/index` and `config.k8s.io/id`.
func GetInternalAnnotations(rn *yaml.RNode) map[string]string { func GetInternalAnnotations(rn *yaml.RNode) map[string]string {
annotations := rn.GetAnnotations() meta, _ := rn.GetMeta()
annotations := meta.Annotations
result := make(map[string]string) result := make(map[string]string)
for k, v := range annotations { for k, v := range annotations {
if strings.HasPrefix(k, internalPrefix) || k == LegacyPathAnnotation || k == LegacyIndexAnnotation || k == LegacyIdAnnotation { if strings.HasPrefix(k, internalPrefix) || k == LegacyPathAnnotation || k == LegacyIndexAnnotation || k == LegacyIdAnnotation {