...

Source file src/sigs.k8s.io/kustomize/api/internal/accumulator/resaccumulator.go

Documentation: sigs.k8s.io/kustomize/api/internal/accumulator

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package accumulator
     5  
     6  import (
     7  	"fmt"
     8  	"log"
     9  	"strings"
    10  
    11  	"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
    12  	"sigs.k8s.io/kustomize/api/resmap"
    13  	"sigs.k8s.io/kustomize/api/types"
    14  	"sigs.k8s.io/kustomize/kyaml/resid"
    15  )
    16  
    17  // ResAccumulator accumulates resources and the rules
    18  // used to customize those resources.  It's a ResMap
    19  // plus stuff needed to modify the ResMap.
    20  type ResAccumulator struct {
    21  	resMap  resmap.ResMap
    22  	tConfig *builtinconfig.TransformerConfig
    23  	varSet  types.VarSet
    24  }
    25  
    26  func MakeEmptyAccumulator() *ResAccumulator {
    27  	ra := &ResAccumulator{}
    28  	ra.resMap = resmap.New()
    29  	ra.tConfig = &builtinconfig.TransformerConfig{}
    30  	ra.varSet = types.NewVarSet()
    31  	return ra
    32  }
    33  
    34  // ResMap returns a copy of the internal resMap.
    35  func (ra *ResAccumulator) ResMap() resmap.ResMap {
    36  	return ra.resMap.ShallowCopy()
    37  }
    38  
    39  // Vars returns a copy of underlying vars.
    40  func (ra *ResAccumulator) Vars() []types.Var {
    41  	return ra.varSet.AsSlice()
    42  }
    43  
    44  func (ra *ResAccumulator) AppendAll(resources resmap.ResMap) error {
    45  	return ra.resMap.AppendAll(resources)
    46  }
    47  
    48  func (ra *ResAccumulator) AbsorbAll(resources resmap.ResMap) error {
    49  	return ra.resMap.AbsorbAll(resources)
    50  }
    51  
    52  func (ra *ResAccumulator) MergeConfig(
    53  	tConfig *builtinconfig.TransformerConfig) (err error) {
    54  	ra.tConfig, err = ra.tConfig.Merge(tConfig)
    55  	return err
    56  }
    57  
    58  func (ra *ResAccumulator) GetTransformerConfig() *builtinconfig.TransformerConfig {
    59  	return ra.tConfig
    60  }
    61  
    62  // MergeVars accumulates vars into ResAccumulator.
    63  // A Var is a tuple of name, object reference and field reference.
    64  // This func takes a list of vars from the current kustomization file and
    65  // annotates the accumulated resources with the names of the vars that match
    66  // those resources.  E.g. if there's a var named "sam" that wants to get
    67  // its data from a ConfigMap named "james", and the resource list contains a
    68  // ConfigMap named "james", then that ConfigMap will be annotated with the
    69  // var name "sam".  Later this annotation is used to find the data for "sam"
    70  // by digging into a particular fieldpath of "james".
    71  func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
    72  	for _, v := range incoming {
    73  		targetId := resid.NewResIdWithNamespace(v.ObjRef.GVK(), v.ObjRef.Name, v.ObjRef.Namespace)
    74  		idMatcher := targetId.GvknEquals
    75  		if targetId.Namespace != "" || targetId.IsClusterScoped() {
    76  			// Preserve backward compatibility. An empty namespace means
    77  			// wildcard search on the namespace hence we still use GvknEquals
    78  			idMatcher = targetId.Equals
    79  		}
    80  		matched := ra.resMap.GetMatchingResourcesByAnyId(idMatcher)
    81  		if len(matched) > 1 {
    82  			return fmt.Errorf(
    83  				"found %d resId matches for var %s "+
    84  					"(unable to disambiguate)",
    85  				len(matched), v)
    86  		}
    87  		if len(matched) == 1 {
    88  			matched[0].AppendRefVarName(v)
    89  		}
    90  	}
    91  	return ra.varSet.MergeSlice(incoming)
    92  }
    93  
    94  func (ra *ResAccumulator) MergeAccumulator(other *ResAccumulator) (err error) {
    95  	err = ra.AppendAll(other.resMap)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	err = ra.MergeConfig(other.tConfig)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	return ra.varSet.MergeSet(other.varSet)
   104  }
   105  
   106  func (ra *ResAccumulator) findVarValueFromResources(v types.Var) (interface{}, error) {
   107  	for _, res := range ra.resMap.Resources() {
   108  		for _, varName := range res.GetRefVarNames() {
   109  			if varName == v.Name {
   110  				s, err := res.GetFieldValue(v.FieldRef.FieldPath)
   111  				if err != nil {
   112  					return "", fmt.Errorf(
   113  						"field specified in var '%v' "+
   114  							"not found in corresponding resource", v)
   115  				}
   116  				return s, nil
   117  			}
   118  		}
   119  	}
   120  	return "", fmt.Errorf(
   121  		"var '%v' cannot be mapped to a field "+
   122  			"in the set of known resources", v)
   123  }
   124  
   125  // makeVarReplacementMap returns a map of Var names to
   126  // their final values. The values are strings intended
   127  // for substitution wherever the $(var.Name) occurs.
   128  func (ra *ResAccumulator) makeVarReplacementMap() (map[string]interface{}, error) {
   129  	result := map[string]interface{}{}
   130  	for _, v := range ra.Vars() {
   131  		s, err := ra.findVarValueFromResources(v)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  		result[v.Name] = s
   136  	}
   137  	return result, nil
   138  }
   139  
   140  func (ra *ResAccumulator) Transform(t resmap.Transformer) error {
   141  	return t.Transform(ra.resMap)
   142  }
   143  
   144  func (ra *ResAccumulator) ResolveVars() error {
   145  	replacementMap, err := ra.makeVarReplacementMap()
   146  	if err != nil {
   147  		return err
   148  	}
   149  	if len(replacementMap) == 0 {
   150  		return nil
   151  	}
   152  	t := newRefVarTransformer(
   153  		replacementMap, ra.tConfig.VarReference)
   154  	err = ra.Transform(t)
   155  	if len(t.UnusedVars()) > 0 {
   156  		log.Printf(
   157  			"well-defined vars that were never replaced: %s\n",
   158  			strings.Join(t.UnusedVars(), ","))
   159  	}
   160  	return err
   161  }
   162  
   163  func (ra *ResAccumulator) FixBackReferences() (err error) {
   164  	if ra.tConfig.NameReference == nil {
   165  		return nil
   166  	}
   167  	return ra.Transform(
   168  		newNameReferenceTransformer(ra.tConfig.NameReference))
   169  }
   170  
   171  // Intersection drops the resources which "other" does not have.
   172  func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
   173  	otherIds := other.AllIds()
   174  	for _, curId := range ra.resMap.AllIds() {
   175  		toDelete := true
   176  		for _, otherId := range otherIds {
   177  			if otherId == curId {
   178  				toDelete = false
   179  				break
   180  			}
   181  		}
   182  		if toDelete {
   183  			err := ra.resMap.Remove(curId)
   184  			if err != nil {
   185  				return err
   186  			}
   187  		}
   188  	}
   189  	return nil
   190  }
   191  

View as plain text