// Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 package accumulator import ( "fmt" expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/transform" "sigs.k8s.io/kustomize/api/types" ) type refVarTransformer struct { varMap map[string]interface{} replacementCounts map[string]int fieldSpecs []types.FieldSpec mappingFunc func(string) interface{} } // newRefVarTransformer returns a new refVarTransformer // that replaces $(VAR) style variables with values. // The fieldSpecs are the places to look for occurrences of $(VAR). func newRefVarTransformer( varMap map[string]interface{}, fs []types.FieldSpec) *refVarTransformer { return &refVarTransformer{ varMap: varMap, fieldSpecs: fs, } } // replaceVars accepts as 'in' a string, or string array, which can have // embedded instances of $VAR style variables, e.g. a container command string. // The function returns the string with the variables expanded to their final // values. func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) { switch vt := in.(type) { case []interface{}: var xs []interface{} for _, a := range in.([]interface{}) { x, ok := a.(string) if !ok { return nil, fmt.Errorf("expected array of strings, found %v", in) } xs = append(xs, expansion2.Expand(x, rv.mappingFunc)) } return xs, nil case map[string]interface{}: inMap := in.(map[string]interface{}) xs := make(map[string]interface{}, len(inMap)) for k, v := range inMap { s, ok := v.(string) if !ok { // This field can not contain a $(VAR) since it is not // of string type. For instance .spec.replicas: 3 in // a Deployment object xs[k] = v } else { // This field can potentially contains a $(VAR) since it is // of string type. For instance .spec.replicas: $(REPLICAS) // in a Deployment object xs[k] = expansion2.Expand(s, rv.mappingFunc) } } return xs, nil case interface{}: s, ok := in.(string) if !ok { // This field can not contain a $(VAR) since it is not of string type. return in, nil } // This field can potentially contain a $(VAR) since it is // of string type. return expansion2.Expand(s, rv.mappingFunc), nil // staticcheck erroneously claims that `case nil` // is unreachable here, so suppressing it. //nolint:staticcheck case nil: return nil, nil default: return "", fmt.Errorf("invalid type encountered %T", vt) } } // UnusedVars returns slice of Var names that were unused // after a Transform run. func (rv *refVarTransformer) UnusedVars() []string { var unused []string for k := range rv.varMap { _, ok := rv.replacementCounts[k] if !ok { unused = append(unused, k) } } return unused } // Transform replaces $(VAR) style variables with values. func (rv *refVarTransformer) Transform(m resmap.ResMap) error { rv.replacementCounts = make(map[string]int) rv.mappingFunc = expansion2.MappingFuncFor( rv.replacementCounts, rv.varMap) for _, res := range m.Resources() { for _, fieldSpec := range rv.fieldSpecs { if res.OrgId().IsSelected(&fieldSpec.Gvk) { if err := transform.MutateField( res.Map(), fieldSpec.PathSlice(), false, rv.replaceVars); err != nil { return err } } } } return nil }