diff --git a/examples/transformerconfigs/README.md b/examples/transformerconfigs/README.md index 292cc3e4b..dcab70902 100644 --- a/examples/transformerconfigs/README.md +++ b/examples/transformerconfigs/README.md @@ -1,37 +1,46 @@ # Transformer Configurations -Kustomize computes the resources by applying a series of transformers: -- namespace transformer -- prefix/suffix transformer -- label transformer -- annotation transformer -- name reference transformer -- variable reference transformer +Kustomize creates new resources by applying a series of transformations to an original +set of resources. Kustomize provides the following default transformers: -Each transformer takes a list of resources and modifies certain fields. The modification is based on the transformer's rule. -The fields to update is the transformer's configuration, which is a list of filedspec that can be represented in YAML format. +- namespace +- prefix/suffix +- label +- annotation +- name reference +- variable reference -## fieldSpec -FieldSpec is a type to represent a path to a field in one kind of resources. It has following format -``` +A `fieldSpec` list, in a transformer's configuration, determines which resource types and which fields +within those types the transformer can modify. + +## FieldSpec + +FieldSpec is a type that represents a path to a field in one kind of resource. + +```yaml group: some-group version: some-version kind: some-kind path: path/to/the/field create: false ``` -If `create` is set to true, it indicates the transformer to create the path if it is not found in the resources. This is most useful for label and annotation transformers, where the path for labels or annotations may not be set before the transformation. -## prefix/suffix transformer -Name prefix suffix transformer adds prefix and suffix to the `metadata/name` field for all resources with following configuration: -``` +If `create` is set to `true`, the transformer creates the path to the field in the resource if the path is not already found. This is most useful for label and annotation transformers, where the path for labels or annotations may not be set before the transformation. + +## Prefix/suffix transformer + +The prefix/suffix transformer adds a prefix/suffix to the `metadata/name` field for all resources. Here is the default prefix transformer configuration: + +```yaml namePrefix: - path: metadata/name ``` -## label transformer -Label transformer adds labels to `metadata/labels` field for all resources. It also adds labels to `spec/selector` field in all Service and to `spec/selector/matchLabels` field in all Deployment. -``` +## Label transformer + +The label transformer adds labels to the `metadata/labels` field for all resources. It also adds labels to the `spec/selector` field in all Service resources as well as the `spec/selector/matchLabels` field in all Deployment resources. + +```yaml commonLabels: - path: metadata/labels create: true @@ -44,15 +53,16 @@ commonLabels: - path: spec/selector/matchLabels create: true kind: Deployment - (etc.) ``` -## name reference transformer -Name reference transformer's configuration is different from all other transformers. It contains a list of namebackreferences, which represented all the possible fields that a type could be used as a reference in other types of resources. A namebackreference contains a type such as ConfigMap as well as a list of FieldSpecs where ConfigMap is referenced. Here is an example. -``` +## Name reference transformer + +Name reference transformer's configuration is different from all other transformers. It contains a list of `nameReferences`, which represent all of the possible fields that a type could be used as a reference in other types of resources. A `nameReference` contains a type such as ConfigMap as well as a list of `fieldSpecs` where ConfigMap is referenced in other resources. Here is an example: + +```yaml kind: ConfigMap version: v1 -FieldSpecs: +fieldSpecs: - kind: Pod version: v1 path: spec/volumes/configMap/name @@ -60,10 +70,11 @@ FieldSpecs: path: spec/template/spec/volumes/configMap/name - kind: Job path: spec/template/spec/volumes/configMap/name - (etc.) -``` -Name reference transformer configuration contains a list of such namebackreferences for ConfigMap, Secret, Service, Role, ServiceAccount and so on. ``` + +Name reference transformer's configuration contains a list of `nameReferences` for resources such as ConfigMap, Secret, Service, Role, and ServiceAccount. Here is an example configuration: + +```yaml nameReference: - kind: ConfigMap version: v1 @@ -74,7 +85,7 @@ nameReference: - path: spec/containers/env/valueFrom/configMapKeyRef/name version: v1 kind: Pod - (etc.) + # ... - kind: Secret version: v1 fieldSpecs: @@ -84,12 +95,12 @@ nameReference: - path: spec/containers/env/valueFrom/secretKeyRef/name version: v1 kind: Pod - (etc.) ``` -## customizing transformer configurations +## Customizing transformer configurations + +Kustomize has a default set of transformer configurations. You can save the default transformer configurations to a local directory by calling `kustomize config save -d`, and modify and use these configurations. Kustomize also supports adding new transformer configurations to kustomization.yaml. This tutorial shows how to customize those configurations to: -Kustomize has a default set of configurations. They can be saved to local directory through `kustomize config save -d`. Kustomize allows modifying those configuration files and using them in kustomization.yaml file. This tutorial shows how to customize those configurations to - [support a CRD type](crd/README.md) - add extra fields for variable substitution -- add extra fields for name reference +- add extra fields for name reference \ No newline at end of file diff --git a/k8sdeps/configmapandsecret/configmapfactory.go b/k8sdeps/configmapandsecret/configmapfactory.go index a40792258..27897c705 100644 --- a/k8sdeps/configmapandsecret/configmapfactory.go +++ b/k8sdeps/configmapandsecret/configmapfactory.go @@ -21,8 +21,7 @@ import ( "fmt" "unicode/utf8" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" "sigs.k8s.io/kustomize/k8sdeps/kv/plugin" "sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/types" @@ -40,8 +39,8 @@ func NewFactory( } func makeFreshConfigMap( - args *types.ConfigMapArgs) *corev1.ConfigMap { - cm := &corev1.ConfigMap{} + args *types.ConfigMapArgs) *v1.ConfigMap { + cm := &v1.ConfigMap{} cm.APIVersion = "v1" cm.Kind = "ConfigMap" cm.Name = args.Name @@ -52,7 +51,7 @@ func makeFreshConfigMap( // MakeConfigMap returns a new ConfigMap, or nil and an error. func (f *Factory) MakeConfigMap( - args *types.ConfigMapArgs) (*corev1.ConfigMap, error) { + args *types.ConfigMapArgs) (*v1.ConfigMap, error) { all, err := f.loadKvPairs(args.GeneratorArgs) if err != nil { return nil, err diff --git a/k8sdeps/factory.go b/k8sdeps/factory.go deleted file mode 100644 index a422b25a4..000000000 --- a/k8sdeps/factory.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package k8sdeps provides kustomize factory with k8s dependencies -package k8sdeps - -import ( - "sigs.k8s.io/kustomize/k8sdeps/kunstruct" - "sigs.k8s.io/kustomize/k8sdeps/transformer" - "sigs.k8s.io/kustomize/k8sdeps/validator" - "sigs.k8s.io/kustomize/pkg/factory" -) - -// NewFactory creates an instance of KustFactory using k8sdeps factories -func NewFactory() *factory.KustFactory { - return factory.NewKustFactory( - kunstruct.NewKunstructuredFactoryImpl(), - validator.NewKustValidator(), - transformer.NewFactoryImpl(), - ) -} diff --git a/k8sdeps/kunstruct/factory.go b/k8sdeps/kunstruct/factory.go index 3c3644c3f..ef40209b9 100644 --- a/k8sdeps/kunstruct/factory.go +++ b/k8sdeps/kunstruct/factory.go @@ -32,13 +32,21 @@ import ( // KunstructuredFactoryImpl hides construction using apimachinery types. type KunstructuredFactoryImpl struct { + generatorMetaArgs *types.GeneratorMetaArgs } var _ ifc.KunstructuredFactory = &KunstructuredFactoryImpl{} // NewKunstructuredFactoryImpl returns a factory. func NewKunstructuredFactoryImpl() ifc.KunstructuredFactory { - return &KunstructuredFactoryImpl{} + return NewKunstructuredFactoryWithGeneratorArgs( + &types.GeneratorMetaArgs{}) +} + +// NewKunstructuredFactoryWithGeneratorArgs returns a factory. +func NewKunstructuredFactoryWithGeneratorArgs( + gma *types.GeneratorMetaArgs) ifc.KunstructuredFactory { + return &KunstructuredFactoryImpl{gma} } // SliceFromBytes returns a slice of Kunstructured. @@ -82,7 +90,10 @@ func (kf *KunstructuredFactoryImpl) MakeConfigMap( ldr ifc.Loader, options *types.GeneratorOptions, args *types.ConfigMapArgs) (ifc.Kunstructured, error) { - o, err := configmapandsecret.NewFactory(ldr, options, plugin.NewRegistry(ldr)).MakeConfigMap(args) + o, err := configmapandsecret.NewFactory( + ldr, options, + plugin.NewConfiguredRegistry( + ldr, &kf.generatorMetaArgs.PluginConfig)).MakeConfigMap(args) if err != nil { return nil, err } @@ -94,7 +105,10 @@ func (kf *KunstructuredFactoryImpl) MakeSecret( ldr ifc.Loader, options *types.GeneratorOptions, args *types.SecretArgs) (ifc.Kunstructured, error) { - o, err := configmapandsecret.NewFactory(ldr, options, plugin.NewRegistry(ldr)).MakeSecret(args) + o, err := configmapandsecret.NewFactory( + ldr, options, + plugin.NewConfiguredRegistry( + ldr, &kf.generatorMetaArgs.PluginConfig)).MakeSecret(args) if err != nil { return nil, err } diff --git a/k8sdeps/kv/plugin/goplugin.go b/k8sdeps/kv/plugin/goplugin.go index d316a76cd..4b67f9dd2 100644 --- a/k8sdeps/kv/plugin/goplugin.go +++ b/k8sdeps/kv/plugin/goplugin.go @@ -18,23 +18,45 @@ package plugin import ( "fmt" - "os" + "path/filepath" "plugin" + + "sigs.k8s.io/kustomize/pkg/types" ) var _ Factory = &goFactory{} const ( - dir = "$HOME/.config/kustomize/plugins/kvsource" + kvSourcesDir = "kvSources" + EnableGoPluginsFlagName = "enable_alpha_goplugins_accept_panic_risk" + EnableGoPluginsFlagHelp = ` +Warning: the main program may panic and exit on an +attempt to use a goplugin that was compiled under +conditions differing from the those in effect when +main was compiled. It's safest to use this flag in +the context of a container image holding both the +main and the goplugins it needs, all built on the +same machine, with the same transitive libs and +the same compiler version. +` + errorFmt = ` +enable go plugins by specifying flag + --%s +Place .so files in + %s +%s +` ) -func newGoFactory() *goFactory { +func newGoFactory(c *types.PluginConfig) *goFactory { return &goFactory{ + config: c, plugins: make(map[string]KVSource), } } type goFactory struct { + config *types.PluginConfig plugins map[string]KVSource } @@ -43,7 +65,19 @@ func (p *goFactory) load(name string) (KVSource, error) { return plug, nil } - goPlugin, err := plugin.Open(fmt.Sprintf("%s/kustomize-%s.so", os.ExpandEnv(dir), name)) + dir := filepath.Join( + p.config.DirectoryPath, + kvSourcesDir) + if !p.config.GoEnabled { + return nil, fmt.Errorf( + errorFmt, + EnableGoPluginsFlagName, + dir, + EnableGoPluginsFlagHelp) + } + + goPlugin, err := plugin.Open( + filepath.Join(dir, name+".so")) if err != nil { return nil, err } diff --git a/k8sdeps/kv/plugin/registry.go b/k8sdeps/kv/plugin/registry.go index 3883922c7..183ed16e3 100644 --- a/k8sdeps/kv/plugin/registry.go +++ b/k8sdeps/kv/plugin/registry.go @@ -18,8 +18,10 @@ package plugin import ( "fmt" - + "path/filepath" "sigs.k8s.io/kustomize/pkg/ifc" + "sigs.k8s.io/kustomize/pkg/pgmconfig" + "sigs.k8s.io/kustomize/pkg/types" ) // Registry holds all the plugin factories. @@ -28,17 +30,36 @@ type Registry struct { ldr ifc.Loader } -// NewRegistry returns a new Registry loaded with all the factories. -func NewRegistry(ldr ifc.Loader) Registry { +const ( + PluginsDir = "plugins" +) + +func DefaultPluginConfig() types.PluginConfig { + return types.PluginConfig{ + GoEnabled: false, + DirectoryPath: filepath.Join( + pgmconfig.ConfigRoot(), PluginsDir), + } +} + +// NewConfiguredRegistry returns a new Registry loaded +// with all the factories and a custom PluginConfig. +func NewConfiguredRegistry( + ldr ifc.Loader, pc *types.PluginConfig) Registry { return Registry{ ldr: ldr, factories: map[string]Factory{ - "go": newGoFactory(), + "go": newGoFactory(pc), "builtin": newBuiltinFactory(ldr), }, } } +// NewRegistry returns a new Registry with default config. +func NewRegistry(ldr ifc.Loader) Registry { + return NewConfiguredRegistry(ldr, &types.PluginConfig{}) +} + // Load returns a plugin by type and name, func (r *Registry) Load(pluginType, name string) (KVSource, error) { factory, exists := r.factories[pluginType] diff --git a/kustomize.go b/kustomize.go index e17481de9..584b559e8 100644 --- a/kustomize.go +++ b/kustomize.go @@ -19,12 +19,11 @@ package main import ( "os" - "sigs.k8s.io/kustomize/k8sdeps" "sigs.k8s.io/kustomize/pkg/commands" ) func main() { - if err := commands.NewDefaultCommand(k8sdeps.NewFactory()).Execute(); err != nil { + if err := commands.NewDefaultCommand().Execute(); err != nil { os.Exit(1) } os.Exit(0) diff --git a/pkg/commands/build/build.go b/pkg/commands/build/build.go index e62747e32..1176f25b1 100644 --- a/pkg/commands/build/build.go +++ b/pkg/commands/build/build.go @@ -21,10 +21,10 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/pkg/constants" "sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/ifc/transformer" "sigs.k8s.io/kustomize/pkg/loader" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/target" ) @@ -67,7 +67,7 @@ func NewCmdBuild( cmd := &cobra.Command{ Use: "build [path]", - Short: "Print current configuration per contents of " + constants.KustomizationFileNames[0], + Short: "Print current configuration per contents of " + pgmconfig.KustomizationFileNames[0], Example: examples, SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -88,7 +88,7 @@ func NewCmdBuild( // Validate validates build command. func (o *Options) Validate(args []string) error { if len(args) > 1 { - return errors.New("specify one path to " + constants.KustomizationFileNames[0]) + return errors.New("specify one path to " + pgmconfig.KustomizationFileNames[0]) } if len(args) == 0 { o.kustomizationPath = "./" diff --git a/pkg/commands/build/build_test.go b/pkg/commands/build/build_test.go index b06ccdea2..792d95aac 100644 --- a/pkg/commands/build/build_test.go +++ b/pkg/commands/build/build_test.go @@ -19,7 +19,7 @@ package build import ( "testing" - "sigs.k8s.io/kustomize/pkg/constants" + "sigs.k8s.io/kustomize/pkg/pgmconfig" ) func TestNewOptionsToSilenceCodeInspectionError(t *testing.T) { @@ -39,7 +39,7 @@ func TestBuildValidate(t *testing.T) { {"file", []string{"beans"}, "beans", ""}, {"path", []string{"a/b/c"}, "a/b/c", ""}, {"path", []string{"too", "many"}, - "", "specify one path to " + constants.KustomizationFileNames[0]}, + "", "specify one path to " + pgmconfig.KustomizationFileNames[0]}, } for _, mycase := range cases { opts := Options{} diff --git a/pkg/commands/commands.go b/pkg/commands/commands.go index 3651ae6cf..5a4ff0d05 100644 --- a/pkg/commands/commands.go +++ b/pkg/commands/commands.go @@ -20,35 +20,57 @@ package commands import ( "flag" "os" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/k8sdeps/kunstruct" + "sigs.k8s.io/kustomize/k8sdeps/kv/plugin" + "sigs.k8s.io/kustomize/k8sdeps/transformer" + "sigs.k8s.io/kustomize/k8sdeps/validator" "sigs.k8s.io/kustomize/pkg/commands/build" "sigs.k8s.io/kustomize/pkg/commands/edit" "sigs.k8s.io/kustomize/pkg/commands/misc" - "sigs.k8s.io/kustomize/pkg/factory" "sigs.k8s.io/kustomize/pkg/fs" + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/resource" + "sigs.k8s.io/kustomize/pkg/types" ) // NewDefaultCommand returns the default (aka root) command for kustomize command. -func NewDefaultCommand(f *factory.KustFactory) *cobra.Command { - fsys := fs.MakeRealFS() +func NewDefaultCommand() *cobra.Command { + fSys := fs.MakeRealFS() stdOut := os.Stdout c := &cobra.Command{ - Use: "kustomize", - Short: "kustomize manages declarative configuration of Kubernetes", + Use: pgmconfig.PgmName, + Short: "Manages declarative configuration of Kubernetes", Long: ` -kustomize manages declarative configuration of Kubernetes. - +Manages declarative configuration of Kubernetes. See https://sigs.k8s.io/kustomize `, } + // Configuration for ConfigMap and Secret generators. + genMetaArgs := types.GeneratorMetaArgs{ + PluginConfig: plugin.DefaultPluginConfig(), + } + + c.Flags().BoolVar( + &genMetaArgs.PluginConfig.GoEnabled, + plugin.EnableGoPluginsFlagName, + false, plugin.EnableGoPluginsFlagHelp) + // Not advertising this alpha feature. + c.Flags().MarkHidden(plugin.EnableGoPluginsFlagName) + + uf := kunstruct.NewKunstructuredFactoryWithGeneratorArgs(&genMetaArgs) + c.AddCommand( - // TODO: Make consistent API for newCmd* functions. - build.NewCmdBuild(stdOut, fsys, f.ResmapF, f.TransformerF), - edit.NewCmdEdit(fsys, f.ValidatorF, f.UnstructF), - misc.NewCmdConfig(fsys), + build.NewCmdBuild( + stdOut, fSys, + resmap.NewFactory(resource.NewFactory(uf)), + transformer.NewFactoryImpl()), + edit.NewCmdEdit(fSys, validator.NewKustValidator(), uf), + misc.NewCmdConfig(fSys), misc.NewCmdVersion(stdOut), ) c.PersistentFlags().AddGoFlagSet(flag.CommandLine) diff --git a/pkg/commands/edit/add/addmetadata.go b/pkg/commands/edit/add/addmetadata.go index d9104f35d..b4e14f34e 100644 --- a/pkg/commands/edit/add/addmetadata.go +++ b/pkg/commands/edit/add/addmetadata.go @@ -22,8 +22,8 @@ import ( "github.com/spf13/cobra" "sigs.k8s.io/kustomize/pkg/commands/kustfile" - "sigs.k8s.io/kustomize/pkg/constants" "sigs.k8s.io/kustomize/pkg/fs" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/types" ) @@ -59,7 +59,7 @@ func newCmdAddAnnotation(fSys fs.FileSystem, v func(map[string]string) error) *c o.mapValidator = v cmd := &cobra.Command{ Use: "annotation", - Short: "Adds one or more commonAnnotations to " + constants.KustomizationFileNames[0], + Short: "Adds one or more commonAnnotations to " + pgmconfig.KustomizationFileNames[0], Example: ` add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`, RunE: func(cmd *cobra.Command, args []string) error { @@ -76,7 +76,7 @@ func newCmdAddLabel(fSys fs.FileSystem, v func(map[string]string) error) *cobra. o.mapValidator = v cmd := &cobra.Command{ Use: "label", - Short: "Adds one or more commonLabels to " + constants.KustomizationFileNames[0], + Short: "Adds one or more commonLabels to " + pgmconfig.KustomizationFileNames[0], Example: ` add label {labelKey1:labelValue1},{labelKey2:labelValue2}`, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/pkg/commands/kustfile/kustomizationfile.go b/pkg/commands/kustfile/kustomizationfile.go index cf66745ec..42b93d789 100644 --- a/pkg/commands/kustfile/kustomizationfile.go +++ b/pkg/commands/kustfile/kustomizationfile.go @@ -27,8 +27,8 @@ import ( "strings" "github.com/ghodss/yaml" - "sigs.k8s.io/kustomize/pkg/constants" "sigs.k8s.io/kustomize/pkg/fs" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/types" ) @@ -129,7 +129,7 @@ func NewKustomizationFile(fSys fs.FileSystem) (*kustomizationFile, error) { // n func (mf *kustomizationFile) validate() error { match := 0 var path []string - for _, kfilename := range constants.KustomizationFileNames { + for _, kfilename := range pgmconfig.KustomizationFileNames { if mf.fSys.Exists(kfilename) { match += 1 path = append(path, kfilename) @@ -138,7 +138,7 @@ func (mf *kustomizationFile) validate() error { switch match { case 0: - return fmt.Errorf("Missing kustomization file '%s'.\n", constants.KustomizationFileNames[0]) + return fmt.Errorf("Missing kustomization file '%s'.\n", pgmconfig.KustomizationFileNames[0]) case 1: mf.path = path[0] default: diff --git a/pkg/commands/kustfile/kustomizationfile_test.go b/pkg/commands/kustfile/kustomizationfile_test.go index 79e3a5da3..0795986c3 100644 --- a/pkg/commands/kustfile/kustomizationfile_test.go +++ b/pkg/commands/kustfile/kustomizationfile_test.go @@ -21,8 +21,8 @@ import ( "strings" "testing" - "sigs.k8s.io/kustomize/pkg/constants" "sigs.k8s.io/kustomize/pkg/fs" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/types" ) @@ -112,12 +112,12 @@ configMapGenerator: name: my-configmap ` fakeFS := fs.MakeFakeFS() - fakeFS.WriteFile(constants.KustomizationFileNames[1], []byte(kcontent)) + fakeFS.WriteFile(pgmconfig.KustomizationFileNames[1], []byte(kcontent)) k, err := NewKustomizationFile(fakeFS) if err != nil { t.Fatalf("Unexpected Error: %v", err) } - if k.path != constants.KustomizationFileNames[1] { + if k.path != pgmconfig.KustomizationFileNames[1] { t.Fatalf("Load incorrect file path %s", k.path) } } diff --git a/pkg/factory/factory.go b/pkg/factory/factory.go deleted file mode 100644 index e71669a87..000000000 --- a/pkg/factory/factory.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -// Package factory provides factories for kustomize. -package factory - -import ( - "sigs.k8s.io/kustomize/pkg/ifc" - "sigs.k8s.io/kustomize/pkg/ifc/transformer" - "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/resource" -) - -// KustFactory provides different factories for kustomize -type KustFactory struct { - ResmapF *resmap.Factory - TransformerF transformer.Factory - ValidatorF ifc.Validator - UnstructF ifc.KunstructuredFactory -} - -// NewKustFactory creats a KustFactory instance -func NewKustFactory(u ifc.KunstructuredFactory, v ifc.Validator, t transformer.Factory) *KustFactory { - return &KustFactory{ - ResmapF: resmap.NewFactory(resource.NewFactory(u)), - TransformerF: t, - ValidatorF: v, - UnstructF: u, - } -} diff --git a/pkg/fs/fakefs.go b/pkg/fs/fakefs.go index 59c0966b2..0f2a4eedf 100644 --- a/pkg/fs/fakefs.go +++ b/pkg/fs/fakefs.go @@ -22,7 +22,7 @@ import ( "sort" "strings" - "sigs.k8s.io/kustomize/pkg/constants" + "sigs.k8s.io/kustomize/pkg/pgmconfig" ) var _ FileSystem = &fakeFs{} @@ -158,7 +158,7 @@ func (fs *fakeFs) ReadFile(name string) ([]byte, error) { } func (fs *fakeFs) ReadTestKustomization() ([]byte, error) { - return fs.ReadFile(constants.KustomizationFileNames[0]) + return fs.ReadFile(pgmconfig.KustomizationFileNames[0]) } // WriteFile always succeeds and does nothing. @@ -176,7 +176,7 @@ func (fs *fakeFs) WriteTestKustomization() { // WriteTestKustomizationWith writes a standard test file. func (fs *fakeFs) WriteTestKustomizationWith(bytes []byte) { - fs.WriteFile(constants.KustomizationFileNames[0], bytes) + fs.WriteFile(pgmconfig.KustomizationFileNames[0], bytes) } func (fs *fakeFs) pathMatch(path, pattern string) bool { diff --git a/pkg/loader/fileloader_test.go b/pkg/loader/fileloader_test.go index b8affe10e..68114f368 100644 --- a/pkg/loader/fileloader_test.go +++ b/pkg/loader/fileloader_test.go @@ -25,8 +25,8 @@ import ( "strings" "testing" - "sigs.k8s.io/kustomize/pkg/constants" "sigs.k8s.io/kustomize/pkg/git" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/ifc" @@ -341,7 +341,7 @@ func TestNewLoaderAtGitClone(t *testing.T) { fSys.MkdirAll(coRoot) fSys.MkdirAll(coRoot + "/" + pathInRepo) fSys.WriteFile( - coRoot+"/"+pathInRepo+"/"+constants.KustomizationFileNames[0], + coRoot+"/"+pathInRepo+"/"+pgmconfig.KustomizationFileNames[0], []byte(` whatever `)) diff --git a/pkg/pgmconfig/config.go b/pkg/pgmconfig/config.go new file mode 100644 index 000000000..d7a27422a --- /dev/null +++ b/pkg/pgmconfig/config.go @@ -0,0 +1,54 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package commands holds the CLI glue mapping textual commands/args to method calls. +package pgmconfig + +import ( + "os" + "path/filepath" + "runtime" +) + +const ( + XDG_CONFIG_HOME = "XDG_CONFIG_HOME" + defaultConfigSubdir = ".config" +) + +// Use https://github.com/kirsle/configdir instead? +func ConfigRoot() string { + dir := os.Getenv(XDG_CONFIG_HOME) + if len(dir) == 0 { + dir = filepath.Join( + homeDir(), defaultConfigSubdir) + } + return filepath.Join(dir, PgmName) +} + +func homeDir() string { + home := os.Getenv(homeEnv()) + if len(home) > 0 { + return home + } + return "~" +} + +func homeEnv() string { + if runtime.GOOS == "windows" { + return "USERPROFILE" + } + return "HOME" +} diff --git a/pkg/pgmconfig/config_test.go b/pkg/pgmconfig/config_test.go new file mode 100644 index 000000000..2833afea7 --- /dev/null +++ b/pkg/pgmconfig/config_test.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pgmconfig + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestConfigDirNoXdg(t *testing.T) { + xdg, isSet := os.LookupEnv(XDG_CONFIG_HOME) + if isSet { + os.Unsetenv(XDG_CONFIG_HOME) + } + s := ConfigRoot() + if isSet { + os.Setenv(XDG_CONFIG_HOME, xdg) + } + if !strings.HasSuffix( + s, + rootedPath(defaultConfigSubdir, PgmName)) { + t.Fatalf("unexpected config dir: %s", s) + } +} + +func rootedPath(elem ...string) string { + return string(filepath.Separator) + filepath.Join(elem...) +} + +func TestConfigDirWithXdg(t *testing.T) { + xdg, isSet := os.LookupEnv(XDG_CONFIG_HOME) + os.Setenv(XDG_CONFIG_HOME, rootedPath("blah")) + s := ConfigRoot() + if isSet { + os.Setenv(XDG_CONFIG_HOME, xdg) + } + if s != rootedPath("blah", PgmName) { + t.Fatalf("unexpected config dir: %s", s) + } +} diff --git a/pkg/constants/constants.go b/pkg/pgmconfig/constants.go similarity index 76% rename from pkg/constants/constants.go rename to pkg/pgmconfig/constants.go index dd50230fb..008b69da9 100644 --- a/pkg/constants/constants.go +++ b/pkg/pgmconfig/constants.go @@ -15,14 +15,18 @@ limitations under the License. */ // Package constants holds global constants for the kustomize tool. -package constants +package pgmconfig -// KustomizationFileNames is a list of filenames that can be recognized and consumbed -// by Kustomize. -// In each directory, Kustomize searches for file with the name in this list. -// Only one match is allowed. +// KustomizationFileNames is a list of filenames +// that kustomize recognizes. +// To avoid ambiguity, a directory cannot contain +// more than one match to this list. var KustomizationFileNames = []string{ "kustomization.yaml", "kustomization.yml", "Kustomization", } + +const ( + PgmName = "kustomize" +) diff --git a/pkg/target/generatoroptions_test.go b/pkg/target/generatoroptions_test.go index 4dbdbb588..fc1bd792e 100644 --- a/pkg/target/generatoroptions_test.go +++ b/pkg/target/generatoroptions_test.go @@ -14,6 +14,9 @@ limitations under the License. package target_test import ( + "path/filepath" + "sigs.k8s.io/kustomize/pkg/types" + "strings" "testing" ) @@ -69,3 +72,47 @@ metadata: name: shouldNotHaveHash `) } + +func TestGoPluginNotEnabled(t *testing.T) { + th := NewKustTestHarness(t, "/app") + th.writeK("/app", ` +secretGenerator: +- name: attemptGoPlugin + kvSources: + - name: foo + pluginType: go + args: + - someArg + - someOtherArg +`) + _, err := th.makeKustTarget().MakeCustomizedResMap() + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains(err.Error(), "enable go plugins by ") { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestGoPluginDoesNotExist(t *testing.T) { + th := NewKustTestHarnessWithPluginConfig( + t, "/app", types.PluginConfig{GoEnabled: true}) + th.writeK("/app", ` +secretGenerator: +- name: attemptGoPlugin + kvSources: + - name: foo + pluginType: go + args: + - someArg + - someOtherArg +`) + _, err := th.makeKustTarget().MakeCustomizedResMap() + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains(err.Error(), + filepath.Join("kvSources", "foo.so")) { + t.Fatalf("unexpected err: %v", err) + } +} diff --git a/pkg/target/kusttarget.go b/pkg/target/kusttarget.go index 5bbd2198c..6a6eb952b 100644 --- a/pkg/target/kusttarget.go +++ b/pkg/target/kusttarget.go @@ -25,11 +25,11 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" - "sigs.k8s.io/kustomize/pkg/constants" "sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc/transformer" interror "sigs.k8s.io/kustomize/pkg/internal/error" patchtransformer "sigs.k8s.io/kustomize/pkg/patch/transformer" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/transformers" @@ -89,7 +89,7 @@ func commaOr(q []string) string { func loadKustFile(ldr ifc.Loader) ([]byte, error) { var content []byte match := 0 - for _, kf := range constants.KustomizationFileNames { + for _, kf := range pgmconfig.KustomizationFileNames { c, err := ldr.Load(kf) if err == nil { match += 1 @@ -100,7 +100,7 @@ func loadKustFile(ldr ifc.Loader) ([]byte, error) { case 0: return nil, fmt.Errorf( "unable to find one of %v in directory '%s'", - commaOr(quoted(constants.KustomizationFileNames)), ldr.Root()) + commaOr(quoted(pgmconfig.KustomizationFileNames)), ldr.Root()) case 1: return content, nil default: diff --git a/pkg/target/kusttestharness_test.go b/pkg/target/kusttestharness_test.go index 74cbab348..ea3b712bd 100644 --- a/pkg/target/kusttestharness_test.go +++ b/pkg/target/kusttestharness_test.go @@ -21,13 +21,14 @@ package target_test import ( "fmt" "path/filepath" + "sigs.k8s.io/kustomize/k8sdeps/kv/plugin" "strings" "testing" "sigs.k8s.io/kustomize/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/k8sdeps/transformer" - "sigs.k8s.io/kustomize/pkg/constants" "sigs.k8s.io/kustomize/pkg/internal/loadertest" + "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resource" . "sigs.k8s.io/kustomize/pkg/target" @@ -42,10 +43,18 @@ type KustTestHarness struct { } func NewKustTestHarness(t *testing.T, path string) *KustTestHarness { + return NewKustTestHarnessWithPluginConfig( + t, path, plugin.DefaultPluginConfig()) +} + +func NewKustTestHarnessWithPluginConfig( + t *testing.T, path string, + config types.PluginConfig) *KustTestHarness { return &KustTestHarness{ t: t, rf: resmap.NewFactory(resource.NewFactory( - kunstruct.NewKunstructuredFactoryImpl())), + kunstruct.NewKunstructuredFactoryWithGeneratorArgs( + &types.GeneratorMetaArgs{PluginConfig: config}))), ldr: loadertest.NewFakeLoader(path)} } @@ -66,7 +75,7 @@ func (th *KustTestHarness) writeF(dir string, content string) { } func (th *KustTestHarness) writeK(dir string, content string) { - th.writeF(filepath.Join(dir, constants.KustomizationFileNames[0]), ` + th.writeF(filepath.Join(dir, pgmconfig.KustomizationFileNames[0]), ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization `+content) diff --git a/pkg/target/transformersimage_test.go b/pkg/target/transformersimage_test.go new file mode 100644 index 000000000..7d09bdf49 --- /dev/null +++ b/pkg/target/transformersimage_test.go @@ -0,0 +1,170 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package target_test + +import ( + "testing" +) + +func makeTransfomersImageBase(th *KustTestHarness) { + th.writeK("/app/base", ` +resources: +- deploy1.yaml +- random.yaml +images: +- name: nginx + newTag: v2 +- name: my-nginx + newTag: previous +- name: myprivaterepohostname:1234/my/image + newTag: v1.0.1 +- name: foobar + digest: sha256:24a0c4b4 +- name: alpine + newName: myprivaterepohostname:1234/my/cool-alpine +- name: gcr.io:8080/my-project/my-cool-app + newName: my-cool-app +- name: postgres + newName: my-postgres + newTag: v3 +- name: docker + newName: my-docker + digest: sha256:25a0d4b4 +`) + th.writeF("/app/base/deploy1.yaml", ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + initContainers: + - name: nginx2 + image: my-nginx:1.8.0 + - name: init-alpine + image: alpine:1.8.0 + containers: + - name: ngnix + image: nginx:1.7.9 + - name: repliaced-with-digest + image: foobar:1 + - name: postgresdb + image: postgres:1.8.0 +`) + th.writeF("/app/base/random.yaml", ` +kind: randomKind +metadata: + name: random +spec: + template: + spec: + containers: + - name: ngnix1 + image: nginx +spec2: + template: + spec: + containers: + - name: nginx3 + image: nginx:v1 + - name: nginx4 + image: my-nginx:latest +spec3: + template: + spec: + initContainers: + - name: postgresdb + image: postgres:alpine-9 + - name: init-docker + image: docker:17-git + - name: myImage + image: myprivaterepohostname:1234/my/image:latest + - name: myImage2 + image: myprivaterepohostname:1234/my/image + - name: my-app + image: my-app-image:v1 + - name: my-cool-app + image: gcr.io:8080/my-project/my-cool-app:latest +`) +} + +func TestTransfomersImageDefaultConfig(t *testing.T) { + th := NewKustTestHarness(t, "/app/base") + makeTransfomersImageBase(th) + m, err := th.makeKustTarget().MakeCustomizedResMap() + if err != nil { + t.Fatalf("Err: %v", err) + } + th.assertActualEqualsExpected(m, ` +apiVersion: v1 +group: apps +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:v2 + name: ngnix + - image: foobar@sha256:24a0c4b4 + name: repliaced-with-digest + - image: my-postgres:v3 + name: postgresdb + initContainers: + - image: my-nginx:previous + name: nginx2 + - image: myprivaterepohostname:1234/my/cool-alpine:1.8.0 + name: init-alpine +--- +kind: randomKind +metadata: + name: random +spec: + template: + spec: + containers: + - image: nginx:v2 + name: ngnix1 +spec2: + template: + spec: + containers: + - image: nginx:v2 + name: nginx3 + - image: my-nginx:previous + name: nginx4 +spec3: + template: + spec: + initContainers: + - image: my-postgres:v3 + name: postgresdb + - image: my-docker@sha256:25a0d4b4 + name: init-docker + - image: myprivaterepohostname:1234/my/image:v1.0.1 + name: myImage + - image: myprivaterepohostname:1234/my/image:v1.0.1 + name: myImage2 + - image: my-app-image:v1 + name: my-app + - image: my-cool-app:latest + name: my-cool-app +`) +} diff --git a/pkg/transformers/config/defaultconfig/varreference.go b/pkg/transformers/config/defaultconfig/varreference.go index 77bffbeda..5f89dfd4f 100644 --- a/pkg/transformers/config/defaultconfig/varreference.go +++ b/pkg/transformers/config/defaultconfig/varreference.go @@ -19,98 +19,77 @@ package defaultconfig const ( varReferenceFieldSpecs = ` varReference: -- path: spec/template/spec/initContainers/command - kind: StatefulSet - -- path: spec/template/spec/containers/command - kind: StatefulSet - -- path: spec/template/spec/initContainers/command - kind: Deployment - -- path: spec/template/spec/containers/command - kind: Deployment - -- path: spec/template/spec/initContainers/command - kind: DaemonSet - -- path: spec/template/spec/containers/command - kind: DaemonSet - -- path: spec/template/spec/containers/command - kind: Job +- path: spec/jobTemplate/spec/template/spec/containers/args + kind: CronJob - path: spec/jobTemplate/spec/template/spec/containers/command kind: CronJob -- path: spec/template/spec/initContainers/args - kind: StatefulSet - -- path: spec/template/spec/containers/args - kind: StatefulSet - -- path: spec/template/spec/initContainers/args - kind: Deployment - -- path: spec/template/spec/containers/args - kind: Deployment - -- path: spec/template/spec/initContainers/args - kind: DaemonSet - -- path: spec/template/spec/containers/args - kind: DaemonSet - -- path: spec/template/spec/containers/args - kind: Job - -- path: spec/jobTemplate/spec/template/spec/containers/args - kind: CronJob - -- path: spec/template/spec/initContainers/env/value - kind: StatefulSet - -- path: spec/template/spec/containers/env/value - kind: StatefulSet - -- path: spec/template/spec/initContainers/env/value - kind: Deployment - -- path: spec/template/spec/containers/env/value - kind: Deployment - -- path: spec/template/spec/initContainers/env/value - kind: DaemonSet - -- path: spec/template/spec/containers/env/value - kind: DaemonSet - -- path: spec/template/spec/containers/env/value - kind: Job - -- path: spec/template/spec/initContainers/env/value - kind: Job - - path: spec/jobTemplate/spec/template/spec/containers/env/value kind: CronJob -- path: spec/containers/command - kind: Pod +- path: spec/jobTemplate/spec/template/spec/containers/volumeMounts/mountPath + kind: CronJob -- path: spec/containers/args - kind: Pod +- path: spec/jobTemplate/spec/template/spec/initContainers/args + kind: CronJob -- path: spec/containers/env/value - kind: Pod +- path: spec/jobTemplate/spec/template/spec/initContainers/command + kind: CronJob -- path: spec/initContainers/command - kind: Pod +- path: spec/jobTemplate/spec/template/spec/initContainers/env/value + kind: CronJob -- path: spec/initContainers/args - kind: Pod +- path: spec/jobTemplate/spec/template/spec/initContainers/volumeMounts/mountPath + kind: CronJob -- path: spec/initContainers/env/value - kind: Pod +- path: spec/template/spec/containers/args + kind: DaemonSet + +- path: spec/template/spec/containers/command + kind: DaemonSet + +- path: spec/template/spec/containers/env/value + kind: DaemonSet + +- path: spec/template/spec/containers/volumeMounts/mountPath + kind: DaemonSet + +- path: spec/template/spec/initContainers/args + kind: DaemonSet + +- path: spec/template/spec/initContainers/command + kind: DaemonSet + +- path: spec/template/spec/initContainers/env/value + kind: DaemonSet + +- path: spec/template/spec/initContainers/volumeMounts/mountPath + kind: DaemonSet + +- path: spec/template/spec/containers/args + kind: Deployment + +- path: spec/template/spec/containers/command + kind: Deployment + +- path: spec/template/spec/containers/env/value + kind: Deployment + +- path: spec/template/spec/containers/volumeMounts/mountPath + kind: Deployment + +- path: spec/template/spec/initContainers/args + kind: Deployment + +- path: spec/template/spec/initContainers/command + kind: Deployment + +- path: spec/template/spec/initContainers/env/value + kind: Deployment + +- path: spec/template/spec/initContainers/volumeMounts/mountPath + kind: Deployment - path: spec/rules/host kind: Ingress @@ -118,47 +97,101 @@ varReference: - path: spec/tls/hosts kind: Ingress +- path: spec/template/spec/containers/args + kind: Job + +- path: spec/template/spec/containers/command + kind: Job + +- path: spec/template/spec/containers/env/value + kind: Job + - path: spec/template/spec/containers/volumeMounts/mountPath - kind: StatefulSet + kind: Job + +- path: spec/template/spec/initContainers/args + kind: Job + +- path: spec/template/spec/initContainers/command + kind: Job + +- path: spec/template/spec/initContainers/env/value + kind: Job - path: spec/template/spec/initContainers/volumeMounts/mountPath - kind: StatefulSet + kind: Job + +- path: spec/containers/args + kind: Pod + +- path: spec/containers/command + kind: Pod + +- path: spec/containers/env/value + kind: Pod - path: spec/containers/volumeMounts/mountPath kind: Pod +- path: spec/initContainers/args + kind: Pod + +- path: spec/initContainers/command + kind: Pod + +- path: spec/initContainers/env/value + kind: Pod + - path: spec/initContainers/volumeMounts/mountPath kind: Pod +- path: spec/template/spec/containers/args + kind: ReplicaSet + +- path: spec/template/spec/containers/command + kind: ReplicaSet + +- path: spec/template/spec/containers/env/value + kind: ReplicaSet + - path: spec/template/spec/containers/volumeMounts/mountPath kind: ReplicaSet +- path: spec/template/spec/initContainers/args + kind: ReplicaSet + +- path: spec/template/spec/initContainers/command + kind: ReplicaSet + +- path: spec/template/spec/initContainers/env/value + kind: ReplicaSet + - path: spec/template/spec/initContainers/volumeMounts/mountPath kind: ReplicaSet -- path: spec/template/spec/containers/volumeMounts/mountPath - kind: Job +- path: spec/template/spec/containers/args + kind: StatefulSet -- path: spec/template/spec/initContainers/volumeMounts/mountPath - kind: Job +- path: spec/template/spec/containers/command + kind: StatefulSet + +- path: spec/template/spec/containers/env/value + kind: StatefulSet - path: spec/template/spec/containers/volumeMounts/mountPath - kind: CronJob + kind: StatefulSet + +- path: spec/template/spec/initContainers/args + kind: StatefulSet + +- path: spec/template/spec/initContainers/command + kind: StatefulSet + +- path: spec/template/spec/initContainers/env/value + kind: StatefulSet - path: spec/template/spec/initContainers/volumeMounts/mountPath - kind: CronJob - -- path: spec/template/spec/containers/volumeMounts/mountPath - kind: DaemonSet - -- path: spec/template/spec/initContainers/volumeMounts/mountPath - kind: DaemonSet - -- path: spec/template/spec/containers/volumeMounts/mountPath - kind: Deployment - -- path: spec/template/spec/initContainers/volumeMounts/mountPath - kind: Deployment + kind: StatefulSet - path: metadata/labels ` diff --git a/pkg/transformers/image_test.go b/pkg/transformers/image_test.go deleted file mode 100644 index c62ba3432..000000000 --- a/pkg/transformers/image_test.go +++ /dev/null @@ -1,266 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package transformers - -import ( - "reflect" - "testing" - - "sigs.k8s.io/kustomize/k8sdeps/kunstruct" - "sigs.k8s.io/kustomize/pkg/gvk" - "sigs.k8s.io/kustomize/pkg/image" - "sigs.k8s.io/kustomize/pkg/resid" - "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/resource" -) - -func TestImageTransformer(t *testing.T) { - var rf = resource.NewFactory( - kunstruct.NewKunstructuredFactoryImpl()) - - m := resmap.ResMap{ - resid.NewResId(deploy, "deploy1"): rf.FromMap( - map[string]interface{}{ - "group": "apps", - "apiVersion": "v1", - "kind": "Deployment", - "metadata": map[string]interface{}{ - "name": "deploy1", - }, - "spec": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "initContainers": []interface{}{ - map[string]interface{}{ - "name": "nginx2", - "image": "my-nginx:1.8.0", - }, - map[string]interface{}{ - "name": "init-alpine", - "image": "alpine:1.8.0", - }, - }, - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx", - "image": "nginx:1.7.9", - }, - map[string]interface{}{ - "name": "replaced-with-digest", - "image": "foobar:1", - }, - map[string]interface{}{ - "name": "postgresdb", - "image": "postgres:1.8.0", - }, - }, - }, - }, - }, - }), - resid.NewResId(gvk.Gvk{Kind: "randomKind"}, "random"): rf.FromMap( - map[string]interface{}{ - "spec": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx1", - "image": "nginx", - }, - }, - }, - }, - }, - "spec2": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx3", - "image": "nginx:v1", - }, - map[string]interface{}{ - "name": "nginx4", - "image": "my-nginx:latest", - }, - }, - }, - }, - }, - "spec3": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "initContainers": []interface{}{ - map[string]interface{}{ - "name": "postgresdb", - "image": "postgres:alpine-9", - }, - map[string]interface{}{ - "name": "init-docker", - "image": "docker:17-git", - }, - map[string]interface{}{ - "name": "myimage", - "image": "myprivaterepohostname:1234/my/image:latest", - }, - map[string]interface{}{ - "name": "myimage2", - "image": "myprivaterepohostname:1234/my/image", - }, - map[string]interface{}{ - "name": "my-app", - "image": "my-app-image:v1", - }, - map[string]interface{}{ - "name": "my-cool-app", - "image": "gcr.io:8080/my-project/my-cool-app:latest", - }, - }, - }, - }, - }, - }), - } - expected := resmap.ResMap{ - resid.NewResId(deploy, "deploy1"): rf.FromMap( - map[string]interface{}{ - "group": "apps", - "apiVersion": "v1", - "kind": "Deployment", - "metadata": map[string]interface{}{ - "name": "deploy1", - }, - "spec": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "initContainers": []interface{}{ - map[string]interface{}{ - "name": "nginx2", - "image": "my-nginx:previous", - }, - map[string]interface{}{ - "name": "init-alpine", - "image": "myprivaterepohostname:1234/my/cool-alpine:1.8.0", - }, - }, - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx", - "image": "nginx:v2", - }, - map[string]interface{}{ - "name": "replaced-with-digest", - "image": "foobar@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3", - }, - map[string]interface{}{ - "name": "postgresdb", - "image": "my-postgres:v3", - }, - }, - }, - }, - }, - }), - resid.NewResId(gvk.Gvk{Kind: "randomKind"}, "random"): rf.FromMap( - map[string]interface{}{ - "spec": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx1", - "image": "nginx:v2", - }, - }, - }, - }, - }, - "spec2": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx3", - "image": "nginx:v2", - }, - map[string]interface{}{ - "name": "nginx4", - "image": "my-nginx:previous", - }, - }, - }, - }, - }, - "spec3": map[string]interface{}{ - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "initContainers": []interface{}{ - map[string]interface{}{ - "name": "postgresdb", - "image": "my-postgres:v3", - }, - map[string]interface{}{ - "name": "init-docker", - "image": "my-docker@sha256:25a0d4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3", - }, - map[string]interface{}{ - "name": "myimage", - "image": "myprivaterepohostname:1234/my/image:v1.0.1", - }, - map[string]interface{}{ - "name": "myimage2", - "image": "myprivaterepohostname:1234/my/image:v1.0.1", - }, - map[string]interface{}{ - "name": "my-app", - "image": "gcr.io/my-project/my-app-image:v1", - }, - map[string]interface{}{ - "name": "my-cool-app", - "image": "my-cool-app:latest", - }, - }, - }, - }, - }, - }), - } - - it, err := NewImageTransformer([]image.Image{ - {Name: "nginx", NewTag: "v2"}, - {Name: "my-nginx", NewTag: "previous"}, - {Name: "myprivaterepohostname:1234/my/image", NewTag: "v1.0.1"}, - {Name: "foobar", Digest: "sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"}, - {Name: "alpine", NewName: "myprivaterepohostname:1234/my/cool-alpine"}, - {Name: "my-app-image", NewName: "gcr.io/my-project/my-app-image"}, - {Name: "gcr.io:8080/my-project/my-cool-app", NewName: "my-cool-app"}, - {Name: "postgres", NewName: "my-postgres", NewTag: "v3"}, - {Name: "docker", NewName: "my-docker", Digest: "sha256:25a0d4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"}, - }) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - err = it.Transform(m) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(m, expected) { - err = expected.ErrorIfNotEqual(m) - t.Fatalf("actual doesn't match expected: %v. Actual %+v", err, m) - } -} diff --git a/pkg/types/kustomization.go b/pkg/types/kustomization.go index c92867422..39a08700d 100644 --- a/pkg/types/kustomization.go +++ b/pkg/types/kustomization.go @@ -194,6 +194,25 @@ type GeneratorArgs struct { KVSources []KVSource `json:",inline,omitempty" yaml:",inline,omitempty"` } +// GeneratorMetaArgs contains arguments common to generators +// that come from somewhere other than a kustomization file. +type GeneratorMetaArgs struct { + PluginConfig PluginConfig +} + +// PluginConfig holds plugin configuration. +type PluginConfig struct { + // DirectoryPath is an absolute path to a + // directory containing kustomize plugins. + // This directory may contain subdirectories + // further categorizing plugins. + DirectoryPath string + + // GoEnabled is true if goplugins are enabled. + // See https://golang.org/pkg/plugin + GoEnabled bool +} + // ConfigMapArgs contains the metadata of how to generate a configmap. type ConfigMapArgs struct { // GeneratorArgs for the configmap.