From 58cda0d1303b4e13a6eae7d73b23f85eec4b36e3 Mon Sep 17 00:00:00 2001 From: Anna Song Date: Fri, 14 Oct 2022 11:08:30 -0700 Subject: [PATCH] temp: handle conversion between kustomization and plugins TODO: finish tests --- .../target/kusttarget_configplugin.go | 623 ++++++++-------- .../target/kusttarget_configplugin_test.go | 705 ++++++++++++++++++ api/internal/target/kusttarget_writeplugin.go | 182 +++++ .../target/kusttarget_writeplugin_test.go | 1 + 4 files changed, 1197 insertions(+), 314 deletions(-) create mode 100644 api/internal/target/kusttarget_configplugin_test.go create mode 100644 api/internal/target/kusttarget_writeplugin.go create mode 100644 api/internal/target/kusttarget_writeplugin_test.go diff --git a/api/internal/target/kusttarget_configplugin.go b/api/internal/target/kusttarget_configplugin.go index 9a3c63086fe..22149993619 100644 --- a/api/internal/target/kusttarget_configplugin.go +++ b/api/internal/target/kusttarget_configplugin.go @@ -31,15 +31,16 @@ import ( // image tag transforms. In these cases, we'll need // N plugin instances with differing configurations. -func (kt *KustTarget) configureBuiltinGenerators() ( - result []*resmap.GeneratorWithProperties, err error) { +func (kt *KustTarget) configureBuiltinGenerators() (result []*resmap.GeneratorWithProperties, err error) { + allConfigurators := GetGeneratorConfigurators() + // TODO(annasong): make copy of rFactory, pLdr.Config() + pluginHelper := resmap.NewPluginHelpers(kt.ldr, kt.validator, kt.rFactory, kt.pLdr.Config()) for _, bpt := range []builtinhelpers.BuiltinPluginType{ builtinhelpers.ConfigMapGenerator, builtinhelpers.SecretGenerator, builtinhelpers.HelmChartInflationGenerator, } { - r, err := generatorConfigurators[bpt]( - kt, bpt, builtinhelpers.GeneratorFactories[bpt]) + r, err := allConfigurators[bpt](kt.kustomization, pluginHelper) if err != nil { return nil, err } @@ -66,9 +67,11 @@ func (kt *KustTarget) configureBuiltinGenerators() ( return result, nil } -func (kt *KustTarget) configureBuiltinTransformers( - tc *builtinconfig.TransformerConfig) ( +func (kt *KustTarget) configureBuiltinTransformers(tc *builtinconfig.TransformerConfig) ( result []*resmap.TransformerWithProperties, err error) { + allConfigurators := GetTransformerConfigurators() + // TODO(annasong): make copy of kt.rFactory, kt.pLdr.Config() + pluginHelper := resmap.NewPluginHelpers(kt.ldr, kt.validator, kt.rFactory, kt.pLdr.Config()) for _, bpt := range []builtinhelpers.BuiltinPluginType{ builtinhelpers.PatchStrategicMergeTransformer, builtinhelpers.PatchTransformer, @@ -82,8 +85,7 @@ func (kt *KustTarget) configureBuiltinTransformers( builtinhelpers.ImageTagTransformer, builtinhelpers.ReplacementTransformer, } { - r, err := transformerConfigurators[bpt]( - kt, bpt, builtinhelpers.TransformerFactories[bpt], tc) + r, err := allConfigurators[bpt](kt.kustomization, tc, pluginHelper) if err != nil { return nil, err } @@ -108,339 +110,332 @@ func (kt *KustTarget) configureBuiltinTransformers( return result, nil } -type gFactory func() resmap.GeneratorPlugin +// GetGeneratorConfigurators returns configurators for built-in generators with corresponding kustomization fields. +// The configurators only operate on non-deprecated kustomization fields. +func GetGeneratorConfigurators() map[builtinhelpers.BuiltinPluginType]func(*types.Kustomization, *resmap.PluginHelpers) ( + []resmap.Generator, error) { -var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func( - kt *KustTarget, - bpt builtinhelpers.BuiltinPluginType, - factory gFactory) (result []resmap.Generator, err error){ - builtinhelpers.SecretGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) ( - result []resmap.Generator, err error) { - var c struct { - types.SecretArgs - } - for _, args := range kt.kustomization.SecretGenerator { - c.SecretArgs = args - c.SecretArgs.Options = types.MergeGlobalOptionsIntoLocal( - c.SecretArgs.Options, kt.kustomization.GeneratorOptions) - p := f() - err := kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { + configurator := func(plugin builtinhelpers.BuiltinPluginType, h *resmap.PluginHelpers, inputs [][]byte) ( + []resmap.Generator, error) { + generators := make([]resmap.Generator, len(inputs)) + for i, input := range inputs { + configurable := builtinhelpers.GeneratorFactories[plugin]() + if err := configurable.Config(h, input); err != nil { return nil, err } - result = append(result, p) + generators[i] = configurable } - return - }, + return generators, nil + } - builtinhelpers.ConfigMapGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) ( - result []resmap.Generator, err error) { - var c struct { - types.ConfigMapArgs - } - for _, args := range kt.kustomization.ConfigMapGenerator { - c.ConfigMapArgs = args - c.ConfigMapArgs.Options = types.MergeGlobalOptionsIntoLocal( - c.ConfigMapArgs.Options, kt.kustomization.GeneratorOptions) - p := f() - err := kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err + return map[builtinhelpers.BuiltinPluginType]func(*types.Kustomization, *resmap.PluginHelpers) ( + []resmap.Generator, error){ + builtinhelpers.SecretGenerator: func(k *types.Kustomization, h *resmap.PluginHelpers) ([]resmap.Generator, error) { + var plugin types.SecretArgs + inputs := make([][]byte, len(k.SecretGenerator)) + for i, args := range k.SecretGenerator { + if args.EnvSource != "" { + return nil, errors.Errorf("deprecated field env: %s on secretGenerator", args.EnvSource) + } + plugin = args + plugin.Options = types.MergeGlobalOptionsIntoLocal(plugin.Options, k.GeneratorOptions) + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization secretGenerator") + } + inputs[i] = b } - result = append(result, p) - } - return - }, - - builtinhelpers.HelmChartInflationGenerator: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) ( - result []resmap.Generator, err error) { - var c struct { - types.HelmGlobals - types.HelmChart - } - var globals types.HelmGlobals - if kt.kustomization.HelmGlobals != nil { - globals = *kt.kustomization.HelmGlobals - } - for _, chart := range kt.kustomization.HelmCharts { - c.HelmGlobals = globals - c.HelmChart = chart - p := f() - if err = kt.configureBuiltinPlugin(p, c, bpt); err != nil { - return nil, err + return configurator(builtinhelpers.SecretGenerator, h, inputs) + }, + builtinhelpers.ConfigMapGenerator: func(k *types.Kustomization, h *resmap.PluginHelpers) ([]resmap.Generator, error) { + var plugin types.ConfigMapArgs + inputs := make([][]byte, len(k.ConfigMapGenerator)) + for i, args := range k.ConfigMapGenerator { + if args.EnvSource != "" { + return nil, errors.Errorf("deprecated field env: %s on configMapGenerator", args.EnvSource) + } + plugin = args + plugin.Options = types.MergeGlobalOptionsIntoLocal(plugin.Options, k.GeneratorOptions) + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization configMapGenerator") + } + inputs[i] = b } - result = append(result, p) - } - return - }, + return configurator(builtinhelpers.ConfigMapGenerator, h, inputs) + }, + builtinhelpers.HelmChartInflationGenerator: func(k *types.Kustomization, h *resmap.PluginHelpers) ([]resmap.Generator, error) { + if k.HelmChartInflationGenerator != nil { + return nil, errors.Errorf("deprecated field helmChartInflationGenerator: %v", k.HelmChartInflationGenerator) + } + var plugin struct { + types.HelmGlobals + types.HelmChart + } + var globals types.HelmGlobals + if k.HelmGlobals != nil { + globals = *k.HelmGlobals + } + inputs := make([][]byte, len(k.HelmCharts)) + for i, chart := range k.HelmCharts { + plugin.HelmGlobals = globals + plugin.HelmChart = chart + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization helmCharts") + } + inputs[i] = b + } + return configurator(builtinhelpers.HelmChartInflationGenerator, h, inputs) + }, + } } -type tFactory func() resmap.TransformerPlugin - -var transformerConfigurators = map[builtinhelpers.BuiltinPluginType]func( - kt *KustTarget, - bpt builtinhelpers.BuiltinPluginType, - f tFactory, - tc *builtinconfig.TransformerConfig) (result []resmap.Transformer, err error){ - builtinhelpers.NamespaceTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if kt.kustomization.Namespace == "" { - return - } - var c struct { - types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` - FieldSpecs []types.FieldSpec - } - c.Namespace = kt.kustomization.Namespace - c.FieldSpecs = tc.NameSpace - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err +// GetTransformerConfigurators returns configurators for built-in transformers with kustomization fields. +// The configurators only operate on non-deprecated kustomization fields and use the passed in configurations instead of +// the kustomization field. +func GetTransformerConfigurators() map[builtinhelpers.BuiltinPluginType]func(*types.Kustomization, + *builtinconfig.TransformerConfig, *resmap.PluginHelpers) ([]resmap.Transformer, error) { + configurator := func(plugin builtinhelpers.BuiltinPluginType, h *resmap.PluginHelpers, inputs [][]byte) ( + []resmap.Transformer, error) { + transformers := make([]resmap.Transformer, len(inputs)) + for i, input := range inputs { + configurable := builtinhelpers.TransformerFactories[plugin]() + if err := configurable.Config(h, input); err != nil { + return nil, err + } + transformers[i] = configurable } - result = append(result, p) - return - }, + return transformers, nil + } + return map[builtinhelpers.BuiltinPluginType]func(*types.Kustomization, *builtinconfig.TransformerConfig, + *resmap.PluginHelpers) ([]resmap.Transformer, error){ - builtinhelpers.PatchJson6902Transformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - var c struct { - Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` - Path string `json:"path,omitempty" yaml:"path,omitempty"` - JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"` - } - for _, args := range kt.kustomization.PatchesJson6902 { - c.Target = args.Target - c.Path = args.Path - c.JsonOp = args.Patch - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) + builtinhelpers.NamespaceTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if k.Namespace == "" { + return make([]resmap.Transformer, 0), nil + } + var plugin struct { + types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + FieldSpecs []types.FieldSpec + } + plugin.Namespace = k.Namespace + plugin.FieldSpecs = config.NameSpace + b, err := yaml.Marshal(&plugin) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to serialize kustomization namespace") } - result = append(result, p) - } - return - }, - builtinhelpers.PatchStrategicMergeTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if len(kt.kustomization.PatchesStrategicMerge) == 0 { - return - } - var c struct { - Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"` - } - c.Paths = kt.kustomization.PatchesStrategicMerge - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err - } - result = append(result, p) - return - }, - builtinhelpers.PatchTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if len(kt.kustomization.Patches) == 0 { - return - } - var c struct { - Path string `json:"path,omitempty" yaml:"path,omitempty"` - Patch string `json:"patch,omitempty" yaml:"patch,omitempty"` - Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` - Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"` - } - for _, pc := range kt.kustomization.Patches { - c.Target = pc.Target - c.Patch = pc.Patch - c.Path = pc.Path - c.Options = pc.Options - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) + return configurator(builtinhelpers.NamespaceTransformer, h, [][]byte{b}) + }, + + builtinhelpers.PatchJson6902Transformer: func(k *types.Kustomization, _ *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + var plugin struct { + Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` + JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"` + } + inputs := make([][]byte, len(k.PatchesJson6902)) + for i, args := range k.PatchesJson6902 { + plugin.Target = args.Target + plugin.Path = args.Path + plugin.JsonOp = args.Patch + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization patchesJson6902") + } + inputs[i] = b + } + return configurator(builtinhelpers.PatchJson6902Transformer, h, inputs) + }, + builtinhelpers.PatchStrategicMergeTransformer: func(k *types.Kustomization, _ *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if len(k.PatchesStrategicMerge) == 0 { + return make([]resmap.Transformer, 0), nil + } + var plugin struct { + Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"` + } + plugin.Paths = k.PatchesStrategicMerge + b, err := yaml.Marshal(&plugin) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to serialize kustomization patchesStrategicMerge") } - result = append(result, p) - } - return - }, - builtinhelpers.LabelTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if len(kt.kustomization.Labels) == 0 && len(kt.kustomization.CommonLabels) == 0 { - return - } - for _, label := range kt.kustomization.Labels { - var c struct { + return configurator(builtinhelpers.PatchStrategicMergeTransformer, h, [][]byte{b}) + }, + builtinhelpers.PatchTransformer: func(k *types.Kustomization, _ *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if len(k.Patches) == 0 { + return make([]resmap.Transformer, 0), nil + } + var plugin types.Patch + inputs := make([][]byte, len(k.Patches)) + for i, pc := range k.Patches { + plugin = pc + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization patches") + } + inputs[i] = b + } + return configurator(builtinhelpers.PatchTransformer, h, inputs) + }, + builtinhelpers.LabelTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if len(k.Labels) == 0 && len(k.CommonLabels) == 0 { + return make([]resmap.Transformer, 0), nil + } + var plugin struct { Labels map[string]string FieldSpecs []types.FieldSpec } - c.Labels = label.Pairs - fss := types.FsSlice(label.FieldSpecs) - // merge the custom fieldSpecs with the default - if label.IncludeSelectors { - fss, err = fss.MergeAll(tc.CommonLabels) - } else { - // merge spec/template/metadata fieldSpecs if includeTemplate flag is true - if label.IncludeTemplates { - fss, err = fss.MergeAll(tc.TemplateLabels) - if err != nil { - return nil, errors.Wrap(err, "failed to merge template fieldSpec") + inputs := make([][]byte, len(k.Labels)+1) + for i, label := range k.Labels { + var err error + + plugin.Labels = label.Pairs + fss := types.FsSlice(label.FieldSpecs) + // merge the custom fieldSpecs with the default + if label.IncludeSelectors { + fss, err = fss.MergeAll(config.CommonLabels) + } else { + // merge spec/template/metadata fieldSpecs if includeTemplate flag is true + if label.IncludeTemplates { + fss, err = fss.MergeAll(config.TemplateLabels) + if err != nil { + return nil, errors.Wrap(err, "failed to merge template fieldSpec") + } } + // only add to metadata by default + fss, err = fss.MergeOne(types.FieldSpec{Path: "metadata/labels", CreateIfNotPresent: true}) } - // only add to metadata by default - fss, err = fss.MergeOne(types.FieldSpec{Path: "metadata/labels", CreateIfNotPresent: true}) + if err != nil { + return nil, err + } + plugin.FieldSpecs = fss + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization labels") + } + inputs[i] = b } + + plugin.Labels = k.CommonLabels + plugin.FieldSpecs = config.CommonLabels + b, err := yaml.Marshal(&plugin) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to serialize kustomization commonLabels") } - c.FieldSpecs = fss - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) + inputs[len(inputs)-1] = b + return configurator(builtinhelpers.LabelTransformer, h, inputs) + }, + builtinhelpers.AnnotationsTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if len(k.CommonAnnotations) == 0 { + return make([]resmap.Transformer, 0), nil + } + var plugin struct { + Annotations map[string]string + FieldSpecs []types.FieldSpec + } + plugin.Annotations = k.CommonAnnotations + plugin.FieldSpecs = config.CommonAnnotations + b, err := yaml.Marshal(&plugin) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to serialize kustomization commonAnnotations") } - result = append(result, p) - } - var c struct { - Labels map[string]string - FieldSpecs []types.FieldSpec - } - c.Labels = kt.kustomization.CommonLabels - c.FieldSpecs = tc.CommonLabels - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err - } - result = append(result, p) - return - }, - builtinhelpers.AnnotationsTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if len(kt.kustomization.CommonAnnotations) == 0 { - return - } - var c struct { - Annotations map[string]string - FieldSpecs []types.FieldSpec - } - c.Annotations = kt.kustomization.CommonAnnotations - c.FieldSpecs = tc.CommonAnnotations - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err - } - result = append(result, p) - return - }, - builtinhelpers.PrefixTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if kt.kustomization.NamePrefix == "" { - return - } - var c struct { - Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` - FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` - } - c.Prefix = kt.kustomization.NamePrefix - c.FieldSpecs = tc.NamePrefix - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err - } - result = append(result, p) - return - }, - builtinhelpers.SuffixTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if kt.kustomization.NameSuffix == "" { - return - } - var c struct { - Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` - FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` - } - c.Suffix = kt.kustomization.NameSuffix - c.FieldSpecs = tc.NameSuffix - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err - } - result = append(result, p) - return - }, - builtinhelpers.ImageTagTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - var c struct { - ImageTag types.Image - FieldSpecs []types.FieldSpec - } - for _, args := range kt.kustomization.Images { - c.ImageTag = args - c.FieldSpecs = tc.Images - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) + return configurator(builtinhelpers.AnnotationsTransformer, h, [][]byte{b}) + }, + builtinhelpers.PrefixTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if k.NamePrefix == "" { + return make([]resmap.Transformer, 0), nil + } + var plugin struct { + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + } + plugin.Prefix = k.NamePrefix + plugin.FieldSpecs = config.NamePrefix + b, err := yaml.Marshal(&plugin) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to serialize kustomization namePrefix") } - result = append(result, p) - } - return - }, - builtinhelpers.ReplacementTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - if len(kt.kustomization.Replacements) == 0 { - return - } - var c struct { - Replacements []types.ReplacementField - } - c.Replacements = kt.kustomization.Replacements - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) - if err != nil { - return nil, err - } - result = append(result, p) - return result, nil - }, - builtinhelpers.ReplicaCountTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - var c struct { - Replica types.Replica - FieldSpecs []types.FieldSpec - } - for _, args := range kt.kustomization.Replicas { - c.Replica = args - c.FieldSpecs = tc.Replicas - p := f() - err = kt.configureBuiltinPlugin(p, c, bpt) + return configurator(builtinhelpers.PrefixTransformer, h, [][]byte{b}) + }, + builtinhelpers.SuffixTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if k.NameSuffix == "" { + return make([]resmap.Transformer, 0), nil + } + var plugin struct { + Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + } + plugin.Suffix = k.NameSuffix + plugin.FieldSpecs = config.NameSuffix + b, err := yaml.Marshal(&plugin) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to serialize kustomization nameSuffix") } - result = append(result, p) - } - return - }, - // No kustomization file keyword for this yet. - builtinhelpers.ValueAddTransformer: func( - kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( - result []resmap.Transformer, err error) { - return nil, fmt.Errorf("valueadd keyword not yet defined") - }, + return configurator(builtinhelpers.SuffixTransformer, h, [][]byte{b}) + }, + builtinhelpers.ImageTagTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + var plugin struct { + ImageTag types.Image + FieldSpecs []types.FieldSpec + } + inputs := make([][]byte, len(k.Images)) + for i, args := range k.Images { + plugin.ImageTag = args + plugin.FieldSpecs = config.Images + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization images") + } + inputs[i] = b + } + return configurator(builtinhelpers.ImageTagTransformer, h, inputs) + }, + builtinhelpers.ReplacementTransformer: func(k *types.Kustomization, _ *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + if len(k.Replacements) == 0 { + return make([]resmap.Transformer, 0), nil + } + var plugin struct { + Replacements []types.ReplacementField + } + plugin.Replacements = k.Replacements + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization replacements") + } + return configurator(builtinhelpers.ReplacementTransformer, h, [][]byte{b}) + }, + builtinhelpers.ReplicaCountTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + var plugin struct { + Replica types.Replica + FieldSpecs []types.FieldSpec + } + inputs := make([][]byte, len(k.Replicas)) + for i, args := range k.Replicas { + plugin.Replica = args + plugin.FieldSpecs = config.Replicas + b, err := yaml.Marshal(&plugin) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize kustomization replicas") + } + inputs[i] = b + } + return configurator(builtinhelpers.ReplicaCountTransformer, h, inputs) + }, + // No kustomization file keyword for this yet. + builtinhelpers.ValueAddTransformer: func(k *types.Kustomization, config *builtinconfig.TransformerConfig, + h *resmap.PluginHelpers) ([]resmap.Transformer, error) { + return nil, fmt.Errorf("valueadd keyword not yet defined") + }, + } } diff --git a/api/internal/target/kusttarget_configplugin_test.go b/api/internal/target/kusttarget_configplugin_test.go new file mode 100644 index 00000000000..c515ae7cde1 --- /dev/null +++ b/api/internal/target/kusttarget_configplugin_test.go @@ -0,0 +1,705 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "sigs.k8s.io/kustomize/api/hasher" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" + "sigs.k8s.io/kustomize/api/internal/target" + "sigs.k8s.io/kustomize/api/internal/validate" + "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/yaml" +) + +func createHelper(t *testing.T, fSys filesys.FileSystem) *resmap.PluginHelpers { + t.Helper() + + ldr, err := loader.NewLoader(loader.RestrictionRootOnly, ".", fSys) + require.NoError(t, err) + + validator := validate.NewFieldValidator() + resmapFactory := resmap.NewFactory(resource.NewFactory(&hasher.Hasher{})) + pluginConfig := &types.PluginConfig{ + PluginRestrictions: types.PluginRestrictionsBuiltinsOnly, + BpLoadingOptions: types.BploUseStaticallyLinked, + HelmConfig: types.HelmConfig{ + Enabled: true, + Command: "helmV3", + }, + } + return resmap.NewPluginHelpers(ldr, validator, resmapFactory, pluginConfig) +} + +func checkResMap(t *testing.T, rm resmap.ResMap, content string) { + t.Helper() + + actual, err := rm.AsYaml() + require.NoError(t, err) + require.Equal(t, content, string(actual)) +} + +func TestGetGeneratorConfiguratorConfigMap(t *testing.T) { + req := require.New(t) + fSys := filesys.MakeFsInMemory() + req.NoError(fSys.WriteFile("/application.properties", []byte(`GRAPE=plum`))) + req.NoError(fSys.WriteFile("/server.properties", []byte(`PLUOT=tangerine`))) + + var k types.Kustomization + req.NoError(yaml.Unmarshal([]byte(`configMapGenerator: +- name: my-map + behavior: merge + files: + - application.properties + envs: + - server.properties + literals: + - APPLE=orange + options: + disableNameSuffixHash: true`), &k)) + + configurator, exists := target.GetGeneratorConfigurators()[builtinhelpers.ConfigMapGenerator] + req.True(exists) + generators, err := configurator(&k, createHelper(t, fSys)) + req.NoError(err) + + req.Len(generators, 1) + rm, err := generators[0].Generate() + req.NoError(err) + + checkResMap(t, rm, `apiVersion: v1 +data: + APPLE: orange + PLUOT: tangerine + application.properties: GRAPE=plum +kind: ConfigMap +metadata: + annotations: + internal.config.kubernetes.io/generatorBehavior: merge + name: my-map +`) +} + +func TestGetGeneratorConfiguratorSecret(t *testing.T) { + req := require.New(t) + + var k types.Kustomization + // test global generator options + // and multiple generators + req.NoError(yaml.Unmarshal([]byte(`generatorOptions: + labels: + kustomize.generated.resources: some-value +secretGenerator: +- name: one + namespace: secrets + literals: + - APPLE=orange + - GRAPE=plum +- name: two + options: + labels: + app.kubernetes.io/name: secret-two`), &k)) + + configurator, exists := target.GetGeneratorConfigurators()[builtinhelpers.SecretGenerator] + req.True(exists) + generators, err := configurator(&k, createHelper(t, filesys.MakeFsInMemory())) + req.NoError(err) + + expectedSecrets := []string{ + `apiVersion: v1 +data: + APPLE: b3Jhbmdl + GRAPE: cGx1bQ== +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/generatorBehavior: unspecified + internal.config.kubernetes.io/needsHashSuffix: enabled + labels: + kustomize.generated.resources: some-value + name: one + namespace: secrets +type: Opaque +`, + `apiVersion: v1 +data: {} +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/generatorBehavior: unspecified + internal.config.kubernetes.io/needsHashSuffix: enabled + labels: + app.kubernetes.io/name: secret-two + kustomize.generated.resources: some-value + name: two +type: Opaque +`, + } + req.Len(generators, len(expectedSecrets)) + for i := 0; i < len(generators); i++ { + rm, err := generators[i].Generate() + req.NoError(err) + checkResMap(t, rm, expectedSecrets[i]) + } +} + +func TestGetGeneratedConfiguratorDeprecated(t *testing.T) { + tests := map[builtinhelpers.BuiltinPluginType]string{ + builtinhelpers.ConfigMapGenerator: `configMapGenerator: +- name: deprecated + env: test.properties`, + builtinhelpers.SecretGenerator: `secretGenerator: +- name: deprecated + env: test.properties`, + // Note: no way to unit test helm working properly; must rely on integration test + builtinhelpers.HelmChartInflationGenerator: `helmChartInflationGenerator: +- chartName: test`, + } + for pluginType, kustomization := range tests { + t.Run(pluginType.String(), func(t *testing.T) { + var k types.Kustomization + require.NoError(t, yaml.Unmarshal([]byte(kustomization), &k)) + + configurator, exists := target.GetGeneratorConfigurators()[pluginType] + require.True(t, exists) + _, err := configurator(&k, createHelper(t, filesys.MakeFsInMemory())) + require.Error(t, err) + }) + } +} + +func TestGetTransformerConfiguratorEmpty(t *testing.T) { + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.PatchTransformer] + require.True(t, exists) + var kust types.Kustomization + var transformerConfig builtinconfig.TransformerConfig + transformers, err := configurator(&kust, &transformerConfig, createHelper(t, filesys.MakeFsInMemory())) + require.NoError(t, err) + require.Empty(t, transformers) +} + +func TestGetTransformerConfiguratorNamespace(t *testing.T) { + req := require.New(t) + fSys := filesys.MakeFsInMemory() + req.NoError(fSys.WriteFile("/namespace-configurations", []byte(`namespace: +- path: metadata/name + kind: Namespace`))) + + var k types.Kustomization + // check configurations kustomization field not used + req.NoError(yaml.Unmarshal([]byte(`configurations: +- namespace-configurations +namespace: custom`), &k)) + h := createHelper(t, filesys.MakeFsInMemory()) + + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.NamespaceTransformer] + req.True(exists) + + transformers, err := configurator(&k, &builtinconfig.TransformerConfig{}, h) + req.NoError(err) + req.Len(transformers, 1) + + rm, err := h.ResmapFactory().NewResMapFromBytes([]byte(`apiVersion: v1 +kind: Namespace +metadata: + name: should-not-be-touched +--- +apiVersion: v1 +kind: ConfigMap +data: {} +metadata: + name: just-for-show + namespace: not-custom`)) + req.NoError(err) + req.NoError(transformers[0].Transform(rm)) + checkResMap(t, rm, `apiVersion: v1 +kind: Namespace +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Namespace + internal.config.kubernetes.io/previousNames: should-not-be-touched + internal.config.kubernetes.io/previousNamespaces: _non_namespaceable_ + name: should-not-be-touched +--- +apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: ConfigMap + internal.config.kubernetes.io/previousNames: just-for-show + internal.config.kubernetes.io/previousNamespaces: not-custom + name: just-for-show + namespace: custom +`) +} + +func TestGetTransformerConfiguratorPatchJson(t *testing.T) { + // req := require.New(t) + // fSys := filesys.MakeFsInMemory() + // + // var k types.Kustomization + // req.NoError(yaml.Unmarshal([]byte(`patchesJson6902: + //- path: json-ops.yaml + // target: + // name: map + // namespace: mine + //- patch: |- + // - op: add + // path: /`), &k)) + // h := createHelper(t, fSys) + // + // configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.PatchJson6902Transformer] + // req.True(exists) + // transformers, err := configurator(&k, &builtinconfig.TransformerConfig{}, h) + // req.NoError(err) + // req.Len(transformers, 1) + // + // rm := +} + +func TestGetTransformerConfiguratorPatchStrategicMerge(t *testing.T) { + +} + +func TestGetTransformerConfiguratorPatch(t *testing.T) { + +} + +func TestGetTransformerConfiguratorLabel(t *testing.T) { + req := require.New(t) + + var k types.Kustomization + req.NoError(yaml.Unmarshal([]byte(`commonLabels: + separate.transformer: true +labels: +- pairs: + plain: 1 + no.field.specs: 1 +- pairs: + template: 2 + includeTemplates: true +- pairs: + merged.field.specs: 3 + includeSelectors: true + fields: + - path: spec/selector/matchLabels + kind: Deployment + version: v1 + create: true`), &k)) + h := createHelper(t, filesys.MakeFsInMemory()) + + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.LabelTransformer] + req.True(exists) + + transformers, err := configurator(&k, &builtinconfig.TransformerConfig{ + CommonLabels: types.FsSlice{ + types.FieldSpec{ + Path: "metadata/labels", + CreateIfNotPresent: true, + // check labels can operate without commonLabels configurations + Gvk: resid.Gvk{ + Kind: "Service", + }, + }, + types.FieldSpec{ + Path: "spec/selector", + CreateIfNotPresent: true, + Gvk: resid.Gvk{ + Version: "v1", // use of version + Kind: "Service", + }, + }, + }, + TemplateLabels: types.FsSlice{ + types.FieldSpec{ + Path: "spec/template/metadata/labels", + Gvk: resid.Gvk{ + Version: "v1", // use of version + Kind: "Deployment", + }, + }, + }, + }, h) + req.NoError(err) + req.Len(transformers, 4) + + const service = `apiVersion: apps/v1 +kind: Service +metadata: + name: my-service +` + const deployment = `apiVersion: v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + metadata: + labels: + needs: to-exist-already +` + results := map[string]bool{ + `apiVersion: apps/v1 +kind: Service +metadata: + labels: + no.field.specs: "1" + plain: "1" + name: my-service +--- +apiVersion: v1 +kind: Deployment +metadata: + labels: + no.field.specs: "1" + plain: "1" + name: my-deployment +spec: + template: + metadata: + labels: + needs: to-exist-already +`: false, + `apiVersion: apps/v1 +kind: Service +metadata: + labels: + template: "2" + name: my-service +--- +apiVersion: v1 +kind: Deployment +metadata: + labels: + template: "2" + name: my-deployment +spec: + template: + metadata: + labels: + needs: to-exist-already + template: "2" +`: false, + `apiVersion: apps/v1 +kind: Service +metadata: + labels: + merged.field.specs: "3" + name: my-service +spec: + selector: + merged.field.specs: "3" +--- +apiVersion: v1 +kind: Deployment +metadata: + name: my-deployment +spec: + selector: + matchLabels: + merged.field.specs: "3" + template: + metadata: + labels: + needs: to-exist-already +`: false, + fmt.Sprintf(`%s--- +%s`, `apiVersion: apps/v1 +kind: Service +metadata: + labels: + separate.transformer: "true" + name: my-service +spec: + selector: + separate.transformer: "true" +`, deployment): false, + } + for _, transformer := range transformers { + rm, err := h.ResmapFactory().NewResMapFromBytes([]byte(fmt.Sprintf(`%s--- +%s`, service, deployment))) + req.NoError(err) + req.NoError(transformer.Transform(rm)) + + result, err := rm.AsYaml() + req.NoError(err) + + seen, exists := results[string(result)] + req.Truef(exists, "transformed resmap not expected") + req.Falsef(seen, "repetitive label transformer returned; produces resmap %s", string(result)) + } +} + +func TestGetTransformerConfiguratorAnnotations(t *testing.T) { + req := require.New(t) + + var k types.Kustomization + req.NoError(yaml.Unmarshal([]byte(`commonAnnotations: + kustomization.configurations: false + transformer.configurations: true`), &k)) + h := createHelper(t, filesys.MakeFsInMemory()) + + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.AnnotationsTransformer] + req.True(exists) + + transformers, err := configurator(&k, &builtinconfig.TransformerConfig{ + CommonAnnotations: types.FsSlice{ + types.FieldSpec{ + Gvk: resid.Gvk{ + Kind: "ConfigMap", + }, + Path: "metadata/annotations", + // test CreateIfNotPresent field is respected + }, + }, + }, h) + req.NoError(err) + req.Len(transformers, 1) + + const absentPath = `apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + name: a-map` + + rm, err := h.ResmapFactory().NewResMapFromBytes([]byte(fmt.Sprintf(`%s +--- +apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + annotations: + target: true + name: my-map`, absentPath))) + req.NoError(err) + req.NoError(transformers[0].Transform(rm)) + checkResMap(t, rm, fmt.Sprintf(`%s +--- +apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + annotations: + kustomization.configurations: "false" + target: true + transformer.configurations: "true" + name: my-map +`, absentPath)) +} + +func TestGetTransformerConfiguratorPrefix(t *testing.T) { + req := require.New(t) + + var k types.Kustomization + req.NoError(yaml.Unmarshal([]byte(`namePrefix: my-`), &k)) + h := createHelper(t, filesys.MakeFsInMemory()) + + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.PrefixTransformer] + req.True(exists) + transformers, err := configurator(&k, &builtinconfig.TransformerConfig{ + NamePrefix: types.FsSlice{ + types.FieldSpec{ + Path: "metadata/name", + }, + }, + }, h) + req.NoError(err) + req.Len(transformers, 1) + + rm, err := h.ResmapFactory().NewResMapFromBytes([]byte(`apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + name: map`)) + req.NoError(err) + req.NoError(transformers[0].Transform(rm)) + checkResMap(t, rm, `apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + annotations: + internal.config.kubernetes.io/prefixes: my- + internal.config.kubernetes.io/previousKinds: ConfigMap + internal.config.kubernetes.io/previousNames: map + internal.config.kubernetes.io/previousNamespaces: default + name: my-map +`) +} + +func TestGetTransformerConfiguratorSuffix(t *testing.T) { + req := require.New(t) + + var k types.Kustomization + req.NoError(yaml.Unmarshal([]byte(`nameSuffix: -mine`), &k)) + h := createHelper(t, filesys.MakeFsInMemory()) + + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.SuffixTransformer] + req.True(exists) + transformers, err := configurator(&k, &builtinconfig.TransformerConfig{ + NameSuffix: types.FsSlice{ + types.FieldSpec{ + Path: "metadata/name", + }, + }, + }, h) + req.NoError(err) + req.Len(transformers, 1) + + rm, err := h.ResmapFactory().NewResMapFromBytes([]byte(`apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + name: map`)) + req.NoError(err) + req.NoError(transformers[0].Transform(rm)) + checkResMap(t, rm, `apiVersion: v1 +data: {} +kind: ConfigMap +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: ConfigMap + internal.config.kubernetes.io/previousNames: map + internal.config.kubernetes.io/previousNamespaces: default + internal.config.kubernetes.io/suffixes: -mine + name: map-mine +`) +} + +func TestGetTransformerConfiguratorImage(t *testing.T) { + req := require.New(t) + + var k types.Kustomization + // multiple transformers + req.NoError(yaml.Unmarshal([]byte(`images: +- name: postgres + newName: kustomize + newTag: v4.5.7 +- name: nginx + tagSuffix: -mine +- name: alpine + digest: sha256:xxx`), &k)) + h := createHelper(t, filesys.MakeFsInMemory()) + + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.ImageTagTransformer] + req.True(exists) + // Note: non-empty config would break tag suffix behavior due to + // https://github.com/kubernetes-sigs/kustomize/issues/4814 + transformers, err := configurator(&k, &builtinconfig.TransformerConfig{}, h) + req.NoError(err) + req.Len(transformers, 3) + + const deploymentf = `apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - image: %s + name: my-alpine + - image: %s + name: my-nginx + - image: %s + name: my-postgres +` + for i, result := range []string{ + fmt.Sprintf(deploymentf, "alpine:3.7", "nginx:1.7.9", "kustomize:v4.5.7"), + fmt.Sprintf(deploymentf, "alpine:3.7", "nginx:1.7.9-mine", "postgres:8"), + fmt.Sprintf(deploymentf, "alpine@sha256:xxx", "nginx:1.7.9", "postgres:8"), + } { + rm, err := h.ResmapFactory().NewResMapFromBytes([]byte( + fmt.Sprintf(deploymentf, "alpine:3.7", "nginx:1.7.9", "postgres:8"))) + req.NoError(err) + req.NoError(transformers[i].Transform(rm)) + checkResMap(t, rm, result) + } +} + +func TestGetTransformerConfiguratorReplacement(t *testing.T) { + +} + +func TestGetTransformerConfiguratorReplica(t *testing.T) { + req := require.New(t) + + var k types.Kustomization + req.NoError(yaml.Unmarshal([]byte(`replicas: +- name: my-set +- name: my-deployment + count: 10`), &k)) + h := createHelper(t, filesys.MakeFsInMemory()) + + configurator, exists := target.GetTransformerConfigurators()[builtinhelpers.ReplicaCountTransformer] + req.True(exists) + // multiple field specs + transformers, err := configurator(&k, &builtinconfig.TransformerConfig{ + Replicas: types.FsSlice{ + types.FieldSpec{ + Gvk: resid.Gvk{ + Kind: "ReplicaSet", + }, + Path: "spec/replicas", + }, + types.FieldSpec{ + Gvk: resid.Gvk{ + Kind: "Deployment", + }, + Path: "spec/replicas", + }, + }, + }, h) + req.NoError(err) + req.Len(transformers, 2) + + const set = `apiVersion: apps/v1 +kind: ReplicaSet +metadata: + name: my-set +spec: + replicas: 2` + const deployment = `apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + replicas: 2` + for i, result := range []string{ + fmt.Sprintf(`apiVersion: apps/v1 +kind: ReplicaSet +metadata: + name: my-set +spec: + replicas: 0 +--- +%s +`, deployment), + fmt.Sprintf(`%s +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + replicas: 10 +`, set), + } { + rm, err := h.ResmapFactory().NewResMapFromBytes([]byte(fmt.Sprintf(`%s +--- +%s`, set, deployment))) + req.NoError(err) + req.NoError(transformers[i].Transform(rm)) + checkResMap(t, rm, result) + } +} diff --git a/api/internal/target/kusttarget_writeplugin.go b/api/internal/target/kusttarget_writeplugin.go new file mode 100644 index 00000000000..a12aca2ddc1 --- /dev/null +++ b/api/internal/target/kusttarget_writeplugin.go @@ -0,0 +1,182 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target + +import ( + "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/yaml" +) + +func checkType(plugin builtinhelpers.BuiltinPluginType, actual types.TypeMeta) error { + if actual.APIVersion != "builtin" { + return errors.Errorf("apiVersion '%s' is not builtin", actual.APIVersion) + } + if actual.Kind != plugin.String() { + return errors.Errorf("kind '%s' is not %s", actual.Kind, plugin) + } + return nil +} + +// GetPluginWriters returns plugin writers for certain built-in plugins. +// +// The writer functions try to treat the resources as the built-in plugin type specified by the key and +// write them back to the kustomization via the most direct fields. For example, global kustomization fields, say, +// generatorOptions, will be passed up for local ones. +func GetPluginWriters() map[builtinhelpers.BuiltinPluginType]func(*types.Kustomization, []resmap.APIObject) error { + return map[builtinhelpers.BuiltinPluginType]func(*types.Kustomization, []resmap.APIObject) error{ + builtinhelpers.SecretGenerator: func(k *types.Kustomization, plugins []resmap.APIObject) error { + const pluginType = builtinhelpers.SecretGenerator + newSecrets := make([]types.SecretArgs, len(plugins)) + for i, p := range plugins { + var plugin struct { + types.TypeMeta + types.SecretArgs + } + content, err := p.YAML() + if err != nil { + return err + } + // should not strict unmarshal per Config() standards in plugins + if err = yaml.Unmarshal(content, &plugin); err != nil { + return errors.WrapPrefixf(err, "unable to extract %s from resource", pluginType) + } + if err = checkType(pluginType, plugin.TypeMeta); err != nil { + return errors.WrapPrefixf(err, "resource '%s' is not %s", string(content), pluginType) + } + // empty plugins can still be added to kustomization, per user-input kustomization standards + newSecrets[i] = plugin.SecretArgs + } + k.SecretGenerator = newSecrets + return nil + }, + builtinhelpers.ConfigMapGenerator: func(k *types.Kustomization, plugins []resmap.APIObject) error { + const pluginType = builtinhelpers.ConfigMapGenerator + newMaps := make([]types.ConfigMapArgs, len(plugins)) + for i, p := range plugins { + var plugin struct { + types.TypeMeta + types.ConfigMapArgs + } + content, err := p.YAML() + if err != nil { + return err + } + if err = yaml.Unmarshal(content, &plugin); err != nil { + return errors.WrapPrefixf(err, "unable to extract %s from resource", pluginType) + } + if err = checkType(pluginType, plugin.TypeMeta); err != nil { + return errors.WrapPrefixf(err, "resource '%s' is not %s", string(content), pluginType) + } + newMaps[i] = plugin.ConfigMapArgs + } + k.ConfigMapGenerator = newMaps + return nil + }, + builtinhelpers.PatchTransformer: func(k *types.Kustomization, plugins []resmap.APIObject) error { + const pluginType = builtinhelpers.PatchTransformer + patches := make([]types.Patch, len(plugins)) + for i, p := range plugins { + var plugin struct { + types.TypeMeta + types.Patch + } + content, err := p.YAML() + if err != nil { + return err + } + if err = yaml.Unmarshal(content, &plugin); err != nil { + return errors.WrapPrefixf(err, "unable to extract %s from resource", pluginType) + } + if err = checkType(pluginType, plugin.TypeMeta); err != nil { + return errors.WrapPrefixf(err, "resource '%s' is not %s", string(content), pluginType) + } + patches[i] = plugin.Patch + } + k.Patches = patches + return nil + }, + builtinhelpers.PatchJson6902Transformer: func(k *types.Kustomization, plugins []resmap.APIObject) error { + const pluginType = builtinhelpers.PatchJson6902Transformer + patches := make([]types.Patch, len(plugins)) + for i, p := range plugins { + var plugin struct { + types.TypeMeta + Target *types.Selector + Path string + JsonOp string + } + content, err := p.YAML() + if err != nil { + return err + } + if err = yaml.Unmarshal(content, &plugin); err != nil { + return errors.WrapPrefixf(err, "unable to extract %s from resource", pluginType) + } + if err = checkType(pluginType, plugin.TypeMeta); err != nil { + return errors.WrapPrefixf(err, "resource '%s' is not %s", string(content), pluginType) + } + patches[i] = types.Patch{ + Target: plugin.Target, + Path: plugin.Path, + Patch: plugin.JsonOp, + } + } + k.PatchesJson6902 = patches + return nil + }, + builtinhelpers.PatchStrategicMergeTransformer: func(k *types.Kustomization, plugins []resmap.APIObject) error { + const pluginType = builtinhelpers.PatchStrategicMergeTransformer + var patches []types.PatchStrategicMerge + for _, p := range plugins { + var plugin struct { + types.TypeMeta + Paths []types.PatchStrategicMerge + Patch string `json:"patches" yaml:"patches"` + } + content, err := p.YAML() + if err != nil { + return err + } + if err = yaml.Unmarshal(content, &plugin); err != nil { + return errors.WrapPrefixf(err, "unable to extract %s from resource", pluginType) + } + if err = checkType(pluginType, plugin.TypeMeta); err != nil { + return errors.WrapPrefixf(err, "resource '%s' is not %s", string(content), pluginType) + } + if plugin.Patch != "" { + plugin.Paths = append(plugin.Paths, types.PatchStrategicMerge(plugin.Patch)) + } + patches = append(patches, plugin.Paths...) + } + k.PatchesStrategicMerge = patches + return nil + }, + builtinhelpers.ReplacementTransformer: func(k *types.Kustomization, plugins []resmap.APIObject) error { + const pluginType = builtinhelpers.ReplacementTransformer + var replacements []types.ReplacementField + for _, p := range plugins { + var plugin struct { + types.TypeMeta + Replacements []types.ReplacementField + } + content, err := p.YAML() + if err != nil { + return err + } + if err = yaml.Unmarshal(content, &plugin); err != nil { + return errors.WrapPrefixf(err, "unable to extract %s from resource", pluginType) + } + if err = checkType(pluginType, plugin.TypeMeta); err != nil { + return errors.WrapPrefixf(err, "resource '%s' is not %s", string(content), pluginType) + } + replacements = append(replacements, plugin.Replacements...) + } + k.Replacements = replacements + return nil + }, + } +} diff --git a/api/internal/target/kusttarget_writeplugin_test.go b/api/internal/target/kusttarget_writeplugin_test.go new file mode 100644 index 00000000000..ec6c15d2ee1 --- /dev/null +++ b/api/internal/target/kusttarget_writeplugin_test.go @@ -0,0 +1 @@ +package target