mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 01:50:55 +00:00
Improve command package isolation.
This commit is contained in:
98
pkg/commands/edit/add/addbase.go
Normal file
98
pkg/commands/edit/add/addbase.go
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
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 add
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type addBaseOptions struct {
|
||||
baseDirectoryPaths string
|
||||
}
|
||||
|
||||
// newCmdAddBase adds the file path of the kustomize base to the kustomization file.
|
||||
func newCmdAddBase(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addBaseOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "base",
|
||||
Short: "Adds one or more bases to the kustomization.yaml in current directory",
|
||||
Example: `
|
||||
add base {filepath1},{filepath2}`,
|
||||
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.RunAddBase(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates addBase command.
|
||||
func (o *addBaseOptions) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("must specify a base directory")
|
||||
}
|
||||
o.baseDirectoryPaths = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete completes addBase command.
|
||||
func (o *addBaseOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunAddBase runs addBase command (do real work).
|
||||
func (o *addBaseOptions) RunAddBase(fsys fs.FileSystem) error {
|
||||
mf, err := kustfile.NewKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := mf.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// split directory paths
|
||||
paths := strings.Split(o.baseDirectoryPaths, ",")
|
||||
for _, path := range paths {
|
||||
if !fsys.Exists(path) {
|
||||
return errors.New(path + " does not exist")
|
||||
}
|
||||
if kustfile.StringInSlice(path, m.Bases) {
|
||||
return fmt.Errorf("base %s already in kustomization file", path)
|
||||
}
|
||||
m.Bases = append(m.Bases, path)
|
||||
|
||||
}
|
||||
|
||||
return mf.Write(m)
|
||||
}
|
||||
100
pkg/commands/edit/add/addbase_test.go
Normal file
100
pkg/commands/edit/add/addbase_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
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 add
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
baseDirectoryPaths = "my/path/to/wonderful/base,other/path/to/even/more/wonderful/base"
|
||||
)
|
||||
|
||||
func TestAddBaseHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
bases := strings.Split(baseDirectoryPaths, ",")
|
||||
for _, base := range bases {
|
||||
fakeFS.Mkdir(base)
|
||||
}
|
||||
fakeFS.WriteTestKustomization()
|
||||
|
||||
cmd := newCmdAddBase(fakeFS)
|
||||
args := []string{baseDirectoryPaths}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
|
||||
for _, base := range bases {
|
||||
if !strings.Contains(string(content), base) {
|
||||
t.Errorf("expected base name in kustomization")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddBaseAlreadyThere(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
// Create fake directories
|
||||
bases := strings.Split(baseDirectoryPaths, ",")
|
||||
for _, base := range bases {
|
||||
fakeFS.Mkdir(base)
|
||||
}
|
||||
fakeFS.WriteTestKustomization()
|
||||
|
||||
cmd := newCmdAddBase(fakeFS)
|
||||
args := []string{baseDirectoryPaths}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected cmd error: %v", err)
|
||||
}
|
||||
// adding an existing base should return an error
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err == nil {
|
||||
t.Errorf("expected already there problem")
|
||||
}
|
||||
var expectedErrors []string
|
||||
for _, base := range bases {
|
||||
msg := "base " + base + " already in kustomization file"
|
||||
expectedErrors = append(expectedErrors, msg)
|
||||
if !kustfile.StringInSlice(msg, expectedErrors) {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAddBaseNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdAddBase(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "must specify a base directory" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
175
pkg/commands/edit/add/addmetadata.go
Normal file
175
pkg/commands/edit/add/addmetadata.go
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
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 add
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
// kindOfAdd is the kind of metadata being added: label or annotation
|
||||
type kindOfAdd int
|
||||
|
||||
const (
|
||||
annotation kindOfAdd = iota
|
||||
label
|
||||
)
|
||||
|
||||
func (k kindOfAdd) String() string {
|
||||
kinds := [...]string{
|
||||
"annotation",
|
||||
"label",
|
||||
}
|
||||
if k < 0 || k > 1 {
|
||||
return "Unknown metadatakind"
|
||||
}
|
||||
return kinds[k]
|
||||
}
|
||||
|
||||
type addMetadataOptions struct {
|
||||
metadata map[string]string
|
||||
mapValidator func(map[string]string) error
|
||||
kind kindOfAdd
|
||||
}
|
||||
|
||||
// newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file.
|
||||
func newCmdAddAnnotation(fSys fs.FileSystem, v func(map[string]string) error) *cobra.Command {
|
||||
var o addMetadataOptions
|
||||
o.kind = annotation
|
||||
o.mapValidator = v
|
||||
cmd := &cobra.Command{
|
||||
Use: "annotation",
|
||||
Short: "Adds one or more commonAnnotations to " + constants.KustomizationFileName,
|
||||
Example: `
|
||||
add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return o.runE(args, fSys, o.addAnnotations)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// newCmdAddLabel adds one or more commonLabels to the kustomization file.
|
||||
func newCmdAddLabel(fSys fs.FileSystem, v func(map[string]string) error) *cobra.Command {
|
||||
var o addMetadataOptions
|
||||
o.kind = label
|
||||
o.mapValidator = v
|
||||
cmd := &cobra.Command{
|
||||
Use: "label",
|
||||
Short: "Adds one or more commonLabels to " + constants.KustomizationFileName,
|
||||
Example: `
|
||||
add label {labelKey1:labelValue1},{labelKey2:labelValue2}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return o.runE(args, fSys, o.addLabels)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) runE(
|
||||
args []string, fSys fs.FileSystem, adder func(*types.Kustomization) error) error {
|
||||
err := o.validateAndParse(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kf, err := kustfile.NewKustomizationFile(constants.KustomizationFileName, fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := kf.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adder(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return kf.Write(m)
|
||||
}
|
||||
|
||||
// validateAndParse validates `add` commands and parses them into o.metadata
|
||||
func (o *addMetadataOptions) validateAndParse(args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("must specify %s", o.kind)
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("%ss must be comma-separated, with no spaces", o.kind)
|
||||
}
|
||||
m, err := o.convertToMap(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = o.mapValidator(m); err != nil {
|
||||
return err
|
||||
}
|
||||
o.metadata = m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) convertToMap(arg string) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
inputs := strings.Split(arg, ",")
|
||||
for _, input := range inputs {
|
||||
kv := strings.Split(input, ":")
|
||||
if len(kv[0]) < 1 {
|
||||
return nil, o.makeError(input, "empty key")
|
||||
}
|
||||
if len(kv) > 2 {
|
||||
return nil, o.makeError(input, "too many colons")
|
||||
}
|
||||
if len(kv) > 1 {
|
||||
result[kv[0]] = kv[1]
|
||||
} else {
|
||||
result[kv[0]] = ""
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) addAnnotations(m *types.Kustomization) error {
|
||||
if m.CommonAnnotations == nil {
|
||||
m.CommonAnnotations = make(map[string]string)
|
||||
}
|
||||
return o.writeToMap(m.CommonAnnotations, annotation)
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) addLabels(m *types.Kustomization) error {
|
||||
if m.CommonLabels == nil {
|
||||
m.CommonLabels = make(map[string]string)
|
||||
}
|
||||
return o.writeToMap(m.CommonLabels, label)
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) writeToMap(m map[string]string, kind kindOfAdd) error {
|
||||
for k, v := range o.metadata {
|
||||
if _, ok := m[k]; ok {
|
||||
return fmt.Errorf("%s %s already in kustomization file", kind, k)
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) makeError(input string, message string) error {
|
||||
return fmt.Errorf("invalid %s: %s (%s)", o.kind, input, message)
|
||||
}
|
||||
274
pkg/commands/edit/add/addmetadata_test.go
Normal file
274
pkg/commands/edit/add/addmetadata_test.go
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
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 add
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
"sigs.k8s.io/kustomize/pkg/validators"
|
||||
)
|
||||
|
||||
func makeKustomization(t *testing.T) *types.Kustomization {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteTestKustomization()
|
||||
kf, err := kustfile.NewKustomizationFile(constants.KustomizationFileName, fakeFS)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected new error %v", err)
|
||||
}
|
||||
m, err := kf.Read()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error %v", err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func TestRunAddAnnotation(t *testing.T) {
|
||||
var o addMetadataOptions
|
||||
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||
|
||||
m := makeKustomization(t)
|
||||
err := o.addAnnotations(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
// adding the same test input should not work
|
||||
err = o.addAnnotations(m)
|
||||
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.addAnnotations(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
err := cmd.Execute()
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "must specify annotation" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationInvalidFormat(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeSadMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"whatever:whatever"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != validators.SAD {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationManyArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteTestKustomization()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"k1:v1,k2:v2,k3:v3,k4:v5"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationNoKey(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{":nokey"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid annotation: :nokey (empty key)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationTooManyColons(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"key:v1:v2"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid annotation: key:v1:v2 (too many colons)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationNoValue(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteTestKustomization()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"no:,value"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationMultipleArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteTestKustomization()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"this:annotation", "has:spaces"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "annotations must be comma-separated, with no spaces" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAddLabel(t *testing.T) {
|
||||
var o addMetadataOptions
|
||||
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||
|
||||
m := makeKustomization(t)
|
||||
err := o.addLabels(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
// adding the same test input should not work
|
||||
err = o.addLabels(m)
|
||||
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.addLabels(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
err := cmd.Execute()
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "must specify label" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelInvalidFormat(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeSadMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"exclamation!:point"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != validators.SAD {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelNoKey(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{":nokey"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid label: :nokey (empty key)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelTooManyColons(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"key:v1:v2"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid label: key:v1:v2 (too many colons)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelNoValue(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteTestKustomization()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"no,value:"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelMultipleArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteTestKustomization()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"this:input", "has:spaces"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "labels must be comma-separated, with no spaces" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
101
pkg/commands/edit/add/addpatch.go
Normal file
101
pkg/commands/edit/add/addpatch.go
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright 2017 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 add
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/patch"
|
||||
)
|
||||
|
||||
type addPatchOptions struct {
|
||||
patchFilePaths []string
|
||||
}
|
||||
|
||||
// newCmdAddPatch adds the name of a file containing a patch to the kustomization file.
|
||||
func newCmdAddPatch(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addPatchOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "patch",
|
||||
Short: "Add the name of a file containing a patch to the kustomization file.",
|
||||
Example: `
|
||||
add patch {filepath}`,
|
||||
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.RunAddPatch(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates addPatch command.
|
||||
func (o *addPatchOptions) Validate(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("must specify a patch file")
|
||||
}
|
||||
o.patchFilePaths = args
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete completes addPatch command.
|
||||
func (o *addPatchOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunAddPatch runs addPatch command (do real work).
|
||||
func (o *addPatchOptions) RunAddPatch(fsys fs.FileSystem) error {
|
||||
patches, err := globPatterns(fsys, o.patchFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(patches) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mf, err := kustfile.NewKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := mf.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range patches {
|
||||
if patch.Exist(m.PatchesStrategicMerge, p) || kustfile.StringInSlice(p, m.Patches) {
|
||||
log.Printf("patch %s already in kustomization file", p)
|
||||
continue
|
||||
}
|
||||
m.PatchesStrategicMerge = patch.Append(m.PatchesStrategicMerge, p)
|
||||
}
|
||||
|
||||
return mf.Write(m)
|
||||
}
|
||||
90
pkg/commands/edit/add/addpatch_test.go
Normal file
90
pkg/commands/edit/add/addpatch_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2017 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 add
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
patchFileName = "myWonderfulPatch.yaml"
|
||||
patchFileContent = `
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
`
|
||||
)
|
||||
|
||||
func TestAddPatchHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
||||
fakeFS.WriteFile(patchFileName+"another", []byte(patchFileContent))
|
||||
fakeFS.WriteTestKustomization()
|
||||
|
||||
cmd := newCmdAddPatch(fakeFS)
|
||||
args := []string{patchFileName + "*"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(content), patchFileName) {
|
||||
t.Errorf("expected patch name in kustomization")
|
||||
}
|
||||
if !strings.Contains(string(content), patchFileName+"another") {
|
||||
t.Errorf("expected patch name in kustomization")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPatchAlreadyThere(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
||||
fakeFS.WriteTestKustomization()
|
||||
|
||||
cmd := newCmdAddPatch(fakeFS)
|
||||
args := []string{patchFileName}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected cmd error: %v", err)
|
||||
}
|
||||
|
||||
// adding an existing patch shouldn't return an error
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPatchNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdAddPatch(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "must specify a patch file" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
100
pkg/commands/edit/add/addresource.go
Normal file
100
pkg/commands/edit/add/addresource.go
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2017 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 add
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type addResourceOptions struct {
|
||||
resourceFilePaths []string
|
||||
}
|
||||
|
||||
// newCmdAddResource adds the name of a file containing a resource to the kustomization file.
|
||||
func newCmdAddResource(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addResourceOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "resource",
|
||||
Short: "Add the name of a file containing a resource to the kustomization file.",
|
||||
Example: `
|
||||
add resource {filepath}`,
|
||||
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.RunAddResource(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates addResource command.
|
||||
func (o *addResourceOptions) Validate(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("must specify a resource file")
|
||||
}
|
||||
o.resourceFilePaths = args
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete completes addResource command.
|
||||
func (o *addResourceOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunAddResource runs addResource command (do real work).
|
||||
func (o *addResourceOptions) RunAddResource(fsys fs.FileSystem) error {
|
||||
resources, err := globPatterns(fsys, o.resourceFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resources) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mf, err := kustfile.NewKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := mf.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, resource := range resources {
|
||||
if kustfile.StringInSlice(resource, m.Resources) {
|
||||
log.Printf("resource %s already in kustomization file", resource)
|
||||
continue
|
||||
}
|
||||
m.Resources = append(m.Resources, resource)
|
||||
}
|
||||
|
||||
return mf.Write(m)
|
||||
}
|
||||
89
pkg/commands/edit/add/addresource_test.go
Normal file
89
pkg/commands/edit/add/addresource_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2017 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 add
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
resourceFileName = "myWonderfulResource.yaml"
|
||||
resourceFileContent = `
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
`
|
||||
)
|
||||
|
||||
func TestAddResourceHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||
fakeFS.WriteFile(resourceFileName+"another", []byte(resourceFileContent))
|
||||
fakeFS.WriteTestKustomization()
|
||||
|
||||
cmd := newCmdAddResource(fakeFS)
|
||||
args := []string{resourceFileName + "*"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(content), resourceFileName) {
|
||||
t.Errorf("expected resource name in kustomization")
|
||||
}
|
||||
if !strings.Contains(string(content), resourceFileName+"another") {
|
||||
t.Errorf("expected resource name in kustomization")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddResourceAlreadyThere(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||
fakeFS.WriteTestKustomization()
|
||||
|
||||
cmd := newCmdAddResource(fakeFS)
|
||||
args := []string{resourceFileName}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected cmd error: %v", err)
|
||||
}
|
||||
|
||||
// adding an existing resource doesn't return an error
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error :%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddResourceNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdAddResource(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "must specify a resource file" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
62
pkg/commands/edit/add/all.go
Normal file
62
pkg/commands/edit/add/all.go
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2017 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 add
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
)
|
||||
|
||||
// NewCmdAdd returns an instance of 'add' subcommand.
|
||||
func NewCmdAdd(fsys fs.FileSystem, v ifc.Validator) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Adds configmap/resource/patch/base to the kustomization file.",
|
||||
Long: "",
|
||||
Example: `
|
||||
# Adds a configmap to the kustomization file
|
||||
kustomize edit add configmap NAME --from-literal=k=v
|
||||
|
||||
# Adds a resource to the kustomization
|
||||
kustomize edit add resource <filepath>
|
||||
|
||||
# Adds a patch to the kustomization
|
||||
kustomize edit add patch <filepath>
|
||||
|
||||
# Adds one or more base directories to the kustomization
|
||||
kustomize edit add base <filepath>
|
||||
kustomize edit add base <filepath1>,<filepath2>,<filepath3>
|
||||
|
||||
# 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}
|
||||
`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
c.AddCommand(
|
||||
newCmdAddResource(fsys),
|
||||
newCmdAddPatch(fsys),
|
||||
newCmdAddConfigMap(fsys),
|
||||
newCmdAddBase(fsys),
|
||||
newCmdAddLabel(fsys, v.MakeLabelValidator()),
|
||||
newCmdAddAnnotation(fsys, v.MakeAnnotationValidator()),
|
||||
)
|
||||
return c
|
||||
}
|
||||
61
pkg/commands/edit/add/cmapflagsandargs.go
Normal file
61
pkg/commands/edit/add/cmapflagsandargs.go
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
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 add
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
// cMapFlagsAndArgs encapsulates the options for add configmap commands.
|
||||
type cMapFlagsAndArgs struct {
|
||||
// Name of configMap/Secret (required)
|
||||
Name string
|
||||
// FileSources to derive the configMap/Secret from (optional)
|
||||
FileSources []string
|
||||
// LiteralSources to derive the configMap/Secret from (optional)
|
||||
LiteralSources []string
|
||||
// EnvFileSource to derive the configMap/Secret from (optional)
|
||||
// TODO: Rationalize this name with Generic.EnvSource
|
||||
EnvFileSource string
|
||||
}
|
||||
|
||||
// Validate validates required fields are set to support structured generation.
|
||||
func (a *cMapFlagsAndArgs) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("name must be specified once")
|
||||
}
|
||||
a.Name = args[0]
|
||||
if len(a.EnvFileSource) == 0 && len(a.FileSources) == 0 && len(a.LiteralSources) == 0 {
|
||||
return fmt.Errorf("at least from-env-file, or from-file or from-literal must be set")
|
||||
}
|
||||
if len(a.EnvFileSource) > 0 && (len(a.FileSources) > 0 || len(a.LiteralSources) > 0) {
|
||||
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
|
||||
}
|
||||
// TODO: Should we check if the path exists? if it's valid, if it's within the same (sub-)directory?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *cMapFlagsAndArgs) ExpandFileSource(fSys fs.FileSystem) error {
|
||||
result, err := globPatterns(fSys, a.FileSources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.FileSources = result
|
||||
return nil
|
||||
}
|
||||
104
pkg/commands/edit/add/cmapflagsandargs_test.go
Normal file
104
pkg/commands/edit/add/cmapflagsandargs_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 add
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestDataConfigValidation_NoName(t *testing.T) {
|
||||
config := cMapFlagsAndArgs{}
|
||||
|
||||
if config.Validate([]string{}) == nil {
|
||||
t.Fatal("Validation should fail if no name is specified")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
|
||||
config := cMapFlagsAndArgs{}
|
||||
|
||||
if config.Validate([]string{"name", "othername"}) == nil {
|
||||
t.Fatal("Validation should fail if more than one name is specified")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataConfigValidation_Flags(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config cMapFlagsAndArgs
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
name: "env-file-source and literal are both set",
|
||||
config: cMapFlagsAndArgs{
|
||||
LiteralSources: []string{"one", "two"},
|
||||
EnvFileSource: "three",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "env-file-source and from-file are both set",
|
||||
config: cMapFlagsAndArgs{
|
||||
FileSources: []string{"one", "two"},
|
||||
EnvFileSource: "three",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "we don't have any option set",
|
||||
config: cMapFlagsAndArgs{},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "we have from-file and literal ",
|
||||
config: cMapFlagsAndArgs{
|
||||
LiteralSources: []string{"one", "two"},
|
||||
FileSources: []string{"three", "four"},
|
||||
},
|
||||
shouldFail: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.config.Validate([]string{"name"}) == nil && test.shouldFail {
|
||||
t.Fatalf("Validation should fail if %s", test.name)
|
||||
} else if test.config.Validate([]string{"name"}) != nil && !test.shouldFail {
|
||||
t.Fatalf("Validation should succeed if %s", test.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandFileSource(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.Create("dir/config1")
|
||||
fakeFS.Create("dir/config2")
|
||||
fakeFS.Create("dir/reademe")
|
||||
config := cMapFlagsAndArgs{
|
||||
FileSources: []string{"dir/config*"},
|
||||
}
|
||||
config.ExpandFileSource(fakeFS)
|
||||
expected := []string{
|
||||
"dir/config1",
|
||||
"dir/config2",
|
||||
}
|
||||
if !reflect.DeepEqual(config.FileSources, expected) {
|
||||
t.Fatalf("FileSources is not correctly expanded: %v", config.FileSources)
|
||||
}
|
||||
}
|
||||
145
pkg/commands/edit/add/configmap.go
Normal file
145
pkg/commands/edit/add/configmap.go
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright 2017 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 add
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/configmapandsecret"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
// newCmdAddConfigMap returns a new command.
|
||||
func newCmdAddConfigMap(fSys fs.FileSystem) *cobra.Command {
|
||||
var flagsAndArgs cMapFlagsAndArgs
|
||||
cmd := &cobra.Command{
|
||||
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
|
||||
Short: "Adds a configmap to the kustomization file.",
|
||||
Long: "",
|
||||
Example: `
|
||||
# Adds a configmap to the kustomization file (with a specified key)
|
||||
kustomize edit add configmap my-configmap --from-file=my-key=file/path --from-literal=my-literal=12345
|
||||
|
||||
# Adds a configmap to the kustomization file (key is the filename)
|
||||
kustomize edit add configmap my-configmap --from-file=file/path
|
||||
|
||||
# Adds a configmap from env-file
|
||||
kustomize edit add configmap my-configmap --from-env-file=env/path.env
|
||||
`,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
err := flagsAndArgs.ExpandFileSource(fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = flagsAndArgs.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the kustomization file.
|
||||
mf, err := kustfile.NewKustomizationFile(constants.KustomizationFileName, fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kustomization, err := mf.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add the flagsAndArgs map to the kustomization file.
|
||||
err = addConfigMap(
|
||||
kustomization, flagsAndArgs,
|
||||
configmapandsecret.NewConfigMapFactory(
|
||||
fSys, loader.NewFileLoader(fSys)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write out the kustomization file with added configmap.
|
||||
return mf.Write(kustomization)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringSliceVar(
|
||||
&flagsAndArgs.FileSources,
|
||||
"from-file",
|
||||
[]string{},
|
||||
"Key file can be specified using its file path, in which case file basename will be used as configmap "+
|
||||
"key, or optionally with a key and file path, in which case the given key will be used. Specifying a "+
|
||||
"directory will iterate each named file in the directory whose basename is a valid configmap key.")
|
||||
cmd.Flags().StringArrayVar(
|
||||
&flagsAndArgs.LiteralSources,
|
||||
"from-literal",
|
||||
[]string{},
|
||||
"Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
|
||||
cmd.Flags().StringVar(
|
||||
&flagsAndArgs.EnvFileSource,
|
||||
"from-env-file",
|
||||
"",
|
||||
"Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// addConfigMap adds a configmap to a kustomization file.
|
||||
// Note: error may leave kustomization file in an undefined state.
|
||||
// Suggest passing a copy of kustomization file.
|
||||
func addConfigMap(
|
||||
k *types.Kustomization,
|
||||
flagsAndArgs cMapFlagsAndArgs,
|
||||
factory *configmapandsecret.ConfigMapFactory) error {
|
||||
cmArgs := makeConfigMapArgs(k, flagsAndArgs.Name)
|
||||
err := mergeFlagsIntoCmArgs(&cmArgs.DataSources, flagsAndArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate by trying to create corev1.configmap.
|
||||
_, _, err = factory.MakeUnstructAndGenerateName(cmArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
|
||||
for i, v := range m.ConfigMapGenerator {
|
||||
if name == v.Name {
|
||||
return &m.ConfigMapGenerator[i]
|
||||
}
|
||||
}
|
||||
// config map not found, create new one and add it to the kustomization file.
|
||||
cm := &types.ConfigMapArgs{Name: name}
|
||||
m.ConfigMapGenerator = append(m.ConfigMapGenerator, *cm)
|
||||
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
|
||||
}
|
||||
|
||||
func mergeFlagsIntoCmArgs(src *types.DataSources, flags cMapFlagsAndArgs) error {
|
||||
src.LiteralSources = append(src.LiteralSources, flags.LiteralSources...)
|
||||
src.FileSources = append(src.FileSources, flags.FileSources...)
|
||||
if src.EnvSource != "" && src.EnvSource != flags.EnvFileSource {
|
||||
return fmt.Errorf("updating existing env source '%s' not allowed", src.EnvSource)
|
||||
}
|
||||
src.EnvSource = flags.EnvFileSource
|
||||
return nil
|
||||
}
|
||||
129
pkg/commands/edit/add/configmap_test.go
Normal file
129
pkg/commands/edit/add/configmap_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright 2017 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 add
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func TestNewAddConfigMapIsNotNil(t *testing.T) {
|
||||
if newCmdAddConfigMap(fs.MakeFakeFS()) == nil {
|
||||
t.Fatal("newCmdAddConfigMap shouldn't be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeConfigMapArgs(t *testing.T) {
|
||||
cmName := "test-config-name"
|
||||
|
||||
kustomization := &types.Kustomization{
|
||||
NamePrefix: "test-name-prefix",
|
||||
}
|
||||
|
||||
if len(kustomization.ConfigMapGenerator) != 0 {
|
||||
t.Fatal("Initial kustomization should not have any configmaps")
|
||||
}
|
||||
args := makeConfigMapArgs(kustomization, cmName)
|
||||
|
||||
if args == nil {
|
||||
t.Fatalf("args should always be non-nil")
|
||||
}
|
||||
|
||||
if len(kustomization.ConfigMapGenerator) != 1 {
|
||||
t.Fatalf("Kustomization should have newly created configmap")
|
||||
}
|
||||
|
||||
if &kustomization.ConfigMapGenerator[len(kustomization.ConfigMapGenerator)-1] != args {
|
||||
t.Fatalf("Pointer address for newly inserted configmap generator should be same")
|
||||
}
|
||||
|
||||
args2 := makeConfigMapArgs(kustomization, cmName)
|
||||
|
||||
if args2 != args {
|
||||
t.Fatalf("should have returned an existing args with name: %v", cmName)
|
||||
}
|
||||
|
||||
if len(kustomization.ConfigMapGenerator) != 1 {
|
||||
t.Fatalf("Should not insert configmap for an existing name: %v", cmName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) {
|
||||
ds := &types.DataSources{}
|
||||
|
||||
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k1=v1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge initial literal source should not return error")
|
||||
}
|
||||
|
||||
if len(ds.LiteralSources) != 1 {
|
||||
t.Fatalf("Initial literal source should have been added")
|
||||
}
|
||||
|
||||
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k2=v2"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge second literal source should not return error")
|
||||
}
|
||||
|
||||
if len(ds.LiteralSources) != 2 {
|
||||
t.Fatalf("Second literal source should have been added")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeFlagsIntoCmArgs_FileSources(t *testing.T) {
|
||||
ds := &types.DataSources{}
|
||||
|
||||
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge initial file source should not return error")
|
||||
}
|
||||
|
||||
if len(ds.FileSources) != 1 {
|
||||
t.Fatalf("Initial file source should have been added")
|
||||
}
|
||||
|
||||
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file2"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge second file source should not return error")
|
||||
}
|
||||
|
||||
if len(ds.FileSources) != 2 {
|
||||
t.Fatalf("Second file source should have been added")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeFlagsIntoCmArgs_EnvSource(t *testing.T) {
|
||||
envFileName := "env1"
|
||||
envFileName2 := "env2"
|
||||
ds := &types.DataSources{}
|
||||
|
||||
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge initial env source should not return error")
|
||||
}
|
||||
|
||||
if ds.EnvSource != envFileName {
|
||||
t.Fatalf("Initial env source filename should have been added")
|
||||
}
|
||||
|
||||
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName2})
|
||||
if err == nil {
|
||||
t.Fatalf("Updating env source should return an error")
|
||||
}
|
||||
}
|
||||
39
pkg/commands/edit/add/util.go
Normal file
39
pkg/commands/edit/add/util.go
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
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 add
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func globPatterns(fsys fs.FileSystem, patterns []string) ([]string, error) {
|
||||
var result []string
|
||||
for _, pattern := range patterns {
|
||||
files, err := fsys.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(files) == 0 {
|
||||
log.Printf("%s has no match", pattern)
|
||||
continue
|
||||
}
|
||||
result = append(result, files...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user