mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Fixed bug where an explicitly set null value in yaml was cleared, even when the patch did not operate on that field.
189 lines
4.8 KiB
Go
189 lines
4.8 KiB
Go
// Copyright 2019 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Package merge2 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()
|
|
}
|
|
|
|
// MergeStrings 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
|
|
}
|
|
|
|
// If Origin is missing, preserve explicitly set null in Dest ("null", "~", etc)
|
|
if nodes.Origin().IsNil() && !nodes.Dest().IsNil() && len(nodes.Dest().YNode().Value) > 0 {
|
|
// Return a new node so that it won't have a "!!null" tag and therefore won't be cleared.
|
|
return yaml.NewScalarRNode(nodes.Dest().YNode().Value), 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
|
|
}
|