182
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/merge2.go
generated
vendored
Normal file
182
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/merge2.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package merge contains libraries for merging fields from one RNode to another
|
||||
// RNode
|
||||
package merge2
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
)
|
||||
|
||||
// Merge merges fields from src into dest.
|
||||
func Merge(src, dest *yaml.RNode, mergeOptions yaml.MergeOptions) (*yaml.RNode, error) {
|
||||
return walk.Walker{
|
||||
Sources: []*yaml.RNode{dest, src},
|
||||
Visitor: Merger{},
|
||||
MergeOptions: mergeOptions,
|
||||
}.Walk()
|
||||
}
|
||||
|
||||
// Merge parses the arguments, and merges fields from srcStr into destStr.
|
||||
func MergeStrings(srcStr, destStr string, infer bool, mergeOptions yaml.MergeOptions) (string, error) {
|
||||
src, err := yaml.Parse(srcStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dest, err := yaml.Parse(destStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result, err := walk.Walker{
|
||||
Sources: []*yaml.RNode{dest, src},
|
||||
Visitor: Merger{},
|
||||
InferAssociativeLists: infer,
|
||||
MergeOptions: mergeOptions,
|
||||
}.Walk()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result.String()
|
||||
}
|
||||
|
||||
type Merger struct {
|
||||
// for forwards compatibility when new functions are added to the interface
|
||||
}
|
||||
|
||||
var _ walk.Visitor = Merger{}
|
||||
|
||||
func (m Merger) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if err := m.SetComments(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.SetStyle(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
// Add
|
||||
ps, _ := determineSmpDirective(nodes.Origin())
|
||||
if ps == smpDelete {
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
if nodes.Origin().IsTaggedNull() {
|
||||
// clear the value
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
ps, err := determineSmpDirective(nodes.Origin())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch ps {
|
||||
case smpDelete:
|
||||
return walk.ClearNode, nil
|
||||
case smpReplace:
|
||||
return nodes.Origin(), nil
|
||||
default:
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m Merger) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if err := m.SetComments(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.SetStyle(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Override value
|
||||
if nodes.Origin() != nil {
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
// Keep
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Merger) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind walk.ListKind) (*yaml.RNode, error) {
|
||||
if err := m.SetComments(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.SetStyle(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if kind == walk.NonAssociateList {
|
||||
// Override value
|
||||
if nodes.Origin() != nil {
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
// Keep
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
// Add
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
// Clear
|
||||
if nodes.Origin().IsTaggedNull() {
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
ps, err := determineSmpDirective(nodes.Origin())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch ps {
|
||||
case smpDelete:
|
||||
return walk.ClearNode, nil
|
||||
case smpReplace:
|
||||
return nodes.Origin(), nil
|
||||
default:
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m Merger) SetStyle(sources walk.Sources) error {
|
||||
source := sources.Origin()
|
||||
dest := sources.Dest()
|
||||
if dest == nil || dest.YNode() == nil || source == nil || source.YNode() == nil {
|
||||
// avoid panic
|
||||
return nil
|
||||
}
|
||||
|
||||
// copy the style from the source.
|
||||
// special case: if the dest was an empty map or seq, then it probably had
|
||||
// folded style applied, but we actually want to keep the style of the origin
|
||||
// in this case (even if it was the default). otherwise the merged elements
|
||||
// will get folded even though this probably isn't what is desired.
|
||||
if dest.YNode().Kind != yaml.ScalarNode && len(dest.YNode().Content) == 0 {
|
||||
dest.YNode().Style = source.YNode().Style
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetComments copies the dest comments to the source comments if they are present
|
||||
// on the source.
|
||||
func (m Merger) SetComments(sources walk.Sources) error {
|
||||
source := sources.Origin()
|
||||
dest := sources.Dest()
|
||||
if dest == nil || dest.YNode() == nil || source == nil || source.YNode() == nil {
|
||||
// avoid panic
|
||||
return nil
|
||||
}
|
||||
if source.YNode().FootComment != "" {
|
||||
dest.YNode().FootComment = source.YNode().FootComment
|
||||
}
|
||||
if source.YNode().HeadComment != "" {
|
||||
dest.YNode().HeadComment = source.YNode().HeadComment
|
||||
}
|
||||
if source.YNode().LineComment != "" {
|
||||
dest.YNode().LineComment = source.YNode().LineComment
|
||||
}
|
||||
return nil
|
||||
}
|
||||
101
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective.go
generated
vendored
Normal file
101
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package merge2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// A strategic merge patch directive.
|
||||
// See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
//
|
||||
//go:generate stringer -type=smpDirective -linecomment
|
||||
type smpDirective int
|
||||
|
||||
const (
|
||||
smpUnknown smpDirective = iota // unknown
|
||||
smpReplace // replace
|
||||
smpDelete // delete
|
||||
smpMerge // merge
|
||||
)
|
||||
|
||||
const strategicMergePatchDirectiveKey = "$patch"
|
||||
|
||||
// Examine patch for a strategic merge patch directive.
|
||||
// If found, return it, and remove the directive from the patch.
|
||||
func determineSmpDirective(patch *yaml.RNode) (smpDirective, error) {
|
||||
if patch == nil {
|
||||
return smpMerge, nil
|
||||
}
|
||||
switch patch.YNode().Kind {
|
||||
case yaml.SequenceNode:
|
||||
return determineSequenceNodePatchStrategy(patch)
|
||||
case yaml.MappingNode:
|
||||
return determineMappingNodePatchStrategy(patch)
|
||||
default:
|
||||
return smpUnknown, fmt.Errorf(
|
||||
"no implemented strategic merge patch strategy for '%s' ('%s')",
|
||||
patch.YNode().ShortTag(), patch.MustString())
|
||||
}
|
||||
}
|
||||
|
||||
func determineSequenceNodePatchStrategy(patch *yaml.RNode) (smpDirective, error) {
|
||||
// get the $patch element
|
||||
node, err := patch.Pipe(yaml.GetElementByKey(strategicMergePatchDirectiveKey))
|
||||
// if there are more than 1 key/value pair in the map, then this $patch
|
||||
// is not for the sequence
|
||||
if err != nil || node == nil || node.YNode() == nil || len(node.Content()) > 2 {
|
||||
return smpMerge, nil
|
||||
}
|
||||
// get the value
|
||||
value, err := node.Pipe(yaml.Get(strategicMergePatchDirectiveKey))
|
||||
if err != nil || value == nil || value.YNode() == nil {
|
||||
return smpMerge, nil
|
||||
}
|
||||
v := value.YNode().Value
|
||||
if v == smpDelete.String() {
|
||||
return smpDelete, elideSequencePatchDirective(patch, v)
|
||||
}
|
||||
if v == smpReplace.String() {
|
||||
return smpReplace, elideSequencePatchDirective(patch, v)
|
||||
}
|
||||
if v == smpMerge.String() {
|
||||
return smpMerge, elideSequencePatchDirective(patch, v)
|
||||
}
|
||||
return smpUnknown, fmt.Errorf(
|
||||
"unknown patch strategy '%s'", v)
|
||||
}
|
||||
|
||||
func determineMappingNodePatchStrategy(patch *yaml.RNode) (smpDirective, error) {
|
||||
node, err := patch.Pipe(yaml.Get(strategicMergePatchDirectiveKey))
|
||||
if err != nil || node == nil || node.YNode() == nil {
|
||||
return smpMerge, nil
|
||||
}
|
||||
v := node.YNode().Value
|
||||
if v == smpDelete.String() {
|
||||
return smpDelete, elideMappingPatchDirective(patch)
|
||||
}
|
||||
if v == smpReplace.String() {
|
||||
return smpReplace, elideMappingPatchDirective(patch)
|
||||
}
|
||||
if v == smpMerge.String() {
|
||||
return smpMerge, elideMappingPatchDirective(patch)
|
||||
}
|
||||
return smpUnknown, fmt.Errorf(
|
||||
"unknown patch strategy '%s'", v)
|
||||
}
|
||||
|
||||
func elideMappingPatchDirective(patch *yaml.RNode) error {
|
||||
return patch.PipeE(yaml.Clear(strategicMergePatchDirectiveKey))
|
||||
}
|
||||
|
||||
func elideSequencePatchDirective(patch *yaml.RNode, value string) error {
|
||||
return patch.PipeE(yaml.ElementSetter{
|
||||
Element: nil,
|
||||
Keys: []string{strategicMergePatchDirectiveKey},
|
||||
Values: []string{value},
|
||||
})
|
||||
}
|
||||
26
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective_string.go
generated
vendored
Normal file
26
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective_string.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Code generated by "stringer -type=smpDirective -linecomment"; DO NOT EDIT.
|
||||
|
||||
package merge2
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[smpUnknown-0]
|
||||
_ = x[smpReplace-1]
|
||||
_ = x[smpDelete-2]
|
||||
_ = x[smpMerge-3]
|
||||
}
|
||||
|
||||
const _smpDirective_name = "unknownreplacedeletemerge"
|
||||
|
||||
var _smpDirective_index = [...]uint8{0, 7, 14, 20, 25}
|
||||
|
||||
func (i smpDirective) String() string {
|
||||
if i < 0 || i >= smpDirective(len(_smpDirective_index)-1) {
|
||||
return "smpDirective(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _smpDirective_name[_smpDirective_index[i]:_smpDirective_index[i+1]]
|
||||
}
|
||||
45
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/merge3.go
generated
vendored
Normal file
45
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/merge3.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package merge contains libraries for merging fields from one RNode to another
|
||||
// RNode
|
||||
package merge3
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
)
|
||||
|
||||
func Merge(dest, original, update *yaml.RNode) (*yaml.RNode, error) {
|
||||
// if update == nil && original != nil => declarative deletion
|
||||
|
||||
return walk.Walker{
|
||||
Visitor: Visitor{},
|
||||
VisitKeysAsScalars: true,
|
||||
Sources: []*yaml.RNode{dest, original, update}}.Walk()
|
||||
}
|
||||
|
||||
func MergeStrings(dest, original, update string, infer bool) (string, error) {
|
||||
srcOriginal, err := yaml.Parse(original)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
srcUpdated, err := yaml.Parse(update)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
d, err := yaml.Parse(dest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result, err := walk.Walker{
|
||||
InferAssociativeLists: infer,
|
||||
Visitor: Visitor{},
|
||||
VisitKeysAsScalars: true,
|
||||
Sources: []*yaml.RNode{d, srcOriginal, srcUpdated}}.Walk()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
172
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/visitor.go
generated
vendored
Normal file
172
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/visitor.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package merge3
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
)
|
||||
|
||||
type ConflictStrategy uint
|
||||
|
||||
const (
|
||||
// TODO: Support more strategies
|
||||
TakeUpdate ConflictStrategy = 1 + iota
|
||||
)
|
||||
|
||||
type Visitor struct{}
|
||||
|
||||
func (m Visitor) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
if nodes.Dest() == nil && nodes.Updated() == nil {
|
||||
// implicitly cleared missing from both dest and update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
if nodes.Dest() == nil {
|
||||
// not cleared, but missing from the dest
|
||||
// initialize a new value that can be recursively merged
|
||||
return yaml.NewRNode(&yaml.Node{Kind: yaml.MappingNode}), nil
|
||||
}
|
||||
|
||||
// recursively merge the dest with the original and updated
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) visitAList(nodes walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && !yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// implicitly cleared from update -- element was deleted
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
// not cleared, but missing from the dest
|
||||
// initialize a new value that can be recursively merged
|
||||
return yaml.NewRNode(&yaml.Node{Kind: yaml.SequenceNode}), nil
|
||||
}
|
||||
|
||||
// recursively merge the dest with the original and updated
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return nil, nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
values, err := m.getStrValues(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if (values.Dest == "" || values.Dest == values.Origin) && values.Origin != values.Update {
|
||||
// if local is nil or is unchanged but there is new update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
|
||||
if nodes.Updated().YNode().Value != nodes.Origin().YNode().Value {
|
||||
// value changed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
|
||||
// unchanged between origin and update, keep the dest
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) visitNAList(nodes walk.Sources) (*yaml.RNode, error) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value not present in source or dest
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
// compare origin and update values to see if they have changed
|
||||
values, err := m.getStrValues(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if values.Update != values.Origin {
|
||||
// value changed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
|
||||
// unchanged between origin and update, keep the dest
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind walk.ListKind) (*yaml.RNode, error) {
|
||||
if kind == walk.AssociativeList {
|
||||
return m.visitAList(nodes, s)
|
||||
}
|
||||
// non-associative list
|
||||
return m.visitNAList(nodes)
|
||||
}
|
||||
|
||||
func (m Visitor) getStrValues(nodes walk.Sources) (strValues, error) {
|
||||
var uStr, oStr, dStr string
|
||||
var err error
|
||||
if nodes.Updated() != nil && nodes.Updated().YNode() != nil {
|
||||
s := nodes.Updated().YNode().Style
|
||||
defer func() {
|
||||
nodes.Updated().YNode().Style = s
|
||||
}()
|
||||
nodes.Updated().YNode().Style = yaml.FlowStyle | yaml.SingleQuotedStyle
|
||||
uStr, err = nodes.Updated().String()
|
||||
if err != nil {
|
||||
return strValues{}, err
|
||||
}
|
||||
}
|
||||
if nodes.Origin() != nil && nodes.Origin().YNode() != nil {
|
||||
s := nodes.Origin().YNode().Style
|
||||
defer func() {
|
||||
nodes.Origin().YNode().Style = s
|
||||
}()
|
||||
nodes.Origin().YNode().Style = yaml.FlowStyle | yaml.SingleQuotedStyle
|
||||
oStr, err = nodes.Origin().String()
|
||||
if err != nil {
|
||||
return strValues{}, err
|
||||
}
|
||||
}
|
||||
if nodes.Dest() != nil && nodes.Dest().YNode() != nil {
|
||||
s := nodes.Dest().YNode().Style
|
||||
defer func() {
|
||||
nodes.Dest().YNode().Style = s
|
||||
}()
|
||||
nodes.Dest().YNode().Style = yaml.FlowStyle | yaml.SingleQuotedStyle
|
||||
dStr, err = nodes.Dest().String()
|
||||
if err != nil {
|
||||
return strValues{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return strValues{Origin: oStr, Update: uStr, Dest: dStr}, nil
|
||||
}
|
||||
|
||||
type strValues struct {
|
||||
Origin string
|
||||
Update string
|
||||
Dest string
|
||||
}
|
||||
|
||||
var _ walk.Visitor = Visitor{}
|
||||
44
vendor/sigs.k8s.io/kustomize/kyaml/yaml/schema/schema.go
generated
vendored
Normal file
44
vendor/sigs.k8s.io/kustomize/kyaml/yaml/schema/schema.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package schema contains libraries for working with the yaml and openapi packages.
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// IsAssociative returns true if all elements in the list contain an
|
||||
// AssociativeSequenceKey as a field.
|
||||
func IsAssociative(schema *openapi.ResourceSchema, nodes []*yaml.RNode, infer bool) bool {
|
||||
if schema != nil {
|
||||
return schemaHasMergeStrategy(schema)
|
||||
}
|
||||
if !infer {
|
||||
return false
|
||||
}
|
||||
for i := range nodes {
|
||||
node := nodes[i]
|
||||
if yaml.IsMissingOrNull(node) {
|
||||
continue
|
||||
}
|
||||
if node.IsAssociative() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func schemaHasMergeStrategy(schema *openapi.ResourceSchema) bool {
|
||||
tmp, _ := schema.PatchStrategyAndKey()
|
||||
strategies := strings.Split(tmp, ",")
|
||||
for _, s := range strategies {
|
||||
if s == "merge" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
385
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/associative_sequence.go
generated
vendored
Normal file
385
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/associative_sequence.go
generated
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// appendListNode will append the nodes from src to dst and return dst.
|
||||
// src and dst should be both sequence node. key is used to call ElementSetter.
|
||||
// ElementSetter will use key-value pair to find and set the element in sequence
|
||||
// node.
|
||||
func appendListNode(dst, src *yaml.RNode, keys []string) (*yaml.RNode, error) {
|
||||
var err error
|
||||
for _, elem := range src.Content() {
|
||||
// If key is empty, we know this is a scalar value and we can directly set the
|
||||
// node
|
||||
if keys[0] == "" {
|
||||
_, err = dst.Pipe(yaml.ElementSetter{
|
||||
Element: elem,
|
||||
Keys: []string{""},
|
||||
Values: []string{elem.Value},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// we need to get the value for key so that we can find the element to set
|
||||
// in sequence.
|
||||
v := []string{}
|
||||
for _, key := range keys {
|
||||
tmpNode := yaml.NewRNode(elem)
|
||||
valueNode, err := tmpNode.Pipe(yaml.Get(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if valueNode.IsNil() {
|
||||
// no key found, directly append to dst
|
||||
err = dst.PipeE(yaml.Append(elem))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
v = append(v, valueNode.YNode().Value)
|
||||
}
|
||||
|
||||
// When there are multiple keys, ElementSetter appends the node to dst
|
||||
// even if the output is already in dst. We remove the node from dst to
|
||||
// prevent duplicates.
|
||||
if len(keys) > 1 {
|
||||
_, err = dst.Pipe(yaml.ElementSetter{
|
||||
Keys: keys,
|
||||
Values: v,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// We use the key and value from elem to find the corresponding element in dst.
|
||||
// Then we will use ElementSetter to replace the element with elem. If we cannot
|
||||
// find the item, the element will be appended.
|
||||
_, err = dst.Pipe(yaml.ElementSetter{
|
||||
Element: elem,
|
||||
Keys: keys,
|
||||
Values: v,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// validateKeys returns a list of valid key-value pairs
|
||||
// if secondary merge key values are missing, use only the available merge keys
|
||||
func validateKeys(valuesList [][]string, values []string, keys []string) ([]string, []string) {
|
||||
validKeys := make([]string, 0)
|
||||
validValues := make([]string, 0)
|
||||
validKeySet := sets.String{}
|
||||
for _, values := range valuesList {
|
||||
for i, v := range values {
|
||||
if v != "" {
|
||||
validKeySet.Insert(keys[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
if validKeySet.Len() == 0 { // if values missing, fall back to primary keys
|
||||
return keys, values
|
||||
}
|
||||
for _, k := range keys {
|
||||
if validKeySet.Has(k) {
|
||||
validKeys = append(validKeys, k)
|
||||
}
|
||||
}
|
||||
for i, v := range values {
|
||||
if v != "" || validKeySet.Has(keys[i]) {
|
||||
validValues = append(validValues, v)
|
||||
}
|
||||
}
|
||||
return validKeys, validValues
|
||||
}
|
||||
|
||||
// mergeValues merges values together - e.g. if two containerPorts
|
||||
// have the same port and targetPort but one has an empty protocol
|
||||
// and the other doesn't, they are treated as the same containerPort
|
||||
func mergeValues(valuesList [][]string) [][]string {
|
||||
for i, values1 := range valuesList {
|
||||
for j, values2 := range valuesList {
|
||||
if matched, values := match(values1, values2); matched {
|
||||
valuesList[i] = values
|
||||
valuesList[j] = values
|
||||
}
|
||||
}
|
||||
}
|
||||
return valuesList
|
||||
}
|
||||
|
||||
// two values match if they have at least one common element and
|
||||
// corresponding elements only differ if one is an empty string
|
||||
func match(values1 []string, values2 []string) (bool, []string) {
|
||||
if len(values1) != len(values2) {
|
||||
return false, nil
|
||||
}
|
||||
var commonElement bool
|
||||
var res []string
|
||||
for i := range values1 {
|
||||
if values1[i] == values2[i] {
|
||||
commonElement = true
|
||||
res = append(res, values1[i])
|
||||
continue
|
||||
}
|
||||
if values1[i] != "" && values2[i] != "" {
|
||||
return false, nil
|
||||
}
|
||||
if values1[i] != "" {
|
||||
res = append(res, values1[i])
|
||||
} else {
|
||||
res = append(res, values2[i])
|
||||
}
|
||||
}
|
||||
return commonElement, res
|
||||
}
|
||||
|
||||
// setAssociativeSequenceElements recursively set the elements in the list
|
||||
func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []string, dest *yaml.RNode) (*yaml.RNode, error) {
|
||||
// itemsToBeAdded contains the items that will be added to dest
|
||||
itemsToBeAdded := yaml.NewListRNode()
|
||||
var schema *openapi.ResourceSchema
|
||||
if l.Schema != nil {
|
||||
schema = l.Schema.Elements()
|
||||
}
|
||||
if len(keys) > 1 {
|
||||
valuesList = mergeValues(valuesList)
|
||||
}
|
||||
|
||||
// each element in valuesList is a list of values corresponding to the keys
|
||||
// for example, for the following yaml:
|
||||
// - containerPort: 8080
|
||||
// protocol: UDP
|
||||
// - containerPort: 8080
|
||||
// protocol: TCP
|
||||
// `keys` would be [containerPort, protocol]
|
||||
// and `valuesList` would be [ [8080, UDP], [8080, TCP] ]
|
||||
var validKeys []string
|
||||
var validValues []string
|
||||
for _, values := range valuesList {
|
||||
if len(values) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
validKeys, validValues = validateKeys(valuesList, values, keys)
|
||||
val, err := Walker{
|
||||
VisitKeysAsScalars: l.VisitKeysAsScalars,
|
||||
InferAssociativeLists: l.InferAssociativeLists,
|
||||
Visitor: l,
|
||||
Schema: schema,
|
||||
Sources: l.elementValueList(validKeys, validValues),
|
||||
MergeOptions: l.MergeOptions,
|
||||
}.Walk()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exit := false
|
||||
for i, key := range validKeys {
|
||||
// delete the node from **dest** if it's null or empty
|
||||
if yaml.IsMissingOrNull(val) || yaml.IsEmptyMap(val) {
|
||||
_, err = dest.Pipe(yaml.ElementSetter{
|
||||
Keys: validKeys,
|
||||
Values: validValues,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exit = true
|
||||
} else if val.Field(key) == nil && validValues[i] != "" {
|
||||
// make sure the key is set on the field
|
||||
_, err = val.Pipe(yaml.SetField(key, yaml.NewScalarRNode(validValues[i])))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if exit {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the val to the sequence. val will replace the item in the sequence if
|
||||
// there is an item that matches all key-value pairs. Otherwise val will be appended
|
||||
// the the sequence.
|
||||
_, err = itemsToBeAdded.Pipe(yaml.ElementSetter{
|
||||
Element: val.YNode(),
|
||||
Keys: validKeys,
|
||||
Values: validValues,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if len(valuesList) > 0 {
|
||||
if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
|
||||
// items from patches are needed to be prepended. so we append the
|
||||
// dest to itemsToBeAdded
|
||||
dest, err = appendListNode(itemsToBeAdded, dest, validKeys)
|
||||
} else {
|
||||
// append the items
|
||||
dest, err = appendListNode(dest, itemsToBeAdded, validKeys)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// sequence is empty
|
||||
if yaml.IsMissingOrNull(dest) {
|
||||
return nil, nil
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// get the merge key(s) from schema
|
||||
var strategy string
|
||||
var keys []string
|
||||
if l.Schema != nil {
|
||||
strategy, keys = l.Schema.PatchStrategyAndKeyList()
|
||||
}
|
||||
if strategy == "" && len(keys) == 0 { // neither strategy nor keys 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
|
||||
}
|
||||
if key != "" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
|
||||
// non-primitive associative list -- merge the elements
|
||||
values := l.elementValues(keys)
|
||||
if len(values) != 0 || len(keys) > 0 {
|
||||
return l.setAssociativeSequenceElements(values, keys, dest)
|
||||
}
|
||||
|
||||
// primitive associative list -- merge the values
|
||||
return l.setAssociativeSequenceElements(l.elementPrimitiveValues(), []string{""}, dest)
|
||||
}
|
||||
|
||||
// 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(keys []string) [][]string {
|
||||
// use slice to to keep elements in the original order
|
||||
var returnValues [][]string
|
||||
var seen sets.StringList
|
||||
|
||||
// if we are doing append, dest node should be the first.
|
||||
// otherwise dest node should be the last.
|
||||
beginIdx := 0
|
||||
if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
|
||||
beginIdx = 1
|
||||
}
|
||||
for i := range l.Sources {
|
||||
src := l.Sources[(i+beginIdx)%len(l.Sources)]
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// add the value of the field for each element
|
||||
// don't check error, we know this is a list node
|
||||
values, _ := src.ElementValuesList(keys)
|
||||
for _, s := range values {
|
||||
if len(s) == 0 || seen.Has(s) {
|
||||
continue
|
||||
}
|
||||
returnValues = append(returnValues, s)
|
||||
seen = seen.Insert(s)
|
||||
}
|
||||
}
|
||||
return returnValues
|
||||
}
|
||||
|
||||
// elementPrimitiveValues returns the primitive values in an associative list -- eg. finalizers
|
||||
func (l Walker) elementPrimitiveValues() [][]string {
|
||||
// use slice to to keep elements in the original order
|
||||
var returnValues [][]string
|
||||
seen := sets.String{}
|
||||
// if we are doing append, dest node should be the first.
|
||||
// otherwise dest node should be the last.
|
||||
beginIdx := 0
|
||||
if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
|
||||
beginIdx = 1
|
||||
}
|
||||
for i := range l.Sources {
|
||||
src := l.Sources[(i+beginIdx)%len(l.Sources)]
|
||||
if src == 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 src.YNode().Content {
|
||||
if seen.Has(item.Value) {
|
||||
continue
|
||||
}
|
||||
returnValues = append(returnValues, []string{item.Value})
|
||||
seen.Insert(item.Value)
|
||||
}
|
||||
}
|
||||
return returnValues
|
||||
}
|
||||
|
||||
// fieldValue returns a slice containing each source's value for fieldName
|
||||
func (l Walker) elementValueList(keys []string, values []string) []*yaml.RNode {
|
||||
keys, values = validateKeys([][]string{values}, values, keys)
|
||||
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].ElementList(keys, values))
|
||||
}
|
||||
return fields
|
||||
}
|
||||
173
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/map.go
generated
vendored
Normal file
173
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/map.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/sets"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// walkMap returns the value of VisitMap
|
||||
//
|
||||
// - call VisitMap
|
||||
// - set the return value on l.Dest
|
||||
// - walk each source field
|
||||
// - set each source field value on l.Dest
|
||||
func (l Walker) walkMap() (*yaml.RNode, error) {
|
||||
// get the new map value
|
||||
dest, err := l.Sources.setDestNode(l.VisitMap(l.Sources, l.Schema))
|
||||
if dest == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// recursively set the field values on the map
|
||||
for _, key := range l.fieldNames() {
|
||||
var res *yaml.RNode
|
||||
var keys []*yaml.RNode
|
||||
if l.VisitKeysAsScalars {
|
||||
// visit the map keys as if they were scalars,
|
||||
// this is necessary if doing things such as copying
|
||||
// comments
|
||||
for i := range l.Sources {
|
||||
// construct the sources from the keys
|
||||
if l.Sources[i] == nil {
|
||||
keys = append(keys, nil)
|
||||
continue
|
||||
}
|
||||
field := l.Sources[i].Field(key)
|
||||
if field == nil || yaml.IsMissingOrNull(field.Key) {
|
||||
keys = append(keys, nil)
|
||||
continue
|
||||
}
|
||||
keys = append(keys, field.Key)
|
||||
}
|
||||
// visit the sources as a scalar
|
||||
// keys don't have any schema --pass in nil
|
||||
res, err = l.Visitor.VisitScalar(keys, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var s *openapi.ResourceSchema
|
||||
if l.Schema != nil {
|
||||
s = l.Schema.Field(key)
|
||||
}
|
||||
fv, commentSch := l.fieldValue(key)
|
||||
if commentSch != nil {
|
||||
s = commentSch
|
||||
}
|
||||
val, err := Walker{
|
||||
VisitKeysAsScalars: l.VisitKeysAsScalars,
|
||||
InferAssociativeLists: l.InferAssociativeLists,
|
||||
Visitor: l,
|
||||
Schema: s,
|
||||
Sources: fv,
|
||||
MergeOptions: l.MergeOptions,
|
||||
Path: append(l.Path, key)}.Walk()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// transfer the comments of res to dest node
|
||||
var comments yaml.Comments
|
||||
if !yaml.IsMissingOrNull(res) {
|
||||
comments = yaml.Comments{
|
||||
LineComment: res.YNode().LineComment,
|
||||
HeadComment: res.YNode().HeadComment,
|
||||
FootComment: res.YNode().FootComment,
|
||||
}
|
||||
if len(keys) > 0 && !yaml.IsMissingOrNull(keys[DestIndex]) {
|
||||
keys[DestIndex].YNode().HeadComment = res.YNode().HeadComment
|
||||
keys[DestIndex].YNode().LineComment = res.YNode().LineComment
|
||||
keys[DestIndex].YNode().FootComment = res.YNode().FootComment
|
||||
}
|
||||
}
|
||||
|
||||
// this handles empty and non-empty values
|
||||
_, err = dest.Pipe(yaml.FieldSetter{Name: key, Comments: comments, Value: val})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
// valueIfPresent returns node.Value if node is non-nil, otherwise returns nil
|
||||
func (l Walker) valueIfPresent(node *yaml.MapNode) (*yaml.RNode, *openapi.ResourceSchema) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// parse the schema for the field if present
|
||||
var s *openapi.ResourceSchema
|
||||
fm := fieldmeta.FieldMeta{}
|
||||
var err error
|
||||
// check the value for a schema
|
||||
if err = fm.Read(node.Value); err == nil {
|
||||
s = &openapi.ResourceSchema{Schema: &fm.Schema}
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
r, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
|
||||
if err == nil && r != nil {
|
||||
s.Schema = r
|
||||
}
|
||||
}
|
||||
}
|
||||
// check the key for a schema -- this will be used
|
||||
// when the value is a Sequence (comments are attached)
|
||||
// to the key
|
||||
if fm.IsEmpty() {
|
||||
if err = fm.Read(node.Key); err == nil {
|
||||
s = &openapi.ResourceSchema{Schema: &fm.Schema}
|
||||
}
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
r, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
|
||||
if err == nil && r != nil {
|
||||
s.Schema = r
|
||||
}
|
||||
}
|
||||
}
|
||||
return node.Value, s
|
||||
}
|
||||
|
||||
// fieldNames returns a sorted slice containing the names of all fields that appear in any of
|
||||
// the sources
|
||||
func (l Walker) fieldNames() []string {
|
||||
fields := sets.String{}
|
||||
for _, s := range l.Sources {
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
// don't check error, we know this is a mapping node
|
||||
sFields, _ := s.Fields()
|
||||
fields.Insert(sFields...)
|
||||
}
|
||||
result := fields.List()
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// fieldValue returns a slice containing each source's value for fieldName
|
||||
func (l Walker) fieldValue(fieldName string) ([]*yaml.RNode, *openapi.ResourceSchema) {
|
||||
var fields []*yaml.RNode
|
||||
var sch *openapi.ResourceSchema
|
||||
for i := range l.Sources {
|
||||
if l.Sources[i] == nil {
|
||||
fields = append(fields, nil)
|
||||
continue
|
||||
}
|
||||
field := l.Sources[i].Field(fieldName)
|
||||
f, s := l.valueIfPresent(field)
|
||||
fields = append(fields, f)
|
||||
if sch == nil && !s.IsMissingOrNull() {
|
||||
sch = s
|
||||
}
|
||||
}
|
||||
return fields, sch
|
||||
}
|
||||
13
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/nonassociative_sequence.go
generated
vendored
Normal file
13
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/nonassociative_sequence.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// walkNonAssociativeSequence returns the value of VisitList
|
||||
func (l Walker) walkNonAssociativeSequence() (*yaml.RNode, error) {
|
||||
return l.VisitList(l.Sources, l.Schema, NonAssociateList)
|
||||
}
|
||||
11
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/scalar.go
generated
vendored
Normal file
11
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/scalar.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
// walkScalar returns the value of VisitScalar
|
||||
func (l Walker) walkScalar() (*yaml.RNode, error) {
|
||||
return l.VisitScalar(l.Sources, l.Schema)
|
||||
}
|
||||
28
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/visitor.go
generated
vendored
Normal file
28
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/visitor.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type ListKind int32
|
||||
|
||||
const (
|
||||
AssociativeList ListKind = 1 + iota
|
||||
NonAssociateList
|
||||
)
|
||||
|
||||
// Visitor is invoked by walk with source and destination node pairs
|
||||
type Visitor interface {
|
||||
VisitMap(Sources, *openapi.ResourceSchema) (*yaml.RNode, error)
|
||||
|
||||
VisitScalar(Sources, *openapi.ResourceSchema) (*yaml.RNode, error)
|
||||
|
||||
VisitList(Sources, *openapi.ResourceSchema, ListKind) (*yaml.RNode, error)
|
||||
}
|
||||
|
||||
// ClearNode is returned if GrepFilter should do nothing after calling Set
|
||||
var ClearNode *yaml.RNode
|
||||
186
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/walk.go
generated
vendored
Normal file
186
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/walk.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/schema"
|
||||
)
|
||||
|
||||
// Walker walks the Source RNode and modifies the RNode provided to GrepFilter.
|
||||
type Walker struct {
|
||||
// Visitor is invoked by GrepFilter
|
||||
Visitor
|
||||
|
||||
Schema *openapi.ResourceSchema
|
||||
|
||||
// Source is the RNode to walk. All Source fields and associative list elements
|
||||
// will be visited.
|
||||
Sources Sources
|
||||
|
||||
// Path is the field path to the current Source Node.
|
||||
Path []string
|
||||
|
||||
// InferAssociativeLists if set to true will infer merge strategies for
|
||||
// fields which it doesn't have the schema based on the fields in the
|
||||
// list elements.
|
||||
InferAssociativeLists bool
|
||||
|
||||
// VisitKeysAsScalars if true will call VisitScalar on map entry keys,
|
||||
// providing nil as the OpenAPI schema.
|
||||
VisitKeysAsScalars bool
|
||||
|
||||
// MergeOptions is a struct to store options for merge
|
||||
MergeOptions yaml.MergeOptions
|
||||
}
|
||||
|
||||
// Kind returns the kind of the first non-null node in Sources.
|
||||
func (l Walker) Kind() yaml.Kind {
|
||||
for _, s := range l.Sources {
|
||||
if !yaml.IsMissingOrNull(s) {
|
||||
return s.YNode().Kind
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Walk will recursively traverse every item in the Sources and perform corresponding
|
||||
// actions on them
|
||||
func (l Walker) Walk() (*yaml.RNode, error) {
|
||||
l.Schema = l.GetSchema()
|
||||
|
||||
// invoke the handler for the corresponding node type
|
||||
switch l.Kind() {
|
||||
case yaml.MappingNode:
|
||||
if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.MappingNode, l.Sources...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.walkMap()
|
||||
case yaml.SequenceNode:
|
||||
if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.SequenceNode, l.Sources...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// AssociativeSequence means the items in the sequence are associative. They can be merged
|
||||
// according to merge key.
|
||||
if schema.IsAssociative(l.Schema, l.Sources, l.InferAssociativeLists) {
|
||||
return l.walkAssociativeSequence()
|
||||
}
|
||||
return l.walkNonAssociativeSequence()
|
||||
|
||||
case yaml.ScalarNode:
|
||||
if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.ScalarNode, l.Sources...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.walkScalar()
|
||||
case 0:
|
||||
// walk empty nodes as maps
|
||||
return l.walkMap()
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l Walker) GetSchema() *openapi.ResourceSchema {
|
||||
for i := range l.Sources {
|
||||
r := l.Sources[i]
|
||||
if yaml.IsMissingOrNull(r) {
|
||||
continue
|
||||
}
|
||||
|
||||
fm := fieldmeta.FieldMeta{}
|
||||
if err := fm.Read(r); err == nil && !fm.IsEmpty() {
|
||||
// per-field schema, this is fine
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
// resolve the reference
|
||||
s, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
|
||||
if err == nil && s != nil {
|
||||
fm.Schema = *s
|
||||
}
|
||||
}
|
||||
return &openapi.ResourceSchema{Schema: &fm.Schema}
|
||||
}
|
||||
}
|
||||
|
||||
if l.Schema != nil {
|
||||
return l.Schema
|
||||
}
|
||||
for i := range l.Sources {
|
||||
r := l.Sources[i]
|
||||
if yaml.IsMissingOrNull(r) {
|
||||
continue
|
||||
}
|
||||
|
||||
m, _ := r.GetMeta()
|
||||
if m.Kind == "" || m.APIVersion == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
s := openapi.SchemaForResourceType(yaml.TypeMeta{Kind: m.Kind, APIVersion: m.APIVersion})
|
||||
if s != nil {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
DestIndex = iota
|
||||
OriginIndex
|
||||
UpdatedIndex
|
||||
)
|
||||
|
||||
// Sources are a list of RNodes. First item is the dest node, followed by
|
||||
// multiple source nodes.
|
||||
type Sources []*yaml.RNode
|
||||
|
||||
// Dest returns the destination node
|
||||
func (s Sources) Dest() *yaml.RNode {
|
||||
if len(s) <= DestIndex {
|
||||
return nil
|
||||
}
|
||||
return s[DestIndex]
|
||||
}
|
||||
|
||||
// Origin returns the origin node
|
||||
func (s Sources) Origin() *yaml.RNode {
|
||||
if len(s) <= OriginIndex {
|
||||
return nil
|
||||
}
|
||||
return s[OriginIndex]
|
||||
}
|
||||
|
||||
// Updated returns the updated node
|
||||
func (s Sources) Updated() *yaml.RNode {
|
||||
if len(s) <= UpdatedIndex {
|
||||
return nil
|
||||
}
|
||||
return s[UpdatedIndex]
|
||||
}
|
||||
|
||||
func (s Sources) String() string {
|
||||
var values []string
|
||||
for i := range s {
|
||||
str, err := s[i].String()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
values = append(values, str)
|
||||
}
|
||||
return strings.Join(values, "\n")
|
||||
}
|
||||
|
||||
// setDestNode sets the destination source node
|
||||
func (s Sources) setDestNode(node *yaml.RNode, err error) (*yaml.RNode, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s[0] = node
|
||||
return node, nil
|
||||
}
|
||||
Reference in New Issue
Block a user