From afac2fb46a477e324bf198eef7e0426bda1052ed Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Mon, 16 Jul 2018 13:57:28 -0700 Subject: [PATCH] Uses single file for both addLabel and addAnnotation commands, as the code is nearly identical. Tests included. --- pkg/commands/addannotation.go | 119 ++++++++-------- pkg/commands/addlabel.go | 120 ++++++++-------- pkg/commands/addmetadata.go | 149 ++++++++++++++++++++ pkg/commands/addmetadata_test.go | 226 +++++++++++++++++++++++++++++++ 4 files changed, 496 insertions(+), 118 deletions(-) create mode 100644 pkg/commands/addmetadata.go create mode 100644 pkg/commands/addmetadata_test.go diff --git a/pkg/commands/addannotation.go b/pkg/commands/addannotation.go index 0658eeb1e..a409e008d 100644 --- a/pkg/commands/addannotation.go +++ b/pkg/commands/addannotation.go @@ -17,24 +17,24 @@ limitations under the License. package commands import ( - "errors" - "fmt" - "regexp" - "strings" + // "errors" + // "fmt" + // "regexp" + // "strings" "github.com/spf13/cobra" - "github.com/kubernetes-sigs/kustomize/pkg/constants" + // "github.com/kubernetes-sigs/kustomize/pkg/constants" "github.com/kubernetes-sigs/kustomize/pkg/fs" ) -type addAnnotationOptions struct { - annotations string -} +// type addAnnotationOptions struct { +// annotations string +// } // newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file. func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { - var o addAnnotationOptions + var o addMetadataOptions cmd := &cobra.Command{ Use: "annotation", @@ -42,7 +42,8 @@ func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { Example: ` add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`, RunE: func(cmd *cobra.Command, args []string) error { - err := o.Validate(args) + mdKind := "annotation" + err := o.Validate(args, mdKind) if err != nil { return err } @@ -50,62 +51,62 @@ func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - return o.RunAddAnnotation(fsys) + return o.RunAddMetadata(fsys, mdKind) }, } return cmd } -// Validate validates addAnnotation command. -func (o *addAnnotationOptions) Validate(args []string) error { - if len(args) < 1 { - return errors.New("must specify an annotation") - } - if len(args) > 1 { - return errors.New("annotations must be comma-separated, with no spaces. See help text for example.") - } - inputs := strings.Split(args[0], ",") - for _, input := range inputs { - ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) - if err != nil { - return err - } - if !ok { - return fmt.Errorf("invalid annotation format: %s", input) - } - } - o.annotations = args[0] - return nil -} +// // Validate validates addAnnotation command. +// func (o *addAnnotationOptions) Validate(args []string) error { +// if len(args) < 1 { +// return errors.New("must specify an annotation") +// } +// if len(args) > 1 { +// return errors.New("annotations must be comma-separated, with no spaces. See help text for example.") +// } +// inputs := strings.Split(args[0], ",") +// for _, input := range inputs { +// ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) +// if err != nil { +// return err +// } +// if !ok { +// return fmt.Errorf("invalid annotation format: %s", input) +// } +// } +// o.annotations = args[0] +// return nil +// } -// Complete completes addAnnotation command. -func (o *addAnnotationOptions) Complete(cmd *cobra.Command, args []string) error { - return nil -} +// // Complete completes addAnnotation command. +// func (o *addAnnotationOptions) Complete(cmd *cobra.Command, args []string) error { +// return nil +// } -// RunAddAnnotation runs addAnnotation command (do real work). -func (o *addAnnotationOptions) RunAddAnnotation(fsys fs.FileSystem) error { - mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) - if err != nil { - return err - } +// // RunAddAnnotation runs addAnnotation command (do real work). +// func (o *addAnnotationOptions) RunAddAnnotation(fsys fs.FileSystem) error { +// mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) +// if err != nil { +// return err +// } - m, err := mf.read() - if err != nil { - return err - } +// m, err := mf.read() +// if err != nil { +// return err +// } - if m.CommonAnnotations == nil { - m.CommonAnnotations = make(map[string]string) - } - annotations := strings.Split(o.annotations, ",") - for _, ann := range annotations { - kv := strings.Split(ann, ":") - if _, ok := m.CommonAnnotations[kv[0]]; ok { - return fmt.Errorf("annotation %s already in kustomization file", kv[0]) - } - m.CommonAnnotations[kv[0]] = kv[1] - } +// if m.CommonAnnotations == nil { +// m.CommonAnnotations = make(map[string]string) +// } +// annotations := strings.Split(o.annotations, ",") +// for _, ann := range annotations { +// kv := strings.Split(ann, ":") +// if _, ok := m.CommonAnnotations[kv[0]]; ok { +// return fmt.Errorf("annotation %s already in kustomization file", kv[0]) +// } +// m.CommonAnnotations[kv[0]] = kv[1] +// } - return mf.write(m) -} +// return mf.write(m) +// } diff --git a/pkg/commands/addlabel.go b/pkg/commands/addlabel.go index 7f794e4b5..fa672ea21 100644 --- a/pkg/commands/addlabel.go +++ b/pkg/commands/addlabel.go @@ -17,24 +17,25 @@ limitations under the License. package commands import ( - "errors" - "fmt" - "regexp" - "strings" + // "errors" + // "fmt" + // "regexp" + // "strings" "github.com/spf13/cobra" - "github.com/kubernetes-sigs/kustomize/pkg/constants" + // "github.com/kubernetes-sigs/kustomize/pkg/constants" "github.com/kubernetes-sigs/kustomize/pkg/fs" ) -type addLabelOptions struct { - labels string -} + +// type addLabelOptions struct { +// labels string +// } // newCmdAddLabel adds one or more commonLabels to the kustomization file. func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { - var o addLabelOptions + var o addMetadataOptions cmd := &cobra.Command{ Use: "label", @@ -42,7 +43,8 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { Example: ` add label {labelKey1:labelValue1},{labelKey2:labelValue2}`, RunE: func(cmd *cobra.Command, args []string) error { - err := o.Validate(args) + mdKind := "label" + err := o.Validate(args, mdKind) if err != nil { return err } @@ -50,65 +52,65 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - return o.RunAddLabel(fsys) + return o.RunAddMetadata(fsys, mdKind) }, } return cmd } -// Validate validates addLabel command. -func (o *addLabelOptions) Validate(args []string) error { - if len(args) < 1 { - return errors.New("must specify a label") - } - if len(args) > 1 { - return errors.New("labels must be comma-separated, with no spaces. See help text for example.") - } - inputs := strings.Split(args[0], ",") - for _, input := range inputs { - ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) - if err != nil { - return err - } - if !ok { - return fmt.Errorf("invalid label format: %s", input) - } +// // Validate validates addLabel command. +// func (o *addLabelOptions) Validate(args []string) error { +// if len(args) < 1 { +// return errors.New("must specify a label") +// } +// if len(args) > 1 { +// return errors.New("labels must be comma-separated, with no spaces. See help text for example.") +// } +// inputs := strings.Split(args[0], ",") +// for _, input := range inputs { +// ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) +// if err != nil { +// return err +// } +// if !ok { +// return fmt.Errorf("invalid label format: %s", input) +// } - } +// } - o.labels = args[0] - return nil -} +// o.labels = args[0] +// return nil +// } -// Complete completes addLabel command. -func (o *addLabelOptions) Complete(cmd *cobra.Command, args []string) error { - return nil -} +// // Complete completes addLabel command. +// func (o *addLabelOptions) Complete(cmd *cobra.Command, args []string) error { +// return nil +// } -// RunAddLabel runs addLabel command (do real work). -func (o *addLabelOptions) RunAddLabel(fsys fs.FileSystem) error { - mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) - if err != nil { - return err - } +// // RunAddLabel runs addLabel command (do real work). +// func (o *addLabelOptions) RunAddLabel(fsys fs.FileSystem) error { +// mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) +// if err != nil { +// return err +// } - m, err := mf.read() - if err != nil { - return err - } +// m, err := mf.read() +// if err != nil { +// return err +// } - if m.CommonLabels == nil { - m.CommonLabels = make(map[string]string) - } +// if m.CommonLabels == nil { +// m.CommonLabels = make(map[string]string) +// } - labels := strings.Split(o.labels, ",") - for _, label := range labels { - kv := strings.Split(label, ":") - if _, ok := m.CommonLabels[kv[0]]; ok { - return fmt.Errorf("label %s already in kustomization file", kv[0]) - } - m.CommonLabels[kv[0]] = kv[1] - } +// labels := strings.Split(o.labels, ",") +// for _, label := range labels { +// kv := strings.Split(label, ":") +// if _, ok := m.CommonLabels[kv[0]]; ok { +// return fmt.Errorf("label %s already in kustomization file", kv[0]) +// } +// m.CommonLabels[kv[0]] = kv[1] +// } - return mf.write(m) -} +// return mf.write(m) +// } diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go new file mode 100644 index 000000000..6420cd842 --- /dev/null +++ b/pkg/commands/addmetadata.go @@ -0,0 +1,149 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package commands + +import ( + "fmt" + "github.com/kubernetes-sigs/kustomize/pkg/constants" + "github.com/kubernetes-sigs/kustomize/pkg/fs" + "github.com/spf13/cobra" + "regexp" + "strings" +) + +type addMetadataOptions struct { + metadata string +} + +// newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file. +func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { + var o addMetadataOptions + + cmd := &cobra.Command{ + Use: "annotation", + Short: "Adds one or more commonAnnotations to the kustomization.yaml in current directory", + Example: ` + add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`, + RunE: func(cmd *cobra.Command, args []string) error { + mdKind := "annotation" + err := o.Validate(args, mdKind) + if err != nil { + return err + } + err = o.Complete(cmd, args) + if err != nil { + return err + } + return o.RunAddMetadata(fsys, mdKind) + }, + } + return cmd +} + +// newCmdAddLabel adds one or more commonLabels to the kustomization file. +func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { + var o addMetadataOptions + + cmd := &cobra.Command{ + Use: "label", + Short: "Adds one or more commonLabels to the kustomization.yaml in current directory", + Example: ` + add label {labelKey1:labelValue1},{labelKey2:labelValue2}`, + RunE: func(cmd *cobra.Command, args []string) error { + mdKind := "label" + err := o.Validate(args, mdKind) + if err != nil { + return err + } + err = o.Complete(cmd, args) + if err != nil { + return err + } + return o.RunAddMetadata(fsys, mdKind) + }, + } + return cmd +} + +// Validate validates addLabel and addAnnotation commands. +func (o *addMetadataOptions) Validate(args []string, mdKind string) error { + + if len(args) < 1 { + return fmt.Errorf("must specify %s", mdKind) + } + if len(args) > 1 { + return fmt.Errorf("%ss must be comma-separated, with no spaces. See help text for example", mdKind) + } + inputs := strings.Split(args[0], ",") + for _, input := range inputs { + ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("invalid %s format: %s", mdKind, input) + } + } + + o.metadata = args[0] + return nil +} + +// Complete completes addMetadata command. +func (o *addMetadataOptions) Complete(cmd *cobra.Command, args []string) error { + return nil +} + +// RunAddMetadata runs addLabel and addAnnotation commands (do real work). +func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, mdKind string) error { + mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) + if err != nil { + return err + } + + m, err := mf.read() + if err != nil { + return err + } + + if mdKind == "label" && m.CommonLabels == nil { + m.CommonLabels = make(map[string]string) + } + + if mdKind == "annotation" && m.CommonAnnotations == nil { + m.CommonAnnotations = make(map[string]string) + } + + entries := strings.Split(o.metadata, ",") + for _, entry := range entries { + kv := strings.Split(entry, ":") + if mdKind == "label" { + if _, ok := m.CommonLabels[kv[0]]; ok { + return fmt.Errorf("%s %s already in kustomization file", mdKind, kv[0]) + } + m.CommonLabels[kv[0]] = kv[1] + } + if mdKind == "annotation" { + if _, ok := m.CommonAnnotations[kv[0]]; ok { + return fmt.Errorf("%s %s already in kustomization file", mdKind, kv[0]) + } + m.CommonAnnotations[kv[0]] = kv[1] + } + } + + return mf.write(m) +} diff --git a/pkg/commands/addmetadata_test.go b/pkg/commands/addmetadata_test.go new file mode 100644 index 000000000..5d65f1567 --- /dev/null +++ b/pkg/commands/addmetadata_test.go @@ -0,0 +1,226 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package commands + +import ( + "testing" + + "strings" + + "github.com/kubernetes-sigs/kustomize/pkg/constants" + "github.com/kubernetes-sigs/kustomize/pkg/fs" +) + +func TestLabelsValid(t *testing.T) { + var testcases = []struct { + input string + valid bool + name string + }{ + { + input: "owls:great,unicorns:magical", + valid: true, + name: "Adds two labels", + }, + { + input: "owls:great", + valid: false, + name: "Label keys must be unique", + }, + { + input: "otters:cute", + valid: true, + name: "Adds single label", + }, + { + input: "dogs,cats", + valid: false, + name: "Does not contain colon", + }, + { + input: ":noKey", + valid: false, + name: "Missing key", + }, + { + input: "noValue:", + valid: false, + name: "Missing value", + }, + } + fakeFS := fs.MakeFakeFS() + fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent)) + cmd := newCmdAddLabel(fakeFS) + + for _, tc := range testcases { + labels := strings.Split(tc.input, ",") + //run command with test input + args := []string{tc.input} + err := cmd.RunE(cmd, args) + + if err != nil && tc.valid { + t.Errorf("for test case %s, unexpected cmd error: %v", tc.name, err) + } + if err == nil && !tc.valid { + t.Errorf("unexpected error: expected invalid annotation format error for test case %v", tc.name) + } + if err == nil && (tc.name == "Label keys must be unique") { + t.Errorf("unexpected error: for test case %s, expected already there problem", tc.name) + } + + content, readErr := fakeFS.ReadFile(constants.KustomizationFileName) + if readErr != nil { + t.Errorf("unexpected read error: %v", readErr) + } + //check if valid input was added to commonLabels + for _, label := range labels { + key := strings.Split(label, ":")[0] + if !strings.Contains(string(content), key) && tc.valid { + t.Errorf("unexpected error: for test case %s, expected key to be in file.", tc.name) + } + } + + } + +} + +func TestAddLabelNoArgs(t *testing.T) { + fakeFS := fs.MakeFakeFS() + + cmd := newCmdAddLabel(fakeFS) + err := cmd.Execute() + if err == nil { + t.Errorf("expected an error but error is: %v", err) + } + if err != nil && err.Error() != "must specify label" { + t.Errorf("incorrect error: %v", err.Error()) + } +} + +func TestAddLabelMultipleArgs(t *testing.T) { + fakeFS := fs.MakeFakeFS() + fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent)) + cmd := newCmdAddLabel(fakeFS) + args := []string{"this:input", "has:spaces"} + err := cmd.RunE(cmd, args) + if err == nil { + t.Errorf("expected an error but error is: %v", err) + } + if err != nil && err.Error() != "labels must be comma-separated, with no spaces. See help text for example" { + t.Errorf("incorrect error: %v", err.Error()) + } +} + +func TestAnnotationsValid(t *testing.T) { + var testcases = []struct { + input string + valid bool + name string + }{ + { + input: "cats:great,dogs:okay", + valid: true, + name: "Adds two annotations", + }, + { + input: "cats:great", + valid: false, + name: "Annotation keys must be unique", + }, + { + input: "owls:best", + valid: true, + name: "Adds single annotation", + }, + { + input: "cake,cookies", + valid: false, + name: "Does not contain colon", + }, + { + input: ":hasNoKey", + valid: false, + name: "Missing key", + }, + { + input: "hasNoValue:", + valid: false, + name: "Missing value", + }, + } + fakeFS := fs.MakeFakeFS() + fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent)) + cmd := newCmdAddAnnotation(fakeFS) + + for _, tc := range testcases { + annotations := strings.Split(tc.input, ",") + //run command with test input + args := []string{tc.input} + err := cmd.RunE(cmd, args) + + if err != nil && tc.valid { + t.Errorf("for test case %s, unexpected cmd error: %v", tc.name, err) + } + if err == nil && !tc.valid { + t.Errorf("unexpected error: expected invalid annotation format error for test case %v", tc.name) + } + if err == nil && (tc.name == "Annotation keys must be unique") { + t.Errorf("unexpected error: for test case %s, expected already there problem", tc.name) + } + + content, readErr := fakeFS.ReadFile(constants.KustomizationFileName) + if readErr != nil { + t.Errorf("unexpected read error: %v", readErr) + } + //check if valid input was added to commonAnnotations + for _, annotation := range annotations { + key := strings.Split(annotation, ":")[0] + if !strings.Contains(string(content), key) && tc.valid { + t.Errorf("unexpected error: for test case %s, expected key to be in file.", tc.name) + } + } + + } + +} + +func TestAddAnnotationNoArgs(t *testing.T) { + fakeFS := fs.MakeFakeFS() + + cmd := newCmdAddAnnotation(fakeFS) + err := cmd.Execute() + if err == nil { + t.Errorf("expected an error but error is %v", err) + } + if err != nil && err.Error() != "must specify annotation" { + t.Errorf("incorrect error: %v", err.Error()) + } +} + +func TestAddAnnotationMultipleArgs(t *testing.T) { + fakeFS := fs.MakeFakeFS() + fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent)) + cmd := newCmdAddAnnotation(fakeFS) + args := []string{"this:annotation", "has:spaces"} + err := cmd.RunE(cmd, args) + if err == nil { + t.Errorf("expected an error but error is %v", err) + } + if err != nil && err.Error() != "annotations must be comma-separated, with no spaces. See help text for example" { + t.Errorf("incorrect error: %v", err.Error()) + } +} \ No newline at end of file