...

Source file src/sigs.k8s.io/kustomize/kyaml/order/syncorder.go

Documentation: sigs.k8s.io/kustomize/kyaml/order

     1  // Copyright 2021 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package order
     5  
     6  import (
     7  	"sigs.k8s.io/kustomize/kyaml/errors"
     8  	"sigs.k8s.io/kustomize/kyaml/yaml"
     9  )
    10  
    11  // SyncOrder recursively sorts the map node keys in 'to' node to match the order of
    12  // map node keys in 'from' node at same tree depth, additional keys are moved to the end
    13  // Field order might be altered due to round-tripping in arbitrary functions.
    14  // This functionality helps to retain the original order of fields to avoid unnecessary diffs.
    15  func SyncOrder(from, to *yaml.RNode) error {
    16  	// from node should not be modified, it should be just used as a reference
    17  	fromCopy := from.Copy()
    18  	if err := syncOrder(fromCopy, to); err != nil {
    19  		return errors.Errorf("failed to sync field order: %q", err.Error())
    20  	}
    21  	rearrangeHeadCommentOfSeqNode(to.YNode())
    22  	return nil
    23  }
    24  
    25  func syncOrder(from, to *yaml.RNode) error {
    26  	if from.IsNilOrEmpty() || to.IsNilOrEmpty() {
    27  		return nil
    28  	}
    29  	switch from.YNode().Kind {
    30  	case yaml.DocumentNode:
    31  		// Traverse the child of the documents
    32  		return syncOrder(yaml.NewRNode(from.YNode()), yaml.NewRNode(to.YNode()))
    33  	case yaml.MappingNode:
    34  		return VisitFields(from, to, func(fNode, tNode *yaml.MapNode) error {
    35  			// Traverse each field value
    36  			if fNode == nil || tNode == nil {
    37  				return nil
    38  			}
    39  			return syncOrder(fNode.Value, tNode.Value)
    40  		})
    41  	case yaml.SequenceNode:
    42  		return VisitElements(from, to, syncOrder) // Traverse each list element
    43  	}
    44  	return nil
    45  }
    46  
    47  // VisitElements calls fn for each element in a SequenceNode.
    48  // Returns an error for non-SequenceNodes
    49  func VisitElements(from, to *yaml.RNode, fn func(fNode, tNode *yaml.RNode) error) error {
    50  	fElements, err := from.Elements()
    51  	if err != nil {
    52  		return errors.Wrap(err)
    53  	}
    54  
    55  	tElements, err := to.Elements()
    56  	if err != nil {
    57  		return errors.Wrap(err)
    58  	}
    59  	for i := range fElements {
    60  		if i >= len(tElements) {
    61  			return nil
    62  		}
    63  		if err := fn(fElements[i], tElements[i]); err != nil {
    64  			return errors.Wrap(err)
    65  		}
    66  	}
    67  	return nil
    68  }
    69  
    70  // VisitFields calls fn for each field in the RNode.
    71  // Returns an error for non-MappingNodes.
    72  func VisitFields(from, to *yaml.RNode, fn func(fNode, tNode *yaml.MapNode) error) error {
    73  	srcFieldNames, err := from.Fields()
    74  	if err != nil {
    75  		return nil
    76  	}
    77  	yaml.SyncMapNodesOrder(from, to)
    78  	// visit each field
    79  	for _, fieldName := range srcFieldNames {
    80  		if err := fn(from.Field(fieldName), to.Field(fieldName)); err != nil {
    81  			return errors.Wrap(err)
    82  		}
    83  	}
    84  	return nil
    85  }
    86  
    87  // rearrangeHeadCommentOfSeqNode addresses a remote corner case due to moving a
    88  // map node in a sequence node with a head comment to the top
    89  func rearrangeHeadCommentOfSeqNode(node *yaml.Node) {
    90  	if node == nil {
    91  		return
    92  	}
    93  	switch node.Kind {
    94  	case yaml.DocumentNode:
    95  		for _, node := range node.Content {
    96  			rearrangeHeadCommentOfSeqNode(node)
    97  		}
    98  
    99  	case yaml.MappingNode:
   100  		for _, node := range node.Content {
   101  			rearrangeHeadCommentOfSeqNode(node)
   102  		}
   103  
   104  	case yaml.SequenceNode:
   105  		for _, node := range node.Content {
   106  			// for each child mapping node, transfer the head comment of it's
   107  			// first child scalar node to the head comment of itself
   108  			if len(node.Content) > 0 && node.Content[0].Kind == yaml.ScalarNode {
   109  				if node.HeadComment == "" {
   110  					node.HeadComment = node.Content[0].HeadComment
   111  					continue
   112  				}
   113  
   114  				if node.Content[0].HeadComment != "" {
   115  					node.HeadComment += "\n" + node.Content[0].HeadComment
   116  					node.Content[0].HeadComment = ""
   117  				}
   118  			}
   119  		}
   120  	}
   121  }
   122  

View as plain text