diff --git a/kustomize/internal/commands/edit/all.go b/kustomize/internal/commands/edit/all.go index 72bc9f9df..61f24bdce 100644 --- a/kustomize/internal/commands/edit/all.go +++ b/kustomize/internal/commands/edit/all.go @@ -40,7 +40,10 @@ func NewCmdEdit( fSys, kv.NewLoader(loader.NewFileLoaderAtCwd(fSys), v), kf), - set.NewCmdSet(fSys, v), + set.NewCmdSet( + fSys, + kv.NewLoader(loader.NewFileLoaderAtCwd(fSys), v), + v), fix.NewCmdFix(fSys), remove.NewCmdRemove(fSys, v), ) diff --git a/kustomize/internal/commands/edit/set/all.go b/kustomize/internal/commands/edit/set/all.go index 582389e67..4a92c10c4 100644 --- a/kustomize/internal/commands/edit/set/all.go +++ b/kustomize/internal/commands/edit/set/all.go @@ -10,7 +10,7 @@ import ( ) // NewCmdSet returns an instance of 'set' subcommand. -func NewCmdSet(fSys filesys.FileSystem, v ifc.Validator) *cobra.Command { +func NewCmdSet(fSys filesys.FileSystem, ldr ifc.KvLoader, v ifc.Validator) *cobra.Command { c := &cobra.Command{ Use: "set", Short: "Sets the value of different fields in kustomization file.", @@ -31,6 +31,7 @@ func NewCmdSet(fSys filesys.FileSystem, v ifc.Validator) *cobra.Command { newCmdSetNamespace(fSys, v), newCmdSetImage(fSys), newCmdSetReplicas(fSys), + newCmdSetLabel(fSys, ldr.Validator().MakeLabelValidator()), ) return c } diff --git a/kustomize/internal/commands/edit/set/setlabel.go b/kustomize/internal/commands/edit/set/setlabel.go new file mode 100644 index 000000000..49e807b88 --- /dev/null +++ b/kustomize/internal/commands/edit/set/setlabel.go @@ -0,0 +1,88 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package set + +import ( + "fmt" + + "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/api/filesys" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile" + "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util" +) + +type setLabelOptions struct { + metadata map[string]string + mapValidator func(map[string]string) error +} + +// newCmdSetLabel sets one or more commonLabels to the kustomization file. +func newCmdSetLabel(fSys filesys.FileSystem, v func(map[string]string) error) *cobra.Command { + var o setLabelOptions + o.mapValidator = v + cmd := &cobra.Command{ + Use: "label", + Short: "Sets one or more commonLabels in " + + konfig.DefaultKustomizationFileName(), + Example: ` + set label {labelKey1:labelValue1} {labelKey2:labelValue2}`, + RunE: func(cmd *cobra.Command, args []string) error { + return o.runE(args, fSys, o.setLabels) + }, + } + return cmd +} + +func (o *setLabelOptions) runE( + args []string, fSys filesys.FileSystem, setter func(*types.Kustomization) error) error { + err := o.validateAndParse(args) + if err != nil { + return err + } + kf, err := kustfile.NewKustomizationFile(fSys) + if err != nil { + return err + } + m, err := kf.Read() + if err != nil { + return err + } + err = setter(m) + if err != nil { + return err + } + return kf.Write(m) +} + +// validateAndParse validates `set` commands and parses them into o.metadata +func (o *setLabelOptions) validateAndParse(args []string) error { + if len(args) < 1 { + return fmt.Errorf("must specify label") + } + m, err := util.ConvertSliceToMap(args, "label") + if err != nil { + return err + } + if err = o.mapValidator(m); err != nil { + return err + } + o.metadata = m + return nil +} + +func (o *setLabelOptions) setLabels(m *types.Kustomization) error { + if m.CommonLabels == nil { + m.CommonLabels = make(map[string]string) + } + return o.writeToMap(m.CommonLabels) +} + +func (o *setLabelOptions) writeToMap(m map[string]string) error { + for k, v := range o.metadata { + m[k] = v + } + return nil +} diff --git a/kustomize/internal/commands/edit/set/setlabel_test.go b/kustomize/internal/commands/edit/set/setlabel_test.go new file mode 100644 index 000000000..b5de25c74 --- /dev/null +++ b/kustomize/internal/commands/edit/set/setlabel_test.go @@ -0,0 +1,153 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package set + +import ( + "testing" + + "sigs.k8s.io/kustomize/api/filesys" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile" + testutils_test "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/testutils" +) + +func makeKustomization(t *testing.T) *types.Kustomization { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomization(fSys) + kf, err := kustfile.NewKustomizationFile(fSys) + if err != nil { + t.Errorf("unexpected new error %v", err) + } + m, err := kf.Read() + if err != nil { + t.Errorf("unexpected read error %v", err) + } + return m +} + +func TestRunSetLabel(t *testing.T) { + var o setLabelOptions + o.metadata = map[string]string{"owls": "cute", "otters": "adorable"} + + m := makeKustomization(t) + err := o.setLabels(m) + if err != nil { + t.Errorf("unexpected error: could not write to kustomization file") + } + // adding the same test input should work + err = o.setLabels(m) + if err != nil { + t.Errorf("unexpected error: could not write to kustomization file") + } + // adding new labels should work + o.metadata = map[string]string{"new": "label", "owls": "not cute"} + err = o.setLabels(m) + if err != nil { + t.Errorf("unexpected error: could not write to kustomization file") + } +} + +func TestSetLabelNoArgs(t *testing.T) { + fSys := filesys.MakeFsInMemory() + v := valtest_test.MakeHappyMapValidator(t) + cmd := newCmdSetLabel(fSys, v.Validator) + err := cmd.Execute() + v.VerifyNoCall() + if err == nil { + t.Errorf("expected an error") + } + if err.Error() != "must specify label" { + t.Errorf("incorrect error: %v", err.Error()) + } +} + +func TestSetLabelInvalidFormat(t *testing.T) { + fSys := filesys.MakeFsInMemory() + v := valtest_test.MakeSadMapValidator(t) + cmd := newCmdSetLabel(fSys, v.Validator) + args := []string{"exclamation!:point"} + err := cmd.RunE(cmd, args) + v.VerifyCall() + if err == nil { + t.Errorf("expected an error") + } + if err.Error() != valtest_test.SAD { + t.Errorf("incorrect error: %v", err.Error()) + } +} + +func TestSetLabelNoKey(t *testing.T) { + fSys := filesys.MakeFsInMemory() + v := valtest_test.MakeHappyMapValidator(t) + cmd := newCmdSetLabel(fSys, v.Validator) + args := []string{":nokey"} + err := cmd.RunE(cmd, args) + v.VerifyNoCall() + if err == nil { + t.Errorf("expected an error") + } + if err.Error() != "invalid label: ':nokey' (need k:v pair where v may be quoted)" { + t.Errorf("incorrect error: %v", err.Error()) + } +} + +func TestSetLabelTooManyColons(t *testing.T) { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomization(fSys) + v := valtest_test.MakeHappyMapValidator(t) + cmd := newCmdSetLabel(fSys, v.Validator) + args := []string{"key:v1:v2"} + err := cmd.RunE(cmd, args) + v.VerifyCall() + if err != nil { + t.Errorf("unexpected error: %v", err.Error()) + } +} + +func TestSetLabelNoValue(t *testing.T) { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomization(fSys) + v := valtest_test.MakeHappyMapValidator(t) + cmd := newCmdSetLabel(fSys, v.Validator) + args := []string{"no,value:"} + err := cmd.RunE(cmd, args) + v.VerifyCall() + if err != nil { + t.Errorf("unexpected error: %v", err.Error()) + } +} + +func TestSetLabelMultipleArgs(t *testing.T) { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomization(fSys) + v := valtest_test.MakeHappyMapValidator(t) + cmd := newCmdSetLabel(fSys, v.Validator) + args := []string{"this:input", "has:spaces"} + err := cmd.RunE(cmd, args) + v.VerifyCall() + if err != nil { + t.Errorf("unexpected error: %v", err.Error()) + } +} + +func TestSetLabelExisting(t *testing.T) { + fSys := filesys.MakeFsInMemory() + testutils_test.WriteTestKustomization(fSys) + v := valtest_test.MakeHappyMapValidator(t) + cmd := newCmdSetLabel(fSys, v.Validator) + args := []string{"key:foo"} + err := cmd.RunE(cmd, args) + v.VerifyCall() + if err != nil { + t.Errorf("unexpected error: %v", err.Error()) + } + v = valtest_test.MakeHappyMapValidator(t) + cmd = newCmdSetLabel(fSys, v.Validator) + err = cmd.RunE(cmd, args) + v.VerifyCall() + if err != nil { + t.Errorf("unexpected error: %v", err.Error()) + } +}