From 1e3824057bd56d32404e85056a7344fdd0275504 Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Tue, 19 Jun 2018 17:00:25 -0700 Subject: [PATCH 01/12] Implements labels and annotations as subcommands of edit --- pkg/commands/addannotation.go | 111 +++++++++++++++++++++++++++ pkg/commands/addannotation_test.go | 17 +++++ pkg/commands/addlabel.go | 119 +++++++++++++++++++++++++++++ pkg/commands/addlabel_test.go | 17 +++++ pkg/commands/commands.go | 11 +++ 5 files changed, 275 insertions(+) create mode 100644 pkg/commands/addannotation.go create mode 100644 pkg/commands/addannotation_test.go create mode 100644 pkg/commands/addlabel.go create mode 100644 pkg/commands/addlabel_test.go diff --git a/pkg/commands/addannotation.go b/pkg/commands/addannotation.go new file mode 100644 index 000000000..225a1f335 --- /dev/null +++ b/pkg/commands/addannotation.go @@ -0,0 +1,111 @@ +/* +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 ( + "errors" + "fmt" + "strings" + "regexp" + + "github.com/spf13/cobra" + + "github.com/kubernetes-sigs/kustomize/pkg/constants" + "github.com/kubernetes-sigs/kustomize/pkg/fs" +) + +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 + + 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 { + err := o.Validate(args) + if err != nil { + return err + } + err = o.Complete(cmd, args) + if err != nil { + return err + } + return o.RunAddAnnotation(fsys) + }, + } + 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 +} + +// 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 + } + + 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] + } + + return mf.write(m) +} diff --git a/pkg/commands/addannotation_test.go b/pkg/commands/addannotation_test.go new file mode 100644 index 000000000..d40208840 --- /dev/null +++ b/pkg/commands/addannotation_test.go @@ -0,0 +1,17 @@ +/* +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 \ No newline at end of file diff --git a/pkg/commands/addlabel.go b/pkg/commands/addlabel.go new file mode 100644 index 000000000..80accf280 --- /dev/null +++ b/pkg/commands/addlabel.go @@ -0,0 +1,119 @@ +/* +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 ( + "regexp" + "errors" + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/kubernetes-sigs/kustomize/pkg/constants" + "github.com/kubernetes-sigs/kustomize/pkg/fs" +) + +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 + + 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 { + err := o.Validate(args) + if err != nil { + return err + } + err = o.Complete(cmd, args) + if err != nil { + return err + } + return o.RunAddLabel(fsys) + }, + } + return cmd +} + +// Validate validates addLabel command. +// TODO: make sure label is of correct format key:value +func (o *addLabelOptions) Validate(args []string) error { + for _, arg := range args { + fmt.Println(arg + "****") + } + + 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 +} + +// 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 + } + + m, err := mf.read() + if err != nil { + return err + } + + 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] + } + + return mf.write(m) +} diff --git a/pkg/commands/addlabel_test.go b/pkg/commands/addlabel_test.go new file mode 100644 index 000000000..d40208840 --- /dev/null +++ b/pkg/commands/addlabel_test.go @@ -0,0 +1,17 @@ +/* +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 \ No newline at end of file diff --git a/pkg/commands/commands.go b/pkg/commands/commands.go index 9c7b2c36e..ba6dcdbea 100644 --- a/pkg/commands/commands.go +++ b/pkg/commands/commands.go @@ -96,6 +96,15 @@ func newCmdAdd(fsys fs.FileSystem) *cobra.Command { # Adds one or more base directories to the kustomization kustomize edit add base kustomize edit add base ,, +<<<<<<< HEAD +======= + + # Adds one or more commonLabels to the kustomization + kustomize edit add label {labelKey1:labelValue1},{labelKey2:labelValue2} + + # Adds one or more commonAnnotations to the kustomization + kustomize edit add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2} +>>>>>>> Implements labels and annotations as subcommands of edit `, Args: cobra.MinimumNArgs(1), } @@ -104,6 +113,8 @@ func newCmdAdd(fsys fs.FileSystem) *cobra.Command { newCmdAddPatch(fsys), newCmdAddConfigMap(fsys), newCmdAddBase(fsys), + newCmdAddLabel(fsys), + newCmdAddAnnotation(fsys), ) return c } From 20fd433f750e6748d2ecbcbe2a5e49e3ed559715 Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Fri, 29 Jun 2018 15:44:00 -0700 Subject: [PATCH 02/12] Add tests --- pkg/commands/addannotation.go | 8 +-- pkg/commands/addannotation_test.go | 111 ++++++++++++++++++++++++++++- pkg/commands/addlabel.go | 13 ++-- pkg/commands/addlabel_test.go | 111 ++++++++++++++++++++++++++++- pkg/commands/commands.go | 3 - 5 files changed, 228 insertions(+), 18 deletions(-) diff --git a/pkg/commands/addannotation.go b/pkg/commands/addannotation.go index 225a1f335..0658eeb1e 100644 --- a/pkg/commands/addannotation.go +++ b/pkg/commands/addannotation.go @@ -19,8 +19,8 @@ package commands import ( "errors" "fmt" - "strings" "regexp" + "strings" "github.com/spf13/cobra" @@ -64,9 +64,9 @@ func (o *addAnnotationOptions) Validate(args []string) error { 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],",") + inputs := strings.Split(args[0], ",") for _, input := range inputs { - ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) + ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) if err != nil { return err } @@ -95,7 +95,7 @@ func (o *addAnnotationOptions) RunAddAnnotation(fsys fs.FileSystem) error { return err } - if m.CommonAnnotations == nil{ + if m.CommonAnnotations == nil { m.CommonAnnotations = make(map[string]string) } annotations := strings.Split(o.annotations, ",") diff --git a/pkg/commands/addannotation_test.go b/pkg/commands/addannotation_test.go index d40208840..faa9b7813 100644 --- a/pkg/commands/addannotation_test.go +++ b/pkg/commands/addannotation_test.go @@ -14,4 +14,113 @@ See the License for the specific language governing permissions and limitations under the License. */ -package commands \ No newline at end of file +package commands + +import ( + "testing" + + "strings" + + "github.com/kubernetes-sigs/kustomize/pkg/constants" + "github.com/kubernetes-sigs/kustomize/pkg/fs" +) + +func TestAnnotationsValid(t *testing.T) { + var testcases = []struct { + input string + valid bool + name string + }{ + { + input: "owls:great,unicorns:magical", + valid: true, + name: "Adds two annotations", + }, + { + input: "owls:great", + valid: false, + name: "Annotation keys must be unique", + }, + { + input: "otters:cute", + valid: true, + name: "Adds single annotation", + }, + { + 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 := 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 an 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: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() != "annotations must be comma-separated, with no spaces. See help text for example." { + t.Errorf("incorrect error: %v", err.Error()) + } +} diff --git a/pkg/commands/addlabel.go b/pkg/commands/addlabel.go index 80accf280..7f794e4b5 100644 --- a/pkg/commands/addlabel.go +++ b/pkg/commands/addlabel.go @@ -17,9 +17,9 @@ limitations under the License. package commands import ( - "regexp" "errors" "fmt" + "regexp" "strings" "github.com/spf13/cobra" @@ -57,21 +57,16 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { } // Validate validates addLabel command. -// TODO: make sure label is of correct format key:value func (o *addLabelOptions) Validate(args []string) error { - for _, arg := range args { - fmt.Println(arg + "****") - } - 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],",") + inputs := strings.Split(args[0], ",") for _, input := range inputs { - ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) + ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, input) if err != nil { return err } @@ -102,7 +97,7 @@ func (o *addLabelOptions) RunAddLabel(fsys fs.FileSystem) error { return err } - if m.CommonLabels == nil{ + if m.CommonLabels == nil { m.CommonLabels = make(map[string]string) } diff --git a/pkg/commands/addlabel_test.go b/pkg/commands/addlabel_test.go index d40208840..97309a5f4 100644 --- a/pkg/commands/addlabel_test.go +++ b/pkg/commands/addlabel_test.go @@ -14,4 +14,113 @@ See the License for the specific language governing permissions and limitations under the License. */ -package commands \ No newline at end of file +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 a 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()) + } +} diff --git a/pkg/commands/commands.go b/pkg/commands/commands.go index ba6dcdbea..dd2180d57 100644 --- a/pkg/commands/commands.go +++ b/pkg/commands/commands.go @@ -96,15 +96,12 @@ func newCmdAdd(fsys fs.FileSystem) *cobra.Command { # Adds one or more base directories to the kustomization kustomize edit add base kustomize edit add base ,, -<<<<<<< HEAD -======= # Adds one or more commonLabels to the kustomization kustomize edit add label {labelKey1:labelValue1},{labelKey2:labelValue2} # Adds one or more commonAnnotations to the kustomization kustomize edit add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2} ->>>>>>> Implements labels and annotations as subcommands of edit `, Args: cobra.MinimumNArgs(1), } From afac2fb46a477e324bf198eef7e0426bda1052ed Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Mon, 16 Jul 2018 13:57:28 -0700 Subject: [PATCH 03/12] 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 From 187415430f4d4bf5a7cecc18b92eb5cb4e0995d1 Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Tue, 17 Jul 2018 13:50:12 -0700 Subject: [PATCH 04/12] Removed individual files in favor of combined metadata file --- pkg/commands/addannotation.go | 112 ------------------------- pkg/commands/addannotation_test.go | 126 ----------------------------- pkg/commands/addlabel.go | 116 -------------------------- pkg/commands/addlabel_test.go | 126 ----------------------------- pkg/commands/addmetadata.go | 20 +++-- pkg/commands/addmetadata_test.go | 2 +- 6 files changed, 13 insertions(+), 489 deletions(-) delete mode 100644 pkg/commands/addannotation.go delete mode 100644 pkg/commands/addannotation_test.go delete mode 100644 pkg/commands/addlabel.go delete mode 100644 pkg/commands/addlabel_test.go diff --git a/pkg/commands/addannotation.go b/pkg/commands/addannotation.go deleted file mode 100644 index a409e008d..000000000 --- a/pkg/commands/addannotation.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package commands - -import ( - // "errors" - // "fmt" - // "regexp" - // "strings" - - "github.com/spf13/cobra" - - // "github.com/kubernetes-sigs/kustomize/pkg/constants" - "github.com/kubernetes-sigs/kustomize/pkg/fs" -) - -// type addAnnotationOptions struct { -// annotations 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 -} - -// // 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 -// } - -// // 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 -// } - -// 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) -// } diff --git a/pkg/commands/addannotation_test.go b/pkg/commands/addannotation_test.go deleted file mode 100644 index faa9b7813..000000000 --- a/pkg/commands/addannotation_test.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package commands - -import ( - "testing" - - "strings" - - "github.com/kubernetes-sigs/kustomize/pkg/constants" - "github.com/kubernetes-sigs/kustomize/pkg/fs" -) - -func TestAnnotationsValid(t *testing.T) { - var testcases = []struct { - input string - valid bool - name string - }{ - { - input: "owls:great,unicorns:magical", - valid: true, - name: "Adds two annotations", - }, - { - input: "owls:great", - valid: false, - name: "Annotation keys must be unique", - }, - { - input: "otters:cute", - valid: true, - name: "Adds single annotation", - }, - { - 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 := 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 an 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: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() != "annotations must be comma-separated, with no spaces. See help text for example." { - t.Errorf("incorrect error: %v", err.Error()) - } -} diff --git a/pkg/commands/addlabel.go b/pkg/commands/addlabel.go deleted file mode 100644 index fa672ea21..000000000 --- a/pkg/commands/addlabel.go +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package commands - -import ( - // "errors" - // "fmt" - // "regexp" - // "strings" - - "github.com/spf13/cobra" - - // "github.com/kubernetes-sigs/kustomize/pkg/constants" - "github.com/kubernetes-sigs/kustomize/pkg/fs" -) - - -// type addLabelOptions struct { -// labels string -// } - -// 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 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 -// } - -// // 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 -// } - -// m, err := mf.read() -// if err != nil { -// return err -// } - -// 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] -// } - -// return mf.write(m) -// } diff --git a/pkg/commands/addlabel_test.go b/pkg/commands/addlabel_test.go deleted file mode 100644 index 97309a5f4..000000000 --- a/pkg/commands/addlabel_test.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package 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 a 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()) - } -} diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go index 6420cd842..1a546a75a 100644 --- a/pkg/commands/addmetadata.go +++ b/pkg/commands/addmetadata.go @@ -18,13 +18,17 @@ package commands import ( "fmt" + "regexp" + "strings" + "github.com/kubernetes-sigs/kustomize/pkg/constants" "github.com/kubernetes-sigs/kustomize/pkg/fs" "github.com/spf13/cobra" - "regexp" - "strings" ) +const label = "label" +const ann = "annotation" + type addMetadataOptions struct { metadata string } @@ -39,7 +43,7 @@ func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { Example: ` add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`, RunE: func(cmd *cobra.Command, args []string) error { - mdKind := "annotation" + mdKind := ann err := o.Validate(args, mdKind) if err != nil { return err @@ -64,7 +68,7 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { Example: ` add label {labelKey1:labelValue1},{labelKey2:labelValue2}`, RunE: func(cmd *cobra.Command, args []string) error { - mdKind := "label" + mdKind := label err := o.Validate(args, mdKind) if err != nil { return err @@ -120,24 +124,24 @@ func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, mdKind string) e return err } - if mdKind == "label" && m.CommonLabels == nil { + if mdKind == label && m.CommonLabels == nil { m.CommonLabels = make(map[string]string) } - if mdKind == "annotation" && m.CommonAnnotations == nil { + if mdKind == ann && 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 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 mdKind == ann { if _, ok := m.CommonAnnotations[kv[0]]; ok { return fmt.Errorf("%s %s already in kustomization file", mdKind, kv[0]) } diff --git a/pkg/commands/addmetadata_test.go b/pkg/commands/addmetadata_test.go index 5d65f1567..6faa42cef 100644 --- a/pkg/commands/addmetadata_test.go +++ b/pkg/commands/addmetadata_test.go @@ -223,4 +223,4 @@ func TestAddAnnotationMultipleArgs(t *testing.T) { 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 +} From 924aa6fb294abb11b7330a110cc364eb99817afb Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Mon, 13 Aug 2018 07:46:06 -0700 Subject: [PATCH 05/12] Use iota declaration for constants and implements string method for KindOfAdd metadata --- pkg/commands/addmetadata.go | 52 +++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go index 1a546a75a..defd699a7 100644 --- a/pkg/commands/addmetadata.go +++ b/pkg/commands/addmetadata.go @@ -26,8 +26,24 @@ import ( "github.com/spf13/cobra" ) -const label = "label" -const ann = "annotation" +// KindOfAdd is the kind of metadata being added: label or annotation +type KindOfAdd int + +const ( + annotation KindOfAdd = iota + label +) + +func (kind KindOfAdd) String() string { + kinds := [...]string{ + "annotation", + "label", + } + if kind < 0 || kind > 1 { + return "Unknown metadatakind" + } + return kinds[kind] +} type addMetadataOptions struct { metadata string @@ -43,8 +59,7 @@ func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { Example: ` add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`, RunE: func(cmd *cobra.Command, args []string) error { - mdKind := ann - err := o.Validate(args, mdKind) + err := o.Validate(args, annotation) if err != nil { return err } @@ -52,7 +67,7 @@ func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - return o.RunAddMetadata(fsys, mdKind) + return o.RunAddMetadata(fsys, annotation) }, } return cmd @@ -68,8 +83,7 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { Example: ` add label {labelKey1:labelValue1},{labelKey2:labelValue2}`, RunE: func(cmd *cobra.Command, args []string) error { - mdKind := label - err := o.Validate(args, mdKind) + err := o.Validate(args, label) if err != nil { return err } @@ -77,20 +91,20 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - return o.RunAddMetadata(fsys, mdKind) + return o.RunAddMetadata(fsys, label) }, } return cmd } // Validate validates addLabel and addAnnotation commands. -func (o *addMetadataOptions) Validate(args []string, mdKind string) error { +func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { if len(args) < 1 { - return fmt.Errorf("must specify %s", mdKind) + return fmt.Errorf("must specify %s", k) } if len(args) > 1 { - return fmt.Errorf("%ss must be comma-separated, with no spaces. See help text for example", mdKind) + return fmt.Errorf("%ss must be comma-separated, with no spaces. See help text for example", k) } inputs := strings.Split(args[0], ",") for _, input := range inputs { @@ -99,7 +113,7 @@ func (o *addMetadataOptions) Validate(args []string, mdKind string) error { return err } if !ok { - return fmt.Errorf("invalid %s format: %s", mdKind, input) + return fmt.Errorf("invalid %s format: %s", k, input) } } @@ -113,7 +127,7 @@ func (o *addMetadataOptions) Complete(cmd *cobra.Command, args []string) error { } // RunAddMetadata runs addLabel and addAnnotation commands (do real work). -func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, mdKind string) error { +func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, k KindOfAdd) error { mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) if err != nil { return err @@ -124,26 +138,26 @@ func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, mdKind string) e return err } - if mdKind == label && m.CommonLabels == nil { + if k == label && m.CommonLabels == nil { m.CommonLabels = make(map[string]string) } - if mdKind == ann && m.CommonAnnotations == nil { + if k == 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 k == label { if _, ok := m.CommonLabels[kv[0]]; ok { - return fmt.Errorf("%s %s already in kustomization file", mdKind, kv[0]) + return fmt.Errorf("%s %s already in kustomization file", k, kv[0]) } m.CommonLabels[kv[0]] = kv[1] } - if mdKind == ann { + if k == annotation { if _, ok := m.CommonAnnotations[kv[0]]; ok { - return fmt.Errorf("%s %s already in kustomization file", mdKind, kv[0]) + return fmt.Errorf("%s %s already in kustomization file", k, kv[0]) } m.CommonAnnotations[kv[0]] = kv[1] } From 6a2786a5c47c8a36707b05903388e5555943c68c Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Mon, 13 Aug 2018 07:49:51 -0700 Subject: [PATCH 06/12] Remove Complete function and references --- pkg/commands/addmetadata.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go index defd699a7..4de9beb6b 100644 --- a/pkg/commands/addmetadata.go +++ b/pkg/commands/addmetadata.go @@ -63,10 +63,6 @@ func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - err = o.Complete(cmd, args) - if err != nil { - return err - } return o.RunAddMetadata(fsys, annotation) }, } @@ -87,10 +83,6 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - err = o.Complete(cmd, args) - if err != nil { - return err - } return o.RunAddMetadata(fsys, label) }, } @@ -121,11 +113,6 @@ func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { 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, k KindOfAdd) error { mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) From 3b644474c41bcf3c84ca450e446eea3e02b877d7 Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Wed, 15 Aug 2018 03:01:29 -0700 Subject: [PATCH 07/12] Parse data into string map for easy access in RunAddMetadata --- pkg/commands/addmetadata.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go index 4de9beb6b..d4898b5cb 100644 --- a/pkg/commands/addmetadata.go +++ b/pkg/commands/addmetadata.go @@ -34,19 +34,19 @@ const ( label ) -func (kind KindOfAdd) String() string { +func (k KindOfAdd) String() string { kinds := [...]string{ "annotation", "label", } - if kind < 0 || kind > 1 { + if k < 0 || k > 1 { return "Unknown metadatakind" } - return kinds[kind] + return kinds[k] } type addMetadataOptions struct { - metadata string + metadata map[string]string } // newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file. @@ -108,8 +108,13 @@ func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { return fmt.Errorf("invalid %s format: %s", k, input) } } - - o.metadata = args[0] + //parse annotation keys and values into metadata + entries := strings.Split(args[0], ",") + o.metadata = make(map[string]string) + for _, entry := range entries { + kv := strings.Split(entry, ":") + o.metadata[kv[0]] = kv[1] + } return nil } @@ -133,20 +138,18 @@ func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, k KindOfAdd) err m.CommonAnnotations = make(map[string]string) } - entries := strings.Split(o.metadata, ",") - for _, entry := range entries { - kv := strings.Split(entry, ":") + for key, value := range o.metadata { if k == label { - if _, ok := m.CommonLabels[kv[0]]; ok { - return fmt.Errorf("%s %s already in kustomization file", k, kv[0]) + if _, ok := m.CommonLabels[key]; ok { + return fmt.Errorf("%s %s already in kustomization file", k, key) } - m.CommonLabels[kv[0]] = kv[1] + m.CommonLabels[key] = value } if k == annotation { - if _, ok := m.CommonAnnotations[kv[0]]; ok { - return fmt.Errorf("%s %s already in kustomization file", k, kv[0]) + if _, ok := m.CommonAnnotations[key]; ok { + return fmt.Errorf("%s %s already in kustomization file", k, key) } - m.CommonAnnotations[kv[0]] = kv[1] + m.CommonAnnotations[key] = value } } From 524d593c5c18ce90e73a4d440d651d1a2885b229 Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Wed, 15 Aug 2018 03:51:56 -0700 Subject: [PATCH 08/12] Separate functions for RunnAddLabel and RunAddAnnotation --- pkg/commands/addmetadata.go | 46 +++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go index d4898b5cb..7579ca961 100644 --- a/pkg/commands/addmetadata.go +++ b/pkg/commands/addmetadata.go @@ -63,7 +63,7 @@ func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - return o.RunAddMetadata(fsys, annotation) + return o.RunAddAnnotation(fsys, annotation) }, } return cmd @@ -83,7 +83,7 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { if err != nil { return err } - return o.RunAddMetadata(fsys, label) + return o.RunAddLabel(fsys, label) }, } return cmd @@ -118,33 +118,22 @@ func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { return nil } -// RunAddMetadata runs addLabel and addAnnotation commands (do real work). -func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, k KindOfAdd) error { +// RunAddAnnotation runs addAnnotation command, doing the real work. +func (o *addMetadataOptions) RunAddAnnotation(fsys fs.FileSystem, k KindOfAdd) error { mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) if err != nil { return err } - m, err := mf.read() if err != nil { return err } - if k == label && m.CommonLabels == nil { - m.CommonLabels = make(map[string]string) - } - - if k == annotation && m.CommonAnnotations == nil { + if m.CommonAnnotations == nil { m.CommonAnnotations = make(map[string]string) } for key, value := range o.metadata { - if k == label { - if _, ok := m.CommonLabels[key]; ok { - return fmt.Errorf("%s %s already in kustomization file", k, key) - } - m.CommonLabels[key] = value - } if k == annotation { if _, ok := m.CommonAnnotations[key]; ok { return fmt.Errorf("%s %s already in kustomization file", k, key) @@ -152,6 +141,29 @@ func (o *addMetadataOptions) RunAddMetadata(fsys fs.FileSystem, k KindOfAdd) err m.CommonAnnotations[key] = value } } - + return mf.write(m) +} + +// RunAddLabel runs addLabel command, doing the real work. +func (o *addMetadataOptions) RunAddLabel(fsys fs.FileSystem, k KindOfAdd) error { + mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) + if err != nil { + return err + } + m, err := mf.read() + if err != nil { + return err + } + + if m.CommonLabels == nil { + m.CommonLabels = make(map[string]string) + } + + for key, value := range o.metadata { + if _, ok := m.CommonLabels[key]; ok { + return fmt.Errorf("%s %s already in kustomization file", k, key) + } + m.CommonLabels[key] = value + } return mf.write(m) } From 77f481177958fb79333ffce5f4d39e44d36098a0 Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Thu, 16 Aug 2018 02:36:11 -0700 Subject: [PATCH 09/12] Tests test Validate function --- pkg/commands/addmetadata.go | 3 +- pkg/commands/addmetadata_test.go | 160 ++++++++++--------------------- 2 files changed, 51 insertions(+), 112 deletions(-) diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go index 7579ca961..d28c689ea 100644 --- a/pkg/commands/addmetadata.go +++ b/pkg/commands/addmetadata.go @@ -91,7 +91,7 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { // Validate validates addLabel and addAnnotation commands. func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { - + o.metadata = make(map[string]string) if len(args) < 1 { return fmt.Errorf("must specify %s", k) } @@ -110,7 +110,6 @@ func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { } //parse annotation keys and values into metadata entries := strings.Split(args[0], ",") - o.metadata = make(map[string]string) for _, entry := range entries { kv := strings.Split(entry, ":") o.metadata[kv[0]] = kv[1] diff --git a/pkg/commands/addmetadata_test.go b/pkg/commands/addmetadata_test.go index 6faa42cef..f55b41b7c 100644 --- a/pkg/commands/addmetadata_test.go +++ b/pkg/commands/addmetadata_test.go @@ -17,34 +17,36 @@ limitations under the License. package commands import ( + "reflect" "testing" - "strings" - "github.com/kubernetes-sigs/kustomize/pkg/constants" "github.com/kubernetes-sigs/kustomize/pkg/fs" ) -func TestLabelsValid(t *testing.T) { +func TestInputValid(t *testing.T) { var testcases = []struct { - input string - valid bool - name string + input string + valid bool + name string + expectedData map[string]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", + name: "Adds single input", + expectedData: map[string]string{ + "otters": "cute", + }, + }, + { + input: "owls:great,unicorns:magical", + valid: true, + name: "Adds two items", + expectedData: map[string]string{ + "owls": "great", + "unicorns": "magical", + }, }, { input: "dogs,cats", @@ -61,41 +63,52 @@ func TestLabelsValid(t *testing.T) { valid: false, name: "Missing value", }, + { + input: "exclamation!:point", + valid: false, + name: "Non-alphanumeric input", + }, + { + input: "123:45", + valid: true, + name: "Numeric input is allowed", + expectedData: map[string]string{ + "123": "45", + }, + }, + { + input: "", + valid: false, + name: "Empty input", + }, } - fakeFS := fs.MakeFakeFS() - fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent)) - cmd := newCmdAddLabel(fakeFS) - + var o addMetadataOptions for _, tc := range testcases { - labels := strings.Split(tc.input, ",") - //run command with test input + args := []string{tc.input} - err := cmd.RunE(cmd, args) + err := o.Validate(args, label) //use label since in Validate kindofAdd is only used for error messages 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) + t.Errorf("unexpected error: expected invalid format error for test case %v", tc.name) } - if err == nil && (tc.name == "Label keys must be unique") { + if err == nil && (tc.name == "Metadata 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) + //o.metadata should be the same as expectedData + if tc.valid { + if !reflect.DeepEqual(o.metadata, tc.expectedData) { + t.Errorf("unexpeceted error: for test case %s, unexpected data was added", tc.name) + } + } else { + if len(o.metadata) != 0 { + t.Errorf("unexpeceted error: for test case %s, expected no data to be added", tc.name) } } - } - } func TestAddLabelNoArgs(t *testing.T) { @@ -125,79 +138,6 @@ func TestAddLabelMultipleArgs(t *testing.T) { } } -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() From 31dd8fc5b12aaf138d82b846e0d783ec24e8002f Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Thu, 16 Aug 2018 03:05:56 -0700 Subject: [PATCH 10/12] Restructured tests --- pkg/commands/addmetadata_test.go | 114 ++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 34 deletions(-) diff --git a/pkg/commands/addmetadata_test.go b/pkg/commands/addmetadata_test.go index f55b41b7c..2f195a4a9 100644 --- a/pkg/commands/addmetadata_test.go +++ b/pkg/commands/addmetadata_test.go @@ -17,6 +17,7 @@ limitations under the License. package commands import ( + "fmt" "reflect" "testing" @@ -76,11 +77,6 @@ func TestInputValid(t *testing.T) { "123": "45", }, }, - { - input: "", - valid: false, - name: "Empty input", - }, } var o addMetadataOptions for _, tc := range testcases { @@ -97,20 +93,97 @@ func TestInputValid(t *testing.T) { if err == nil && (tc.name == "Metadata keys must be unique") { t.Errorf("unexpected error: for test case %s, expected already there problem", tc.name) } + if err != nil && (tc.name == "Multiple args") { + fmt.Println(err.Error()) + if err.Error() != "must specify label" { + t.Errorf("unexpected error: for test case %s, expected must specify label error", tc.name) + } + } //o.metadata should be the same as expectedData if tc.valid { if !reflect.DeepEqual(o.metadata, tc.expectedData) { - t.Errorf("unexpeceted error: for test case %s, unexpected data was added", tc.name) + t.Errorf("unexpected error: for test case %s, unexpected data was added", tc.name) } } else { if len(o.metadata) != 0 { - t.Errorf("unexpeceted error: for test case %s, expected no data to be added", tc.name) + t.Errorf("unexpected error: for test case %s, expected no data to be added", tc.name) } } } } +func TestRunAddAnnotation(t *testing.T) { + fakeFS := fs.MakeFakeFS() + fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent)) + var o addMetadataOptions + o.metadata = map[string]string{"owls": "cute", "otters": "adorable"} + + err := o.RunAddAnnotation(fakeFS, annotation) + if err != nil { + t.Errorf("unexpected error: could not write to kustomization file") + } + // adding the same test input should not work + err = o.RunAddAnnotation(fakeFS, annotation) + if err == nil { + t.Errorf("expected already in kustomization file error") + } + // adding new annotations should work + o.metadata = map[string]string{"new": "annotation"} + err = o.RunAddAnnotation(fakeFS, annotation) + if err != nil { + t.Errorf("unexpected error: could not write to kustomization file") + } +} + +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()) + } +} + +func TestRunAddLabel(t *testing.T) { + fakeFS := fs.MakeFakeFS() + fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent)) + var o addMetadataOptions + o.metadata = map[string]string{"owls": "cute", "otters": "adorable"} + + err := o.RunAddLabel(fakeFS, label) + if err != nil { + t.Errorf("unexpected error: could not write to kustomization file") + } + // adding the same test input should not work + err = o.RunAddLabel(fakeFS, label) + if err == nil { + t.Errorf("expected already in kustomization file error") + } + // adding new labels should work + o.metadata = map[string]string{"new": "label"} + err = o.RunAddLabel(fakeFS, label) + if err != nil { + t.Errorf("unexpected error: could not write to kustomization file") + } +} + func TestAddLabelNoArgs(t *testing.T) { fakeFS := fs.MakeFakeFS() @@ -137,30 +210,3 @@ func TestAddLabelMultipleArgs(t *testing.T) { t.Errorf("incorrect error: %v", err.Error()) } } - -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()) - } -} From 11c04dd6c473010b4c64b20951691770623437cc Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Wed, 22 Aug 2018 18:15:34 +0200 Subject: [PATCH 11/12] Removes semantic validation from addmetadata.go and tests. Due to moving some input parsing to the Validate method, it was renamed to reflect this additional purpose. Tests were removed where appropriate. --- pkg/commands/addmetadata.go | 36 +++++++++++++----------- pkg/commands/addmetadata_test.go | 48 +++++++++----------------------- 2 files changed, 33 insertions(+), 51 deletions(-) diff --git a/pkg/commands/addmetadata.go b/pkg/commands/addmetadata.go index d28c689ea..56e57ba32 100644 --- a/pkg/commands/addmetadata.go +++ b/pkg/commands/addmetadata.go @@ -18,11 +18,11 @@ package commands import ( "fmt" - "regexp" "strings" "github.com/kubernetes-sigs/kustomize/pkg/constants" "github.com/kubernetes-sigs/kustomize/pkg/fs" + "github.com/kubernetes-sigs/kustomize/pkg/validate" "github.com/spf13/cobra" ) @@ -59,7 +59,7 @@ 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, annotation) + err := o.ValidateAndParse(args, annotation) if err != nil { return err } @@ -79,7 +79,7 @@ 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, label) + err := o.ValidateAndParse(args, label) if err != nil { return err } @@ -89,8 +89,8 @@ func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command { return cmd } -// Validate validates addLabel and addAnnotation commands. -func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { +// ValidateAndParse validates addLabel and addAnnotation commands and parses them into o.metadata +func (o *addMetadataOptions) ValidateAndParse(args []string, k KindOfAdd) error { o.metadata = make(map[string]string) if len(args) < 1 { return fmt.Errorf("must specify %s", k) @@ -100,18 +100,22 @@ func (o *addMetadataOptions) Validate(args []string, k KindOfAdd) error { } 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 + switch k { + case label: + valid, err := validate.IsValidLabel(input) + if !valid { + return err + } + case annotation: + valid, err := validate.IsValidAnnotation(input) + if !valid { + return err + } + default: + return fmt.Errorf("unknown metadata kind %s", k) } - if !ok { - return fmt.Errorf("invalid %s format: %s", k, input) - } - } - //parse annotation keys and values into metadata - entries := strings.Split(args[0], ",") - for _, entry := range entries { - kv := strings.Split(entry, ":") + //parse annotation keys and values into metadata + kv := strings.Split(input, ":") o.metadata[kv[0]] = kv[1] } return nil diff --git a/pkg/commands/addmetadata_test.go b/pkg/commands/addmetadata_test.go index 2f195a4a9..e796ff04b 100644 --- a/pkg/commands/addmetadata_test.go +++ b/pkg/commands/addmetadata_test.go @@ -17,7 +17,6 @@ limitations under the License. package commands import ( - "fmt" "reflect" "testing" @@ -25,12 +24,13 @@ import ( "github.com/kubernetes-sigs/kustomize/pkg/fs" ) -func TestInputValid(t *testing.T) { +func TestParseValidateInput(t *testing.T) { var testcases = []struct { input string valid bool name string expectedData map[string]string + kind KindOfAdd }{ { input: "otters:cute", @@ -39,6 +39,7 @@ func TestInputValid(t *testing.T) { expectedData: map[string]string{ "otters": "cute", }, + kind: label, }, { input: "owls:great,unicorns:magical", @@ -48,26 +49,7 @@ func TestInputValid(t *testing.T) { "owls": "great", "unicorns": "magical", }, - }, - { - input: "dogs,cats", - valid: false, - name: "Does not contain colon", - }, - { - input: ":noKey", - valid: false, - name: "Missing key", - }, - { - input: "noValue:", - valid: false, - name: "Missing value", - }, - { - input: "exclamation!:point", - valid: false, - name: "Non-alphanumeric input", + kind: label, }, { input: "123:45", @@ -76,30 +58,26 @@ func TestInputValid(t *testing.T) { expectedData: map[string]string{ "123": "45", }, + kind: annotation, + }, + { + input: " ", + valid: false, + name: "Empty space input", + expectedData: nil, + kind: annotation, }, } var o addMetadataOptions for _, tc := range testcases { - args := []string{tc.input} - err := o.Validate(args, label) //use label since in Validate kindofAdd is only used for error messages - + err := o.ValidateAndParse(args, tc.kind) 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 format error for test case %v", tc.name) } - if err == nil && (tc.name == "Metadata keys must be unique") { - t.Errorf("unexpected error: for test case %s, expected already there problem", tc.name) - } - if err != nil && (tc.name == "Multiple args") { - fmt.Println(err.Error()) - if err.Error() != "must specify label" { - t.Errorf("unexpected error: for test case %s, expected must specify label error", tc.name) - } - } - //o.metadata should be the same as expectedData if tc.valid { if !reflect.DeepEqual(o.metadata, tc.expectedData) { From b3993dc87453dc1ec036e8283f644348fcb2d90f Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Wed, 22 Aug 2018 18:20:51 +0200 Subject: [PATCH 12/12] Adds starter validation framework for semantic validation of inputs. --- pkg/validate/validate.go | 35 ++++++++++++ pkg/validate/validate_test.go | 103 ++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 pkg/validate/validate.go create mode 100644 pkg/validate/validate_test.go diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go new file mode 100644 index 000000000..94ae5de11 --- /dev/null +++ b/pkg/validate/validate.go @@ -0,0 +1,35 @@ +package validate + +import ( + "fmt" + "regexp" +) + +// TODO: these are rudimentary placeholder validation functions and need +// additional work to truly match expected syntax rules. + +// IsValidLabel checks whether a label key/value pair has correct syntax and +// character set +func IsValidLabel(keyval string) (bool, error) { + ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, keyval) + if err != nil { + return false, err + } + if !ok { + return false, fmt.Errorf("invalid label format: %s", keyval) + } + return true, nil +} + +// IsValidAnnotation checks whether an annotation key/value pair has correct +// syntax and character set +func IsValidAnnotation(keyval string) (bool, error) { + ok, err := regexp.MatchString(`\A([a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)\z`, keyval) + if err != nil { + return false, err + } + if !ok { + return false, fmt.Errorf("invalid annotation format: %s", keyval) + } + return true, nil +} diff --git a/pkg/validate/validate_test.go b/pkg/validate/validate_test.go new file mode 100644 index 000000000..ff84f82b2 --- /dev/null +++ b/pkg/validate/validate_test.go @@ -0,0 +1,103 @@ +package validate + +import "testing" + +func TestIsValidLabel(t *testing.T) { + testcases := []struct { + input, name string + valid bool + }{ + { + input: "otters:cute", + valid: true, + name: "Valid input format", + }, + { + input: "dogs,cats", + valid: false, + name: "Does not contain colon", + }, + { + input: ":noKey", + valid: false, + name: "Missing key", + }, + { + input: "noValue:", + valid: false, + name: "Missing value", + }, + { + input: "exclamation!:point", + valid: false, + name: "Non-alphanumeric input", + }, + { + input: "123:45", + valid: true, + name: "Numeric input is allowed", + }, + } + for _, tc := range testcases { + ok, err := IsValidLabel(tc.input) + if tc.valid && err != nil { + t.Errorf("unexpected error: for test case %s, expected no error but got: %s", tc.name, err.Error()) + } + if ok && !tc.valid { + t.Errorf("for test case %s, expected invalid label format error", tc.name) + } + if !ok && tc.valid { + t.Errorf("unexpected error: for test case %s, expected test to pass", tc.name) + } + } +} + +func TestIsValidAnnotation(t *testing.T) { + testcases := []struct { + input, name string + valid bool + }{ + { + input: "owls:adorable", + valid: true, + name: "Valid input format", + }, + { + input: "cake,cookies", + valid: false, + name: "Does not contain colon", + }, + { + input: ":noKey", + valid: false, + name: "Missing key", + }, + { + input: "noValue:", + valid: false, + name: "Missing value", + }, + { + input: "exclamation!:point", + valid: false, + name: "Input has a bang!", + }, + { + input: "987:65", + valid: true, + name: "Numeric input is valid", + }, + } + for _, tc := range testcases { + ok, err := IsValidAnnotation(tc.input) + if tc.valid && err != nil { + t.Errorf("unexpected error: for test case %s, expected no error but got: %s", tc.name, err.Error()) + } + if ok && !tc.valid { + t.Errorf("for test case %s, expected invalid annotation format error", tc.name) + } + if !ok && tc.valid { + t.Errorf("unexpected error: for test case %s, expected test to pass", tc.name) + } + } +}