diff --git a/kustomize/commands/edit/add/addbuildmetadata.go b/kustomize/commands/edit/add/addbuildmetadata.go index 43d3ffb36db..5a910c87216 100644 --- a/kustomize/commands/edit/add/addbuildmetadata.go +++ b/kustomize/commands/edit/add/addbuildmetadata.go @@ -4,17 +4,15 @@ package add import ( - "errors" "fmt" - "strings" - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile" + "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util" "sigs.k8s.io/kustomize/kyaml/filesys" ) type addBuildMetadataOptions struct { + *util.BuildMetadataValidator buildMetadataOptions []string } @@ -25,7 +23,7 @@ func newCmdAddBuildMetadata(fSys filesys.FileSystem) *cobra.Command { cmd := &cobra.Command{ Use: "buildmetadata", Short: "Adds one or more buildMetadata options to the kustomization.yaml in the current directory", - Long: `Adds one or more buildMetadata options to the kustomization.yaml in the current directory. + Long: `Adds one or more buildMetadata options to the kustomization.yaml in the current directory. The following options are valid: - originAnnotations - transformerAnnotations @@ -39,7 +37,8 @@ of kustomize managed the resource.`, Example: ` add buildmetadata {option1},{option2}`, RunE: func(cmd *cobra.Command, args []string) error { - err := o.Validate(args) + var err error + o.buildMetadataOptions, err = o.BuildMetadataValidator.Validate(args) if err != nil { return err } @@ -49,23 +48,6 @@ of kustomize managed the resource.`, return cmd } -// Validate validates addBuildMetadata command. -func (o *addBuildMetadataOptions) Validate(args []string) error { - if len(args) == 0 { - return errors.New("must specify a buildMetadata option") - } - if len(args) > 1 { - return fmt.Errorf("too many arguments: %s; to provide multiple buildMetadata options, please separate options by comma", args) - } - o.buildMetadataOptions = strings.Split(args[0], ",") - for _, opt := range o.buildMetadataOptions { - if !kustfile.StringInSlice(opt, types.BuildMetadataOptions) { - return fmt.Errorf("invalid buildMetadata option: %s", opt) - } - } - return nil -} - // RunAddBuildMetadata runs addBuildMetadata command (do real work). func (o *addBuildMetadataOptions) RunAddBuildMetadata(fSys filesys.FileSystem) error { mf, err := kustfile.NewKustomizationFile(fSys) diff --git a/kustomize/commands/edit/add/addbuildmetadata_test.go b/kustomize/commands/edit/add/addbuildmetadata_test.go index 1d5b75d34e8..47d91471a6d 100644 --- a/kustomize/commands/edit/add/addbuildmetadata_test.go +++ b/kustomize/commands/edit/add/addbuildmetadata_test.go @@ -13,46 +13,51 @@ import ( "sigs.k8s.io/kustomize/kyaml/filesys" ) -func TestAddBuildMetadataHappyPath(t *testing.T) { - fSys := filesys.MakeFsInMemory() - testutils_test.WriteTestKustomization(fSys) - cmd := newCmdAddBuildMetadata(fSys) - args := []string{strings.Join(types.BuildMetadataOptions, ",")} - assert.NoError(t, cmd.RunE(cmd, args)) - content, err := testutils_test.ReadTestKustomization(fSys) - assert.NoError(t, err) - for _, opt := range types.BuildMetadataOptions { - assert.Contains(t, string(content), opt) +func TestAddBuildMetadata(t *testing.T) { + tests := map[string]struct { + input string + args []string + expectedErr string + }{ + "happy path": { + input: ``, + args: []string{strings.Join(types.BuildMetadataOptions, ",")}, + }, + "option already there": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +buildMetadata: [originAnnotations]`, + args: []string{types.OriginAnnotations}, + expectedErr: "buildMetadata option originAnnotations already in kustomization file", + }, + "invalid option": { + input: ``, + args: []string{"invalid_option"}, + expectedErr: "invalid buildMetadata option: invalid_option", + }, + "too many args": { + input: ``, + args: []string{"option1", "option2"}, + expectedErr: "too many arguments: [option1 option2]; to provide multiple buildMetadata options, please separate options by comma", + }, } -} - -func TestAddBuildMetadataAlreadyThere(t *testing.T) { - fSys := filesys.MakeFsInMemory() - testutils_test.WriteTestKustomization(fSys) - cmd := newCmdAddBuildMetadata(fSys) - args := []string{strings.Join(types.BuildMetadataOptions, ",")} - assert.NoError(t, cmd.RunE(cmd, args)) - err := cmd.RunE(cmd, args) - assert.Error(t, err) - assert.Contains(t, err.Error(), "already in kustomization file") -} -func TestAddBuildMetadataInvalidArg(t *testing.T) { - fSys := filesys.MakeFsInMemory() - testutils_test.WriteTestKustomization(fSys) - cmd := newCmdAddBuildMetadata(fSys) - args := []string{"invalid_option"} - err := cmd.RunE(cmd, args) - assert.Error(t, err) - assert.Equal(t, "invalid buildMetadata option: invalid_option", err.Error()) -} - -func TestAddBuildMetadataTooManyArgs(t *testing.T) { - fSys := filesys.MakeFsInMemory() - testutils_test.WriteTestKustomization(fSys) - cmd := newCmdAddBuildMetadata(fSys) - args := []string{"option1", "option2"} - err := cmd.RunE(cmd, args) - assert.Error(t, err) - assert.Equal(t, "too many arguments: [option1 option2]; to provide multiple buildMetadata options, please separate options by comma", err.Error()) + for _, tc := range tests { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input)) + cmd := newCmdAddBuildMetadata(fSys) + err := cmd.RunE(cmd, tc.args) + if tc.expectedErr != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.expectedErr) + } else { + assert.NoError(t, err) + content, err := testutils_test.ReadTestKustomization(fSys) + assert.NoError(t, err) + for _, opt := range strings.Split(tc.args[0], ",") { + assert.Contains(t, string(content), opt) + } + } + } } diff --git a/kustomize/commands/edit/remove/all.go b/kustomize/commands/edit/remove/all.go index 4629b489032..14db55ea1a2 100644 --- a/kustomize/commands/edit/remove/all.go +++ b/kustomize/commands/edit/remove/all.go @@ -42,6 +42,7 @@ func NewCmdRemove( newCmdRemoveAnnotation(fSys, v.MakeAnnotationNameValidator()), newCmdRemovePatch(fSys), newCmdRemoveTransformer(fSys), + newCmdRemoveBuildMetadata(fSys), ) return c } diff --git a/kustomize/commands/edit/remove/removebuildmetadata.go b/kustomize/commands/edit/remove/removebuildmetadata.go new file mode 100644 index 00000000000..bba7ccbd0d9 --- /dev/null +++ b/kustomize/commands/edit/remove/removebuildmetadata.go @@ -0,0 +1,72 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remove + +import ( + "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile" + "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +type removeBuildMetadataOptions struct { + *util.BuildMetadataValidator + buildMetadataOptions []string +} + +// newCmdRemoveBuildMetadata removes options to the kustomization's buildMetada field. +func newCmdRemoveBuildMetadata(fSys filesys.FileSystem) *cobra.Command { + var o removeBuildMetadataOptions + + cmd := &cobra.Command{ + Use: "buildmetadata", + Short: "Removes one or more buildMetadata options to the kustomization.yaml in the current directory", + Long: `Removes one or more buildMetadata options to the kustomization.yaml in the current directory. +The following options are valid: + - originAnnotations + - transformerAnnotations + - managedByLabel +originAnnotations will remove the annotation config.kubernetes.io/origin to each resource, describing where +each resource originated from. +transformerAnnotations will remove the annotation alpha.config.kubernetes.io/transformations to each resource, +describing the transformers that have acted upon the resource. +managedByLabel will remove the label app.kubernetes.io/managed-by to each resource, describing which version +of kustomize managed the resource.`, + Example: ` + remove buildmetadata {option1},{option2}`, + RunE: func(cmd *cobra.Command, args []string) error { + var err error + o.buildMetadataOptions, err = o.BuildMetadataValidator.Validate(args) + if err != nil { + return err + } + return o.RunRemoveBuildMetadata(fSys) + }, + } + return cmd +} + +// RunRemoveBuildMetadata runs removeBuildMetadata command (do real work). +func (o *removeBuildMetadataOptions) RunRemoveBuildMetadata(fSys filesys.FileSystem) error { + mf, err := kustfile.NewKustomizationFile(fSys) + if err != nil { + return err + } + m, err := mf.Read() + if err != nil { + return err + } + if len(o.buildMetadataOptions) == 0 { + m.BuildMetadata = nil + } else { + var newOptions []string + for _, opt := range m.BuildMetadata { + if !kustfile.StringInSlice(opt, o.buildMetadataOptions) { + newOptions = append(newOptions, opt) + } + } + m.BuildMetadata = newOptions + } + return mf.Write(m) +} diff --git a/kustomize/commands/edit/remove/removebuildmetadata_test.go b/kustomize/commands/edit/remove/removebuildmetadata_test.go new file mode 100644 index 00000000000..0de7f0b7b02 --- /dev/null +++ b/kustomize/commands/edit/remove/removebuildmetadata_test.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remove + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/types" + testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestRemoveBuildMetadata(t *testing.T) { + tests := map[string]struct { + input string + args []string + expectedErr string + }{ + "happy path": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +buildMetadata: [originAnnotations, transformerAnnotations, managedByLabel]`, + args: []string{types.OriginAnnotations}, + }, + "option already there": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +buildMetadata: [originAnnotations]`, + args: []string{types.OriginAnnotations}, + }, + "invalid option": { + input: ``, + args: []string{"invalid_option"}, + expectedErr: "invalid buildMetadata option: invalid_option", + }, + "too many args": { + input: ``, + args: []string{"option1", "option2"}, + expectedErr: "too many arguments: [option1 option2]; to provide multiple buildMetadata options, please separate options by comma", + }, + } + + for _, tc := range tests { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input)) + cmd := newCmdRemoveBuildMetadata(fSys) + err := cmd.RunE(cmd, tc.args) + if tc.expectedErr != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.expectedErr) + } else { + assert.NoError(t, err) + content, err := testutils_test.ReadTestKustomization(fSys) + assert.NoError(t, err) + for _, opt := range strings.Split(tc.args[0], ",") { + assert.NotContains(t, string(content), opt) + } + } + } +} diff --git a/kustomize/commands/edit/set/all.go b/kustomize/commands/edit/set/all.go index e04f64c1d7e..1f62d62a315 100644 --- a/kustomize/commands/edit/set/all.go +++ b/kustomize/commands/edit/set/all.go @@ -30,6 +30,7 @@ func NewCmdSet(fSys filesys.FileSystem, ldr ifc.KvLoader, v ifc.Validator) *cobr newCmdSetNameSuffix(fSys), newCmdSetNamespace(fSys, v), newCmdSetImage(fSys), + newCmdSetBuildMetadata(fSys), newCmdSetReplicas(fSys), newCmdSetLabel(fSys, ldr.Validator().MakeLabelValidator()), newCmdSetAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()), diff --git a/kustomize/commands/edit/set/setbuildmetadata.go b/kustomize/commands/edit/set/setbuildmetadata.go new file mode 100644 index 00000000000..43a7cc1eaf5 --- /dev/null +++ b/kustomize/commands/edit/set/setbuildmetadata.go @@ -0,0 +1,63 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package set + +import ( + "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile" + "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +type setBuildMetadataOptions struct { + *util.BuildMetadataValidator + buildMetadataOptions []string +} + +// newCmdSetBuildMetadata sets options in the kustomization's buildMetada field. +func newCmdSetBuildMetadata(fSys filesys.FileSystem) *cobra.Command { + var o setBuildMetadataOptions + + cmd := &cobra.Command{ + Use: "buildmetadata", + Short: "Sets one or more buildMetadata options to the kustomization.yaml in the current directory", + Long: `Sets one or more buildMetadata options to the kustomization.yaml in the current directory. +Existing options in the buildMetadata field will be replaced entirely by the new options set by this command. +The following options are valid: + - originAnnotations + - transformerAnnotations + - managedByLabel +originAnnotations will add the annotation config.kubernetes.io/origin to each resource, describing where +each resource originated from. +transformerAnnotations will add the annotation alpha.config.kubernetes.io/transformations to each resource, +describing the transformers that have acted upon the resource. +managedByLabel will add the label app.kubernetes.io/managed-by to each resource, describing which version +of kustomize managed the resource.`, + Example: ` + set buildmetadata {option1},{option2}`, + RunE: func(cmd *cobra.Command, args []string) error { + var err error + o.buildMetadataOptions, err = o.BuildMetadataValidator.Validate(args) + if err != nil { + return err + } + return o.RunSetBuildMetadata(fSys) + }, + } + return cmd +} + +// RunSetBuildMetadata runs setBuildMetadata command (do real work). +func (o *setBuildMetadataOptions) RunSetBuildMetadata(fSys filesys.FileSystem) error { + mf, err := kustfile.NewKustomizationFile(fSys) + if err != nil { + return err + } + m, err := mf.Read() + if err != nil { + return err + } + m.BuildMetadata = o.buildMetadataOptions + return mf.Write(m) +} diff --git a/kustomize/commands/edit/set/setbuildmetadata_test.go b/kustomize/commands/edit/set/setbuildmetadata_test.go new file mode 100644 index 00000000000..9802b189c19 --- /dev/null +++ b/kustomize/commands/edit/set/setbuildmetadata_test.go @@ -0,0 +1,76 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package set + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile" + testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestSetBuildMetadata(t *testing.T) { + tests := map[string]struct { + input string + args []string + expectedErr string + }{ + "happy path": { + input: ``, + args: []string{strings.Join(types.BuildMetadataOptions, ",")}, + }, + "option already there": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +buildMetadata: [originAnnotations]`, + args: []string{types.OriginAnnotations}, + }, + "invalid option": { + input: ``, + args: []string{"invalid_option"}, + expectedErr: "invalid buildMetadata option: invalid_option", + }, + "too many args": { + input: ``, + args: []string{"option1", "option2"}, + expectedErr: "too many arguments: [option1 option2]; to provide multiple buildMetadata options, please separate options by comma", + }, + "remove old options": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +buildMetadata: [originAnnotations, transformerAnnotations, managedByLabel]`, + args: []string{types.OriginAnnotations}, + }, + } + + for _, tc := range tests { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input)) + cmd := newCmdSetBuildMetadata(fSys) + err := cmd.RunE(cmd, tc.args) + if tc.expectedErr != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.expectedErr) + } else { + assert.NoError(t, err) + content, err := testutils_test.ReadTestKustomization(fSys) + assert.NoError(t, err) + args := strings.Split(tc.args[0], ",") + for _, opt := range args { + assert.Contains(t, string(content), opt) + } + mf, err := kustfile.NewKustomizationFile(fSys) + assert.NoError(t, err) + m, err := mf.Read() + assert.NoError(t, err) + assert.Equal(t, len(m.BuildMetadata), len(args)) + } + } +} diff --git a/kustomize/commands/internal/util/util_test.go b/kustomize/commands/internal/util/util_test.go index 6fab551039e..ebb3742c738 100644 --- a/kustomize/commands/internal/util/util_test.go +++ b/kustomize/commands/internal/util/util_test.go @@ -1,3 +1,6 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + package util import ( diff --git a/kustomize/commands/internal/util/validate.go b/kustomize/commands/internal/util/validate.go new file mode 100644 index 00000000000..2c453816775 --- /dev/null +++ b/kustomize/commands/internal/util/validate.go @@ -0,0 +1,31 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package util + +import ( + "errors" + "fmt" + "strings" + + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile" +) + +type BuildMetadataValidator struct{} + +func (b *BuildMetadataValidator) Validate(args []string) ([]string, error) { + if len(args) == 0 { + return nil, errors.New("must specify a buildMetadata option") + } + if len(args) > 1 { + return nil, fmt.Errorf("too many arguments: %s; to provide multiple buildMetadata options, please separate options by comma", args) + } + opts := strings.Split(args[0], ",") + for _, opt := range opts { + if !kustfile.StringInSlice(opt, types.BuildMetadataOptions) { + return nil, fmt.Errorf("invalid buildMetadata option: %s", opt) + } + } + return opts, nil +}