...

Source file src/sigs.k8s.io/kustomize/kyaml/yaml/merge2/merge2.go

Documentation: sigs.k8s.io/kustomize/kyaml/yaml/merge2

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  // Package merge2 contains libraries for merging fields from one RNode to another
     5  // RNode
     6  package merge2
     7  
     8  import (
     9  	"sigs.k8s.io/kustomize/kyaml/openapi"
    10  	"sigs.k8s.io/kustomize/kyaml/yaml"
    11  	"sigs.k8s.io/kustomize/kyaml/yaml/walk"
    12  )
    13  
    14  // Merge merges fields from src into dest.
    15  func Merge(src, dest *yaml.RNode, mergeOptions yaml.MergeOptions) (*yaml.RNode, error) {
    16  	return walk.Walker{
    17  		Sources:      []*yaml.RNode{dest, src},
    18  		Visitor:      Merger{},
    19  		MergeOptions: mergeOptions,
    20  	}.Walk()
    21  }
    22  
    23  // MergeStrings parses the arguments, and merges fields from srcStr into destStr.
    24  func MergeStrings(srcStr, destStr string, infer bool, mergeOptions yaml.MergeOptions) (string, error) {
    25  	src, err := yaml.Parse(srcStr)
    26  	if err != nil {
    27  		return "", err
    28  	}
    29  	dest, err := yaml.Parse(destStr)
    30  	if err != nil {
    31  		return "", err
    32  	}
    33  
    34  	result, err := walk.Walker{
    35  		Sources:               []*yaml.RNode{dest, src},
    36  		Visitor:               Merger{},
    37  		InferAssociativeLists: infer,
    38  		MergeOptions:          mergeOptions,
    39  	}.Walk()
    40  	if err != nil {
    41  		return "", err
    42  	}
    43  
    44  	return result.String()
    45  }
    46  
    47  type Merger struct {
    48  }
    49  
    50  // for forwards compatibility when new functions are added to the interface
    51  var _ walk.Visitor = Merger{}
    52  
    53  func (m Merger) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
    54  	if err := m.SetComments(nodes); err != nil {
    55  		return nil, err
    56  	}
    57  	if err := m.SetStyle(nodes); err != nil {
    58  		return nil, err
    59  	}
    60  	if yaml.IsMissingOrNull(nodes.Dest()) {
    61  		// Add
    62  		ps, _ := determineSmpDirective(nodes.Origin())
    63  		if ps == smpDelete {
    64  			return walk.ClearNode, nil
    65  		}
    66  
    67  		// If Origin is missing, preserve explicitly set null in Dest ("null", "~", etc)
    68  		if nodes.Origin().IsNil() && !nodes.Dest().IsNil() && len(nodes.Dest().YNode().Value) > 0 {
    69  			return yaml.MakePersistentNullNode(nodes.Dest().YNode().Value), nil
    70  		}
    71  
    72  		return nodes.Origin(), nil
    73  	}
    74  	if nodes.Origin().IsTaggedNull() {
    75  		// clear the value
    76  		return walk.ClearNode, nil
    77  	}
    78  
    79  	ps, err := determineSmpDirective(nodes.Origin())
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	switch ps {
    85  	case smpDelete:
    86  		return walk.ClearNode, nil
    87  	case smpReplace:
    88  		return nodes.Origin(), nil
    89  	default:
    90  		return nodes.Dest(), nil
    91  	}
    92  }
    93  
    94  func (m Merger) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
    95  	if err := m.SetComments(nodes); err != nil {
    96  		return nil, err
    97  	}
    98  	if err := m.SetStyle(nodes); err != nil {
    99  		return nil, err
   100  	}
   101  	// Override value
   102  	if nodes.Origin() != nil {
   103  		return nodes.Origin(), nil
   104  	}
   105  	// Keep
   106  	return nodes.Dest(), nil
   107  }
   108  
   109  func (m Merger) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind walk.ListKind) (*yaml.RNode, error) {
   110  	if err := m.SetComments(nodes); err != nil {
   111  		return nil, err
   112  	}
   113  	if err := m.SetStyle(nodes); err != nil {
   114  		return nil, err
   115  	}
   116  	if kind == walk.NonAssociateList {
   117  		// Override value
   118  		if nodes.Origin() != nil {
   119  			return nodes.Origin(), nil
   120  		}
   121  		// Keep
   122  		return nodes.Dest(), nil
   123  	}
   124  
   125  	// Add
   126  	if yaml.IsMissingOrNull(nodes.Dest()) {
   127  		return nodes.Origin(), nil
   128  	}
   129  	// Clear
   130  	if nodes.Origin().IsTaggedNull() {
   131  		return walk.ClearNode, nil
   132  	}
   133  
   134  	ps, err := determineSmpDirective(nodes.Origin())
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	switch ps {
   140  	case smpDelete:
   141  		return walk.ClearNode, nil
   142  	case smpReplace:
   143  		return nodes.Origin(), nil
   144  	default:
   145  		return nodes.Dest(), nil
   146  	}
   147  }
   148  
   149  func (m Merger) SetStyle(sources walk.Sources) error {
   150  	source := sources.Origin()
   151  	dest := sources.Dest()
   152  	if dest == nil || dest.YNode() == nil || source == nil || source.YNode() == nil {
   153  		// avoid panic
   154  		return nil
   155  	}
   156  
   157  	// copy the style from the source.
   158  	// special case: if the dest was an empty map or seq, then it probably had
   159  	// folded style applied, but we actually want to keep the style of the origin
   160  	// in this case (even if it was the default).  otherwise the merged elements
   161  	// will get folded even though this probably isn't what is desired.
   162  	if dest.YNode().Kind != yaml.ScalarNode && len(dest.YNode().Content) == 0 {
   163  		dest.YNode().Style = source.YNode().Style
   164  	}
   165  	return nil
   166  }
   167  
   168  // SetComments copies the dest comments to the source comments if they are present
   169  // on the source.
   170  func (m Merger) SetComments(sources walk.Sources) error {
   171  	source := sources.Origin()
   172  	dest := sources.Dest()
   173  	if dest == nil || dest.YNode() == nil || source == nil || source.YNode() == nil {
   174  		// avoid panic
   175  		return nil
   176  	}
   177  	if source.YNode().FootComment != "" {
   178  		dest.YNode().FootComment = source.YNode().FootComment
   179  	}
   180  	if source.YNode().HeadComment != "" {
   181  		dest.YNode().HeadComment = source.YNode().HeadComment
   182  	}
   183  	if source.YNode().LineComment != "" {
   184  		dest.YNode().LineComment = source.YNode().LineComment
   185  	}
   186  	return nil
   187  }
   188  

View as plain text