...

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

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

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package walk
     5  
     6  import (
     7  	"sort"
     8  
     9  	"sigs.k8s.io/kustomize/kyaml/fieldmeta"
    10  	"sigs.k8s.io/kustomize/kyaml/openapi"
    11  	"sigs.k8s.io/kustomize/kyaml/sets"
    12  	"sigs.k8s.io/kustomize/kyaml/yaml"
    13  )
    14  
    15  // walkMap returns the value of VisitMap
    16  //
    17  // - call VisitMap
    18  // - set the return value on l.Dest
    19  // - walk each source field
    20  // - set each source field value on l.Dest
    21  func (l Walker) walkMap() (*yaml.RNode, error) {
    22  	// get the new map value
    23  	dest, err := l.Sources.setDestNode(l.VisitMap(l.Sources, l.Schema))
    24  	if dest == nil || err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	// recursively set the field values on the map
    29  	for _, key := range l.fieldNames() {
    30  		var res *yaml.RNode
    31  		var keys []*yaml.RNode
    32  		if l.VisitKeysAsScalars {
    33  			// visit the map keys as if they were scalars,
    34  			// this is necessary if doing things such as copying
    35  			// comments
    36  			for i := range l.Sources {
    37  				// construct the sources from the keys
    38  				if l.Sources[i] == nil {
    39  					keys = append(keys, nil)
    40  					continue
    41  				}
    42  				field := l.Sources[i].Field(key)
    43  				if field == nil || yaml.IsMissingOrNull(field.Key) {
    44  					keys = append(keys, nil)
    45  					continue
    46  				}
    47  				keys = append(keys, field.Key)
    48  			}
    49  			// visit the sources as a scalar
    50  			// keys don't have any schema --pass in nil
    51  			res, err = l.Visitor.VisitScalar(keys, nil)
    52  			if err != nil {
    53  				return nil, err
    54  			}
    55  		}
    56  
    57  		var s *openapi.ResourceSchema
    58  		if l.Schema != nil {
    59  			s = l.Schema.Field(key)
    60  		}
    61  		fv, commentSch, keyStyles := l.fieldValue(key)
    62  		if commentSch != nil {
    63  			s = commentSch
    64  		}
    65  		val, err := Walker{
    66  			VisitKeysAsScalars:    l.VisitKeysAsScalars,
    67  			InferAssociativeLists: l.InferAssociativeLists,
    68  			Visitor:               l,
    69  			Schema:                s,
    70  			Sources:               fv,
    71  			MergeOptions:          l.MergeOptions,
    72  			Path:                  append(l.Path, key)}.Walk()
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  
    77  		// transfer the comments of res to dest node
    78  		var comments yaml.Comments
    79  		if !yaml.IsMissingOrNull(res) {
    80  			comments = yaml.Comments{
    81  				LineComment: res.YNode().LineComment,
    82  				HeadComment: res.YNode().HeadComment,
    83  				FootComment: res.YNode().FootComment,
    84  			}
    85  			if len(keys) > 0 && !yaml.IsMissingOrNull(keys[DestIndex]) {
    86  				keys[DestIndex].YNode().HeadComment = res.YNode().HeadComment
    87  				keys[DestIndex].YNode().LineComment = res.YNode().LineComment
    88  				keys[DestIndex].YNode().FootComment = res.YNode().FootComment
    89  			}
    90  		}
    91  
    92  		// this handles empty and non-empty values
    93  		fieldSetter := yaml.FieldSetter{
    94  			Name:           key,
    95  			Comments:       comments,
    96  			AppendKeyStyle: keyStyles[val],
    97  			Value:          val,
    98  		}
    99  		_, err = dest.Pipe(fieldSetter)
   100  		if err != nil {
   101  			return nil, err
   102  		}
   103  	}
   104  
   105  	return dest, nil
   106  }
   107  
   108  // valueIfPresent returns node.Value if node is non-nil, otherwise returns nil
   109  func (l Walker) valueIfPresent(node *yaml.MapNode) (*yaml.RNode, *openapi.ResourceSchema) {
   110  	if node == nil {
   111  		return nil, nil
   112  	}
   113  
   114  	// parse the schema for the field if present
   115  	var s *openapi.ResourceSchema
   116  	fm := fieldmeta.FieldMeta{}
   117  	var err error
   118  	// check the value for a schema
   119  	if err = fm.Read(node.Value); err == nil {
   120  		s = &openapi.ResourceSchema{Schema: &fm.Schema}
   121  		if fm.Schema.Ref.String() != "" {
   122  			r, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
   123  			if err == nil && r != nil {
   124  				s.Schema = r
   125  			}
   126  		}
   127  	}
   128  	// check the key for a schema -- this will be used
   129  	// when the value is a Sequence (comments are attached)
   130  	// to the key
   131  	if fm.IsEmpty() {
   132  		if err = fm.Read(node.Key); err == nil {
   133  			s = &openapi.ResourceSchema{Schema: &fm.Schema}
   134  		}
   135  		if fm.Schema.Ref.String() != "" {
   136  			r, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
   137  			if err == nil && r != nil {
   138  				s.Schema = r
   139  			}
   140  		}
   141  	}
   142  	return node.Value, s
   143  }
   144  
   145  // fieldNames returns a sorted slice containing the names of all fields that appear in any of
   146  // the sources
   147  func (l Walker) fieldNames() []string {
   148  	fields := sets.String{}
   149  	for _, s := range l.Sources {
   150  		if s == nil {
   151  			continue
   152  		}
   153  		// don't check error, we know this is a mapping node
   154  		sFields, _ := s.Fields()
   155  		fields.Insert(sFields...)
   156  	}
   157  	result := fields.List()
   158  	sort.Strings(result)
   159  	return result
   160  }
   161  
   162  // fieldValue returns a slice containing each source's value for fieldName, the
   163  // schema, and a map of each source's value to the style for the source's key.
   164  func (l Walker) fieldValue(fieldName string) ([]*yaml.RNode, *openapi.ResourceSchema, map[*yaml.RNode]yaml.Style) {
   165  	var fields []*yaml.RNode
   166  	var sch *openapi.ResourceSchema
   167  	keyStyles := make(map[*yaml.RNode]yaml.Style, len(l.Sources))
   168  	for i := range l.Sources {
   169  		if l.Sources[i] == nil {
   170  			fields = append(fields, nil)
   171  			continue
   172  		}
   173  		field := l.Sources[i].Field(fieldName)
   174  		f, s := l.valueIfPresent(field)
   175  		fields = append(fields, f)
   176  		if field != nil && field.Key != nil && field.Key.YNode() != nil {
   177  			keyStyles[f] = field.Key.YNode().Style
   178  		}
   179  		if sch == nil && !s.IsMissingOrNull() {
   180  			sch = s
   181  		}
   182  	}
   183  	return fields, sch, keyStyles
   184  }
   185  

View as plain text