...

Source file src/sigs.k8s.io/kustomize/api/types/kustomization.go

Documentation: sigs.k8s.io/kustomize/api/types

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package types
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"reflect"
    11  
    12  	"sigs.k8s.io/kustomize/kyaml/errors"
    13  	"sigs.k8s.io/kustomize/kyaml/filesys"
    14  	"sigs.k8s.io/yaml"
    15  )
    16  
    17  const (
    18  	KustomizationVersion        = "kustomize.config.k8s.io/v1beta1"
    19  	KustomizationKind           = "Kustomization"
    20  	ComponentVersion            = "kustomize.config.k8s.io/v1alpha1"
    21  	ComponentKind               = "Component"
    22  	MetadataNamespacePath       = "metadata/namespace"
    23  	MetadataNamespaceApiVersion = "v1"
    24  	MetadataNamePath            = "metadata/name"
    25  
    26  	OriginAnnotations      = "originAnnotations"
    27  	TransformerAnnotations = "transformerAnnotations"
    28  	ManagedByLabelOption   = "managedByLabel"
    29  )
    30  
    31  var BuildMetadataOptions = []string{OriginAnnotations, TransformerAnnotations, ManagedByLabelOption}
    32  
    33  // Kustomization holds the information needed to generate customized k8s api resources.
    34  type Kustomization struct {
    35  	TypeMeta `json:",inline" yaml:",inline"`
    36  
    37  	// MetaData is a pointer to avoid marshalling empty struct
    38  	MetaData *ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
    39  
    40  	// OpenAPI contains information about what kubernetes schema to use.
    41  	OpenAPI map[string]string `json:"openapi,omitempty" yaml:"openapi,omitempty"`
    42  
    43  	//
    44  	// Operators - what kustomize can do.
    45  	//
    46  
    47  	// NamePrefix will prefix the names of all resources mentioned in the kustomization
    48  	// file including generated configmaps and secrets.
    49  	NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
    50  
    51  	// NameSuffix will suffix the names of all resources mentioned in the kustomization
    52  	// file including generated configmaps and secrets.
    53  	NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"`
    54  
    55  	// Namespace to add to all objects.
    56  	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
    57  
    58  	// CommonLabels to add to all objects and selectors.
    59  	CommonLabels map[string]string `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
    60  
    61  	// Labels to add to all objects but not selectors.
    62  	Labels []Label `json:"labels,omitempty" yaml:"labels,omitempty"`
    63  
    64  	// CommonAnnotations to add to all objects.
    65  	CommonAnnotations map[string]string `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
    66  
    67  	// Deprecated: Use the Patches field instead, which provides a superset of the functionality of PatchesStrategicMerge.
    68  	// PatchesStrategicMerge specifies the relative path to a file
    69  	// containing a strategic merge patch.  Format documented at
    70  	// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
    71  	// URLs and globs are not supported.
    72  	PatchesStrategicMerge []PatchStrategicMerge `json:"patchesStrategicMerge,omitempty" yaml:"patchesStrategicMerge,omitempty"`
    73  
    74  	// Deprecated: Use the Patches field instead, which provides a superset of the functionality of JSONPatches.
    75  	// JSONPatches is a list of JSONPatch for applying JSON patch.
    76  	// Format documented at https://tools.ietf.org/html/rfc6902
    77  	// and http://jsonpatch.com
    78  	PatchesJson6902 []Patch `json:"patchesJson6902,omitempty" yaml:"patchesJson6902,omitempty"`
    79  
    80  	// Patches is a list of patches, where each one can be either a
    81  	// Strategic Merge Patch or a JSON patch.
    82  	// Each patch can be applied to multiple target objects.
    83  	Patches []Patch `json:"patches,omitempty" yaml:"patches,omitempty"`
    84  
    85  	// Images is a list of (image name, new name, new tag or digest)
    86  	// for changing image names, tags or digests. This can also be achieved with a
    87  	// patch, but this operator is simpler to specify.
    88  	Images []Image `json:"images,omitempty" yaml:"images,omitempty"`
    89  
    90  	// Deprecated: Use the Images field instead.
    91  	ImageTags []Image `json:"imageTags,omitempty" yaml:"imageTags,omitempty"`
    92  
    93  	// Replacements is a list of replacements, which will copy nodes from a
    94  	// specified source to N specified targets.
    95  	Replacements []ReplacementField `json:"replacements,omitempty" yaml:"replacements,omitempty"`
    96  
    97  	// Replicas is a list of {resourcename, count} that allows for simpler replica
    98  	// specification. This can also be done with a patch.
    99  	Replicas []Replica `json:"replicas,omitempty" yaml:"replicas,omitempty"`
   100  
   101  	// Deprecated: Vars will be removed in future release. Migrate to Replacements instead.
   102  	// Vars allow things modified by kustomize to be injected into a
   103  	// kubernetes object specification. A var is a name (e.g. FOO) associated
   104  	// with a field in a specific resource instance.  The field must
   105  	// contain a value of type string/bool/int/float, and defaults to the name field
   106  	// of the instance.  Any appearance of "$(FOO)" in the object
   107  	// spec will be replaced at kustomize build time, after the final
   108  	// value of the specified field has been determined.
   109  	Vars []Var `json:"vars,omitempty" yaml:"vars,omitempty"`
   110  
   111  	// SortOptions change the order that kustomize outputs resources.
   112  	SortOptions *SortOptions `json:"sortOptions,omitempty" yaml:"sortOptions,omitempty"`
   113  
   114  	//
   115  	// Operands - what kustomize operates on.
   116  	//
   117  
   118  	// Resources specifies relative paths to files holding YAML representations
   119  	// of kubernetes API objects, or specifications of other kustomizations
   120  	// via relative paths, absolute paths, or URLs.
   121  	Resources []string `json:"resources,omitempty" yaml:"resources,omitempty"`
   122  
   123  	// Components specifies relative paths to specifications of other Components
   124  	// via relative paths, absolute paths, or URLs.
   125  	Components []string `json:"components,omitempty" yaml:"components,omitempty"`
   126  
   127  	// Crds specifies relative paths to Custom Resource Definition files.
   128  	// This allows custom resources to be recognized as operands, making
   129  	// it possible to add them to the Resources list.
   130  	// CRDs themselves are not modified.
   131  	Crds []string `json:"crds,omitempty" yaml:"crds,omitempty"`
   132  
   133  	// Deprecated: Anything that would have been specified here should be specified in the Resources field instead.
   134  	Bases []string `json:"bases,omitempty" yaml:"bases,omitempty"`
   135  
   136  	//
   137  	// Generators (operators that create operands)
   138  	//
   139  
   140  	// ConfigMapGenerator is a list of configmaps to generate from
   141  	// local data (one configMap per list item).
   142  	// The resulting resource is a normal operand, subject to
   143  	// name prefixing, patching, etc.  By default, the name of
   144  	// the map will have a suffix hash generated from its contents.
   145  	ConfigMapGenerator []ConfigMapArgs `json:"configMapGenerator,omitempty" yaml:"configMapGenerator,omitempty"`
   146  
   147  	// SecretGenerator is a list of secrets to generate from
   148  	// local data (one secret per list item).
   149  	// The resulting resource is a normal operand, subject to
   150  	// name prefixing, patching, etc.  By default, the name of
   151  	// the map will have a suffix hash generated from its contents.
   152  	SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"`
   153  
   154  	// HelmGlobals contains helm configuration that isn't chart specific.
   155  	HelmGlobals *HelmGlobals `json:"helmGlobals,omitempty" yaml:"helmGlobals,omitempty"`
   156  
   157  	// HelmCharts is a list of helm chart configuration instances.
   158  	HelmCharts []HelmChart `json:"helmCharts,omitempty" yaml:"helmCharts,omitempty"`
   159  
   160  	// HelmChartInflationGenerator is a list of helm chart configurations.
   161  	// Deprecated.  Auto-converted to HelmGlobals and HelmCharts.
   162  	HelmChartInflationGenerator []HelmChartArgs `json:"helmChartInflationGenerator,omitempty" yaml:"helmChartInflationGenerator,omitempty"`
   163  
   164  	// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
   165  	GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"`
   166  
   167  	// Configurations is a list of transformer configuration files
   168  	Configurations []string `json:"configurations,omitempty" yaml:"configurations,omitempty"`
   169  
   170  	// Generators is a list of files containing custom generators
   171  	Generators []string `json:"generators,omitempty" yaml:"generators,omitempty"`
   172  
   173  	// Transformers is a list of files containing transformers
   174  	Transformers []string `json:"transformers,omitempty" yaml:"transformers,omitempty"`
   175  
   176  	// Validators is a list of files containing validators
   177  	Validators []string `json:"validators,omitempty" yaml:"validators,omitempty"`
   178  
   179  	// BuildMetadata is a list of strings used to toggle different build options
   180  	BuildMetadata []string `json:"buildMetadata,omitempty" yaml:"buildMetadata,omitempty"`
   181  }
   182  
   183  const (
   184  	deprecatedWarningToRunEditFix              = "Run 'kustomize edit fix' to update your Kustomization automatically."
   185  	deprecatedWarningToRunEditFixExperimential = "[EXPERIMENTAL] Run 'kustomize edit fix' to update your Kustomization automatically."
   186  	deprecatedBaseWarningMessage               = "# Warning: 'bases' is deprecated. Please use 'resources' instead." + " " + deprecatedWarningToRunEditFix
   187  	deprecatedImageTagsWarningMessage          = "# Warning: 'imageTags' is deprecated. Please use 'images' instead." + " " + deprecatedWarningToRunEditFix
   188  	deprecatedPatchesJson6902Message           = "# Warning: 'patchesJson6902' is deprecated. Please use 'patches' instead." + " " + deprecatedWarningToRunEditFix
   189  	deprecatedPatchesStrategicMergeMessage     = "# Warning: 'patchesStrategicMerge' is deprecated. Please use 'patches' instead." + " " + deprecatedWarningToRunEditFix
   190  	deprecatedVarsMessage                      = "# Warning: 'vars' is deprecated. Please use 'replacements' instead." + " " + deprecatedWarningToRunEditFixExperimential
   191  	deprecatedCommonLabelsWarningMessage       = "# Warning: 'commonLabels' is deprecated. Please use 'labels' instead." + " " + deprecatedWarningToRunEditFix
   192  )
   193  
   194  // CheckDeprecatedFields check deprecated field is used or not.
   195  func (k *Kustomization) CheckDeprecatedFields() *[]string {
   196  	var warningMessages []string
   197  	if k.Bases != nil {
   198  		warningMessages = append(warningMessages, deprecatedBaseWarningMessage)
   199  	}
   200  	if k.CommonLabels != nil {
   201  		warningMessages = append(warningMessages, deprecatedCommonLabelsWarningMessage)
   202  	}
   203  	if k.ImageTags != nil {
   204  		warningMessages = append(warningMessages, deprecatedImageTagsWarningMessage)
   205  	}
   206  	if k.PatchesJson6902 != nil {
   207  		warningMessages = append(warningMessages, deprecatedPatchesJson6902Message)
   208  	}
   209  	if k.PatchesStrategicMerge != nil {
   210  		warningMessages = append(warningMessages, deprecatedPatchesStrategicMergeMessage)
   211  	}
   212  	if k.Vars != nil {
   213  		warningMessages = append(warningMessages, deprecatedVarsMessage)
   214  	}
   215  	return &warningMessages
   216  }
   217  
   218  // FixKustomization fixes things
   219  // like empty fields that should not be empty, or
   220  // moving content of deprecated fields to newer
   221  // fields.
   222  func (k *Kustomization) FixKustomization() {
   223  	if k.Kind == "" {
   224  		k.Kind = KustomizationKind
   225  	}
   226  	if k.APIVersion == "" {
   227  		if k.Kind == ComponentKind {
   228  			k.APIVersion = ComponentVersion
   229  		} else {
   230  			k.APIVersion = KustomizationVersion
   231  		}
   232  	}
   233  
   234  	// 'bases' field was deprecated in favor of the 'resources' field.
   235  	k.Resources = append(k.Resources, k.Bases...)
   236  	k.Bases = nil
   237  
   238  	// 'imageTags' field was deprecated in favor of the 'images' field.
   239  	k.Images = append(k.Images, k.ImageTags...)
   240  	k.ImageTags = nil
   241  
   242  	for i, g := range k.ConfigMapGenerator {
   243  		if g.EnvSource != "" {
   244  			k.ConfigMapGenerator[i].EnvSources =
   245  				append(g.EnvSources, g.EnvSource) //nolint:gocritic
   246  			k.ConfigMapGenerator[i].EnvSource = ""
   247  		}
   248  	}
   249  	for i, g := range k.SecretGenerator {
   250  		if g.EnvSource != "" {
   251  			k.SecretGenerator[i].EnvSources =
   252  				append(g.EnvSources, g.EnvSource) //nolint:gocritic
   253  			k.SecretGenerator[i].EnvSource = ""
   254  		}
   255  	}
   256  	charts, globals := SplitHelmParameters(k.HelmChartInflationGenerator)
   257  	if k.HelmGlobals == nil {
   258  		if globals.ChartHome != "" || globals.ConfigHome != "" {
   259  			k.HelmGlobals = &globals
   260  		}
   261  	}
   262  	k.HelmCharts = append(k.HelmCharts, charts...)
   263  	// Wipe it for the fix command.
   264  	k.HelmChartInflationGenerator = nil
   265  }
   266  
   267  // FixKustomizationPreMarshalling fixes things
   268  // that should occur after the kustomization file
   269  // has been processed.
   270  func (k *Kustomization) FixKustomizationPreMarshalling(fSys filesys.FileSystem) error {
   271  	// PatchesJson6902 should be under the Patches field.
   272  	k.Patches = append(k.Patches, k.PatchesJson6902...)
   273  	k.PatchesJson6902 = nil
   274  
   275  	if k.PatchesStrategicMerge != nil {
   276  		for _, patchStrategicMerge := range k.PatchesStrategicMerge {
   277  			// check this patch is file path select.
   278  			if _, err := fSys.ReadFile(string(patchStrategicMerge)); err == nil {
   279  				// path patch
   280  				k.Patches = append(k.Patches, Patch{Path: string(patchStrategicMerge)})
   281  			} else {
   282  				// inline string patch
   283  				k.Patches = append(k.Patches, Patch{Patch: string(patchStrategicMerge)})
   284  			}
   285  		}
   286  		k.PatchesStrategicMerge = nil
   287  	}
   288  
   289  	// this fix is not in FixKustomizationPostUnmarshalling because
   290  	// it will break some commands like `create` and `add`. those
   291  	// commands depend on 'commonLabels' field
   292  	if cl := labelFromCommonLabels(k.CommonLabels); cl != nil {
   293  		// check conflicts between commonLabels and labels
   294  		for _, l := range k.Labels {
   295  			for k := range l.Pairs {
   296  				if _, exist := cl.Pairs[k]; exist {
   297  					return fmt.Errorf("label name '%s' exists in both commonLabels and labels", k)
   298  				}
   299  			}
   300  		}
   301  		k.Labels = append(k.Labels, *cl)
   302  		k.CommonLabels = nil
   303  	}
   304  
   305  	return nil
   306  }
   307  
   308  func (k *Kustomization) CheckEmpty() error {
   309  	// generate empty Kustomization
   310  	emptyKustomization := &Kustomization{}
   311  
   312  	// k.TypeMeta is metadata. It Isn't related to whether empty or not.
   313  	emptyKustomization.TypeMeta = k.TypeMeta
   314  
   315  	if reflect.DeepEqual(k, emptyKustomization) {
   316  		return fmt.Errorf("kustomization.yaml is empty")
   317  	}
   318  
   319  	return nil
   320  }
   321  
   322  func (k *Kustomization) EnforceFields() []string {
   323  	var errs []string
   324  	if k.Kind != "" && k.Kind != KustomizationKind && k.Kind != ComponentKind {
   325  		errs = append(errs, "kind should be "+KustomizationKind+" or "+ComponentKind)
   326  	}
   327  	requiredVersion := KustomizationVersion
   328  	if k.Kind == ComponentKind {
   329  		requiredVersion = ComponentVersion
   330  	}
   331  	if k.APIVersion != "" && k.APIVersion != requiredVersion {
   332  		errs = append(errs, "apiVersion for "+k.Kind+" should be "+requiredVersion)
   333  	}
   334  	return errs
   335  }
   336  
   337  // Unmarshal replace k with the content in YAML input y
   338  func (k *Kustomization) Unmarshal(y []byte) error {
   339  	// TODO: switch to strict decoding to catch duplicate keys.
   340  	// We can't do so until there is a yaml decoder that supports anchors AND case-insensitive keys.
   341  	// See https://github.com/kubernetes-sigs/kustomize/issues/5061
   342  	j, err := yaml.YAMLToJSON(y)
   343  	if err != nil {
   344  		return errors.WrapPrefixf(err, "invalid Kustomization")
   345  	}
   346  	dec := json.NewDecoder(bytes.NewReader(j))
   347  	dec.DisallowUnknownFields()
   348  	var nk Kustomization
   349  	err = dec.Decode(&nk)
   350  	if err != nil {
   351  		return errors.WrapPrefixf(err, "invalid Kustomization")
   352  	}
   353  	*k = nk
   354  	return nil
   355  }
   356  

View as plain text