mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
217 lines
5.6 KiB
Go
217 lines
5.6 KiB
Go
// Copyright 2019 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package walk
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/go-errors/errors"
|
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
"sigs.k8s.io/kustomize/kyaml/sets"
|
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
|
|
// may require initializing the dest node
|
|
dest, err := l.Sources.setDestNode(l.VisitList(l.Sources, l.Schema, AssociativeList))
|
|
if dest == nil || err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var key, strategy string
|
|
if l.Schema != nil {
|
|
strategy, key = l.Schema.PatchStrategyAndKey()
|
|
}
|
|
if strategy == "" && key == "" { // neither strategy nor not present in the schema -- infer the key
|
|
// find the list of elements we need to recursively walk
|
|
key, err = l.elementKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// non-primitive associative list -- merge the elements
|
|
if key != "" {
|
|
values := l.elementValues(key)
|
|
|
|
// recursively set the elements in the list
|
|
var s *openapi.ResourceSchema
|
|
if l.Schema != nil {
|
|
s = l.Schema.Elements()
|
|
}
|
|
for _, value := range values {
|
|
val, err := Walker{
|
|
VisitKeysAsScalars: l.VisitKeysAsScalars,
|
|
InferAssociativeLists: l.InferAssociativeLists,
|
|
Visitor: l,
|
|
Schema: s,
|
|
Sources: l.elementValue(key, value),
|
|
}.Walk()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if yaml.IsMissingOrNull(val) || yaml.IsEmptyMap(val) {
|
|
_, err = dest.Pipe(yaml.ElementSetter{Key: key, Value: value})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
continue
|
|
}
|
|
|
|
if val.Field(key) == nil {
|
|
// make sure the key is set on the field
|
|
_, err = val.Pipe(yaml.SetField(key, yaml.NewScalarRNode(value)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// this handles empty and non-empty values
|
|
_, err = dest.Pipe(yaml.ElementSetter{Element: val.YNode(), Key: key, Value: value})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
// field is empty
|
|
if yaml.IsMissingOrNull(dest) {
|
|
return nil, nil
|
|
}
|
|
return dest, nil
|
|
}
|
|
|
|
// primitive associative list -- merge the values
|
|
values := l.elementPrimitiveValues()
|
|
var s *openapi.ResourceSchema
|
|
if l.Schema != nil {
|
|
s = l.Schema.Elements()
|
|
}
|
|
for _, value := range values {
|
|
val, err := Walker{
|
|
VisitKeysAsScalars: l.VisitKeysAsScalars,
|
|
InferAssociativeLists: l.InferAssociativeLists,
|
|
Visitor: l,
|
|
Schema: s,
|
|
Sources: l.elementValue(key /*empty key implies primitive*/, value),
|
|
}.Walk()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if yaml.IsMissingOrNull(val) {
|
|
_, err = dest.Pipe(yaml.ElementSetter{Key: key, Value: value})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
continue
|
|
}
|
|
|
|
if val.Field(key) == nil {
|
|
// make sure the key is set on the field
|
|
_, err = val.Pipe(yaml.SetField(key, yaml.NewScalarRNode(value)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// this handles empty and non-empty values
|
|
_, err = dest.Pipe(yaml.ElementSetter{Element: val.YNode(), Key: key, Value: value})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// field is empty
|
|
if yaml.IsMissingOrNull(dest) {
|
|
return nil, nil
|
|
}
|
|
return dest, nil
|
|
}
|
|
|
|
// elementKey returns the merge key to use for the associative list
|
|
func (l Walker) elementKey() (string, error) {
|
|
var key string
|
|
for i := range l.Sources {
|
|
if l.Sources[i] != nil && len(l.Sources[i].Content()) > 0 {
|
|
newKey := l.Sources[i].GetAssociativeKey()
|
|
if key != "" && key != newKey {
|
|
return "", errors.Errorf(
|
|
"conflicting merge keys [%s,%s] for field %s",
|
|
key, newKey, strings.Join(l.Path, "."))
|
|
}
|
|
key = newKey
|
|
}
|
|
}
|
|
if key == "" {
|
|
return "", errors.Errorf("no merge key found for field %s",
|
|
strings.Join(l.Path, "."))
|
|
}
|
|
return key, nil
|
|
}
|
|
|
|
// elementValues returns a slice containing all values for the field across all elements
|
|
// from all sources.
|
|
// Return value slice is ordered using the original ordering from the elements, where
|
|
// elements missing from earlier sources appear later.
|
|
func (l Walker) elementValues(key string) []string {
|
|
// use slice to to keep elements in the original order
|
|
// dest node must be first
|
|
var returnValues []string
|
|
seen := sets.String{}
|
|
for i := range l.Sources {
|
|
if l.Sources[i] == nil {
|
|
continue
|
|
}
|
|
|
|
// add the value of the field for each element
|
|
// don't check error, we know this is a list node
|
|
values, _ := l.Sources[i].ElementValues(key)
|
|
for _, s := range values {
|
|
if seen.Has(s) {
|
|
continue
|
|
}
|
|
returnValues = append(returnValues, s)
|
|
seen.Insert(s)
|
|
}
|
|
}
|
|
return returnValues
|
|
}
|
|
|
|
// elementPrimitiveValues returns the primitive values in an associative list -- eg. finalizers
|
|
// TODO: figure out the right order -- currently the order is deterministic but may be improved
|
|
// upon.
|
|
func (l Walker) elementPrimitiveValues() []string {
|
|
// use slice to to keep elements in the original order
|
|
// dest node must be first
|
|
var returnValues []string
|
|
seen := sets.String{}
|
|
for i := range l.Sources {
|
|
if l.Sources[i] == nil {
|
|
continue
|
|
}
|
|
|
|
// add the value of the field for each element
|
|
// don't check error, we know this is a list node
|
|
for _, item := range l.Sources[i].YNode().Content {
|
|
if seen.Has(item.Value) {
|
|
continue
|
|
}
|
|
returnValues = append(returnValues, item.Value)
|
|
seen.Insert(item.Value)
|
|
}
|
|
}
|
|
return returnValues
|
|
}
|
|
|
|
// fieldValue returns a slice containing each source's value for fieldName
|
|
func (l Walker) elementValue(key, value string) []*yaml.RNode {
|
|
var fields []*yaml.RNode
|
|
for i := range l.Sources {
|
|
if l.Sources[i] == nil {
|
|
fields = append(fields, nil)
|
|
continue
|
|
}
|
|
fields = append(fields, l.Sources[i].Element(key, value))
|
|
}
|
|
return fields
|
|
}
|