Merge pull request #709 from sethpollack/secret

add add secret command
This commit is contained in:
Jeff Regan
2019-01-25 16:10:24 -08:00
committed by GitHub
7 changed files with 327 additions and 44 deletions

View File

@@ -26,9 +26,12 @@ import (
func NewCmdAdd(fsys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory) *cobra.Command {
c := &cobra.Command{
Use: "add",
Short: "Adds configmap/resource/patch/base to the kustomization file.",
Short: "Adds an item to the kustomization file.",
Long: "",
Example: `
# Adds a secret to the kustomization file
kustomize edit add secret NAME --from-literal=k=v
# Adds a configmap to the kustomization file
kustomize edit add configmap NAME --from-literal=k=v
@@ -53,6 +56,7 @@ func NewCmdAdd(fsys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory)
c.AddCommand(
newCmdAddResource(fsys),
newCmdAddPatch(fsys),
newCmdAddSecret(fsys, kf),
newCmdAddConfigMap(fsys, kf),
newCmdAddBase(fsys),
newCmdAddLabel(fsys, v.MakeLabelValidator()),

View File

@@ -29,7 +29,7 @@ import (
// newCmdAddConfigMap returns a new command.
func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.Command {
var flagsAndArgs cMapFlagsAndArgs
var flags flagsAndArgs
cmd := &cobra.Command{
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
Short: "Adds a configmap to the kustomization file.",
@@ -45,12 +45,12 @@ func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.
kustomize edit add configmap my-configmap --from-env-file=env/path.env
`,
RunE: func(_ *cobra.Command, args []string) error {
err := flagsAndArgs.ExpandFileSource(fSys)
err := flags.ExpandFileSource(fSys)
if err != nil {
return err
}
err = flagsAndArgs.Validate(args)
err = flags.Validate(args)
if err != nil {
return err
}
@@ -68,7 +68,7 @@ func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.
// Add the flagsAndArgs map to the kustomization file.
kf.Set(loader.NewFileLoaderAtCwd(fSys))
err = addConfigMap(kustomization, flagsAndArgs, kf)
err = addConfigMap(kustomization, flags, kf)
if err != nil {
return err
}
@@ -79,19 +79,19 @@ func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.
}
cmd.Flags().StringSliceVar(
&flagsAndArgs.FileSources,
&flags.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,
&flags.LiteralSources,
"from-literal",
[]string{},
"Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
cmd.Flags().StringVar(
&flagsAndArgs.EnvFileSource,
&flags.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).")
@@ -104,9 +104,9 @@ func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.
// Suggest passing a copy of kustomization file.
func addConfigMap(
k *types.Kustomization,
flagsAndArgs cMapFlagsAndArgs, kf ifc.KunstructuredFactory) error {
cmArgs := makeConfigMapArgs(k, flagsAndArgs.Name)
err := mergeFlagsIntoCmArgs(&cmArgs.DataSources, flagsAndArgs)
flags flagsAndArgs, kf ifc.KunstructuredFactory) error {
cmArgs := makeConfigMapArgs(k, flags.Name)
err := mergeFlagsIntoCmArgs(&cmArgs.DataSources, flags)
if err != nil {
return err
}
@@ -130,7 +130,7 @@ func makeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
}
func mergeFlagsIntoCmArgs(src *types.DataSources, flags cMapFlagsAndArgs) error {
func mergeFlagsIntoCmArgs(src *types.DataSources, flags flagsAndArgs) error {
src.LiteralSources = append(src.LiteralSources, flags.LiteralSources...)
src.FileSources = append(src.FileSources, flags.FileSources...)
if src.EnvSource != "" && src.EnvSource != flags.EnvFileSource {

View File

@@ -67,7 +67,7 @@ func TestMakeConfigMapArgs(t *testing.T) {
func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) {
ds := &types.DataSources{}
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k1=v1"}})
err := mergeFlagsIntoCmArgs(ds, flagsAndArgs{LiteralSources: []string{"k1=v1"}})
if err != nil {
t.Fatalf("Merge initial literal source should not return error")
}
@@ -76,7 +76,7 @@ func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) {
t.Fatalf("Initial literal source should have been added")
}
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k2=v2"}})
err = mergeFlagsIntoCmArgs(ds, flagsAndArgs{LiteralSources: []string{"k2=v2"}})
if err != nil {
t.Fatalf("Merge second literal source should not return error")
}
@@ -89,7 +89,7 @@ func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) {
func TestMergeFlagsIntoCmArgs_FileSources(t *testing.T) {
ds := &types.DataSources{}
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file1"}})
err := mergeFlagsIntoCmArgs(ds, flagsAndArgs{FileSources: []string{"file1"}})
if err != nil {
t.Fatalf("Merge initial file source should not return error")
}
@@ -98,7 +98,7 @@ func TestMergeFlagsIntoCmArgs_FileSources(t *testing.T) {
t.Fatalf("Initial file source should have been added")
}
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file2"}})
err = mergeFlagsIntoCmArgs(ds, flagsAndArgs{FileSources: []string{"file2"}})
if err != nil {
t.Fatalf("Merge second file source should not return error")
}
@@ -113,7 +113,7 @@ func TestMergeFlagsIntoCmArgs_EnvSource(t *testing.T) {
envFileName2 := "env2"
ds := &types.DataSources{}
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName})
err := mergeFlagsIntoCmArgs(ds, flagsAndArgs{EnvFileSource: envFileName})
if err != nil {
t.Fatalf("Merge initial env source should not return error")
}
@@ -122,7 +122,7 @@ func TestMergeFlagsIntoCmArgs_EnvSource(t *testing.T) {
t.Fatalf("Initial env source filename should have been added")
}
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName2})
err = mergeFlagsIntoCmArgs(ds, flagsAndArgs{EnvFileSource: envFileName2})
if err == nil {
t.Fatalf("Updating env source should return an error")
}

View File

@@ -22,8 +22,8 @@ import (
"sigs.k8s.io/kustomize/pkg/fs"
)
// cMapFlagsAndArgs encapsulates the options for add configmap commands.
type cMapFlagsAndArgs struct {
// flagsAndArgs encapsulates the options for add secret/configmap commands.
type flagsAndArgs struct {
// Name of configMap/Secret (required)
Name string
// FileSources to derive the configMap/Secret from (optional)
@@ -33,10 +33,12 @@ type cMapFlagsAndArgs struct {
// EnvFileSource to derive the configMap/Secret from (optional)
// TODO: Rationalize this name with Generic.EnvSource
EnvFileSource string
// Type of secret to create
Type string
}
// Validate validates required fields are set to support structured generation.
func (a *cMapFlagsAndArgs) Validate(args []string) error {
func (a *flagsAndArgs) Validate(args []string) error {
if len(args) != 1 {
return fmt.Errorf("name must be specified once")
}
@@ -51,7 +53,7 @@ func (a *cMapFlagsAndArgs) Validate(args []string) error {
return nil
}
func (a *cMapFlagsAndArgs) ExpandFileSource(fSys fs.FileSystem) error {
func (a *flagsAndArgs) ExpandFileSource(fSys fs.FileSystem) error {
result, err := globPatterns(fSys, a.FileSources)
if err != nil {
return err

View File

@@ -23,18 +23,18 @@ import (
"sigs.k8s.io/kustomize/pkg/fs"
)
func TestDataConfigValidation_NoName(t *testing.T) {
config := cMapFlagsAndArgs{}
func TestDataValidation_NoName(t *testing.T) {
fa := flagsAndArgs{}
if config.Validate([]string{}) == nil {
if fa.Validate([]string{}) == nil {
t.Fatal("Validation should fail if no name is specified")
}
}
func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
config := cMapFlagsAndArgs{}
func TestDataValidation_MoreThanOneName(t *testing.T) {
fa := flagsAndArgs{}
if config.Validate([]string{"name", "othername"}) == nil {
if fa.Validate([]string{"name", "othername"}) == nil {
t.Fatal("Validation should fail if more than one name is specified")
}
}
@@ -42,12 +42,12 @@ func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
func TestDataConfigValidation_Flags(t *testing.T) {
tests := []struct {
name string
config cMapFlagsAndArgs
fa flagsAndArgs
shouldFail bool
}{
{
name: "env-file-source and literal are both set",
config: cMapFlagsAndArgs{
fa: flagsAndArgs{
LiteralSources: []string{"one", "two"},
EnvFileSource: "three",
},
@@ -55,7 +55,7 @@ func TestDataConfigValidation_Flags(t *testing.T) {
},
{
name: "env-file-source and from-file are both set",
config: cMapFlagsAndArgs{
fa: flagsAndArgs{
FileSources: []string{"one", "two"},
EnvFileSource: "three",
},
@@ -63,12 +63,12 @@ func TestDataConfigValidation_Flags(t *testing.T) {
},
{
name: "we don't have any option set",
config: cMapFlagsAndArgs{},
fa: flagsAndArgs{},
shouldFail: true,
},
{
name: "we have from-file and literal ",
config: cMapFlagsAndArgs{
fa: flagsAndArgs{
LiteralSources: []string{"one", "two"},
FileSources: []string{"three", "four"},
},
@@ -77,9 +77,9 @@ func TestDataConfigValidation_Flags(t *testing.T) {
}
for _, test := range tests {
if test.config.Validate([]string{"name"}) == nil && test.shouldFail {
if test.fa.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 {
} else if test.fa.Validate([]string{"name"}) != nil && !test.shouldFail {
t.Fatalf("Validation should succeed if %s", test.name)
}
}
@@ -87,18 +87,18 @@ func TestDataConfigValidation_Flags(t *testing.T) {
func TestExpandFileSource(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.Create("dir/config1")
fakeFS.Create("dir/config2")
fakeFS.Create("dir/fa1")
fakeFS.Create("dir/fa2")
fakeFS.Create("dir/reademe")
config := cMapFlagsAndArgs{
FileSources: []string{"dir/config*"},
fa := flagsAndArgs{
FileSources: []string{"dir/fa*"},
}
config.ExpandFileSource(fakeFS)
fa.ExpandFileSource(fakeFS)
expected := []string{
"dir/config1",
"dir/config2",
"dir/fa1",
"dir/fa2",
}
if !reflect.DeepEqual(config.FileSources, expected) {
t.Fatalf("FileSources is not correctly expanded: %v", config.FileSources)
if !reflect.DeepEqual(fa.FileSources, expected) {
t.Fatalf("FileSources is not correctly expanded: %v", fa.FileSources)
}
}

View File

@@ -0,0 +1,146 @@
/*
Copyright 2019 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/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
)
// newCmdAddSecret returns a new command.
func newCmdAddSecret(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.Command {
var flags flagsAndArgs
cmd := &cobra.Command{
Use: "secret NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--type=Opaque|kubernetes.io/tls]",
Short: "Adds a secret to the kustomization file.",
Long: "",
Example: `
# Adds a secret to the kustomization file (with a specified key)
kustomize edit add secret my-secret --from-file=my-key=file/path --from-literal=my-literal=12345
# Adds a secret to the kustomization file (key is the filename)
kustomize edit add secret my-secret --from-file=file/path
# Adds a secret from env-file
kustomize edit add secret my-secret --from-env-file=env/path.env
`,
RunE: func(_ *cobra.Command, args []string) error {
err := flags.ExpandFileSource(fSys)
if err != nil {
return err
}
err = flags.Validate(args)
if err != nil {
return err
}
// Load the kustomization file.
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
kustomization, err := mf.Read()
if err != nil {
return err
}
// Add the flagsAndArgs map to the kustomization file.
kf.Set(loader.NewFileLoaderAtCwd(fSys))
err = addSecret(kustomization, flags, kf)
if err != nil {
return err
}
// Write out the kustomization file with added secret.
return mf.Write(kustomization)
},
}
cmd.Flags().StringSliceVar(
&flags.FileSources,
"from-file",
[]string{},
"Key file can be specified using its file path, in which case file basename will be used as secret "+
"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 secret key.")
cmd.Flags().StringArrayVar(
&flags.LiteralSources,
"from-literal",
[]string{},
"Specify a key and literal value to insert in secret (i.e. mykey=somevalue)")
cmd.Flags().StringVar(
&flags.EnvFileSource,
"from-env-file",
"",
"Specify the path to a file to read lines of key=val pairs to create a secret (i.e. a Docker .env file).")
cmd.Flags().StringVar(
&flags.Type,
"type",
"Opaque",
"Specify the secret type this can be 'Opaque' (default), or 'kubernetes.io/tls'")
return cmd
}
// addSecret adds a secret to a kustomization file.
// Note: error may leave kustomization file in an undefined state.
// Suggest passing a copy of kustomization file.
func addSecret(
k *types.Kustomization,
flags flagsAndArgs, kf ifc.KunstructuredFactory) error {
secretArgs := makeSecretArgs(k, flags.Name, flags.Type)
err := mergeFlagsIntoSecretArgs(&secretArgs.DataSources, flags)
if err != nil {
return err
}
// Validate by trying to create corev1.secret.
_, err = kf.MakeSecret(secretArgs, k.GeneratorOptions)
if err != nil {
return err
}
return nil
}
func makeSecretArgs(m *types.Kustomization, name, secretType string) *types.SecretArgs {
for i, v := range m.SecretGenerator {
if name == v.Name {
return &m.SecretGenerator[i]
}
}
// secret not found, create new one and add it to the kustomization file.
secret := &types.SecretArgs{GeneratorArgs: types.GeneratorArgs{Name: name}, Type: secretType}
m.SecretGenerator = append(m.SecretGenerator, *secret)
return &m.SecretGenerator[len(m.SecretGenerator)-1]
}
func mergeFlagsIntoSecretArgs(src *types.DataSources, flags flagsAndArgs) 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
}

View File

@@ -0,0 +1,131 @@
/*
Copyright 2019 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 TestNewCmdAddSecretIsNotNil(t *testing.T) {
if newCmdAddSecret(fs.MakeFakeFS(), nil) == nil {
t.Fatal("newCmdAddSecret shouldn't be nil")
}
}
func TestMakeSecretArgs(t *testing.T) {
secretName := "test-secret-name"
kustomization := &types.Kustomization{
NamePrefix: "test-name-prefix",
}
secretType := "Opaque"
if len(kustomization.SecretGenerator) != 0 {
t.Fatal("Initial kustomization should not have any secrets")
}
args := makeSecretArgs(kustomization, secretName, secretType)
if args == nil {
t.Fatalf("args should always be non-nil")
}
if len(kustomization.SecretGenerator) != 1 {
t.Fatalf("Kustomization should have newly created secret")
}
if &kustomization.SecretGenerator[len(kustomization.SecretGenerator)-1] != args {
t.Fatalf("Pointer address for newly inserted secret generator should be same")
}
args2 := makeSecretArgs(kustomization, secretName, secretType)
if args2 != args {
t.Fatalf("should have returned an existing args with name: %v", secretName)
}
if len(kustomization.SecretGenerator) != 1 {
t.Fatalf("Should not insert secret for an existing name: %v", secretName)
}
}
func TestMergeFlagsIntoSecretArgs_LiteralSources(t *testing.T) {
ds := &types.DataSources{}
err := mergeFlagsIntoSecretArgs(ds, flagsAndArgs{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 = mergeFlagsIntoSecretArgs(ds, flagsAndArgs{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 TestMergeFlagsIntoSecretArgs_FileSources(t *testing.T) {
ds := &types.DataSources{}
err := mergeFlagsIntoSecretArgs(ds, flagsAndArgs{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 = mergeFlagsIntoSecretArgs(ds, flagsAndArgs{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 TestMergeFlagsIntoSecretArgs_EnvSource(t *testing.T) {
envFileName := "env1"
envFileName2 := "env2"
ds := &types.DataSources{}
err := mergeFlagsIntoSecretArgs(ds, flagsAndArgs{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 = mergeFlagsIntoSecretArgs(ds, flagsAndArgs{EnvFileSource: envFileName2})
if err == nil {
t.Fatalf("Updating env source should return an error")
}
}