diff --git a/cmd/config/configcobra/cfg.go b/cmd/config/configcobra/cfg.go index f4f84dd6d8..83a3276e3b 100644 --- a/cmd/config/configcobra/cfg.go +++ b/cmd/config/configcobra/cfg.go @@ -14,18 +14,9 @@ func GetCfg(name string) *cobra.Command { Short: "Commands for reading and writing configuration", } - cmd.AddCommand(commands.AnnotateCommand(name)) cmd.AddCommand(commands.CatCommand(name)) cmd.AddCommand(commands.CountCommand(name)) - cmd.AddCommand(commands.CreateSetterCommand(name)) - cmd.AddCommand(commands.CreateSubstitutionCommand(name)) - cmd.AddCommand(commands.FmtCommand(name)) cmd.AddCommand(commands.GrepCommand(name)) - cmd.AddCommand(commands.InitCommand(name)) - cmd.AddCommand(commands.ListSettersCommand(name)) - cmd.AddCommand(commands.MergeCommand(name)) - cmd.AddCommand(commands.Merge3Command(name)) - cmd.AddCommand(commands.SetCommand(name)) cmd.AddCommand(commands.TreeCommand(name)) return cmd diff --git a/cmd/config/configcobra/cmds.go b/cmd/config/configcobra/cmds.go index 0a46b03c7a..dd56a0b694 100644 --- a/cmd/config/configcobra/cmds.go +++ b/cmd/config/configcobra/cmds.go @@ -14,27 +14,14 @@ import ( ) // Export commands publicly for composition +// +//nolint:gochecknoglobals var ( - Annotate = commands.AnnotateCommand - Cat = commands.CatCommand - Count = commands.CountCommand - CreateSetter = commands.CreateSetterCommand - CreateSubstitution = commands.CreateSubstitutionCommand - DeleteSetter = commands.DeleteSetterCommand - DeleteSubstitution = commands.DeleteSubstitutionCommand - Fmt = commands.FmtCommand - Grep = commands.GrepCommand - Init = commands.InitCommand - ListSetters = commands.ListSettersCommand - Merge = commands.MergeCommand - Merge3 = commands.Merge3Command - RunFn = commands.RunCommand - Set = commands.SetCommand - Sink = commands.SinkCommand - Source = commands.SourceCommand - Tree = commands.TreeCommand - Wrap = commands.WrapCommand - XArgs = commands.XArgsCommand + Cat = commands.CatCommand + Count = commands.CountCommand + Grep = commands.GrepCommand + RunFn = commands.RunCommand + Tree = commands.TreeCommand StackOnError = &runner.StackOnError ExitOnError = &runner.ExitOnError diff --git a/cmd/config/configcobra/fn.go b/cmd/config/configcobra/fn.go index 3c6710e2ce..89c0d90fa2 100644 --- a/cmd/config/configcobra/fn.go +++ b/cmd/config/configcobra/fn.go @@ -15,10 +15,5 @@ func GetFn(name string) *cobra.Command { } cmd.AddCommand(commands.RunCommand(name)) - cmd.AddCommand(commands.SinkCommand(name)) - cmd.AddCommand(commands.SourceCommand(name)) - cmd.AddCommand(commands.WrapCommand()) - cmd.AddCommand(commands.XArgsCommand()) - return cmd } diff --git a/cmd/config/internal/commands/annotate.go b/cmd/config/internal/commands/annotate.go deleted file mode 100644 index 43da1eb92e..0000000000 --- a/cmd/config/internal/commands/annotate.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "io" - "strings" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/errors" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -// NewAnnotateRunner returns a command runner. -func NewAnnotateRunner(parent string) *AnnotateRunner { - r := &AnnotateRunner{} - c := &cobra.Command{ - Use: "annotate [DIR]", - Args: cobra.MaximumNArgs(1), - Short: commands.AnnotateShort, - Long: commands.AnnotateLong, - Example: commands.AnnotateExamples, - RunE: r.runE, - Deprecated: "use the `commonAnnotations` field in your kustomization file.", - } - runner.FixDocs(parent, c) - r.Command = c - c.Flags().StringVar(&r.Kind, "kind", "", "Resource kind to annotate") - c.Flags().StringVar(&r.ApiVersion, "apiVersion", "", "Resource apiVersion to annotate") - c.Flags().StringVar(&r.Name, "name", "", "Resource name to annotate") - c.Flags().StringVar(&r.Namespace, "namespace", "", "Resource namespace to annotate") - c.Flags().StringSliceVar(&r.Values, "kv", []string{}, "annotation as KEY=VALUE") - c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false, - "add annotations recursively in all the nested subpackages") - return r -} - -func AnnotateCommand(parent string) *cobra.Command { - return NewAnnotateRunner(parent).Command -} - -type AnnotateRunner struct { - Command *cobra.Command - Values []string - Kind string - Name string - ApiVersion string - Namespace string - Path string - RecurseSubPackages bool -} - -func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error { - var input []kio.Reader - var output []kio.Writer - if len(args) == 0 { - rw := &kio.ByteReadWriter{Reader: c.InOrStdin(), Writer: c.OutOrStdout()} - input = []kio.Reader{rw} - output = []kio.Writer{rw} - - return runner.HandleError(c, kio.Pipeline{ - Inputs: input, - Filters: []kio.Filter{r}, - Outputs: output, - }.Execute()) - } - - e := runner.ExecuteCmdOnPkgs{ - Writer: c.OutOrStdout(), - NeedOpenAPI: false, - RecurseSubPackages: r.RecurseSubPackages, - CmdRunner: r, - RootPkgPath: args[0], - } - - err := e.Execute() - if err != nil { - return err - } - return nil -} - -func (r *AnnotateRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - rw := &kio.LocalPackageReadWriter{ - PackagePath: pkgPath, - NoDeleteFiles: true, - PackageFileName: ext.KRMFileName(), - } - input := []kio.Reader{rw} - output := []kio.Writer{rw} - err := kio.Pipeline{ - Inputs: input, - Filters: []kio.Filter{r}, - Outputs: output, - }.Execute() - if err != nil { - // return err if there is only package - if !r.RecurseSubPackages { - return err - } - // print error message and continue if there are multiple packages to annotate - _, _ = fmt.Fprintf(w, "%s\n", err.Error()) - } else { - _, _ = fmt.Fprint(w, "added annotations in the package\n") - } - return nil -} - -func (r *AnnotateRunner) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { - for i := range nodes { - n := nodes[i] - m, err := n.GetMeta() - if err != nil { - return nil, err - } - if r.Kind != "" && r.Kind != m.Kind { - continue - } - if r.ApiVersion != "" && r.ApiVersion != m.APIVersion { - continue - } - if r.Namespace != "" && r.Namespace != m.Namespace { - continue - } - if r.Name != "" && r.Name != m.Name { - continue - } - - for i := range r.Values { - // split key, value pairs - kv := strings.SplitN(r.Values[i], "=", 2) - if len(kv) != 2 { - return nil, errors.Errorf("must specify --kv as KEY=VALUE: %s", r.Values[i]) - } - if err := n.PipeE(yaml.SetAnnotation(kv[0], kv[1])); err != nil { - return nil, err - } - } - } - return nodes, nil -} diff --git a/cmd/config/internal/commands/annotate_test.go b/cmd/config/internal/commands/annotate_test.go deleted file mode 100644 index e214284969..0000000000 --- a/cmd/config/internal/commands/annotate_test.go +++ /dev/null @@ -1,605 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/kyaml/copyutil" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestAnnotateCommand(t *testing.T) { - var tests = []struct { - name string - args []string - expected string - }{ - { - name: "single value", - args: []string{"--kv", "a=b"}, - expected: expectedSingleValue, - }, - { - name: "multi value", - args: []string{"--kv", "a=b", "--kv", "c=d"}, - expected: expectedMultiValue, - }, - { - name: "filter kind", - args: []string{"--kv", "a=b", "--kind", "Service"}, - expected: expectedFilterKindService, - }, - { - name: "filter apiVersion", - args: []string{"--kv", "a=b", "--apiVersion", "v1"}, - expected: expectedFilterApiVersionV1, - }, - { - name: "filter name", - args: []string{"--kv", "a=b", "--name", "bar"}, - expected: expectedFilterNameBar, - }, - { - name: "filter namespace", - args: []string{"--kv", "a=b", "--namespace", "bar"}, - expected: expectedFilterNamespaceBar, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - d := initTestDir(t) - - a := NewAnnotateRunner("") - a.Command.SetArgs(append([]string{d}, tt.args...)) - a.Command.SilenceUsage = true - a.Command.SilenceErrors = true - - err := a.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - actual := &bytes.Buffer{} - err = kio.Pipeline{ - Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: d}}, - Outputs: []kio.Writer{kio.ByteWriter{Writer: actual, KeepReaderAnnotations: true}}, - }.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(tt.expected), - strings.TrimSpace(actual.String())) { - t.FailNow() - } - }) - } -} - -func initTestDir(t *testing.T) string { - t.Helper() - d := t.TempDir() - err := os.WriteFile(filepath.Join(d, "f1.yaml"), []byte(f1Input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - err = os.WriteFile(filepath.Join(d, "f2.yaml"), []byte(f2Input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - return d -} - -var ( - f1Input = ` -kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx -spec: - selector: - app: nginx -` - - f2Input = ` -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - configFn: - container: - image: gcr.io/example/reconciler:v1 - annotations: - config.kubernetes.io/local-config: "true" - namespace: bar -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - namespace: foo -spec: - replicas: 3 -` - - expectedSingleValue = `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - a: 'b' - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx - a: 'b' - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - selector: - app: nginx ---- -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - configFn: - container: - image: gcr.io/example/reconciler:v1 - annotations: - config.kubernetes.io/local-config: "true" - a: 'b' - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: bar -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - a: 'b' - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: foo -spec: - replicas: 3 -` - - expectedMultiValue = `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - a: 'b' - c: 'd' - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx - a: 'b' - c: 'd' - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - selector: - app: nginx ---- -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - configFn: - container: - image: gcr.io/example/reconciler:v1 - annotations: - config.kubernetes.io/local-config: "true" - a: 'b' - c: 'd' - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: bar -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - a: 'b' - c: 'd' - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: foo -spec: - replicas: 3 -` - - expectedFilterKindService = `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx - a: 'b' - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - selector: - app: nginx ---- -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - configFn: - container: - image: gcr.io/example/reconciler:v1 - annotations: - config.kubernetes.io/local-config: "true" - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: bar -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: foo -spec: - replicas: 3 -` - - expectedFilterApiVersionV1 = `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - selector: - app: nginx ---- -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - configFn: - container: - image: gcr.io/example/reconciler:v1 - annotations: - config.kubernetes.io/local-config: "true" - a: 'b' - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: bar -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: foo -spec: - replicas: 3 -` - - expectedFilterNameBar = `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - selector: - app: nginx ---- -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - configFn: - container: - image: gcr.io/example/reconciler:v1 - annotations: - config.kubernetes.io/local-config: "true" - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: bar -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - a: 'b' - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: foo -spec: - replicas: 3 -` - - expectedFilterNamespaceBar = `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f1.yaml' -spec: - selector: - app: nginx ---- -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - configFn: - container: - image: gcr.io/example/reconciler:v1 - annotations: - config.kubernetes.io/local-config: "true" - a: 'b' - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: bar -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f2.yaml' - namespace: foo -spec: - replicas: 3 -` -) - -func TestAnnotateSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - packagePath string - args []string - expected string - }{ - { - name: "annotate-recurse-subpackages", - dataset: "dataset-without-setters", - args: []string{"--kv", "foo=bar", "-R"}, - expected: `${baseDir}/ -added annotations in the package - -${baseDir}/mysql/ -added annotations in the package - -${baseDir}/mysql/storage/ -added annotations in the package -`, - }, - { - name: "annotate-top-level-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql", - args: []string{"--kv", "foo=bar"}, - expected: `${baseDir}/mysql/ -added annotations in the package -`, - }, - { - name: "annotate-nested-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql/storage", - args: []string{"--kv", "foo=bar"}, - expected: `${baseDir}/mysql/storage/ -added annotations in the package -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - sourceDir := filepath.Join("test", "testdata", test.dataset) - baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) - runner := NewAnnotateRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(actual.String(), "\\", "/"), - "//", "/") - - expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expected, "\\", "/") - if !assert.Contains(t, actualNormalized, expectedNormalized) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/cmdcreatesetter.go b/cmd/config/internal/commands/cmdcreatesetter.go deleted file mode 100644 index 45952845d6..0000000000 --- a/cmd/config/internal/commands/cmdcreatesetter.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/spf13/cobra" - "k8s.io/kube-openapi/pkg/validation/spec" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/errors" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" -) - -// NewCreateSetterRunner returns a command runner. -func NewCreateSetterRunner(parent string) *CreateSetterRunner { - r := &CreateSetterRunner{} - set := &cobra.Command{ - Use: "create-setter DIR NAME VALUE", - Args: cobra.RangeArgs(2, 3), - Short: commands.CreateSetterShort, - Long: commands.CreateSetterLong, - Example: commands.CreateSetterExamples, - PreRunE: r.preRunE, - RunE: r.runE, - Deprecated: "setter commands will no longer be available in kustomize v5.\n" + - "See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.", - } - set.Flags().StringVar(&r.FieldValue, "value", "", - "optional flag, alternative to specifying the value as an argument. e.g. used to specify values that start with '-'") - set.Flags().StringVar(&r.SetBy, "set-by", "", - "record who the field was default by.") - set.Flags().StringVar(&r.Description, "description", "", - "record a description for the current setter value.") - set.Flags().StringVar(&r.FieldName, "field", "", - "name of the field to set, a suffix of the path to the field, or the full"+ - " path to the field. Default is to match all fields.") - set.Flags().StringVar(&r.Type, "type", "", - "OpenAPI field type for the setter -- e.g. integer,boolean,string.") - set.Flags().BoolVar(&r.Required, "required", false, - "indicates that this setter must be set by package consumer before live apply/preview") - set.Flags().StringVar(&r.SchemaPath, "schema-path", "", - `openAPI schema file path for setter constraints -- file content `+ - `e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`) - set.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false, - "creates setter recursively in all the nested subpackages") - set.Flags().MarkHidden("version") - runner.FixDocs(parent, set) - r.Command = set - return r -} - -func CreateSetterCommand(parent string) *cobra.Command { - return NewCreateSetterRunner(parent).Command -} - -type CreateSetterRunner struct { - Command *cobra.Command - CreateSetter settersutil.SetterCreator - OpenAPIFile string - SchemaPath string - FieldValue string - SetBy string - Description string - SetterName string - Type string - FieldName string - Schema string - Required bool - RecurseSubPackages bool -} - -func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error { - return runner.HandleError(c, r.createSetter(c, args)) -} - -func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error { - var err error - r.SetterName = args[1] - if len(args) > 2 { - r.FieldValue = args[2] - } - r.FieldName, err = c.Flags().GetString("field") - if err != nil { - return err - } - - if r.Type != "array" && !c.Flag("value").Changed && len(args) < 3 { - return errors.Errorf("setter name and value must be provided, " + - "value can either be an argument or can be passed as a flag --value") - } - - err = r.processSchema() - if err != nil { - return err - } - - if r.Type == "array" { - if !c.Flag("field").Changed { - return errors.Errorf("field flag must be set for array type setters") - } - } - return nil -} - -func (r *CreateSetterRunner) processSchema() error { - sc, err := schemaFromFile(r.SchemaPath) - if err != nil { - return err - } - - flagType := r.Type - var schemaType string - switch { - // json schema allows more than one type to be specified, but openapi - // only allows one. So we follow the openapi convention. - case len(sc.Type) > 1: - return errors.Errorf("only one type is supported: %s", - strings.Join(sc.Type, ", ")) - case len(sc.Type) == 1: - schemaType = sc.Type[0] - } - - // Since type can be set both through the schema file and through the - // --type flag, we make sure the same value is set in both places. If they - // are both set with different values, we return an error. - switch { - case flagType == "" && schemaType != "": - r.Type = schemaType - case flagType != "" && schemaType == "": - sc.Type = []string{flagType} - case flagType != "" && schemaType != "": - if flagType != schemaType { - return errors.Errorf("type provided in type flag (%s) and in schema (%s) doesn't match", - r.Type, sc.Type[0]) - } - } - - // Only marshal the properties in SchemaProps. This means any fields in - // the schema file that isn't recognized will be dropped. - // TODO: Consider if we should return an error here instead of just dropping - // the unknown fields. - b, err := json.Marshal(sc.SchemaProps) - if err != nil { - return errors.Errorf("error marshalling schema: %v", err) - } - r.Schema = string(b) - return nil -} - -func (r *CreateSetterRunner) createSetter(c *cobra.Command, args []string) error { - e := runner.ExecuteCmdOnPkgs{ - NeedOpenAPI: true, - Writer: c.OutOrStdout(), - RootPkgPath: args[0], - RecurseSubPackages: r.RecurseSubPackages, - CmdRunner: r, - } - err := e.Execute() - if err != nil { - return runner.HandleError(c, err) - } - return nil -} - -func (r *CreateSetterRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName())) - if err != nil { - return err - } - r.CreateSetter = settersutil.SetterCreator{ - Name: r.SetterName, - SetBy: r.SetBy, - Description: r.Description, - Type: r.Type, - Schema: r.Schema, - FieldName: r.FieldName, - FieldValue: r.FieldValue, - Required: r.Required, - RecurseSubPackages: r.RecurseSubPackages, - OpenAPIFileName: ext.KRMFileName(), - OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()), - ResourcesPath: pkgPath, - SettersSchema: sc, - } - - err = r.CreateSetter.Create() - if err != nil { - // return err if RecurseSubPackages is false - if !r.CreateSetter.RecurseSubPackages { - return err - } - // print error message and continue if RecurseSubPackages is true - fmt.Fprintf(w, "%s\n", err.Error()) - } else { - fmt.Fprintf(w, "created setter %q\n", r.CreateSetter.Name) - } - return nil -} - -// schemaFromFile reads the contents from schemaPath and returns schema -func schemaFromFile(schemaPath string) (*spec.Schema, error) { - sc := &spec.Schema{} - if schemaPath == "" { - return sc, nil - } - sch, err := os.ReadFile(schemaPath) - if err != nil { - return sc, err - } - - if len(sch) == 0 { - return sc, nil - } - - err = sc.UnmarshalJSON(sch) - if err != nil { - return sc, errors.Errorf("unable to parse schema: %v", err) - } - return sc, nil -} diff --git a/cmd/config/internal/commands/cmdcreatesetter_test.go b/cmd/config/internal/commands/cmdcreatesetter_test.go deleted file mode 100644 index d2a1ff02c4..0000000000 --- a/cmd/config/internal/commands/cmdcreatesetter_test.go +++ /dev/null @@ -1,871 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/copyutil" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestCreateSetterCommand(t *testing.T) { - var tests = []struct { - name string - input string - args []string - schema string - out string - inputOpenAPI string - expectedOpenAPI string - expectedResources string - err string - }{ - { - name: "add replicas", - args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "replicas"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - `, - }, - - { - name: "add replicas no match", - args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - foo: 2 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "replicas"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - foo: 2 - `, - }, - { - name: "error if substitution with same name exists", - args: []string{"my-image", "3", "--description", "hello world", "--set-by", "me"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.substitutions.my-image: - x-k8s-cli: - substitution: - name: my-image - pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - `, - err: `substitution with name "my-image" already exists, substitution and setter can't have same name`, - }, - - { - name: "error if setter with same name exists", - args: []string{ - "my-image", "ubuntu"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image: - x-k8s-cli: - setter: - name: my-image - value: "nginx" - `, - err: `setter with name "my-image" already exists, if you want to modify it, please delete the existing setter and recreate it`, - }, - - { - name: "add replicas with schema", - args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"}, - schema: `{"maximum": 10, "type": "integer"}`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "replicas"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - maximum: 10 - type: integer - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - `, - }, - - { - name: "add replicas not enough arguments", - args: []string{"replicas", "--description", "hello world", "--set-by", "me"}, - err: `setter name and value must be provided, value can either be an argument or can be passed as a flag --value`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter replicas`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - }, - - { - name: "list values with schema", - args: []string{"list", "--description", "hello world", "--set-by", "me", "--type", "array", "--field", "spec.list"}, - schema: `{"maxItems": 3, "type": "array", "items": {"type": "string"}}`, - input: ` -apiVersion: example.com/v1beta1 -kind: Example1 -spec: - list: - - "a" - - "b" - - "c" ---- -apiVersion: example.com/v1beta1 -kind: Example2 -spec: - list: - - "a" - - "b" - - "c" ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - namespace: myspace -spec: - replicas: 3 - template: - spec: - containers: - - name: sidecar - image: nginx:1.7.9 - - name: nginx - image: otherspace/nginx:1.7.9 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "list"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.list: - items: - type: string - maxItems: 3 - type: array - description: hello world - x-k8s-cli: - setter: - name: list - value: "" - listValues: - - a - - b - - c - setBy: me - `, - expectedResources: ` -apiVersion: example.com/v1beta1 -kind: Example1 -spec: - list: # {"$openapi":"list"} - - "a" - - "b" - - "c" ---- -apiVersion: example.com/v1beta1 -kind: Example2 -spec: - list: # {"$openapi":"list"} - - "a" - - "b" - - "c" ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - namespace: myspace -spec: - replicas: 3 - template: - spec: - containers: - - name: sidecar - image: nginx:1.7.9 - - name: nginx - image: otherspace/nginx:1.7.9 - `, - }, - - { - name: "error list path with different values", - args: []string{"list", "--description", "hello world", "--set-by", "me", "--type", "array", "--field", "spec.list"}, - schema: `{"maxItems": 3, "type": "array", "items": {"type": "string"}}`, - input: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: - - "a" - - "b" - - "c" ---- -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: - - "c" - - "d" - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - err: `setters can only be created for fields with same values, encountered different ` + - `array values for specified field path: [c d], [a b c]`, - }, - - { - name: "list values error if field not set", - args: []string{"list", "a", "--description", "hello world", "--set-by", "me", "--type", "array"}, - schema: `{"maxItems": 3, "type": "array", "items": {"type": "string"}}`, - input: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: - - "a" - - "b" - - "c" - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter replicas`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.list: - items: - type: string - maxItems: 3 - type: array - description: hello world - x-k8s-cli: - setter: - name: list - listValues: - - a - - b - - c - setBy: me - `, - expectedResources: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: # {"$openapi":"list"} - - "a" - - "b" - - "c" - `, - err: `field flag must be set for array type setters`, - }, - { - name: "add replicas with value set by flag", - args: []string{"replicas", "--value", "3", "--description", "hello world", "--set-by", "me"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "replicas"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - `, - }, - { - name: "add setter with . in the name", - args: []string{"foo.bar", "3"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "foo.bar"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.foo.bar: - x-k8s-cli: - setter: - name: foo.bar - value: "3" - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"foo.bar"} - `, - }, - { - name: "provide different type values in schema and with flag", - args: []string{"replicas", "3", "--description", "hello world", "--type", "string"}, - schema: `{"type": "integer"}`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - err: `type provided in type flag (string) and in schema (integer) doesn't match`, - }, - { - name: "invalid json in schema", - args: []string{"replicas", "3"}, - schema: `{"foo": bar`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - err: "unable to parse schema: invalid character 'b' looking for beginning of value", - }, - { - name: "unknown fields in schema are dropped", - args: []string{"replicas", "3"}, - schema: `{"foo": "bar"}`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "replicas"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - x-k8s-cli: - setter: - name: replicas - value: "3" - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - `, - }, - { - name: "unknown types are not accepted", - args: []string{"replicas", "3"}, - schema: `{"type": "int"}`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - err: `invalid schema: type "int" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`, - }, - { - name: "unknown types are not accepted in nested structures", - args: []string{"replicas", "3", "--field", "replicas"}, - schema: `{"maxItems": 3, "type": "array", "items": {"type": "foo"}}`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - err: `invalid schema: type "foo" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`, - }, - { - name: "unknown types are not accepted in --type flag", - args: []string{"replicas", "3", "--type", "bar"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - err: `invalid schema: type "bar" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`, - }, - { - name: "unknown properties in schema are dropped", - args: []string{"replicas", "3", "--type", "integer"}, - schema: `{"maximum": 3, "unknown": 42}`, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - out: `created setter "replicas"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - maximum: 3 - type: integer - x-k8s-cli: - setter: - name: replicas - value: "3" - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - `, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - - baseDir := t.TempDir() - - f := filepath.Join(baseDir, "Krmfile") - err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - if test.schema != "" { - sch, err := os.CreateTemp("", "schema.json") - if !assert.NoError(t, err) { - t.FailNow() - } - t.Cleanup(func() { - sch.Close() - os.Remove(sch.Name()) - }) - - err = os.WriteFile(sch.Name(), []byte(test.schema), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - test.args = append(test.args, "--schema-path", sch.Name()) - } - - r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml") - if !assert.NoError(t, err) { - t.FailNow() - } - t.Cleanup(func() { r.Close() }) - err = os.WriteFile(r.Name(), []byte(test.input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - runner := commands.NewCreateSetterRunner("") - out := &bytes.Buffer{} - runner.Command.SetOut(out) - runner.Command.SetArgs(append([]string{baseDir}, test.args...)) - err = runner.Command.Execute() - if test.err != "" { - if !assert.NotNil(t, err) { - t.FailNow() - } else { - assert.Equal(t, err.Error(), test.err) - return - } - } - if !assert.NoError(t, err) { - t.FailNow() - } - - expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expectedOut, "\\", "/") - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(out.String(), "\\", "/"), - "//", "/") - - if !assert.Contains(t, actualNormalized, expectedNormalized) { - t.FailNow() - } - - actualResources, err := os.ReadFile(r.Name()) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedResources), - strings.TrimSpace(string(actualResources))) { - t.FailNow() - } - - actualOpenAPI, err := os.ReadFile(f) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedOpenAPI), - strings.TrimSpace(string(actualOpenAPI))) { - t.FailNow() - } - }) - } -} - -func TestCreateSetterSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - packagePath string - args []string - expected string - }{ - { - name: "create-setter-recurse-subpackages", - dataset: "dataset-without-setters", - args: []string{"namespace", "myspace", "-R"}, - expected: `${baseDir}/mysql/ -created setter "namespace" - -${baseDir}/mysql/storage/ -created setter "namespace" -`, - }, - { - name: "create-setter-top-level-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql", - args: []string{"namespace", "myspace"}, - expected: `${baseDir}/mysql/ -created setter "namespace" -`, - }, - { - name: "create-setter-nested-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql/storage", - args: []string{"namespace", "myspace"}, - expected: `${baseDir}/mysql/storage/ -created setter "namespace" -`, - }, - { - name: "create-setter-already-exists", - dataset: "dataset-with-setters", - packagePath: "mysql", - args: []string{"namespace", "myspace", "-R"}, - expected: `${baseDir}/mysql/ -setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it - -${baseDir}/mysql/nosetters/ -created setter "namespace" - -${baseDir}/mysql/storage/ -setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - sourceDir := filepath.Join("test", "testdata", test.dataset) - baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) - runner := commands.NewCreateSetterRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(actual.String(), "\\", "/"), - "//", "/") - - expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expected, "\\", "/") - if !assert.Contains(t, actualNormalized, expectedNormalized) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/cmdcreatesubstitution.go b/cmd/config/internal/commands/cmdcreatesubstitution.go deleted file mode 100644 index bd03ea9c01..0000000000 --- a/cmd/config/internal/commands/cmdcreatesubstitution.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "io" - "path/filepath" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" -) - -// NewCreateSubstitutionRunner returns a command runner. -func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner { - r := &CreateSubstitutionRunner{} - cs := &cobra.Command{ - Use: "create-subst DIR NAME", - Args: cobra.ExactArgs(2), - PreRun: r.preRun, - RunE: r.runE, - Deprecated: "imperative substitutions will no longer be available in kustomize v5.\n" + - "See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.", - } - cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "", - "name of the field to set -- e.g. --field image") - cs.Flags().StringVar(&r.CreateSubstitution.FieldValue, "field-value", "", - "value of the field to create substitution for -- e.g. --field-value nginx:0.1.0") - cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "", - `substitution pattern -- e.g. --pattern \${my-image-setter}:\${my-tag-setter}`) - cs.Flags().BoolVarP(&r.CreateSubstitution.RecurseSubPackages, "recurse-subpackages", "R", false, - "creates substitution recursively in all the nested subpackages") - _ = cs.MarkFlagRequired("pattern") - _ = cs.MarkFlagRequired("field-value") - runner.FixDocs(parent, cs) - r.Command = cs - return r -} - -func CreateSubstitutionCommand(parent string) *cobra.Command { - return NewCreateSubstitutionRunner(parent).Command -} - -type CreateSubstitutionRunner struct { - Command *cobra.Command - CreateSubstitution settersutil.SubstitutionCreator - OpenAPIFile string - Values []string -} - -func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error { - e := runner.ExecuteCmdOnPkgs{ - NeedOpenAPI: true, - Writer: c.OutOrStdout(), - RootPkgPath: args[0], - RecurseSubPackages: r.CreateSubstitution.RecurseSubPackages, - CmdRunner: r, - } - err := e.Execute() - if err != nil { - return runner.HandleError(c, err) - } - - return nil -} - -func (r *CreateSubstitutionRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName())) - if err != nil { - return err - } - r.CreateSubstitution = settersutil.SubstitutionCreator{ - Name: r.CreateSubstitution.Name, - FieldName: r.CreateSubstitution.FieldName, - FieldValue: r.CreateSubstitution.FieldValue, - RecurseSubPackages: r.CreateSubstitution.RecurseSubPackages, - Pattern: r.CreateSubstitution.Pattern, - OpenAPIFileName: ext.KRMFileName(), - OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()), - ResourcesPath: pkgPath, - SettersSchema: sc, - } - - err = r.CreateSubstitution.Create() - if err != nil { - // return err if RecurseSubPackages is false - if !r.CreateSubstitution.RecurseSubPackages { - return err - } - // print error message and continue if RecurseSubPackages is true - fmt.Fprintf(w, "%s\n", err.Error()) - } else { - fmt.Fprintf(w, "created substitution %q\n", r.CreateSubstitution.Name) - } - return nil -} - -func (r *CreateSubstitutionRunner) preRun(c *cobra.Command, args []string) { - r.CreateSubstitution.Name = args[1] -} diff --git a/cmd/config/internal/commands/cmdcreatesubstitution_test.go b/cmd/config/internal/commands/cmdcreatesubstitution_test.go deleted file mode 100644 index 546981b972..0000000000 --- a/cmd/config/internal/commands/cmdcreatesubstitution_test.go +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/copyutil" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestCreateSubstitutionCommand(t *testing.T) { - var tests = []struct { - name string - inputOpenAPI string - input string - args []string - out string - expectedOpenAPI string - expectedResources string - err string - }{ - { - name: "substitution replicas", - args: []string{ - "my-image-subst", "--field-value", "nginx:1.7.9", "--pattern", "${my-image-setter}:${my-tag-setter}"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 - - name: sidecar - image: sidecar:1.7.9 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: "nginx" - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: "1.7.9" - `, - out: `created substitution "my-image-subst"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: "nginx" - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: "1.7.9" - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}:${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$openapi":"my-image-subst"} - - name: sidecar - image: sidecar:1.7.9 - `, - }, - { - name: "error if substitution with same name exists", - args: []string{"my-image", "--field-value", "some:image", "--pattern", "some:${image}"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.substitutions.my-image: - x-k8s-cli: - substitution: - name: my-image - pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - `, - err: `substitution with name "my-image" already exists`, - }, - { - name: "error if setter with same name exists", - args: []string{ - "my-image", "--field-value", "nginx:1.7.9", "--pattern", "${my-image-setter}:${my-tag-setter}"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image: - x-k8s-cli: - setter: - name: my-image - value: "nginx" - `, - err: `setter with name "my-image" already exists, substitution and setter can't have same name`, - }, - { - name: "substitution and create setters 1", - args: []string{ - "my-image-subst", "--field-value", "something/nginx::1.7.9/nginxotherthing", "--pattern", "something/${my-image-setter}::${my-tag-setter}/nginxotherthing"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing - - name: sidecar - image: sidecar:1.7.9 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - out: `created substitution "my-image-subst"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-image-subst"} - - name: sidecar - image: sidecar:1.7.9 - `, - }, - { - name: "nested substitution", - args: []string{ - "my-nested-subst", "--field-value", "something/nginx::1.7.9/nginxotherthing", - "--pattern", "something/${my-image-subst}/${my-other-setter}"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}::${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - `, - out: `created substitution "my-nested-subst"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}::${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} - `, - }, - { - name: "substitution with non-existing setter with same name", - args: []string{ - "foo", "--field-value", "prefix-1234", "--pattern", "prefix-${foo}"}, - input: ` -apiVersion: test/v1 -kind: Foo -metadata: - name: foo -spec: - setterVal: 1234 - substVal: prefix-1234 - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: test/v1 -kind: Foo -metadata: - name: foo -spec: - setterVal: 1234 - substVal: prefix-1234 - - `, - err: `setters must have different name than the substitution: foo`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - - baseDir := t.TempDir() - f := filepath.Join(baseDir, "Krmfile") - err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml") - if !assert.NoError(t, err) { - t.FailNow() - } - t.Cleanup(func() { r.Close() }) - err = os.WriteFile(r.Name(), []byte(test.input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - runner := commands.NewCreateSubstitutionRunner("") - out := &bytes.Buffer{} - runner.Command.SetOut(out) - runner.Command.SetArgs(append([]string{baseDir}, test.args...)) - err = runner.Command.Execute() - - if test.err != "" { - if !assert.NotNil(t, err) { - t.FailNow() - } - assert.Equal(t, test.err, err.Error()) - return - } - if !assert.NoError(t, err) { - t.FailNow() - } - - expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expectedOut, "\\", "/") - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(out.String(), "\\", "/"), - "//", "/") - - if !assert.Contains(t, actualNormalized, expectedNormalized) { - t.FailNow() - } - - actualResources, err := os.ReadFile(r.Name()) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedResources), - strings.TrimSpace(string(actualResources))) { - t.FailNow() - } - - actualOpenAPI, err := os.ReadFile(f) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedOpenAPI), - strings.TrimSpace(string(actualOpenAPI))) { - t.FailNow() - } - }) - } -} - -func TestCreateSubstSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - packagePath string - args []string - expected string - }{ - { - name: "create-subst-recurse-subpackages", - dataset: "dataset-without-setters", - args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"}, - expected: `${baseDir}/mysql/ -created substitution "image-tag" - -${baseDir}/mysql/storage/ -created substitution "image-tag" -`, - }, - { - name: "create-subst-top-level-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql", - args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}"}, - expected: `${baseDir}/mysql/ -created substitution "image-tag"`, - }, - { - name: "create-subst-nested-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql/storage", - args: []string{"image-tag", "--field-value", "storage:1.7.9", "--pattern", "${image}:${tag}"}, - expected: `${baseDir}/mysql/storage/ -created substitution "image-tag"`, - }, - { - name: "create-subst-already-exists", - dataset: "dataset-with-setters", - packagePath: "mysql", - args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"}, - expected: `${baseDir}/mysql/ -substitution with name "image-tag" already exists - -${baseDir}/mysql/nosetters/ -created substitution "image-tag" - -${baseDir}/mysql/storage/ -created substitution "image-tag"`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - sourceDir := filepath.Join("test", "testdata", test.dataset) - baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) - runner := commands.NewCreateSubstitutionRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(actual.String(), "\\", "/"), - "//", "/") - - expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expected, "\\", "/") - if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/cmddeletesetter.go b/cmd/config/internal/commands/cmddeletesetter.go deleted file mode 100644 index 222c1557a8..0000000000 --- a/cmd/config/internal/commands/cmddeletesetter.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "io" - "path/filepath" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/fieldmeta" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" -) - -// NewDeleteRunner returns a command runner. -func NewDeleteSetterRunner(parent string) *DeleteSetterRunner { - r := &DeleteSetterRunner{} - c := &cobra.Command{ - Use: "delete-setter DIR NAME", - Args: cobra.ExactArgs(2), - Short: commands.DeleteSetterShort, - Long: commands.DeleteSetterLong, - Example: commands.DeleteSetterExamples, - PreRunE: r.preRunE, - RunE: r.runE, - } - c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false, - "deletes setter recursively in all the nested subpackages") - runner.FixDocs(parent, c) - r.Command = c - - return r -} - -func DeleteSetterCommand(parent string) *cobra.Command { - return NewDeleteSetterRunner(parent).Command -} - -type DeleteSetterRunner struct { - Command *cobra.Command - DeleteSetter settersutil.DeleterCreator - OpenAPIFile string - RecurseSubPackages bool -} - -func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error { - r.DeleteSetter.Name = args[1] - r.DeleteSetter.DefinitionPrefix = fieldmeta.SetterDefinitionPrefix - - r.OpenAPIFile = filepath.Join(args[0], ext.KRMFileName()) - - return nil -} - -func (r *DeleteSetterRunner) runE(c *cobra.Command, args []string) error { - e := runner.ExecuteCmdOnPkgs{ - NeedOpenAPI: true, - Writer: c.OutOrStdout(), - RootPkgPath: args[0], - RecurseSubPackages: r.RecurseSubPackages, - CmdRunner: r, - } - err := e.Execute() - if err != nil { - return runner.HandleError(c, err) - } - return nil -} - -func (r *DeleteSetterRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName())) - if err != nil { - return err - } - r.DeleteSetter = settersutil.DeleterCreator{ - Name: r.DeleteSetter.Name, - DefinitionPrefix: fieldmeta.SetterDefinitionPrefix, - RecurseSubPackages: r.RecurseSubPackages, - OpenAPIFileName: ext.KRMFileName(), - OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()), - ResourcesPath: pkgPath, - SettersSchema: sc, - } - - err = r.DeleteSetter.Delete() - if err != nil { - // return err if RecurseSubPackages is false - if !r.DeleteSetter.RecurseSubPackages { - return err - } - // print error message and continue if RecurseSubPackages is true - fmt.Fprintf(w, "%s\n", err.Error()) - } else { - fmt.Fprintf(w, "deleted setter %q\n", r.DeleteSetter.Name) - } - return nil -} diff --git a/cmd/config/internal/commands/cmddeletesetter_test.go b/cmd/config/internal/commands/cmddeletesetter_test.go deleted file mode 100644 index 8722bb28ea..0000000000 --- a/cmd/config/internal/commands/cmddeletesetter_test.go +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/copyutil" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestDeleteSetterCommand(t *testing.T) { - var tests = []struct { - name string - input string - args []string - schema string - out string - inputOpenAPI string - expectedOpenAPI string - expectedResources string - err string - }{ - { - name: "delete replicas", - args: []string{"replicas-setter"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi" : "replicas-setter"} - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas-setter: - description: hello world - x-k8s-cli: - setter: - name: replicas-setter - value: "3" - setBy: me -`, - out: `deleted setter "replicas-setter"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - `, - }, - { - name: "delete only one setter", - args: []string{"replicas-setter"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi" : "replicas-setter"} - foo: nginx # {"$openapi" : "image"} - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas-setter: - description: hello world - x-k8s-cli: - setter: - name: replicas-setter - value: "3" - setBy: me - io.k8s.cli.setters.image: - x-k8s-cli: - setter: - name: image - value: nginx -`, - out: `deleted setter "replicas-setter"`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.image: - x-k8s-cli: - setter: - name: image - value: nginx - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - foo: nginx # {"$openapi" : "image"} - `, - }, - - { - name: "delete array setter", - args: []string{"list"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.list: - items: - type: string - maxItems: 3 - type: array - description: hello world - x-k8s-cli: - setter: - name: list - value: "" - listValues: - - a - - b - - c - setBy: me - `, - input: ` -apiVersion: example.com/v1beta1 -kind: Example1 -spec: - list: # {"$openapi":"list"} - - "a" - - "b" - - "c" ---- -apiVersion: example.com/v1beta1 -kind: Example2 -spec: - list: # {"$openapi":"list2"} - - "a" - - "b" - - "c" - `, - out: `deleted setter "list"`, - expectedResources: ` -apiVersion: example.com/v1beta1 -kind: Example1 -spec: - list: - - "a" - - "b" - - "c" ---- -apiVersion: example.com/v1beta1 -kind: Example2 -spec: - list: # {"$openapi":"list2"} - - "a" - - "b" - - "c" - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -`, - }, - - { - name: "delete non exist setter error", - args: []string{"image"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi" : "replicas-setter"} - `, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas-setter: - description: hello world - x-k8s-cli: - setter: - name: replicas-setter - value: "3" - setBy: me -`, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas-setter: - description: hello world - x-k8s-cli: - setter: - name: replicas-setter - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi" : "replicas-setter"} - `, - err: `setter "image" does not exist`, - }, - { - name: "delete setter used in substitution error", - args: []string{"image-name"}, - input: ` -apiVersion: apps/v1 -kind: Deployment - `, - inputOpenAPI: ` -openAPI: - definitions: - io.k8s.cli.setters.image-name: - x-k8s-cli: - setter: - name: image-name - value: "nginx" - io.k8s.cli.setters.image-tag: - x-k8s-cli: - setter: - name: image-tag - value: "1.8.1" - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE_NAME:IMAGE_TAG - values: - - marker: "IMAGE_NAME" - ref: "#/definitions/io.k8s.cli.setters.image-name" - - marker: "IMAGE_TAG" - ref: "#/definitions/io.k8s.cli.setters.image-tag" -`, - expectedOpenAPI: ` -openAPI: - definitions: - io.k8s.cli.setters.image-name: - x-k8s-cli: - setter: - name: image-name - value: "nginx" - io.k8s.cli.setters.image-tag: - x-k8s-cli: - setter: - name: image-tag - value: "1.8.1" - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE_NAME:IMAGE_TAG - values: - - marker: "IMAGE_NAME" - ref: "#/definitions/io.k8s.cli.setters.image-name" - - marker: "IMAGE_TAG" - ref: "#/definitions/io.k8s.cli.setters.image-tag" - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment - `, - err: `setter "image-name" is used in substitution "image", please delete the parent substitution first`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - - baseDir := t.TempDir() - f := filepath.Join(baseDir, "Krmfile") - err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml") - if !assert.NoError(t, err) { - t.FailNow() - } - t.Cleanup(func() { r.Close() }) - err = os.WriteFile(r.Name(), []byte(test.input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - runner := commands.NewDeleteSetterRunner("") - out := &bytes.Buffer{} - runner.Command.SetOut(out) - runner.Command.SetArgs(append([]string{baseDir}, test.args...)) - err = runner.Command.Execute() - - if test.err != "" { - if !assert.NotNil(t, err) { - t.FailNow() - } - assert.Equal(t, test.err, err.Error()) - return - } - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNorm := strings.ReplaceAll( - strings.ReplaceAll(out.String(), "\\", "/"), - "//", "/") - - expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expectedOut, "\\", "/") - - if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNormalized) { - t.FailNow() - } - - actualResources, err := os.ReadFile(r.Name()) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedResources), - strings.TrimSpace(string(actualResources))) { - t.FailNow() - } - - actualOpenAPI, err := os.ReadFile(f) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedOpenAPI), - strings.TrimSpace(string(actualOpenAPI))) { - t.FailNow() - } - }) - } -} - -func TestDeleteSetterSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - packagePath string - args []string - expected string - }{ - { - name: "delete-setter-recurse-subpackages", - dataset: "dataset-with-setters", - args: []string{"namespace", "-R"}, - expected: `${baseDir}/mysql/ -deleted setter "namespace" - -${baseDir}/mysql/nosetters/ -setter "namespace" does not exist - -${baseDir}/mysql/storage/ -deleted setter "namespace" -`, - }, - { - name: "delete-setter-top-level-pkg-no-recurse-subpackages", - dataset: "dataset-with-setters", - packagePath: "mysql", - args: []string{"namespace"}, - expected: `${baseDir}/mysql/ -deleted setter "namespace" -`, - }, - { - name: "delete-setter-nested-pkg-no-recurse-subpackages", - dataset: "dataset-with-setters", - packagePath: "mysql/storage", - args: []string{"namespace"}, - expected: `${baseDir}/mysql/storage/ -deleted setter "namespace" -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - sourceDir := filepath.Join("test", "testdata", test.dataset) - baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) - runner := commands.NewDeleteSetterRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(actual.String(), "\\", "/"), - "//", "/") - - expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expected, "\\", "/") - if !assert.Equal(t, expectedNormalized, actualNormalized) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/cmddeletesubstitution.go b/cmd/config/internal/commands/cmddeletesubstitution.go deleted file mode 100644 index 3ea178d65d..0000000000 --- a/cmd/config/internal/commands/cmddeletesubstitution.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "io" - "path/filepath" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/fieldmeta" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" -) - -// NewDeleteRunner returns a command runner. -func NewDeleteSubstitutionRunner(parent string) *DeleteSubstitutionRunner { - r := &DeleteSubstitutionRunner{} - c := &cobra.Command{ - Use: "delete-subst DIR NAME", - Args: cobra.ExactArgs(2), - PreRunE: r.preRunE, - RunE: r.runE, - } - c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false, - "deletes substitution recursively in all the nested subpackages") - runner.FixDocs(parent, c) - r.Command = c - - return r -} - -func DeleteSubstitutionCommand(parent string) *cobra.Command { - return NewDeleteSubstitutionRunner(parent).Command -} - -type DeleteSubstitutionRunner struct { - Command *cobra.Command - DeleteSubstitution settersutil.DeleterCreator - OpenAPIFile string - RecurseSubPackages bool -} - -func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) error { - r.DeleteSubstitution.Name = args[1] - r.DeleteSubstitution.DefinitionPrefix = fieldmeta.SubstitutionDefinitionPrefix - - r.OpenAPIFile = filepath.Join(args[0], ext.KRMFileName()) - - return nil -} - -func (r *DeleteSubstitutionRunner) runE(c *cobra.Command, args []string) error { - e := runner.ExecuteCmdOnPkgs{ - NeedOpenAPI: true, - Writer: c.OutOrStdout(), - RootPkgPath: args[0], - RecurseSubPackages: r.RecurseSubPackages, - CmdRunner: r, - } - err := e.Execute() - if err != nil { - return runner.HandleError(c, err) - } - return nil -} - -func (r *DeleteSubstitutionRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName())) - if err != nil { - return err - } - r.DeleteSubstitution = settersutil.DeleterCreator{ - Name: r.DeleteSubstitution.Name, - DefinitionPrefix: fieldmeta.SubstitutionDefinitionPrefix, - RecurseSubPackages: r.RecurseSubPackages, - OpenAPIFileName: ext.KRMFileName(), - OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()), - ResourcesPath: pkgPath, - SettersSchema: sc, - } - - err = r.DeleteSubstitution.Delete() - if err != nil { - // return err if RecurseSubPackages is false - if !r.DeleteSubstitution.RecurseSubPackages { - return err - } - // print error message and continue if RecurseSubPackages is true - fmt.Fprintf(w, "%s\n", err.Error()) - } else { - fmt.Fprintf(w, "deleted substitution %q\n", r.DeleteSubstitution.Name) - } - return nil -} diff --git a/cmd/config/internal/commands/cmddeletesubstitution_test.go b/cmd/config/internal/commands/cmddeletesubstitution_test.go deleted file mode 100644 index 8efba9b498..0000000000 --- a/cmd/config/internal/commands/cmddeletesubstitution_test.go +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/copyutil" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestDeleteSubstitutionCommand(t *testing.T) { - var tests = []struct { - name string - input string - args []string - schema string - out string - inputOpenAPI string - expectedOpenAPI string - expectedResources string - err string - }{ - { - name: "delete only subst if setter has same name - long ref", - args: []string{"my.image"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my.image: - x-k8s-cli: - setter: - name: my.image - value: "nginx" - io.k8s.cli.setters.my-tag: - x-k8s-cli: - setter: - name: my-tag - value: "1.7.9" - io.k8s.cli.substitutions.my.image: - x-k8s-cli: - substitution: - name: my.image - pattern: ${my.image}:${my-tag} - values: - - marker: ${my.image} - ref: '#/definitions/io.k8s.cli.setters.my.image' - - marker: ${my-tag} - ref: '#/definitions/io.k8s.cli.setters.my-tag' - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - template: - spec: - containers: - - name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.my.image"} - image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"} - - name: sidecar - image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"} - `, - out: `deleted substitution "my.image"`, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - template: - spec: - containers: - - name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.my.image"} - image: nginx:1.7.9 - - name: sidecar - image: nginx:1.7.9 - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my.image: - x-k8s-cli: - setter: - name: my.image - value: "nginx" - io.k8s.cli.setters.my-tag: - x-k8s-cli: - setter: - name: my-tag - value: "1.7.9" - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - }, - { - name: "delete subst - short ref", - args: []string{"my-image-sub"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image: - x-k8s-cli: - setter: - name: my-image - value: "nginx" - io.k8s.cli.setters.my-tag: - x-k8s-cli: - setter: - name: my-tag - value: "1.7.9" - io.k8s.cli.substitutions.my-image-sub: - x-k8s-cli: - substitution: - name: my-image-sub - pattern: ${my-image}:${my-tag} - values: - - marker: ${my-image} - ref: '#/definitions/io.k8s.cli.setters.my-image' - - marker: ${my-tag} - ref: '#/definitions/io.k8s.cli.setters.my-tag' - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$openapi":"my-image-sub"} - - name: sidecar - image: nginx:1.7.9 # {"$openapi":"my-image-sub"} - `, - out: `deleted substitution "my-image-sub"`, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 - - name: sidecar - image: nginx:1.7.9 - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image: - x-k8s-cli: - setter: - name: my-image - value: "nginx" - io.k8s.cli.setters.my-tag: - x-k8s-cli: - setter: - name: my-tag - value: "1.7.9" - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - }, - { - name: "error if subst doesn't exist", - args: []string{"my-image-sub-not-present"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image: - x-k8s-cli: - setter: - name: my-image - value: "nginx" - io.k8s.cli.setters.my-tag: - x-k8s-cli: - setter: - name: my-tag - value: "1.7.9" - io.k8s.cli.substitutions.my-image-sub: - x-k8s-cli: - substitution: - name: my-image-sub - pattern: ${my-image}:${my-tag} - values: - - marker: ${my-image} - ref: '#/definitions/io.k8s.cli.setters.my-image' - - marker: ${my-tag} - ref: '#/definitions/io.k8s.cli.setters.my-tag' - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$openapi":"my-image-sub"} - - name: sidecar - image: nginx:1.7.9 # {"$openapi":"my-image-sub"} - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 - - name: sidecar - image: nginx:1.7.9 - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image: - x-k8s-cli: - setter: - name: my-image - value: "nginx" - io.k8s.cli.setters.my-tag: - x-k8s-cli: - setter: - name: my-tag - value: "1.7.9" - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - err: `substitution "my-image-sub-not-present" does not exist`, - }, - - { - name: "substitution referenced by other substitution", - args: []string{"my-image-subst"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}::${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}::${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} - `, - err: `substitution "my-image-subst" is used in substitution "my-nested-subst", please delete the parent substitution first`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - - baseDir := t.TempDir() - f := filepath.Join(baseDir, "Krmfile") - err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml") - if !assert.NoError(t, err) { - t.FailNow() - } - t.Cleanup(func() { r.Close() }) - err = os.WriteFile(r.Name(), []byte(test.input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - runner := commands.NewDeleteSubstitutionRunner("") - out := &bytes.Buffer{} - runner.Command.SetOut(out) - runner.Command.SetArgs(append([]string{baseDir}, test.args...)) - err = runner.Command.Execute() - - if test.err != "" { - if !assert.NotNil(t, err) { - t.FailNow() - } - assert.Equal(t, test.err, err.Error()) - return - } - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNorm := strings.ReplaceAll( - strings.ReplaceAll(out.String(), "\\", "/"), - "//", "/") - expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir) - expectedNorm := strings.ReplaceAll(expectedOut, "\\", "/") - - if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNorm) { - t.FailNow() - } - - actualOpenAPI, err := os.ReadFile(f) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedOpenAPI), - strings.TrimSpace(string(actualOpenAPI))) { - t.FailNow() - } - - actualResources, err := os.ReadFile(r.Name()) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedResources), - strings.TrimSpace(string(actualResources))) { - t.FailNow() - } - }) - } -} - -func TestDeleteSubstitutionSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - packagePath string - args []string - expected string - }{ - { - name: "delete-substitution-recurse-subpackages", - dataset: "dataset-with-setters", - args: []string{"image-tag", "-R"}, - expected: `${baseDir}/mysql/ -deleted substitution "image-tag" - -${baseDir}/mysql/nosetters/ -substitution "image-tag" does not exist - -${baseDir}/mysql/storage/ -substitution "image-tag" does not exist -`, - }, - { - name: "delete-setter-top-level-pkg-no-recurse-subpackages", - dataset: "dataset-with-setters", - packagePath: "mysql", - args: []string{"image-tag"}, - expected: `${baseDir}/mysql/ -deleted substitution "image-tag" -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - sourceDir := filepath.Join("test", "testdata", test.dataset) - baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) - runner := commands.NewDeleteSubstitutionRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(actual.String(), "\\", "/"), - "//", "/") - - expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expected, "\\", "/") - if !assert.Equal(t, expectedNormalized, actualNormalized) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/cmdinit.go b/cmd/config/internal/commands/cmdinit.go deleted file mode 100644 index f6e3ad26b1..0000000000 --- a/cmd/config/internal/commands/cmdinit.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "os" - "path/filepath" - "strings" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/errors" - "sigs.k8s.io/kustomize/kyaml/krmfile" -) - -// GetInitRunner returns a command InitRunner. -func GetInitRunner(name string) *InitRunner { - r := &InitRunner{} - c := &cobra.Command{ - Use: "init DIR...", - Args: cobra.RangeArgs(0, 1), - Short: commands.InitShort, - Long: commands.InitLong, - Example: commands.InitExamples, - RunE: r.runE, - Deprecated: "setter commands and substitutions will no longer be available in kustomize v5.\n" + - "See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.", - } - runner.FixDocs(name, c) - r.Command = c - return r -} - -func InitCommand(name string) *cobra.Command { - return GetInitRunner(name).Command -} - -// InitRunner contains the init function -type InitRunner struct { - Command *cobra.Command -} - -func (r *InitRunner) runE(c *cobra.Command, args []string) error { - var dir string - if len(args) == 0 { - dir = "." - } else { - dir = args[0] - } - filename := filepath.Join(dir, krmfile.KrmfileName) - - if _, err := os.Stat(filename); err == nil || !os.IsNotExist(err) { - return errors.Errorf("directory already initialized with a Krmfile") - } - - err := os.WriteFile(filename, []byte(strings.TrimSpace(` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -`)), 0600) - return errors.Wrap(err) -} diff --git a/cmd/config/internal/commands/cmdlistsetters.go b/cmd/config/internal/commands/cmdlistsetters.go deleted file mode 100644 index 623374c2a1..0000000000 --- a/cmd/config/internal/commands/cmdlistsetters.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/olekukonko/tablewriter" - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/fieldmeta" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/setters2" -) - -// NewListSettersRunner returns a command runner. -func NewListSettersRunner(parent string) *ListSettersRunner { - r := &ListSettersRunner{} - c := &cobra.Command{ - Use: "list-setters DIR [NAME]", - Args: cobra.RangeArgs(1, 2), - Short: commands.ListSettersShort, - Long: commands.ListSettersLong, - Example: commands.ListSettersExamples, - PreRunE: r.preRunE, - RunE: r.runE, - Deprecated: "setter commands will no longer be available in kustomize v5.\n" + - "See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.", - } - c.Flags().BoolVar(&r.Markdown, "markdown", false, - "output as github markdown") - c.Flags().BoolVar(&r.IncludeSubst, "include-subst", false, - "include substitutions in the output") - c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false, - "list setters recursively in all the nested subpackages") - runner.FixDocs(parent, c) - r.Command = c - return r -} - -func ListSettersCommand(parent string) *cobra.Command { - return NewListSettersRunner(parent).Command -} - -type ListSettersRunner struct { - Command *cobra.Command - List setters2.List - Markdown bool - IncludeSubst bool - RecurseSubPackages bool - Name string -} - -func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error { - if len(args) > 1 { - r.Name = args[1] - } - - return nil -} - -func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error { - e := runner.ExecuteCmdOnPkgs{ - NeedOpenAPI: true, - Writer: c.OutOrStdout(), - RootPkgPath: args[0], - RecurseSubPackages: r.RecurseSubPackages, - CmdRunner: r, - } - - err := e.Execute() - if err != nil { - return runner.HandleError(c, err) - } - return nil -} - -func (r *ListSettersRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName())) - if err != nil { - return err - } - r.List = setters2.List{ - Name: r.Name, - OpenAPIFileName: ext.KRMFileName(), - SettersSchema: sc, - } - openAPIPath := filepath.Join(pkgPath, ext.KRMFileName()) - if err := r.ListSetters(w, openAPIPath, pkgPath); err != nil { - return err - } - if r.IncludeSubst { - if err := r.ListSubstitutions(w, openAPIPath); err != nil { - return err - } - } - return nil -} - -func (r *ListSettersRunner) ListSetters(w io.Writer, openAPIPath, resourcePath string) error { - // use setters v2 - if err := r.List.ListSetters(openAPIPath, resourcePath); err != nil { - return err - } - table := newTable(w, r.Markdown) - table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED", "IS SET"}) - for i := range r.List.Setters { - s := r.List.Setters[i] - v := s.Value - - // if the setter is for a list, populate the values - if len(s.ListValues) > 0 { - v = strings.Join(s.ListValues, ",") - v = fmt.Sprintf("[%s]", v) - } - required := "No" - if s.Required { - required = "Yes" - } - isSet := "No" - if s.IsSet { - isSet = "Yes" - } - - table.Append([]string{ - s.Name, v, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count), required, isSet}) - } - table.Render() - - if len(r.List.Setters) == 0 { - // exit non-0 if no matching setters are found - if runner.ExitOnError { - os.Exit(1) - } - } - return nil -} - -func (r *ListSettersRunner) ListSubstitutions(w io.Writer, openAPIPath string) error { - // use setters v2 - if err := r.List.ListSubst(openAPIPath); err != nil { - return err - } - table := newTable(w, r.Markdown) - b := tablewriter.Border{Top: true} - table.SetBorders(b) - - table.SetHeader([]string{"SUBSTITUTION", "PATTERN", "REFERENCES"}) - for i := range r.List.Substitutions { - s := r.List.Substitutions[i] - refs := "" - for _, value := range s.Values { - // trim setter and substitution prefixes - ref := strings.TrimPrefix( - strings.TrimPrefix(value.Ref, fieldmeta.DefinitionsPrefix+fieldmeta.SetterDefinitionPrefix), - fieldmeta.DefinitionsPrefix+fieldmeta.SubstitutionDefinitionPrefix) - refs = refs + "," + ref - } - refs = fmt.Sprintf("[%s]", strings.TrimPrefix(refs, ",")) - table.Append([]string{ - s.Name, s.Pattern, refs}) - } - if len(r.List.Substitutions) == 0 { - return nil - } - table.Render() - - return nil -} - -func newTable(o io.Writer, m bool) *tablewriter.Table { - table := tablewriter.NewWriter(o) - table.SetRowLine(false) - if m { - // markdown format - table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) - table.SetCenterSeparator("|") - } else { - table.SetBorder(false) - table.SetHeaderLine(false) - table.SetColumnSeparator(" ") - table.SetCenterSeparator(" ") - } - table.SetAlignment(tablewriter.ALIGN_LEFT) - return table -} diff --git a/cmd/config/internal/commands/cmdlistsetters_test.go b/cmd/config/internal/commands/cmdlistsetters_test.go deleted file mode 100644 index 85e11786c1..0000000000 --- a/cmd/config/internal/commands/cmdlistsetters_test.go +++ /dev/null @@ -1,527 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestListSettersCommand(t *testing.T) { - var tests = []struct { - name string - openapi string - input string - args []string - expected string - }{ - { - name: "list-replicas", - openapi: ` -openAPI: - definitions: - io.k8s.cli.setters.replicas: - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - required: true - description: "hello world" - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} - `, - expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - replicas 3 me hello world 1 Yes No -`, - }, - - { - name: "list-replicas inconsistent with openapi", - openapi: ` -openAPI: - definitions: - io.k8s.cli.setters.replicas: - x-k8s-cli: - setter: - name: replicas - value: "4" - setBy: me - description: "hello world" - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} - `, - expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - replicas 4 me hello world 1 No No -`, - }, - { - name: "list-multiple", - args: []string{"--include-subst"}, - openapi: ` -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: "hello world 1" - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me1 - io.k8s.cli.setters.image: - description: "hello world 2" - x-k8s-cli: - setter: - name: image - value: "nginx" - setBy: me2 - io.k8s.cli.setters.tag: - description: "hello world 3" - x-k8s-cli: - setter: - name: tag - value: "1.7.9" - setBy: me3 - required: true - isSet: false - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE:TAG - values: - - marker: IMAGE - ref: '#/definitions/io.k8s.cli.setters.image' - - marker: TAG - ref: '#/definitions/io.k8s.cli.setters.tag' - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"} - - name: nginx2 - image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} - `, - expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - image nginx me2 hello world 2 2 No No - replicas 3 me1 hello world 1 1 No No - tag 1.7.9 me3 hello world 3 1 Yes No ---------------- ----------- -------------- - SUBSTITUTION PATTERN REFERENCES - image IMAGE:TAG [image,tag] -`, - }, - { - name: "list-multiple-resources", - args: []string{"--include-subst"}, - openapi: ` -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: "hello world 1" - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me1 - io.k8s.cli.setters.image: - description: "hello world 2" - x-k8s-cli: - setter: - name: image - value: "nginx" - setBy: me2 - io.k8s.cli.setters.tag: - description: "hello world 3" - x-k8s-cli: - setter: - name: tag - value: "1.7.9" - setBy: me3 - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE:TAG - values: - - marker: IMAGE - ref: '#/definitions/io.k8s.cli.setters.image' - - marker: TAG - ref: '#/definitions/io.k8s.cli.setters.tag' - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment-1 -spec: - replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"} - - name: nginx2 - image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment-2 -spec: - replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"} - - name: nginx2 - image: nginx -`, - expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - image nginx me2 hello world 2 3 No No - replicas 3 me1 hello world 1 2 No No - tag 1.7.9 me3 hello world 3 2 No No ---------------- ----------- -------------- - SUBSTITUTION PATTERN REFERENCES - image IMAGE:TAG [image,tag] -`, - }, - { - name: "list-name", - args: []string{"image"}, - openapi: ` -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: "hello world 1" - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me1 - io.k8s.cli.setters.image: - description: "hello world 2" - x-k8s-cli: - setter: - name: image - value: "nginx" - setBy: me2 - required: true - io.k8s.cli.setters.tag: - description: "hello world 3" - x-k8s-cli: - setter: - name: tag - value: "1.7.9" - setBy: me3 - io.k8s.cli.substitutions.image-subst: - x-k8s-cli: - substitution: - name: image-subst - pattern: IMAGE:TAG - values: - - marker: IMAGE - ref: '#/definitions/io.k8s.cli.setters.image' - - marker: TAG - ref: '#/definitions/io.k8s.cli.setters.tag' - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment-1 -spec: - replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-subst"} - - name: nginx2 - image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment-2 -spec: - replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-subst"} - - name: nginx2 - image: nginx -`, - expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - image nginx me2 hello world 2 3 Yes No -`, - }, - - { - name: "list array setter", - openapi: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.list: - items: - type: string - maxItems: 3 - type: array - description: hello world - x-k8s-cli: - setter: - name: list - listValues: - - a - - b - - c - setBy: me - required: true - `, - input: ` -apiVersion: example.com/v1beta1 -kind: Example -metadata: - annotations: - foo: bar -spec: - list: # {"$openapi":"list"} - - "a" - - "b" - - "c" -`, - expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - list [a,b,c] me hello world 1 Yes No -`, - }, - - { - name: "nested substitution", - args: []string{"--include-subst"}, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} - `, - openapi: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - required: true - isSet: true - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}::${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - `, - expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - my-image-setter nginx 2 No No - my-other-setter nginxotherthing 1 No No - my-tag-setter 1.7.9 2 Yes Yes ------------------- ------------------------------------------------ ----------------------------------- - SUBSTITUTION PATTERN REFERENCES - my-image-subst ${my-image-setter}::${my-tag-setter} [my-image-setter,my-tag-setter] - my-nested-subst something/${my-image-subst}/${my-other-setter} [my-image-subst,my-other-setter] -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - - dir := t.TempDir() - - err := os.WriteFile(filepath.Join(dir, "Krmfile"), []byte(test.openapi), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - err = os.WriteFile(filepath.Join(dir, "deployment.yaml"), []byte(test.input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - runner := commands.NewListSettersRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{dir}, test.args...)) - err = runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - if !assert.Contains(t, actual.String(), test.expected) { - t.FailNow() - } - - // make sure that the resources are not altered - actualResources, err := os.ReadFile(filepath.Join(dir, "deployment.yaml")) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.input), - strings.TrimSpace(string(actualResources))) { - t.FailNow() - } - - actualOpenAPI, err := os.ReadFile(filepath.Join(dir, "Krmfile")) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.openapi), - strings.TrimSpace(string(actualOpenAPI))) { - t.FailNow() - } - }) - } -} - -func TestListSettersSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - args []string - expected string - }{ - { - name: "list-replicas", - dataset: "dataset-with-setters", - args: []string{"--include-subst", "-R"}, - expected: ` - -test/testdata/dataset-with-setters/mysql/ - NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - image mysql 1 No No - namespace myspace 1 No No - tag 1.7.9 1 No No ---------------- ----------------- -------------- - SUBSTITUTION PATTERN REFERENCES - image-tag ${image}:${tag} [image,tag] - -test/testdata/dataset-with-setters/mysql/nosetters/ - NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - -test/testdata/dataset-with-setters/mysql/storage/ - NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - namespace myspace 1 No No -`, - }, - { - name: "list-replicas", - dataset: "dataset-with-setters/mysql", - expected: ` - -test/testdata/dataset-with-setters/mysql/ - NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - image mysql 1 No No - namespace myspace 1 No No - tag 1.7.9 1 No No -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - dir := filepath.Join("test", "testdata", test.dataset) - - runner := commands.NewListSettersRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{dir}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll(actual.String(), "\\", "/") - - if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(test.expected)) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/cmdset.go b/cmd/config/internal/commands/cmdset.go deleted file mode 100644 index 1d033f02ff..0000000000 --- a/cmd/config/internal/commands/cmdset.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "io" - "path/filepath" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/errors" - "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" -) - -// NewSetRunner returns a command runner. -func NewSetRunner(parent string) *SetRunner { - r := &SetRunner{} - c := &cobra.Command{ - Use: "set DIR NAME --values [VALUE]", - Args: cobra.MinimumNArgs(2), - Short: commands.SetShort, - Long: commands.SetLong, - Example: commands.SetExamples, - PreRunE: r.preRunE, - RunE: r.runE, - Deprecated: "setter commands will no longer be available in kustomize v5.\n" + - "See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.", - } - runner.FixDocs(parent, c) - r.Command = c - c.Flags().StringArrayVar(&r.Values, "values", []string{}, - "optional flag, the values of the setter to be set to") - c.Flags().StringVar(&r.SetBy, "set-by", "", - "annotate the field with who set it") - c.Flags().StringVar(&r.Description, "description", "", - "annotate the field with a description of its value") - c.Flags().StringVar(&setterVersion, "version", "", - "use this version of the setter format") - c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false, - "sets recursively in all the nested subpackages") - c.Flags().MarkHidden("version") - - return r -} - -var setterVersion string - -func SetCommand(parent string) *cobra.Command { - return NewSetRunner(parent).Command -} - -type SetRunner struct { - Command *cobra.Command - Set settersutil.FieldSetter - OpenAPIFile string - Values []string - SetBy string - Description string - Name string - Value string - ListValues []string - RecurseSubPackages bool -} - -func (r *SetRunner) preRunE(c *cobra.Command, args []string) error { - valueFlagSet := c.Flag("values").Changed - - if valueFlagSet && len(args) > 2 { - return errors.Errorf("value should set either from flag or arg") - } - - // make sure that the value is provided either through values flag or as an arg - if !valueFlagSet && len(args) < 3 { - return errors.Errorf("value must be provided either from flag or arg") - } - - r.Name = args[1] - if valueFlagSet { - r.Value = r.Values[0] - } else { - r.Value = args[2] - } - - // set remaining values as list values - if valueFlagSet && len(r.Values) > 1 { - r.ListValues = r.Values[1:] - } else if !valueFlagSet && len(args) > 3 { - r.ListValues = args[3:] - } - - r.OpenAPIFile = filepath.Join(args[0], ext.KRMFileName()) - return nil -} - -func (r *SetRunner) runE(c *cobra.Command, args []string) error { - e := runner.ExecuteCmdOnPkgs{ - NeedOpenAPI: true, - Writer: c.OutOrStdout(), - RootPkgPath: args[0], - RecurseSubPackages: r.RecurseSubPackages, - CmdRunner: r, - } - err := e.Execute() - if err != nil { - return runner.HandleError(c, err) - } - return nil -} - -func (r *SetRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - r.Set = settersutil.FieldSetter{ - Name: r.Name, - Value: r.Value, - ListValues: r.ListValues, - Description: r.Description, - SetBy: r.SetBy, - Count: 0, - OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()), - OpenAPIFileName: ext.KRMFileName(), - ResourcesPath: pkgPath, - RecurseSubPackages: r.RecurseSubPackages, - IsSet: true, - } - count, err := r.Set.Set() - if err != nil { - // return err if RecurseSubPackages is false - if !r.Set.RecurseSubPackages { - return err - } - // print error message and continue if RecurseSubPackages is true - fmt.Fprintf(w, "%s\n", err.Error()) - } else { - fmt.Fprintf(w, "set %d field(s) of setter %q to value %q\n", count, r.Set.Name, r.Set.Value) - } - return nil -} diff --git a/cmd/config/internal/commands/cmdset_test.go b/cmd/config/internal/commands/cmdset_test.go deleted file mode 100644 index 78c5027c8f..0000000000 --- a/cmd/config/internal/commands/cmdset_test.go +++ /dev/null @@ -1,1136 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/copyutil" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestSetCommand(t *testing.T) { - var tests = []struct { - name string - inputOpenAPI string - input string - args []string - out string - expectedOpenAPI string - expectedResources string - errMsg string - }{ - { - name: "set replicas", - args: []string{"replicas", "4", "--description", "hi there", "--set-by", "pw"}, - out: "set 1 field(s)\n", - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$openapi":"replicas"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hi there - x-k8s-cli: - setter: - name: replicas - value: "4" - setBy: pw - isSet: true - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 4 # {"$openapi":"replicas"} - `, - }, - { - name: "validate length of argument", - args: []string{"--description", "hi there", "--set-by", "pw"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - errMsg: "requires at least 2 arg(s), only received 1", - }, - - { - name: "set replicas no description", - args: []string{"replicas", "4"}, - out: "set 1 field(s)\n", - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "4" - isSet: true - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 4 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - }, - - { - name: "set image with value", - args: []string{"tag", "1.8.1"}, - out: "set 1 field(s)\n", - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.image-setter: - x-k8s-cli: - setter: - name: image-setter - value: "nginx" - io.k8s.cli.setters.tag: - x-k8s-cli: - setter: - name: tag - value: "1.7.9" - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE:TAG - values: - - marker: IMAGE - ref: '#/definitions/io.k8s.cli.setters.image-setter' - - marker: TAG - ref: '#/definitions/io.k8s.cli.setters.tag' - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$openapi":"image"} - - name: sidecar - image: sidecar:1.7.9 - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.image-setter: - x-k8s-cli: - setter: - name: image-setter - value: "nginx" - io.k8s.cli.setters.tag: - x-k8s-cli: - setter: - name: tag - value: "1.8.1" - isSet: true - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE:TAG - values: - - marker: IMAGE - ref: '#/definitions/io.k8s.cli.setters.image-setter' - - marker: TAG - ref: '#/definitions/io.k8s.cli.setters.tag' - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: nginx:1.8.1 # {"$openapi":"image"} - - name: sidecar - image: sidecar:1.7.9 -`, - }, - - { - name: "validate openAPI number", - args: []string{"replicas", "four"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - type: number - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - type: number - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - errMsg: "replicas in body must be of type number", - }, - - { - name: "validate openAPI string maxLength", - args: []string{"name", "wordpress"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.name: - type: string - maxLength: 5 - description: hello world - x-k8s-cli: - setter: - name: name - value: nginx - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.name"} -spec: - replicas: 3 - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.name: - type: string - maxLength: 5 - description: hello world - x-k8s-cli: - setter: - name: name - value: nginx - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.name"} -spec: - replicas: 3 - `, - errMsg: "name in body should be at most 5 chars long", - }, - - { - name: "validate substitution", - args: []string{"tag", "1.8.1"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.image: - x-k8s-cli: - setter: - name: image - value: "nginx" - io.k8s.cli.setters.tag: - type: string - minLength: 6 - x-k8s-cli: - setter: - name: tag - value: "1.7.9" - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE:TAG - values: - - marker: IMAGE - ref: '#/definitions/io.k8s.cli.setters.image' - - marker: TAG - ref: '#/definitions/io.k8s.cli.setters.tag' - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"} - - name: sidecar - image: sidecar:1.7.9 - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.image: - x-k8s-cli: - setter: - name: image - value: "nginx" - io.k8s.cli.setters.tag: - type: string - minLength: 6 - x-k8s-cli: - setter: - name: tag - value: "1.7.9" - io.k8s.cli.substitutions.image: - x-k8s-cli: - substitution: - name: image - pattern: IMAGE:TAG - values: - - marker: IMAGE - ref: '#/definitions/io.k8s.cli.setters.image' - - marker: TAG - ref: '#/definitions/io.k8s.cli.setters.tag' - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"} - - name: sidecar - image: sidecar:1.7.9 -`, - errMsg: "tag in body should be at least 6 chars long", - }, - - { - name: "validate openAPI list values", - args: []string{"list", "10", "hi", "true"}, - inputOpenAPI: ` -kind: Kptfile -openAPI: - definitions: - io.k8s.cli.setters.list: - type: array - maxItems: 2 - items: - type: integer - x-k8s-cli: - setter: - name: list - listValues: - - 0 - `, - input: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"} - - 0 - `, - expectedOpenAPI: ` -kind: Kptfile -openAPI: - definitions: - io.k8s.cli.setters.list: - type: array - maxItems: 2 - items: - type: integer - x-k8s-cli: - setter: - name: list - listValues: - - 0 - `, - expectedResources: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"} - - 0 - `, - errMsg: `list[1] in body must be of type integer: "string" -list[2] in body must be of type integer: "boolean" -list in body should have at most 2 items`, - }, - - { - name: "set replicas with value set by flag", - args: []string{"replicas", "--values", "4", "--description", "hi there"}, - out: "set 1 field(s)\n", - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hi there - x-k8s-cli: - setter: - name: replicas - value: "4" - isSet: true - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 4 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - }, - - { - name: "validate values set from either flag or arg", - args: []string{"replicas", "4", "--values", "4", "--description", "hi there"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - errMsg: `value should set either from flag or arg`, - }, - - { - name: "value must be provided from either flag or arg", - args: []string{"replicas", "--description", "hi there"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.replicas: - description: hello world - x-k8s-cli: - setter: - name: replicas - value: "3" - setBy: me - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} - `, - errMsg: `value must be provided either from flag or arg`, - }, - - { - name: "openAPI list values set by flag success", - args: []string{"list", "--values", "10", "--values", "11"}, - out: "set 1 field(s)\n", - inputOpenAPI: ` -kind: Kptfile -openAPI: - definitions: - io.k8s.cli.setters.list: - type: array - maxItems: 2 - items: - type: integer - x-k8s-cli: - setter: - name: list - listValues: - - 0 - `, - input: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"} - - 0 - `, - expectedOpenAPI: ` -kind: Kptfile -openAPI: - definitions: - io.k8s.cli.setters.list: - type: array - maxItems: 2 - items: - type: integer - x-k8s-cli: - setter: - name: list - listValues: - - "10" - - "11" - isSet: true - `, - expectedResources: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"} - - "10" - - "11" - `, - }, - - { - name: "validate openAPI list values set by flag error", - args: []string{"list", "--values", "10", "--values", "hi", "--values", "true"}, - inputOpenAPI: ` -kind: Kptfile -openAPI: - definitions: - io.k8s.cli.setters.list: - type: array - maxItems: 2 - items: - type: integer - x-k8s-cli: - setter: - name: list - listValues: - - 0 - `, - input: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"} - - 0 - `, - expectedOpenAPI: ` -kind: Kptfile -openAPI: - definitions: - io.k8s.cli.setters.list: - type: array - maxItems: 2 - items: - type: integer - x-k8s-cli: - setter: - name: list - listValues: - - 0 - `, - expectedResources: ` -apiVersion: example.com/v1beta1 -kind: Example -spec: - list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"} - - 0 - `, - errMsg: `list[1] in body must be of type integer: "string" -list[2] in body must be of type integer: "boolean" -list in body should have at most 2 items`, - }, - { - name: "nested substitution", - args: []string{"my-image-setter", "ubuntu"}, - out: "set 2 field(s)\n", - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}::${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: ubuntu - isSet: true - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-image-setter}::${my-tag-setter} - values: - - marker: ${my-image-setter} - ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/ubuntu::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: ubuntu::1.7.9 # {"$openapi":"my-image-subst"} -`, - }, - { - name: "nested cyclic substitution", - args: []string{"my-image-setter", "ubuntu"}, - inputOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-nested-subst}::${my-tag-setter} - values: - - marker: ${my-nested-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-nested-subst' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - `, - input: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} - `, - expectedOpenAPI: ` -apiVersion: v1alpha1 -kind: Example -openAPI: - definitions: - io.k8s.cli.setters.my-image-setter: - x-k8s-cli: - setter: - name: my-image-setter - value: nginx - io.k8s.cli.setters.my-tag-setter: - x-k8s-cli: - setter: - name: my-tag-setter - value: 1.7.9 - io.k8s.cli.substitutions.my-image-subst: - x-k8s-cli: - substitution: - name: my-image-subst - pattern: ${my-nested-subst}::${my-tag-setter} - values: - - marker: ${my-nested-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-nested-subst' - - marker: ${my-tag-setter} - ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' - io.k8s.cli.setters.my-other-setter: - x-k8s-cli: - setter: - name: my-other-setter - value: nginxotherthing - io.k8s.cli.substitutions.my-nested-subst: - x-k8s-cli: - substitution: - name: my-nested-subst - pattern: something/${my-image-subst}/${my-other-setter} - values: - - marker: ${my-image-subst} - ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - - marker: ${my-other-setter} - ref: '#/definitions/io.k8s.cli.setters.my-other-setter' - `, - expectedResources: ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - spec: - containers: - - name: nginx - image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - - name: sidecar - image: nginx::1.7.9 # {"$openapi":"my-image-subst"} -`, - errMsg: "cyclic substitution detected with name my-nested-subst", - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - - baseDir := t.TempDir() - - f := filepath.Join(baseDir, "Krmfile") - err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml") - if !assert.NoError(t, err) { - t.FailNow() - } - t.Cleanup(func() { r.Close() }) - err = os.WriteFile(r.Name(), []byte(test.input), 0600) - if !assert.NoError(t, err) { - t.FailNow() - } - - runner := commands.NewSetRunner("") - out := &bytes.Buffer{} - runner.Command.SetOut(out) - runner.Command.SetArgs(append([]string{baseDir}, test.args...)) - err = runner.Command.Execute() - if test.errMsg != "" { - if !assert.NotNil(t, err) { - t.FailNow() - } - if !assert.Contains(t, err.Error(), test.errMsg) { - t.FailNow() - } - } - - if test.errMsg == "" && !assert.NoError(t, err) { - t.FailNow() - } - - if test.errMsg == "" && !assert.Contains(t, out.String(), strings.TrimSpace(test.out)) { - t.FailNow() - } - - actualResources, err := os.ReadFile(r.Name()) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedResources), - strings.TrimSpace(string(actualResources))) { - t.FailNow() - } - - actualOpenAPI, err := os.ReadFile(f) - if !assert.NoError(t, err) { - t.FailNow() - } - if !assert.Equal(t, - strings.TrimSpace(test.expectedOpenAPI), - strings.TrimSpace(string(actualOpenAPI))) { - t.FailNow() - } - }) - } -} - -func TestSetSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - packagePath string - args []string - expected string - }{ - { - name: "set-recurse-subpackages", - dataset: "dataset-with-setters", - args: []string{"namespace", "otherspace", "-R"}, - expected: `${baseDir}/mysql/ -set 1 field(s) of setter "namespace" to value "otherspace" - -${baseDir}/mysql/nosetters/ -setter "namespace" is not found - -${baseDir}/mysql/storage/ -set 1 field(s) of setter "namespace" to value "otherspace" -`, - }, - { - name: "set-top-level-pkg-no-recurse-subpackages", - dataset: "dataset-with-setters", - packagePath: "mysql", - args: []string{"namespace", "otherspace"}, - expected: `${baseDir}/mysql/ -set 1 field(s) of setter "namespace" to value "otherspace" -`, - }, - { - name: "set-nested-pkg-no-recurse-subpackages", - dataset: "dataset-with-setters", - packagePath: "mysql/storage", - args: []string{"namespace", "otherspace"}, - expected: `${baseDir}/mysql/storage/ -set 1 field(s) of setter "namespace" to value "otherspace" -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - sourceDir := filepath.Join("test", "testdata", test.dataset) - baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) - runner := commands.NewSetRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(actual.String(), "\\", "/"), - "//", "/") - - expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll( - strings.ReplaceAll(expected, "\\", "/"), - "//", "/") - if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/cmdwrap.go b/cmd/config/internal/commands/cmdwrap.go deleted file mode 100644 index 8727446d9e..0000000000 --- a/cmd/config/internal/commands/cmdwrap.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "bytes" - "fmt" - "io" - "os" - "path/filepath" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/kio/filters" -) - -// GetWrapRunner returns a command runner. -func GetWrapRunner() *WrapRunner { - r := &WrapRunner{} - c := &cobra.Command{ - Use: "wrap CMD...", - Short: "Wrap an executable so it implements the config fn interface", - Long: `Wrap an executable so it implements the config fn interface - -wrap simplifies writing config functions by: - -- invoking an executable command converting an input ResourceList into environment -- merging the output onto the original input as a set of patches -- setting filenames on any Resources missing them - -config function authors may use wrap by using it to invoke a command from a container image - -The following are equivalent: - - kyaml wrap -- CMD - - kyaml xargs -- CMD | kyaml merge | kyaml fmt --set-filenames - -Environment Variables: - - KUST_OVERRIDE_DIR: - - Path to a directory containing patches to apply to after merging. -`, - Example: ` - -`, - RunE: r.runE, - PreRunE: r.preRunE, - SilenceUsage: true, - FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true}, - Args: cobra.MinimumNArgs(1), - } - r.Command = c - r.XArgs = GetXArgsRunner() - c.Flags().BoolVar(&r.XArgs.EnvOnly, - "env-only", true, "only set env vars, not arguments.") - c.Flags().StringVar(&r.XArgs.WrapKind, - "wrap-kind", "List", "wrap the input xargs give to the command in this type.") - c.Flags().StringVar(&r.XArgs.WrapVersion, - "wrap-version", "v1", "wrap the input xargs give to the command in this type.") - return r -} - -// WrapRunner contains the run function -type WrapRunner struct { - Command *cobra.Command - XArgs *XArgsRunner - getEnv func(key string) string -} - -const ( - KustMergeEnv = "KUST_MERGE" - KustOverrideDirEnv = "KUST_OVERRIDE_DIR" -) - -func WrapCommand() *cobra.Command { - return GetWrapRunner().Command -} - -func (r *WrapRunner) preRunE(_ *cobra.Command, _ []string) error { - _, err := fmt.Fprintln(os.Stderr, `Command "wrap" is deprecated, this will no longer be available in kustomize v5. -See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`) - return err -} - -func (r *WrapRunner) runE(c *cobra.Command, args []string) error { - if r.getEnv == nil { - r.getEnv = os.Getenv - } - xargsIn := &bytes.Buffer{} - if _, err := io.Copy(xargsIn, c.InOrStdin()); err != nil { - return err - } - mergeInput := bytes.NewBuffer(xargsIn.Bytes()) - // Run the command - xargsOut := &bytes.Buffer{} - r.XArgs.Command.SetArgs(args) - r.XArgs.Command.SetIn(xargsIn) - r.XArgs.Command.SetOut(xargsOut) - r.XArgs.Command.SetErr(os.Stderr) - if err := r.XArgs.Command.Execute(); err != nil { - return err - } - - // merge the results - buff := &kio.PackageBuffer{} - - var fltrs []kio.Filter - var inputs []kio.Reader - if r.getEnv(KustMergeEnv) == "" || r.getEnv(KustMergeEnv) == "true" || r.getEnv(KustMergeEnv) == "1" { - inputs = append(inputs, &kio.ByteReader{Reader: mergeInput}) - fltrs = append(fltrs, &filters.MergeFilter{}) - } - inputs = append(inputs, &kio.ByteReader{Reader: xargsOut}) - - if err := (kio.Pipeline{Inputs: inputs, Filters: fltrs, Outputs: []kio.Writer{buff}}). - Execute(); err != nil { - return err - } - - inputs, fltrs = []kio.Reader{buff}, nil - if r.getEnv(KustOverrideDirEnv) != "" { - // merge the overrides on top of the output - fltrs = append(fltrs, filters.MergeFilter{}) - inputs = append(inputs, - kio.LocalPackageReader{ - OmitReaderAnnotations: true, // don't set path annotations, as they would override - PackagePath: r.getEnv(KustOverrideDirEnv)}) - } - fltrs = append(fltrs, - &filters.FileSetter{ - FilenamePattern: filepath.Join("config", filters.DefaultFilenamePattern)}, - &filters.FormatFilter{}) - - err := kio.Pipeline{ - Inputs: inputs, - Filters: fltrs, - Outputs: []kio.Writer{kio.ByteWriter{ - Sort: true, - KeepReaderAnnotations: true, - Writer: c.OutOrStdout(), - WrappingKind: kio.ResourceListKind, - WrappingAPIVersion: kio.ResourceListAPIVersion}}}.Execute() - return runner.HandleError(c, err) -} diff --git a/cmd/config/internal/commands/cmdwrap_test.go b/cmd/config/internal/commands/cmdwrap_test.go deleted file mode 100644 index d3c261cc81..0000000000 --- a/cmd/config/internal/commands/cmdwrap_test.go +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "bytes" - "path/filepath" - "runtime" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/kyaml/testutil" -) - -const ( - input = `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -functionConfig: - metadata: - name: test - spec: - replicas: 11 -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: test - labels: - app: nginx - name: test - spec: - replicas: 5 - selector: - matchLabels: - app: nginx - name: test - template: - metadata: - labels: - app: nginx - name: test - spec: - containers: - - name: test - image: nginx:v1.7 - ports: - - containerPort: 8080 - name: http - resources: - limits: - cpu: 500m -- apiVersion: v1 - kind: Service - metadata: - name: test - labels: - app: nginx - name: test - spec: - ports: - # This i the port. - - port: 8080 - targetPort: 8080 - name: http - selector: - app: nginx - name: test -` - - output = `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: test - labels: - name: test - app: nginx - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/test_deployment.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/test_deployment.yaml' - spec: - replicas: 11 - selector: - matchLabels: - name: test - app: nginx - template: - metadata: - labels: - name: test - app: nginx - spec: - containers: - - name: test - image: nginx:v1.7 - ports: - - name: http - containerPort: 8080 - resources: - limits: - cpu: 500m -- apiVersion: v1 - kind: Service - metadata: - name: test - labels: - name: test - app: nginx - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/test_service.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/test_service.yaml' - spec: - selector: - name: test - app: nginx - ports: - # This i the port. - - name: http - port: 8080 - targetPort: 8080 -` - - outputNoMerge = `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: test - labels: - name: test - app: nginx - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/test_deployment.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/test_deployment.yaml' - spec: - replicas: 11 - selector: - matchLabels: - name: test - app: nginx - template: - metadata: - labels: - name: test - app: nginx - spec: - containers: - - name: test - image: nginx:v1.7 - ports: - - name: http - containerPort: 8080 -- apiVersion: v1 - kind: Service - metadata: - name: test - labels: - name: test - app: nginx - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/test_service.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/test_service.yaml' - spec: - selector: - name: test - app: nginx - ports: - # This i the port. - - name: http - port: 8080 - targetPort: 8080 -` - - outputOverride = `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: mysql-deployment - namespace: myspace - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml' - spec: - replicas: 3 - template: - spec: - containers: - - name: mysql - image: mysql:1.7.9 -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: nosetters-deployment - namespace: myspace - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml' - spec: - replicas: 4 - template: - spec: - containers: - - name: nosetters - image: nosetters:1.7.7 -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: storage-deployment - namespace: myspace - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml' - spec: - replicas: 4 - template: - spec: - containers: - - name: storage - image: storage:1.7.7 -- apiVersion: apps/v1 - kind: Deployment - metadata: - name: test - labels: - name: test - app: nginx - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/test_deployment.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/test_deployment.yaml' - spec: - replicas: 11 - selector: - matchLabels: - name: test - app: nginx - template: - metadata: - labels: - name: test - app: nginx - spec: - containers: - - name: test - image: nginx:v1.9 - ports: - - name: http - containerPort: 8080 - resources: - limits: - cpu: 500m -- apiVersion: v1 - kind: Service - metadata: - name: test - labels: - name: test - app: nginx - annotations: - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'config/test_service.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'config/test_service.yaml' - spec: - selector: - name: test - app: nginx - ports: - # This i the port. - - name: http - port: 8080 - targetPort: 8080 -` -) - -func TestCmd_wrap(t *testing.T) { - testutil.SkipWindows(t) - - _, dir, _, ok := runtime.Caller(0) - if !assert.True(t, ok) { - t.FailNow() - } - dir = filepath.Dir(dir) - - c := GetWrapRunner() - c.Command.SetIn(bytes.NewBufferString(input)) - out := &bytes.Buffer{} - c.Command.SetOut(out) - args := []string{"--", filepath.Join(dir, "test", "test.sh")} - c.Command.SetArgs(args) - c.XArgs.Args = args - - if !assert.NoError(t, c.Command.Execute()) { - t.FailNow() - } - - assert.Equal(t, output, out.String()) -} - -func TestCmd_wrapNoMerge(t *testing.T) { - testutil.SkipWindows(t) - - _, dir, _, ok := runtime.Caller(0) - if !assert.True(t, ok) { - t.FailNow() - } - dir = filepath.Dir(dir) - - c := GetWrapRunner() - c.getEnv = func(key string) string { - if key == KustMergeEnv { - return "false" - } - return "" - } - c.Command.SetIn(bytes.NewBufferString(input)) - out := &bytes.Buffer{} - c.Command.SetOut(out) - args := []string{"--", filepath.Join(dir, "test", "test.sh")} - c.Command.SetArgs(args) - c.XArgs.Args = args - if !assert.NoError(t, c.Command.Execute()) { - t.FailNow() - } - - assert.Equal(t, outputNoMerge, out.String()) -} - -func TestCmd_wrapOverride(t *testing.T) { - testutil.SkipWindows(t) - - _, dir, _, ok := runtime.Caller(0) - if !assert.True(t, ok) { - t.FailNow() - } - dir = filepath.Dir(dir) - - c := GetWrapRunner() - c.getEnv = func(key string) string { - if key == KustOverrideDirEnv { - return filepath.Join(dir, "test") - } - return "" - } - c.Command.SetIn(bytes.NewBufferString(input)) - out := &bytes.Buffer{} - c.Command.SetOut(out) - args := []string{"--", filepath.Join(dir, "test", "test.sh")} - c.Command.SetArgs(args) - c.XArgs.Args = args - if !assert.NoError(t, c.Command.Execute()) { - t.FailNow() - } - - assert.Equal(t, outputOverride, out.String()) -} diff --git a/cmd/config/internal/commands/cmdxargs.go b/cmd/config/internal/commands/cmdxargs.go deleted file mode 100644 index 135cc0de21..0000000000 --- a/cmd/config/internal/commands/cmdxargs.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "os" - "os/exec" - "strings" - "unicode" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -// GetXArgsRunner returns a command runner. -func GetXArgsRunner() *XArgsRunner { - r := &XArgsRunner{} - c := &cobra.Command{ - Use: "xargs -- CMD...", - Short: "Convert functionConfig to commandline flags and envs", - Long: `Convert functionConfig to commandline flags and envs. - -xargs reads a ResourceList from stdin and parses the functionConfig field. xargs then -reads each of the fields under .spec and parses them as flags. If the fields have non-scalar -values, then xargs encoded the values as yaml strings. - - CMD: - The command to run and pass the functionConfig as arguments. -`, - Example: ` -# given this example functionConfig in config.yaml -kind: Foo -spec: - flag1: value1 - flag2: value2 -items: -- 2 -- 1 - -# this command: -$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml run xargs -- app - -# is equivalent to this command: -$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | app --flag1=value1 --flag2=value2 2 1 - -# echo: prints the app arguments -$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- echo ---flag1=value1 --flag2=value2 2 1 - -# env: prints the app env -$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- env - -# cat: prints the app stdin -- prints the package contents and functionConfig wrapped in a -# ResourceList -$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs --no-flags -- env - -`, - RunE: r.runE, - PreRunE: r.preRunE, - SilenceUsage: true, - FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true}, - Args: cobra.MinimumNArgs(1), - } - r.Command = c - r.Command.Flags().BoolVar(&r.EnvOnly, "env-only", false, "only add env vars, not flags") - c.Flags().StringVar(&r.WrapKind, "wrap-kind", "List", "wrap the input xargs give to the command in this type.") - c.Flags().StringVar(&r.WrapVersion, "wrap-version", "v1", "wrap the input xargs give to the command in this type.") - return r -} - -// Runner contains the run function -type XArgsRunner struct { - Command *cobra.Command - Args []string - EnvOnly bool - WrapKind string - WrapVersion string -} - -func XArgsCommand() *cobra.Command { - return GetXArgsRunner().Command -} - -func (r *XArgsRunner) preRunE(_ *cobra.Command, _ []string) error { - _, err := fmt.Fprintln(os.Stderr, `Command "xargs" is deprecated, this will no longer be available in kustomize v5. -See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`) - return err -} - -func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error { - if len(r.Args) == 0 { - r.Args = os.Args - } - cmdIndex := -1 - for i := range r.Args { - if r.Args[i] == "--" { - cmdIndex = i + 1 - break - } - } - if cmdIndex < 0 { - return fmt.Errorf("must specify -- before command") - } - r.Args = r.Args[cmdIndex:] - run := exec.Command(r.Args[0]) //nolint: gosec - - if len(r.Args) > 1 { - r.Args = r.Args[cmdIndex+1:] - } else { - r.Args = []string{} - } - run.Stdout = c.OutOrStdout() - run.Stderr = c.ErrOrStderr() - - rw := &kio.ByteReadWriter{ - Reader: c.InOrStdin(), - } - nodes, err := rw.Read() - if err != nil { - return err - } - - env := os.Environ() - - // append the config to the flags - if err = func() error { - if rw.FunctionConfig == nil { - return nil - } - str, err := rw.FunctionConfig.String() - if err != nil { - return err - } - // add the API object to the env - env = append(env, fmt.Sprintf("KUST_FUNCTION_CONFIG=%s", str)) - - // parse the fields - meta := rw.FunctionConfig.Field("metadata") - if meta != nil { - err = meta.Value.VisitFields(func(node *yaml.MapNode) error { - if !r.EnvOnly { - r.Args = append(r.Args, fmt.Sprintf("--%s=%s", - node.Key.YNode().Value, parseYNode(node.Value.YNode()))) - } - env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value), - node.Value.YNode().Value)) - return nil - }) - if err != nil { - return err - } - } - - spec := rw.FunctionConfig.Field("spec") - if spec != nil { - err = spec.Value.VisitFields(func(node *yaml.MapNode) error { - if !r.EnvOnly { - r.Args = append(r.Args, fmt.Sprintf("--%s=%s", - node.Key.YNode().Value, parseYNode(node.Value.YNode()))) - } - env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value), - node.Value.YNode().Value)) - return nil - }) - if err != nil { - return err - } - } - - if !r.EnvOnly { - items := rw.FunctionConfig.Field("items") - if items != nil { - err = items.Value.VisitElements(func(node *yaml.RNode) error { - r.Args = append(r.Args, parseYNode(node.YNode())) - return nil - }) - if err != nil { - return err - } - } - } - - if r.WrapKind != "" { - if kind := rw.FunctionConfig.Field("kind"); !kind.IsNilOrEmpty() { - kind.Value.YNode().Value = r.WrapKind - } - rw.WrappingKind = r.WrapKind - } - if r.WrapVersion != "" { - if version := rw.FunctionConfig.Field("apiVersion"); !version.IsNilOrEmpty() { - version.Value.YNode().Value = r.WrapVersion - } - rw.WrappingAPIVersion = r.WrapVersion - } - return nil - }(); err != nil { - return err - } - run.Args = append(run.Args, r.Args...) - run.Env = append(run.Env, env...) - - // write ResourceList to stdin - if err = func() error { - in, err := run.StdinPipe() - if err != nil { - return err - } - defer in.Close() - rw.Writer = in - if r.WrapKind != kio.ResourceListKind { - rw.FunctionConfig = nil - } - return rw.Write(nodes) - }(); err != nil { - return err - } - - return runner.HandleError(c, run.Run()) -} - -func parseYNode(node *yaml.Node) string { - node.Value = strings.TrimSpace(node.Value) - for _, b := range node.Value { - if unicode.IsSpace(b) { - // wrap in '' -- contains whitespace - return fmt.Sprintf("'%s'", node.Value) - } - } - return node.Value -} diff --git a/cmd/config/internal/commands/cmdxargs_test.go b/cmd/config/internal/commands/cmdxargs_test.go deleted file mode 100644 index b3276ce498..0000000000 --- a/cmd/config/internal/commands/cmdxargs_test.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" -) - -const ( - flagsInput = `kind: ResourceList -items: -- apiVersion: apps/v1 - kind: Deployment - spec: - template: - spec: - containers: - - name: nginx - image: nginx -- apiVersion: apps/v1 - kind: Service - spec: {} -functionConfig: - kind: Foo - spec: - a: b - c: d - e: f - items: - - 1 - - 3 - - 2 - - 4 -` - - resourceInput = `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- apiVersion: apps/v1 - kind: Deployment - spec: - template: - spec: - containers: - - name: nginx - image: nginx -- apiVersion: apps/v1 - kind: Service - spec: {} -functionConfig: - kind: Foo -` - - resourceOutput = `apiVersion: v1 -kind: List -items: -- apiVersion: apps/v1 - kind: Deployment - spec: - template: - spec: - containers: - - name: nginx - image: nginx -- apiVersion: apps/v1 - kind: Service - spec: {} -` -) - -func TestXArgs_flags(t *testing.T) { - c := commands.GetXArgsRunner() - c.Command.SetIn(bytes.NewBufferString(flagsInput)) - out := &bytes.Buffer{} - c.Command.SetOut(out) - c.Command.SetArgs([]string{"--", "echo"}) - - c.Args = []string{"--", "echo"} - if !assert.NoError(t, c.Command.Execute()) { - t.FailNow() - } - assert.Equal(t, strings.TrimSpace(`--a=b --c=d --e=f 1 3 2 4`), strings.TrimSpace(out.String())) -} - -func TestXArgs_input(t *testing.T) { - c := commands.GetXArgsRunner() - c.Command.SetIn(bytes.NewBufferString(resourceInput)) - out := &bytes.Buffer{} - c.Command.SetOut(out) - c.Command.SetArgs([]string{"--", "cat"}) - - c.Args = []string{"--", "cat"} - if !assert.NoError(t, c.Command.Execute()) { - t.FailNow() - } - assert.Equal(t, resourceOutput, out.String()) -} - -func TestCmd_env(t *testing.T) { - c := commands.GetXArgsRunner() - c.Command.SetIn(bytes.NewBufferString(flagsInput)) - out := &bytes.Buffer{} - c.Command.SetOut(out) - c.Command.SetArgs([]string{"--env-only", "--", "env"}) - - c.Args = []string{"--", "env"} - if !assert.NoError(t, c.Command.Execute()) { - t.FailNow() - } - assert.Contains(t, out.String(), "\nA=b\nC=d\nE=f\n") -} diff --git a/cmd/config/internal/commands/e2e/create_setter_test.go b/cmd/config/internal/commands/e2e/create_setter_test.go deleted file mode 100644 index 999ec0eeb9..0000000000 --- a/cmd/config/internal/commands/e2e/create_setter_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import ( - "testing" -) - -func TestCreateSetter(t *testing.T) { - tests := []test{ - { - name: "create_setter", - args: []string{"cfg", "create-setter", ".", "replicas", "3"}, - files: map[string]string{ - "deployment.yaml": ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - labels: - app: nginx -spec: - replicas: 3 -`, - "Krmfile": ` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -`, - }, - expectedFiles: map[string]string{ - "deployment.yaml": ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - labels: - app: nginx -spec: - replicas: 3 # {"$openapi":"replicas"} -`, - "Krmfile": ` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -openAPI: - definitions: - io.k8s.cli.setters.replicas: - x-k8s-cli: - setter: - name: replicas - value: "3" -`, - }, - }, - } - runTests(t, tests) -} diff --git a/cmd/config/internal/commands/e2e/init_test.go b/cmd/config/internal/commands/e2e/init_test.go deleted file mode 100644 index 06e93b0b96..0000000000 --- a/cmd/config/internal/commands/e2e/init_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import "testing" - -func TestInit(t *testing.T) { - tests := []test{ - { - name: "init", - args: []string{"cfg", "init"}, - expectedFiles: map[string]string{ - "Krmfile": ` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -`, - }, - }, - - { - name: "init", - args: []string{"cfg", "init", "."}, - expectedFiles: map[string]string{ - "Krmfile": ` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -`, - }, - }, - } - runTests(t, tests) -} diff --git a/cmd/config/internal/commands/e2e/list_setters_test.go b/cmd/config/internal/commands/e2e/list_setters_test.go deleted file mode 100644 index 93f8862275..0000000000 --- a/cmd/config/internal/commands/e2e/list_setters_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import "testing" - -func TestListSetters(t *testing.T) { - tests := []test{ - { - name: "set", - args: []string{"cfg", "list-setters", "."}, - files: map[string]string{ - "deployment.yaml": ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - labels: - app: nginx -spec: - replicas: 3 # {"$openapi":"replicas"} -`, - "Krmfile": ` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -openAPI: - definitions: - io.k8s.cli.setters.replicas: - x-k8s-cli: - setter: - name: replicas - value: "3" -`, - }, - expectedStdOut: ` -./ - NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET - replicas 3 1 No No -`, - }, - } - runTests(t, tests) -} diff --git a/cmd/config/internal/commands/e2e/set_test.go b/cmd/config/internal/commands/e2e/set_test.go deleted file mode 100644 index ca9a21bd59..0000000000 --- a/cmd/config/internal/commands/e2e/set_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import "testing" - -func TestSet(t *testing.T) { - tests := []test{ - { - name: "set", - args: []string{"cfg", "set", ".", "replicas", "4"}, - files: map[string]string{ - "deployment.yaml": ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - labels: - app: nginx -spec: - replicas: 3 # {"$openapi":"replicas"} -`, - "Krmfile": ` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -openAPI: - definitions: - io.k8s.cli.setters.replicas: - x-k8s-cli: - setter: - name: replicas - value: "3" -`, - }, - expectedFiles: map[string]string{ - "deployment.yaml": ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - labels: - app: nginx -spec: - replicas: 4 # {"$openapi":"replicas"} -`, - "Krmfile": ` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -openAPI: - definitions: - io.k8s.cli.setters.replicas: - x-k8s-cli: - setter: - name: replicas - value: "4" - isSet: true -`, - }, - }, - } - runTests(t, tests) -} diff --git a/cmd/config/internal/commands/fmt.go b/cmd/config/internal/commands/fmt.go deleted file mode 100644 index e658cf2b25..0000000000 --- a/cmd/config/internal/commands/fmt.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/kio/filters" -) - -// FmtCmd returns a command FmtRunner. -func GetFmtRunner(name string) *FmtRunner { - r := &FmtRunner{} - c := &cobra.Command{ - Use: "fmt DIR...", - Short: commands.FmtShort, - Long: commands.FmtLong, - Example: commands.FmtExamples, - RunE: r.runE, - PreRunE: r.preRunE, - Deprecated: "imperative formatting will no longer be available in kustomize v5.\n" + - "Declare a formatting transformer in your kustomization instead.", - } - runner.FixDocs(name, c) - c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern, - `pattern to use for generating filenames for resources -- may contain the following -formatting substitution verbs {'%n': 'metadata.name', '%s': 'metadata.namespace', '%k': 'kind'}`) - c.Flags().BoolVar(&r.SetFilenames, "set-filenames", false, - `if true, set default filenames on Resources without them`) - c.Flags().BoolVar(&r.KeepAnnotations, "keep-annotations", false, - `if true, keep index and filename annotations set on Resources.`) - c.Flags().BoolVar(&r.Override, "override", false, - `if true, override existing filepath annotations.`) - c.Flags().BoolVar(&r.UseSchema, "use-schema", false, - `if true, uses openapi resource schema to format resources.`) - c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false, - "formats resource files recursively in all the nested subpackages") - r.Command = c - return r -} - -func FmtCommand(name string) *cobra.Command { - return GetFmtRunner(name).Command -} - -// FmtRunner contains the run function -type FmtRunner struct { - Command *cobra.Command - FilenamePattern string - SetFilenames bool - KeepAnnotations bool - Override bool - UseSchema bool - RecurseSubPackages bool -} - -func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error { - if r.SetFilenames { - r.KeepAnnotations = true - } - return nil -} - -func (r *FmtRunner) runE(c *cobra.Command, args []string) error { - // format stdin if there are no args - if len(args) == 0 { - rw := &kio.ByteReadWriter{ - Reader: c.InOrStdin(), - Writer: c.OutOrStdout(), - KeepReaderAnnotations: r.KeepAnnotations, - } - return runner.HandleError(c, kio.Pipeline{ - Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute()) - } - - for _, rootPkgPath := range args { - e := runner.ExecuteCmdOnPkgs{ - Writer: c.OutOrStdout(), - NeedOpenAPI: false, - RecurseSubPackages: r.RecurseSubPackages, - CmdRunner: r, - RootPkgPath: rootPkgPath, - } - - err := e.Execute() - if err != nil { - return err - } - } - return nil -} - -func (r *FmtRunner) ExecuteCmd(w io.Writer, pkgPath string) error { - rw := &kio.LocalPackageReadWriter{ - NoDeleteFiles: true, - PackagePath: pkgPath, - KeepReaderAnnotations: r.KeepAnnotations, PackageFileName: ext.KRMFileName()} - err := kio.Pipeline{ - Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute() - - if err != nil { - // return err if RecurseSubPackages is false - if !r.RecurseSubPackages { - return err - } - // print error message and continue if RecurseSubPackages is true - fmt.Fprintf(w, "%s\n", err.Error()) - } else { - fmt.Fprint(w, "formatted resource files in the package\n") - } - return nil -} - -func (r *FmtRunner) fmtFilters() []kio.Filter { - fmtFilters := []kio.Filter{filters.FormatFilter{ - UseSchema: r.UseSchema, - }} - - // format with file names - if r.SetFilenames { - fmtFilters = append(fmtFilters, &filters.FileSetter{ - FilenamePattern: r.FilenamePattern, - Override: r.Override, - }) - } - return fmtFilters -} diff --git a/cmd/config/internal/commands/fmt_test.go b/cmd/config/internal/commands/fmt_test.go deleted file mode 100644 index 816718ad46..0000000000 --- a/cmd/config/internal/commands/fmt_test.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/copyutil" - "sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/testutil" -) - -// TestCmd_files verifies the fmt command formats the files -func TestFmtCommand_files(t *testing.T) { - f1, err := os.CreateTemp("", "cmdfmt*.yaml") - if !assert.NoError(t, err) { - return - } - defer os.RemoveAll(f1.Name()) - err = os.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600) - if !assert.NoError(t, err) { - return - } - - f2, err := os.CreateTemp("", "cmdfmt*.yaml") - if !assert.NoError(t, err) { - return - } - defer os.RemoveAll(f2.Name()) - err = os.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600) - if !assert.NoError(t, err) { - return - } - - // fmt the files - r := commands.GetFmtRunner("") - r.Command.SetArgs([]string{f1.Name(), f2.Name()}) - err = r.Command.Execute() - if !assert.NoError(t, err) { - return - } - - // verify the contents - b, err := os.ReadFile(f1.Name()) - if !assert.NoError(t, err) { - return - } - if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) { - return - } - - b, err = os.ReadFile(f2.Name()) - if !assert.NoError(t, err) { - return - } - if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) { - return - } -} - -func TestFmtCommand_stdin(t *testing.T) { - out := &bytes.Buffer{} - r := commands.GetFmtRunner("") - r.Command.SetOut(out) - r.Command.SetIn(bytes.NewReader(testyaml.UnformattedYaml1)) - - // fmt the input - err := r.Command.Execute() - assert.NoError(t, err) - - // verify the output - assert.Contains(t, out.String(), string(testyaml.FormattedYaml1)) -} - -// TestCmd_filesAndstdin verifies that if both files and stdin input are provided, only -// the files are formatted and the input is ignored -func TestFmtCmd_filesAndStdin(t *testing.T) { - f1, err := os.CreateTemp("", "cmdfmt*.yaml") - if !assert.NoError(t, err) { - return - } - err = os.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600) - if !assert.NoError(t, err) { - return - } - - f2, err := os.CreateTemp("", "cmdfmt*.yaml") - if !assert.NoError(t, err) { - return - } - err = os.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600) - if !assert.NoError(t, err) { - return - } - - out := &bytes.Buffer{} - in := &bytes.Buffer{} - r := commands.GetFmtRunner("") - r.Command.SetOut(out) - r.Command.SetIn(in) - - // fmt the files - r = commands.GetFmtRunner("") - r.Command.SetArgs([]string{f1.Name(), f2.Name()}) - err = r.Command.Execute() - if !assert.NoError(t, err) { - return - } - - // verify the output - b, err := os.ReadFile(f1.Name()) - if !assert.NoError(t, err) { - return - } - if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) { - return - } - - b, err = os.ReadFile(f2.Name()) - if !assert.NoError(t, err) { - return - } - if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) { - return - } - err = r.Command.Execute() - if !assert.NoError(t, err) { - return - } - - if !assert.Equal(t, "", out.String()) { - return - } -} - -// TestCmd_files verifies the fmt command formats the files -func TestCmd_failFiles(t *testing.T) { - // fmt the files - r := commands.GetFmtRunner("") - r.Command.SetArgs([]string{"notrealfile"}) - r.Command.SilenceUsage = true - r.Command.SilenceErrors = true - err := r.Command.Execute() - testutil.AssertErrorContains(t, err, "notrealfile:") -} - -// TestCmd_files verifies the fmt command formats the files -func TestCmd_failFileContents(t *testing.T) { - out := &bytes.Buffer{} - r := commands.GetFmtRunner("") - r.Command.SetOut(out) - r.Command.SetIn(strings.NewReader(`{`)) - - // fmt the input - err := r.Command.Execute() - - // expect an error - assert.EqualError(t, err, "MalformedYAMLError: yaml: line 1: did not find expected node content") -} - -func TestFmtSubPackages(t *testing.T) { - var tests = []struct { - name string - dataset string - packagePath string - args []string - expected string - }{ - { - name: "fmt-recurse-subpackages", - dataset: "dataset-with-setters", - args: []string{"-R"}, - expected: `${baseDir}/ -formatted resource files in the package - -${baseDir}/mysql/ -formatted resource files in the package - -${baseDir}/mysql/nosetters/ -formatted resource files in the package - -${baseDir}/mysql/storage/ -formatted resource files in the package -`, - }, - { - name: "fmt-top-level-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql", - expected: `${baseDir}/mysql/ -formatted resource files in the package -`, - }, - { - name: "fmt-nested-pkg-no-recurse-subpackages", - dataset: "dataset-without-setters", - packagePath: "mysql/storage", - expected: `${baseDir}/mysql/storage/ -formatted resource files in the package -`, - }, - } - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - // reset the openAPI afterward - openapi.ResetOpenAPI() - defer openapi.ResetOpenAPI() - sourceDir := filepath.Join("test", "testdata", test.dataset) - baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) - runner := commands.GetFmtRunner("") - actual := &bytes.Buffer{} - runner.Command.SetOut(actual) - runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...)) - err := runner.Command.Execute() - if !assert.NoError(t, err) { - t.FailNow() - } - - // normalize path format for windows - actualNormalized := strings.ReplaceAll( - strings.ReplaceAll(actual.String(), "\\", "/"), - "//", "/") - - expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir) - expectedNormalized := strings.ReplaceAll(expected, "\\", "/") - if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) { - t.FailNow() - } - }) - } -} diff --git a/cmd/config/internal/commands/init_test.go b/cmd/config/internal/commands/init_test.go deleted file mode 100644 index e902fa9ba9..0000000000 --- a/cmd/config/internal/commands/init_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" -) - -func TestInit_args(t *testing.T) { - d := t.TempDir() - - // fmt the files - b := &bytes.Buffer{} - r := commands.GetInitRunner("") - r.Command.SetArgs([]string{d}) - r.Command.SetOut(b) - if !assert.NoError(t, r.Command.Execute()) { - t.FailNow() - } - - actual, err := os.ReadFile(filepath.Join(d, "Krmfile")) - if !assert.NoError(t, err) { - t.FailNow() - } - - if !assert.Equal(t, strings.TrimSpace(` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -`), strings.TrimSpace(string(actual))) { - t.FailNow() - } - - if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5. -See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953. -`, b.String()) { - t.FailNow() - } -} - -func TestInit_noargs(t *testing.T) { - d := t.TempDir() - - cwd, err := os.Getwd() - if !assert.NoError(t, err) { - t.FailNow() - } - - if !assert.NoError(t, os.Chdir(d)) { - t.FailNow() - } - - t.Cleanup(func() { - if !assert.NoError(t, os.Chdir(cwd)) { - t.FailNow() - } - }) - - b := &bytes.Buffer{} - r := commands.GetInitRunner("") - r.Command.SetOut(b) - if !assert.NoError(t, r.Command.Execute()) { - t.FailNow() - } - - actual, err := os.ReadFile(filepath.Join(d, "Krmfile")) - if !assert.NoError(t, err) { - t.FailNow() - } - - if !assert.Equal(t, strings.TrimSpace(` -apiVersion: config.k8s.io/v1alpha1 -kind: Krmfile -`), strings.TrimSpace(string(actual))) { - t.FailNow() - } - - if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5. -See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953. -`, b.String()) { - t.FailNow() - } -} diff --git a/cmd/config/internal/commands/merge.go b/cmd/config/internal/commands/merge.go deleted file mode 100644 index 37cc07ca30..0000000000 --- a/cmd/config/internal/commands/merge.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/kio/filters" -) - -func GetMergeRunner(name string) *MergeRunner { - r := &MergeRunner{} - c := &cobra.Command{ - Use: "merge [SOURCE_DIR...] [DESTINATION_DIR]", - Short: commands.MergeShort, - Long: commands.MergeLong, - Example: commands.MergeExamples, - RunE: r.runE, - Deprecated: "this will no longer be available in kustomize v5.\n" + - "See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.", - } - runner.FixDocs(name, c) - r.Command = c - r.Command.Flags().BoolVar(&r.InvertOrder, "invert-order", false, - "if true, merge Resources in the reverse order") - return r -} - -func MergeCommand(name string) *cobra.Command { - return GetMergeRunner(name).Command -} - -// MergeRunner contains the run function -type MergeRunner struct { - Command *cobra.Command - InvertOrder bool -} - -func (r *MergeRunner) runE(c *cobra.Command, args []string) error { - var inputs []kio.Reader - // add the packages in reverse order -- the arg list should be highest precedence first - // e.g. merge from -> to, but the MergeFilter is highest precedence last - for i := len(args) - 1; i >= 0; i-- { - inputs = append(inputs, kio.LocalPackageReader{PackagePath: args[i]}) - } - // if there is no "from" package, read from stdin - rw := &kio.ByteReadWriter{ - Reader: c.InOrStdin(), - Writer: c.OutOrStdout(), - KeepReaderAnnotations: true, - } - if len(inputs) < 2 { - inputs = append(inputs, rw) - } - - // write to the "to" package if specified - var outputs []kio.Writer - if len(args) != 0 { - outputs = append(outputs, kio.LocalPackageWriter{PackagePath: args[len(args)-1]}) - } - // if there is no "to" package, write to stdout - if len(outputs) == 0 { - outputs = append(outputs, rw) - } - - filters := []kio.Filter{filters.MergeFilter{}, filters.FormatFilter{}} - return runner.HandleError(c, kio.Pipeline{Inputs: inputs, Filters: filters, Outputs: outputs}.Execute()) -} diff --git a/cmd/config/internal/commands/merge3.go b/cmd/config/internal/commands/merge3.go deleted file mode 100644 index a01bd98122..0000000000 --- a/cmd/config/internal/commands/merge3.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/kio/filters" -) - -func GetMerge3Runner(name string) *Merge3Runner { - r := &Merge3Runner{} - c := &cobra.Command{ - Use: "merge3 --ancestor [ORIGINAL_DIR] --from [UPDATED_DIR] --to [DESTINATION_DIR]", - Short: commands.Merge3Short, - Long: commands.Merge3Long, - Example: commands.Merge3Examples, - RunE: r.runE, - Deprecated: "this will no longer be available in kustomize v5.\n" + - "See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.", - } - runner.FixDocs(name, c) - c.Flags().StringVar(&r.ancestor, "ancestor", "", - "Path to original package") - c.Flags().StringVar(&r.fromDir, "from", "", - "Path to updated package") - c.Flags().StringVar(&r.toDir, "to", "", - "Path to destination package") - c.Flags().BoolVar(&r.path, "path-merge-key", false, - "Use the path as part of the merge key when merging resources") - - r.Command = c - return r -} - -func Merge3Command(name string) *cobra.Command { - return GetMerge3Runner(name).Command -} - -// Merge3Runner contains the run function -type Merge3Runner struct { - Command *cobra.Command - ancestor string - fromDir string - toDir string - path bool -} - -func (r *Merge3Runner) runE(_ *cobra.Command, _ []string) error { - matcher := filters.DefaultGVKNNMatcher{MergeOnPath: r.path} - err := filters.Merge3{ - OriginalPath: r.ancestor, - UpdatedPath: r.fromDir, - DestPath: r.toDir, - Matcher: &matcher, - }.Merge() - if err != nil { - return err - } - return nil -} diff --git a/cmd/config/internal/commands/merge3_test.go b/cmd/config/internal/commands/merge3_test.go deleted file mode 100644 index bafb1f4dda..0000000000 --- a/cmd/config/internal/commands/merge3_test.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" - "sigs.k8s.io/kustomize/kyaml/copyutil" -) - -// TestMerge3Command verifies the merge3 correctly applies the diff between 2 sets of resources into another -func TestMerge3Command(t *testing.T) { - datadir := t.TempDir() - - err := os.WriteFile(filepath.Join(datadir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 -kind: Deployment -metadata: - name: app - labels: - app: java -spec: - replicas: 1 - selector: - matchLabels: - app: java - template: - metadata: - labels: - app: java - spec: - restartPolicy: Always - containers: - - name: app - image: gcr.io/project/app:version - command: - - java - - -jar - - /app.jar - ports: - - containerPort: 8080 - envFrom: - - configMapRef: - name: app-config - env: - - name: JAVA_OPTS - value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom - imagePullPolicy: Always - minReadySeconds: 5 -`), 0600) - if !assert.NoError(t, err) { - return - } - - expectedDir := t.TempDir() - - err = os.WriteFile(filepath.Join(expectedDir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 -kind: Deployment -metadata: - name: app - labels: - app: java - new-local: label - new-remote: label -spec: - replicas: 3 - selector: - matchLabels: - app: java - template: - metadata: - labels: - app: java - spec: - restartPolicy: Always - containers: - - name: app - image: gcr.io/project/app:version - command: - - java - - -jar - - /app.jar - - otherstuff - args: - - foo - ports: - - containerPort: 8080 - envFrom: - - configMapRef: - name: app-config - env: - - name: JAVA_OPTS - value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom - imagePullPolicy: Always - minReadySeconds: 20 -`), 0600) - if !assert.NoError(t, err) { - return - } - - updatedDir := t.TempDir() - - err = os.WriteFile(filepath.Join(updatedDir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 -kind: Deployment -metadata: - name: app - labels: - app: java - new-remote: label -spec: - replicas: 3 - selector: - matchLabels: - app: java - template: - metadata: - labels: - app: java - spec: - restartPolicy: Always - containers: - - name: app - image: gcr.io/project/app:version - command: - - java - - -jar - - /app.jar - - otherstuff - ports: - - containerPort: 8080 - envFrom: - - configMapRef: - name: app-config - env: - - name: JAVA_OPTS - value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom - imagePullPolicy: Always - minReadySeconds: 5 -`), 0600) - if !assert.NoError(t, err) { - return - } - - destDir := t.TempDir() - - err = os.WriteFile(filepath.Join(destDir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1 -kind: Deployment -metadata: - name: app - labels: - app: java - new-local: label -spec: - replicas: 2 - selector: - matchLabels: - app: java - template: - metadata: - labels: - app: java - spec: - restartPolicy: Always - containers: - - name: app - image: gcr.io/project/app:version - command: - - java - - -jar - - /app.jar - args: - - foo - ports: - - containerPort: 8080 - envFrom: - - configMapRef: - name: app-config - env: - - name: JAVA_OPTS - value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom - imagePullPolicy: Always - minReadySeconds: 20 -`), 0600) - if !assert.NoError(t, err) { - return - } - - // Perform merge3 with newly created sets - r := commands.GetMerge3Runner("") - r.Command.SetArgs([]string{ - "--ancestor", - datadir, - "--from", - updatedDir, - "--to", - destDir, - }) - if !assert.NoError(t, r.Command.Execute()) { - return - } - - diffs, err := copyutil.Diff(destDir, expectedDir) - if !assert.NoError(t, err) { - t.FailNow() - } - - // Verify there are no diffs - if !assert.Empty(t, diffs.List()) { - t.FailNow() - } -} diff --git a/cmd/config/internal/commands/sink.go b/cmd/config/internal/commands/sink.go deleted file mode 100644 index 653c053022..0000000000 --- a/cmd/config/internal/commands/sink.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/kio/kioutil" -) - -// GetSinkRunner returns a command for Sink. -func GetSinkRunner(name string) *SinkRunner { - r := &SinkRunner{} - c := &cobra.Command{ - Use: "sink DIR", - Short: commands.SinkShort, - Long: commands.SinkLong, - Example: commands.SinkExamples, - RunE: r.runE, - PreRunE: r.preRunE, - Args: cobra.MaximumNArgs(1), - } - runner.FixDocs(name, c) - r.Command = c - return r -} - -func SinkCommand(name string) *cobra.Command { - return GetSinkRunner(name).Command -} - -// SinkRunner contains the run function -type SinkRunner struct { - Command *cobra.Command -} - -func (r *SinkRunner) preRunE(c *cobra.Command, args []string) error { - _, err := fmt.Fprintln(os.Stderr, `Command "sink" is deprecated, this will no longer be available in kustomize v5. -See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`) - return err -} - -func (r *SinkRunner) runE(c *cobra.Command, args []string) error { - var outputs []kio.Writer - if len(args) == 1 { - outputs = []kio.Writer{&kio.LocalPackageWriter{PackagePath: args[0]}} - } else { - outputs = []kio.Writer{&kio.ByteWriter{ - Writer: c.OutOrStdout(), - ClearAnnotations: []string{kioutil.PathAnnotation, kioutil.LegacyPathAnnotation}}, - } - } - - err := kio.Pipeline{ - Inputs: []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}}, - Outputs: outputs}.Execute() - return runner.HandleError(c, err) -} diff --git a/cmd/config/internal/commands/sink_test.go b/cmd/config/internal/commands/sink_test.go deleted file mode 100644 index b992fe2816..0000000000 --- a/cmd/config/internal/commands/sink_test.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" -) - -func TestSinkCommand(t *testing.T) { - d := t.TempDir() - - r := commands.GetSinkRunner("") - r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- kind: Deployment - metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - spec: - replicas: 1 -- kind: Service - metadata: - name: foo - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - spec: - selector: - app: nginx -- apiVersion: v1 - kind: Abstraction - metadata: - name: foo - annotations: - config.kubernetes.io/function: | - container: - image: gcr.io/example/reconciler:v1 - config.kubernetes.io/local-config: "true" - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - spec: - replicas: 3 -- apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - spec: - replicas: 3 -`)) - r.Command.SetArgs([]string{d}) - if !assert.NoError(t, r.Command.Execute()) { - t.FailNow() - } - - actual, err := os.ReadFile(filepath.Join(d, "f1.yaml")) - if !assert.NoError(t, err) { - t.FailNow() - } - expected := `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx -spec: - selector: - app: nginx -` - if !assert.Equal(t, expected, string(actual)) { - t.FailNow() - } - - actual, err = os.ReadFile(filepath.Join(d, "f2.yaml")) - if !assert.NoError(t, err) { - t.FailNow() - } - expected = `apiVersion: v1 -kind: Abstraction -metadata: - name: foo - annotations: - config.kubernetes.io/function: | - container: - image: gcr.io/example/reconciler:v1 - config.kubernetes.io/local-config: "true" -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx -spec: - replicas: 3 -` - if !assert.Equal(t, expected, string(actual)) { - t.FailNow() - } -} - -func TestSinkCommandJSON(t *testing.T) { - d := t.TempDir() - - r := commands.GetSinkRunner("") - r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", - "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', - config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}} -`)) - r.Command.SetArgs([]string{d}) - if !assert.NoError(t, r.Command.Execute()) { - t.FailNow() - } - - actual, err := os.ReadFile(filepath.Join(d, "f1.json")) - if !assert.NoError(t, err) { - t.FailNow() - } - expected := `{ - "kind": "Deployment", - "metadata": { - "annotations": { - "app": "nginx2" - }, - "labels": { - "app": "nginx2" - }, - "name": "foo" - }, - "spec": { - "replicas": 1 - } -} -` - if !assert.Equal(t, expected, string(actual)) { - t.FailNow() - } -} - -func TestSinkCommand_Stdout(t *testing.T) { - // fmt the files - out := &bytes.Buffer{} - r := commands.GetSinkRunner("") - r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- kind: Deployment - metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - spec: - replicas: 1 -- kind: Service - metadata: - name: foo - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - spec: - selector: - app: nginx -- apiVersion: v1 - kind: Abstraction - metadata: - name: foo - annotations: - config.kubernetes.io/function: | - container: - image: gcr.io/example/reconciler:v1 - config.kubernetes.io/local-config: "true" - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - spec: - replicas: 3 -- apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - spec: - replicas: 3 -`)) - - r.Command.SetOut(out) - r.Command.SetArgs([]string{}) - if !assert.NoError(t, r.Command.Execute()) { - t.FailNow() - } - - expected := `kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx -spec: - selector: - app: nginx ---- -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - annotations: - config.kubernetes.io/function: | - container: - image: gcr.io/example/reconciler:v1 - config.kubernetes.io/local-config: "true" -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx -spec: - replicas: 3 -` - if !assert.Equal(t, expected, out.String()) { - t.FailNow() - } -} - -func TestSinkCommandJSON_Stdout(t *testing.T) { - // fmt the files - out := &bytes.Buffer{} - r := commands.GetSinkRunner("") - r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", - "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', - config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}} -`)) - - r.Command.SetOut(out) - r.Command.SetArgs([]string{}) - if !assert.NoError(t, r.Command.Execute()) { - t.FailNow() - } - - expected := `{ - "kind": "Deployment", - "metadata": { - "annotations": { - "app": "nginx2" - }, - "labels": { - "app": "nginx2" - }, - "name": "foo" - }, - "spec": { - "replicas": 1 - } -} -` - if !assert.Equal(t, expected, out.String()) { - println(out.String()) - t.FailNow() - } -} diff --git a/cmd/config/internal/commands/source.go b/cmd/config/internal/commands/source.go deleted file mode 100644 index bce021dea6..0000000000 --- a/cmd/config/internal/commands/source.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/cmd/config/runner" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -// GetSourceRunner returns a command for Source. -func GetSourceRunner(name string) *SourceRunner { - r := &SourceRunner{} - c := &cobra.Command{ - Use: "source DIR", - Short: commands.SourceShort, - Long: commands.SourceLong, - Example: commands.SourceExamples, - RunE: r.runE, - PreRunE: r.preRunE, - } - runner.FixDocs(name, c) - c.Flags().StringVar(&r.WrapKind, "wrap-kind", kio.ResourceListKind, - "output using this format.") - c.Flags().StringVar(&r.WrapApiVersion, "wrap-version", kio.ResourceListAPIVersion, - "output using this format.") - c.Flags().StringVar(&r.FunctionConfig, "function-config", "", - "path to function config.") - r.Command = c - _ = c.MarkFlagFilename("function-config", "yaml", "json", "yml") - return r -} - -func SourceCommand(name string) *cobra.Command { - return GetSourceRunner(name).Command -} - -// SourceRunner contains the run function -type SourceRunner struct { - WrapKind string - WrapApiVersion string - FunctionConfig string - Command *cobra.Command -} - -func (r *SourceRunner) preRunE(c *cobra.Command, args []string) error { - _, err := fmt.Fprintln(os.Stderr, `Command "source" is deprecated, this will no longer be available in kustomize v5. -See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`) - return err -} - -func (r *SourceRunner) runE(c *cobra.Command, args []string) error { - // if there is a function-config specified, emit it - var functionConfig *yaml.RNode - if r.FunctionConfig != "" { - configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig}.Read() - if err != nil { - return err - } - if len(configs) != 1 { - return fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs)) - } - functionConfig = configs[0] - } - - var outputs []kio.Writer - outputs = append(outputs, kio.ByteWriter{ - Writer: c.OutOrStdout(), - KeepReaderAnnotations: true, - WrappingKind: r.WrapKind, - WrappingAPIVersion: r.WrapApiVersion, - FunctionConfig: functionConfig, - }) - - var inputs []kio.Reader - for _, a := range args { - inputs = append(inputs, kio.LocalPackageReader{PackagePath: a, MatchFilesGlob: kio.MatchAll}) - } - if len(inputs) == 0 { - inputs = []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}} - } - - err := kio.Pipeline{Inputs: inputs, Outputs: outputs}.Execute() - return runner.HandleError(c, err) -} diff --git a/cmd/config/internal/commands/source_test.go b/cmd/config/internal/commands/source_test.go deleted file mode 100644 index f128da342f..0000000000 --- a/cmd/config/internal/commands/source_test.go +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/cmd/config/internal/commands" -) - -func TestSourceCommand(t *testing.T) { - d := t.TempDir() - - err := os.WriteFile(filepath.Join(d, "f1.yaml"), []byte(` -kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx -spec: - selector: - app: nginx -`), 0600) - if !assert.NoError(t, err) { - return - } - err = os.WriteFile(filepath.Join(d, "f2.yaml"), []byte(` -apiVersion: v1 -kind: Abstraction -metadata: - name: foo - annotations: - config.kubernetes.io/function: | - container: - image: gcr.io/example/reconciler:v1 - config.kubernetes.io/local-config: "true" -spec: - replicas: 3 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx -spec: - replicas: 3 -`), 0600) - if !assert.NoError(t, err) { - return - } - - // fmt the files - b := &bytes.Buffer{} - r := commands.GetSourceRunner("") - r.Command.SetArgs([]string{d}) - r.Command.SetOut(b) - if !assert.NoError(t, r.Command.Execute()) { - return - } - - if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- kind: Deployment - metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f1.yaml' - spec: - replicas: 1 -- kind: Service - metadata: - name: foo - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f1.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f1.yaml' - spec: - selector: - app: nginx -- apiVersion: v1 - kind: Abstraction - metadata: - name: foo - annotations: - config.kubernetes.io/function: | - container: - image: gcr.io/example/reconciler:v1 - config.kubernetes.io/local-config: "true" - config.kubernetes.io/index: '0' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '0' - internal.config.kubernetes.io/path: 'f2.yaml' - spec: - replicas: 3 -- apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - app: nginx - name: bar - annotations: - app: nginx - config.kubernetes.io/index: '1' - config.kubernetes.io/path: 'f2.yaml' - internal.config.kubernetes.io/index: '1' - internal.config.kubernetes.io/path: 'f2.yaml' - spec: - replicas: 3 -`, b.String()) { - return - } -} - -func TestSourceCommandJSON(t *testing.T) { - d := t.TempDir() - - err := os.WriteFile(filepath.Join(d, "f1.json"), []byte(` -{ - "kind": "Deployment", - "metadata": { - "labels": { - "app": "nginx2" - }, - "name": "foo", - "annotations": { - "app": "nginx2" - } - }, - "spec": { - "replicas": 1 - } -} -`), 0600) - if !assert.NoError(t, err) { - return - } - err = os.WriteFile(filepath.Join(d, "f2.json"), []byte(` -{ - "apiVersion": "v1", - "kind": "Abstraction", - "metadata": { - "name": "foo", - "annotations": { - "config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n", - "config.kubernetes.io/local-config": "true" - } - }, - "spec": { - "replicas": 3 - } -} -`), 0600) - if !assert.NoError(t, err) { - return - } - - // fmt the files - b := &bytes.Buffer{} - r := commands.GetSourceRunner("") - r.Command.SetArgs([]string{d}) - r.Command.SetOut(b) - if !assert.NoError(t, r.Command.Execute()) { - return - } - - if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- {"kind": "Deployment", `+ - `"metadata": {`+ - `"labels": {"app": "nginx2"}, `+ - `"name": "foo", `+ - `"annotations": {`+ - `"app": "nginx2", `+ - `config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f1.json'}}, `+ - `"spec": {"replicas": 1}} -- {"apiVersion": "v1", "kind": "Abstraction", `+ - `"metadata": {`+ - `"name": "foo", `+ - `"annotations": {`+ - `"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n", `+ - `"config.kubernetes.io/local-config": "true", `+ - `config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f2.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f2.json'}}, `+ - `"spec": {"replicas": 3}} -`, b.String()) { - return - } -} - -func TestSourceCommand_Stdin(t *testing.T) { - in := bytes.NewBufferString(` -kind: Deployment -metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 -spec: - replicas: 1 ---- -kind: Service -metadata: - name: foo - annotations: - app: nginx -spec: - selector: - app: nginx -`) - - out := &bytes.Buffer{} - r := commands.GetSourceRunner("") - r.Command.SetArgs([]string{}) - r.Command.SetIn(in) - r.Command.SetOut(out) - if !assert.NoError(t, r.Command.Execute()) { - return - } - - if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- kind: Deployment - metadata: - labels: - app: nginx2 - name: foo - annotations: - app: nginx2 - config.kubernetes.io/index: '0' - internal.config.kubernetes.io/index: '0' - spec: - replicas: 1 -- kind: Service - metadata: - name: foo - annotations: - app: nginx - config.kubernetes.io/index: '1' - internal.config.kubernetes.io/index: '1' - spec: - selector: - app: nginx -`, out.String()) { - return - } -} - -func TestSourceCommandJSON_Stdin(t *testing.T) { - in := bytes.NewBufferString(` -{ - "kind": "Deployment", - "metadata": { - "labels": { - "app": "nginx2" - }, - "name": "foo", - "annotations": { - "app": "nginx2" - } - }, - "spec": { - "replicas": 1 - } -} -`) - - out := &bytes.Buffer{} - r := commands.GetSourceRunner("") - r.Command.SetArgs([]string{}) - r.Command.SetIn(in) - r.Command.SetOut(out) - if !assert.NoError(t, r.Command.Execute()) { - return - } - - if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1 -kind: ResourceList -items: -- {"kind": "Deployment", `+ - `"metadata": {`+ - `"labels": {"app": "nginx2"}, `+ - `"name": "foo", `+ - `"annotations": {`+ - `"app": "nginx2", `+ - `config.kubernetes.io/index: '0', internal.config.kubernetes.io/index: '0'}}, `+ - `"spec": {"replicas": 1}} -`, out.String()) { - return - } -} diff --git a/cmd/config/runner/runner.go b/cmd/config/runner/runner.go index 6e4adb0e18..5cde3ac619 100644 --- a/cmd/config/runner/runner.go +++ b/cmd/config/runner/runner.go @@ -1,6 +1,5 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -// package runner import (