diff --git a/pkg/commands/data_config.go b/pkg/commands/cmapflagsandargs.go similarity index 90% rename from pkg/commands/data_config.go rename to pkg/commands/cmapflagsandargs.go index 9ee241b84..1a593a693 100644 --- a/pkg/commands/data_config.go +++ b/pkg/commands/cmapflagsandargs.go @@ -20,8 +20,8 @@ import ( "fmt" ) -// dataConfig encapsulates the options for add configmap/Secret commands. -type dataConfig struct { +// 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) @@ -34,7 +34,7 @@ type dataConfig struct { } // Validate validates required fields are set to support structured generation. -func (a *dataConfig) Validate(args []string) error { +func (a *cMapFlagsAndArgs) Validate(args []string) error { if len(args) != 1 { return fmt.Errorf("name must be specified once") } diff --git a/pkg/commands/data_config_test.go b/pkg/commands/cmapflagsandargs_test.go similarity index 90% rename from pkg/commands/data_config_test.go rename to pkg/commands/cmapflagsandargs_test.go index 29154f4b0..cc5048d0a 100644 --- a/pkg/commands/data_config_test.go +++ b/pkg/commands/cmapflagsandargs_test.go @@ -21,7 +21,7 @@ import ( ) func TestDataConfigValidation_NoName(t *testing.T) { - config := dataConfig{} + config := cMapFlagsAndArgs{} if config.Validate([]string{}) == nil { t.Fatal("Validation should fail if no name is specified") @@ -29,7 +29,7 @@ func TestDataConfigValidation_NoName(t *testing.T) { } func TestDataConfigValidation_MoreThanOneName(t *testing.T) { - config := dataConfig{} + config := cMapFlagsAndArgs{} if config.Validate([]string{"name", "othername"}) == nil { t.Fatal("Validation should fail if more than one name is specified") @@ -39,12 +39,12 @@ func TestDataConfigValidation_MoreThanOneName(t *testing.T) { func TestDataConfigValidation_Flags(t *testing.T) { tests := []struct { name string - config dataConfig + config cMapFlagsAndArgs shouldFail bool }{ { name: "env-file-source and literal are both set", - config: dataConfig{ + config: cMapFlagsAndArgs{ LiteralSources: []string{"one", "two"}, EnvFileSource: "three", }, @@ -52,7 +52,7 @@ func TestDataConfigValidation_Flags(t *testing.T) { }, { name: "env-file-source and from-file are both set", - config: dataConfig{ + config: cMapFlagsAndArgs{ FileSources: []string{"one", "two"}, EnvFileSource: "three", }, @@ -60,12 +60,12 @@ func TestDataConfigValidation_Flags(t *testing.T) { }, { name: "we don't have any option set", - config: dataConfig{}, + config: cMapFlagsAndArgs{}, shouldFail: true, }, { name: "we have from-file and literal ", - config: dataConfig{ + config: cMapFlagsAndArgs{ LiteralSources: []string{"one", "two"}, FileSources: []string{"three", "four"}, }, diff --git a/pkg/commands/configmap.go b/pkg/commands/configmap.go index 5b7b6efba..2ca97223a 100644 --- a/pkg/commands/configmap.go +++ b/pkg/commands/configmap.go @@ -27,8 +27,8 @@ import ( "github.com/kubernetes-sigs/kustomize/pkg/types" ) -func newCmdAddConfigMap(fsys fs.FileSystem) *cobra.Command { - var config dataConfig +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.", @@ -44,47 +44,47 @@ func newCmdAddConfigMap(fsys fs.FileSystem) *cobra.Command { kustomize edit add configmap my-configmap --from-env-file=env/path.env `, RunE: func(_ *cobra.Command, args []string) error { - err := config.Validate(args) + err := flagsAndArgs.Validate(args) if err != nil { return err } - // Load in the kustomization file. - mf, err := newKustomizationFile(constants.KustomizationFileName, fsys) + // Load the kustomization file. + mf, err := newKustomizationFile(constants.KustomizationFileName, fSys) if err != nil { return err } - m, err := mf.read() + kustomization, err := mf.read() if err != nil { return err } - // Add the config map to the kustomization file. - err = addConfigMap(m, config) + // Add the flagsAndArgs map to the kustomization file. + err = addConfigMap(kustomization, flagsAndArgs, fSys) if err != nil { return err } // Write out the kustomization file with added configmap. - return mf.write(m) + return mf.write(kustomization) }, } cmd.Flags().StringSliceVar( - &config.FileSources, + &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( - &config.LiteralSources, + &flagsAndArgs.LiteralSources, "from-literal", []string{}, "Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)") cmd.Flags().StringVar( - &config.EnvFileSource, + &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).") @@ -92,19 +92,21 @@ func newCmdAddConfigMap(fsys fs.FileSystem) *cobra.Command { return cmd } -// addConfigMap updates a configmap within a kustomization file, using the data in config. +// 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(m *types.Kustomization, config dataConfig) error { - cm := getOrCreateConfigMap(m, config.Name) +func addConfigMap(k *types.Kustomization, flagsAndArgs cMapFlagsAndArgs, fSys fs.FileSystem) error { + cmArgs := makeConfigMapArgs(k, flagsAndArgs.Name) - err := mergeData(&cm.DataSources, config) + err := mergeFlagsIntoCmArgs(&cmArgs.DataSources, flagsAndArgs) if err != nil { return err } + factory := configmapandsecret.NewConfigMapFactory(cmArgs, fSys) + // Validate by trying to create corev1.configmap. - _, _, err = configmapandsecret.MakeConfigmapAndGenerateName(*cm) + _, _, err = factory.MakeUnstructAndGenerateName() if err != nil { return err } @@ -112,7 +114,7 @@ func addConfigMap(m *types.Kustomization, config dataConfig) error { return nil } -func getOrCreateConfigMap(m *types.Kustomization, name string) *types.ConfigMapArgs { +func makeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs { for i, v := range m.ConfigMapGenerator { if name == v.Name { return &m.ConfigMapGenerator[i] @@ -124,13 +126,12 @@ func getOrCreateConfigMap(m *types.Kustomization, name string) *types.ConfigMapA return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1] } -func mergeData(src *types.DataSources, config dataConfig) error { - src.LiteralSources = append(src.LiteralSources, config.LiteralSources...) - src.FileSources = append(src.FileSources, config.FileSources...) - if src.EnvSource != "" && src.EnvSource != config.EnvFileSource { +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 = config.EnvFileSource - + src.EnvSource = flags.EnvFileSource return nil } diff --git a/pkg/commands/configmap_test.go b/pkg/commands/configmap_test.go index 100ea7db5..afbc4ca14 100644 --- a/pkg/commands/configmap_test.go +++ b/pkg/commands/configmap_test.go @@ -29,7 +29,7 @@ func TestNewAddConfigMapIsNotNil(t *testing.T) { } } -func TestGetOrCreateConfigMap(t *testing.T) { +func TestMakeConfigMapArgs(t *testing.T) { cmName := "test-config-name" kustomization := &types.Kustomization{ @@ -39,24 +39,24 @@ func TestGetOrCreateConfigMap(t *testing.T) { if len(kustomization.ConfigMapGenerator) != 0 { t.Fatal("Initial kustomization should not have any configmaps") } - cm := getOrCreateConfigMap(kustomization, cmName) + args := makeConfigMapArgs(kustomization, cmName) - if cm == nil { - t.Fatalf("ConfigMap should always be non-nil") + 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] != cm { - t.Fatalf("Pointer address for newly inserted configmap should be same") + if &kustomization.ConfigMapGenerator[len(kustomization.ConfigMapGenerator)-1] != args { + t.Fatalf("Pointer address for newly inserted configmap generator should be same") } - existingCM := getOrCreateConfigMap(kustomization, cmName) + args2 := makeConfigMapArgs(kustomization, cmName) - if existingCM != cm { - t.Fatalf("should have returned an existing cm with name: %v", cmName) + if args2 != args { + t.Fatalf("should have returned an existing args with name: %v", cmName) } if len(kustomization.ConfigMapGenerator) != 1 { @@ -64,10 +64,10 @@ func TestGetOrCreateConfigMap(t *testing.T) { } } -func TestMergeData_LiteralSources(t *testing.T) { +func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) { ds := &types.DataSources{} - err := mergeData(ds, dataConfig{LiteralSources: []string{"k1=v1"}}) + err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k1=v1"}}) if err != nil { t.Fatalf("Merge initial literal source should not return error") } @@ -76,7 +76,7 @@ func TestMergeData_LiteralSources(t *testing.T) { t.Fatalf("Initial literal source should have been added") } - err = mergeData(ds, dataConfig{LiteralSources: []string{"k2=v2"}}) + err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k2=v2"}}) if err != nil { t.Fatalf("Merge second literal source should not return error") } @@ -86,10 +86,10 @@ func TestMergeData_LiteralSources(t *testing.T) { } } -func TestMergeData_FileSources(t *testing.T) { +func TestMergeFlagsIntoCmArgs_FileSources(t *testing.T) { ds := &types.DataSources{} - err := mergeData(ds, dataConfig{FileSources: []string{"file1"}}) + err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file1"}}) if err != nil { t.Fatalf("Merge initial file source should not return error") } @@ -98,7 +98,7 @@ func TestMergeData_FileSources(t *testing.T) { t.Fatalf("Initial file source should have been added") } - err = mergeData(ds, dataConfig{FileSources: []string{"file2"}}) + err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file2"}}) if err != nil { t.Fatalf("Merge second file source should not return error") } @@ -108,12 +108,12 @@ func TestMergeData_FileSources(t *testing.T) { } } -func TestMergeData_EnvSource(t *testing.T) { +func TestMergeFlagsIntoCmArgs_EnvSource(t *testing.T) { envFileName := "env1" envFileName2 := "env2" ds := &types.DataSources{} - err := mergeData(ds, dataConfig{EnvFileSource: envFileName}) + err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName}) if err != nil { t.Fatalf("Merge initial env source should not return error") } @@ -122,7 +122,7 @@ func TestMergeData_EnvSource(t *testing.T) { t.Fatalf("Initial env source filename should have been added") } - err = mergeData(ds, dataConfig{EnvFileSource: envFileName2}) + err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName2}) if err == nil { t.Fatalf("Updating env source should return an error") } diff --git a/pkg/configmapandsecret/configmap_secret.go b/pkg/configmapandsecret/configmap_secret.go deleted file mode 100644 index 2343e4ef3..000000000 --- a/pkg/configmapandsecret/configmap_secret.go +++ /dev/null @@ -1,80 +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 configmapandsecret generates configmaps and secrets per generator rules. -package configmapandsecret - -import ( - "encoding/json" - "fmt" - cutil "github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret/util" - "github.com/kubernetes-sigs/kustomize/pkg/hash" - "github.com/kubernetes-sigs/kustomize/pkg/types" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" -) - -// MakeConfigmapAndGenerateName makes a configmap and returns the configmap and the name appended with a hash. -func MakeConfigmapAndGenerateName(cm types.ConfigMapArgs) (*unstructured.Unstructured, string, error) { - corev1CM, err := makeConfigMap(cm) - if err != nil { - return nil, "", err - } - h, err := hash.ConfigMapHash(corev1CM) - if err != nil { - return nil, "", err - } - nameWithHash := fmt.Sprintf("%s-%s", corev1CM.GetName(), h) - unstructuredCM, err := objectToUnstructured(corev1CM) - return unstructuredCM, nameWithHash, err -} - -func objectToUnstructured(in runtime.Object) (*unstructured.Unstructured, error) { - marshaled, err := json.Marshal(in) - if err != nil { - return nil, err - } - var out unstructured.Unstructured - err = out.UnmarshalJSON(marshaled) - return &out, err -} - -func makeConfigMap(cm types.ConfigMapArgs) (*corev1.ConfigMap, error) { - corev1cm := &corev1.ConfigMap{} - corev1cm.APIVersion = "v1" - corev1cm.Kind = "ConfigMap" - corev1cm.Name = cm.Name - corev1cm.Data = map[string]string{} - - if cm.EnvSource != "" { - if err := cutil.HandleConfigMapFromEnvFileSource(corev1cm, cm.EnvSource); err != nil { - return nil, err - } - } - if cm.FileSources != nil { - if err := cutil.HandleConfigMapFromFileSources(corev1cm, cm.FileSources); err != nil { - return nil, err - } - } - if cm.LiteralSources != nil { - if err := cutil.HandleConfigMapFromLiteralSources(corev1cm, cm.LiteralSources); err != nil { - return nil, err - } - } - - return corev1cm, nil -} diff --git a/pkg/configmapandsecret/configmapfactory.go b/pkg/configmapandsecret/configmapfactory.go new file mode 100644 index 000000000..1b5a09e5a --- /dev/null +++ b/pkg/configmapandsecret/configmapfactory.go @@ -0,0 +1,192 @@ +/* +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 configmapandsecret generates configmaps and secrets per generator rules. +package configmapandsecret + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "path" + "strings" + + cutil "github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret/util" + "github.com/kubernetes-sigs/kustomize/pkg/fs" + "github.com/kubernetes-sigs/kustomize/pkg/hash" + "github.com/kubernetes-sigs/kustomize/pkg/types" + "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation" +) + +// ConfigMapFactory makes ConfigMaps. +type ConfigMapFactory struct { + args *types.ConfigMapArgs + fSys fs.FileSystem +} + +// NewConfigMapFactory returns a new ConfigMapFactory. +func NewConfigMapFactory(args *types.ConfigMapArgs, fSys fs.FileSystem) *ConfigMapFactory { + return &ConfigMapFactory{args: args, fSys: fSys} +} + +// MakeUnstructAndGenerateName returns an configmap and the name appended with a hash. +func (f *ConfigMapFactory) MakeUnstructAndGenerateName() (*unstructured.Unstructured, string, error) { + cm, err := f.MakeConfigMap() + if err != nil { + return nil, "", err + } + h, err := hash.ConfigMapHash(cm) + if err != nil { + return nil, "", err + } + nameWithHash := fmt.Sprintf("%s-%s", cm.GetName(), h) + unstructuredCM, err := objectToUnstructured(cm) + return unstructuredCM, nameWithHash, err +} + +func objectToUnstructured(in runtime.Object) (*unstructured.Unstructured, error) { + marshaled, err := json.Marshal(in) + if err != nil { + return nil, err + } + var out unstructured.Unstructured + err = out.UnmarshalJSON(marshaled) + return &out, err +} + +// MakeConfigMap returns a new ConfigMap, or nil and an error. +func (f *ConfigMapFactory) MakeConfigMap() (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{} + cm.APIVersion = "v1" + cm.Kind = "ConfigMap" + cm.Name = f.args.Name + cm.Data = map[string]string{} + + if f.args.EnvSource != "" { + if err := f.handleConfigMapFromEnvFileSource(cm); err != nil { + return nil, err + } + } + if f.args.FileSources != nil { + if err := f.handleConfigMapFromFileSources(cm); err != nil { + return nil, err + } + } + if f.args.LiteralSources != nil { + if err := f.handleConfigMapFromLiteralSources(cm); err != nil { + return nil, err + } + } + + return cm, nil +} + +// handleConfigMapFromLiteralSources adds the specified literal source +// information into the provided configMap. +func (f *ConfigMapFactory) handleConfigMapFromLiteralSources(configMap *v1.ConfigMap) error { + for _, literalSource := range f.args.LiteralSources { + keyName, value, err := cutil.ParseLiteralSource(literalSource) + if err != nil { + return err + } + err = addKeyFromLiteralToConfigMap(configMap, keyName, value) + if err != nil { + return err + } + } + return nil +} + +// handleConfigMapFromFileSources adds the specified file source information +// into the provided configMap +func (f *ConfigMapFactory) handleConfigMapFromFileSources(configMap *v1.ConfigMap) error { + for _, fileSource := range f.args.FileSources { + keyName, filePath, err := cutil.ParseFileSource(fileSource) + if err != nil { + return err + } + if !f.fSys.Exists(filePath) { + return fmt.Errorf("unable to read configmap source file %s", filePath) + } + if f.fSys.IsDir(filePath) { + if strings.Contains(fileSource, "=") { + return fmt.Errorf("cannot give a key name for a directory path") + } + fileList, err := ioutil.ReadDir(filePath) + if err != nil { + return fmt.Errorf("error listing files in %s: %v", filePath, err) + } + for _, item := range fileList { + itemPath := path.Join(filePath, item.Name()) + if item.Mode().IsRegular() { + keyName = item.Name() + err = addKeyFromFileToConfigMap(configMap, keyName, itemPath) + if err != nil { + return err + } + } + } + } else { + if err := addKeyFromFileToConfigMap(configMap, keyName, filePath); err != nil { + return err + } + } + } + return nil +} + +// HandleConfigMapFromEnvFileSource adds the specified env file source information +// into the provided configMap +func (f *ConfigMapFactory) handleConfigMapFromEnvFileSource(configMap *v1.ConfigMap) error { + if !f.fSys.Exists(f.args.EnvSource) { + return fmt.Errorf("unable to read configmap env file %s", f.args.EnvSource) + } + if f.fSys.IsDir(f.args.EnvSource) { + return fmt.Errorf("env config file %s cannot be a directory", f.args.EnvSource) + } + + return cutil.AddFromEnvFile(f.args.EnvSource, func(key, value string) error { + return addKeyFromLiteralToConfigMap(configMap, key, value) + }) +} + +// addKeyFromFileToConfigMap adds a key with the given name to a ConfigMap, populating +// the value with the content of the given file path, or returns an error. +func addKeyFromFileToConfigMap(configMap *v1.ConfigMap, keyName, filePath string) error { + data, err := ioutil.ReadFile(filePath) + if err != nil { + return err + } + return addKeyFromLiteralToConfigMap(configMap, keyName, string(data)) +} + +// addKeyFromLiteralToConfigMap adds the given key and data to the given config map, +// returning an error if the key is not valid or if the key already exists. +func addKeyFromLiteralToConfigMap(configMap *v1.ConfigMap, keyName, data string) error { + // Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys. + if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 { + return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ";")) + } + if _, entryExists := configMap.Data[keyName]; entryExists { + return fmt.Errorf("cannot add key %s, another key by that name already exists: %v", keyName, configMap.Data) + } + configMap.Data[keyName] = data + return nil +} diff --git a/pkg/configmapandsecret/configmap_secret_test.go b/pkg/configmapandsecret/configmapfactory_test.go similarity index 67% rename from pkg/configmapandsecret/configmap_secret_test.go rename to pkg/configmapandsecret/configmapfactory_test.go index f140e5a6b..31be33359 100644 --- a/pkg/configmapandsecret/configmap_secret_test.go +++ b/pkg/configmapandsecret/configmapfactory_test.go @@ -20,12 +20,7 @@ import ( "reflect" "testing" - "context" - "os" - "os/exec" - "path/filepath" - "time" - + "github.com/kubernetes-sigs/kustomize/pkg/fs" "github.com/kubernetes-sigs/kustomize/pkg/types" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -98,23 +93,6 @@ func makeLiteralConfigMap(name string) *corev1.ConfigMap { } } -func makeTestSecret(name string) *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Data: map[string][]byte{ - "DB_USERNAME": []byte("admin"), - "DB_PASSWORD": []byte("somepw"), - }, - Type: corev1.SecretTypeOpaque, - } -} - func TestConstructConfigMap(t *testing.T) { type testCase struct { description string @@ -156,7 +134,10 @@ func TestConstructConfigMap(t *testing.T) { } for _, tc := range testCases { - cm, err := makeConfigMap(tc.input) + // TODO: all tests should use a FakeFs + fSys := fs.MakeRealFS() + f := NewConfigMapFactory(&tc.input, fSys) + cm, err := f.MakeConfigMap() if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -166,74 +147,6 @@ func TestConstructConfigMap(t *testing.T) { } } -func TestConstructSecret(t *testing.T) { - secret := types.SecretArgs{ - Name: "secret", - Commands: map[string]string{ - "DB_USERNAME": "printf admin", - "DB_PASSWORD": "printf somepw", - }, - Type: "Opaque", - } - cm, err := makeSecret(secret, ".") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expected := makeTestSecret("secret") - if !reflect.DeepEqual(*cm, *expected) { - t.Fatalf("%#v\ndoesn't match expected:\n%#v", *cm, *expected) - } -} - -func makeSecret(secret types.SecretArgs, path string) (*corev1.Secret, error) { - corev1secret := &corev1.Secret{} - corev1secret.APIVersion = "v1" - corev1secret.Kind = "Secret" - corev1secret.Name = secret.Name - corev1secret.Type = corev1.SecretType(secret.Type) - if corev1secret.Type == "" { - corev1secret.Type = corev1.SecretTypeOpaque - } - corev1secret.Data = map[string][]byte{} - - for k, v := range secret.Commands { - out, err := createSecretKey(path, v) - if err != nil { - return nil, err - } - corev1secret.Data[k] = out - } - - return corev1secret, nil -} - -func createSecretKey(wd string, command string) ([]byte, error) { - fi, err := os.Stat(wd) - if err != nil || !fi.IsDir() { - wd = filepath.Dir(wd) - } - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - cmd := exec.CommandContext(ctx, "sh", "-c", command) - cmd.Dir = wd - - return cmd.Output() -} - -func TestFailConstructSecret(t *testing.T) { - secret := types.SecretArgs{ - Name: "secret", - Commands: map[string]string{ - "FAILURE": "false", // This will fail. - }, - Type: "Opaque", - } - _, err := makeSecret(secret, ".") - if err == nil { - t.Fatalf("Expected failure.") - } -} - func TestObjectConvertToUnstructured(t *testing.T) { type testCase struct { description string diff --git a/pkg/configmapandsecret/util/configmap.go b/pkg/configmapandsecret/util/configmap.go deleted file mode 100644 index 7ec9c84f6..000000000 --- a/pkg/configmapandsecret/util/configmap.go +++ /dev/null @@ -1,134 +0,0 @@ -/* -Copyright 2016 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 util - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/validation" -) - -// HandleConfigMapFromLiteralSources adds the specified literal source -// information into the provided configMap. -func HandleConfigMapFromLiteralSources(configMap *v1.ConfigMap, literalSources []string) error { - for _, literalSource := range literalSources { - keyName, value, err := ParseLiteralSource(literalSource) - if err != nil { - return err - } - err = addKeyFromLiteralToConfigMap(configMap, keyName, value) - if err != nil { - return err - } - } - return nil -} - -// HandleConfigMapFromFileSources adds the specified file source information -// into the provided configMap -func HandleConfigMapFromFileSources(configMap *v1.ConfigMap, fileSources []string) error { - for _, fileSource := range fileSources { - keyName, filePath, err := ParseFileSource(fileSource) - if err != nil { - return err - } - info, err := os.Stat(filePath) - if err != nil { - switch err := err.(type) { - case *os.PathError: - return fmt.Errorf("error reading %s: %v", filePath, err.Err) - default: - return fmt.Errorf("error reading %s: %v", filePath, err) - } - } - if info.IsDir() { - if strings.Contains(fileSource, "=") { - return fmt.Errorf("cannot give a key name for a directory path") - } - fileList, err := ioutil.ReadDir(filePath) - if err != nil { - return fmt.Errorf("error listing files in %s: %v", filePath, err) - } - for _, item := range fileList { - itemPath := path.Join(filePath, item.Name()) - if item.Mode().IsRegular() { - keyName = item.Name() - err = addKeyFromFileToConfigMap(configMap, keyName, itemPath) - if err != nil { - return err - } - } - } - } else { - if err := addKeyFromFileToConfigMap(configMap, keyName, filePath); err != nil { - return err - } - } - } - - return nil -} - -// HandleConfigMapFromEnvFileSource adds the specified env file source information -// into the provided configMap -func HandleConfigMapFromEnvFileSource(configMap *v1.ConfigMap, envFileSource string) error { - info, err := os.Stat(envFileSource) - if err != nil { - switch err := err.(type) { - case *os.PathError: - return fmt.Errorf("error reading %s: %v", envFileSource, err.Err) - default: - return fmt.Errorf("error reading %s: %v", envFileSource, err) - } - } - if info.IsDir() { - return fmt.Errorf("env config file cannot be a directory") - } - - return addFromEnvFile(envFileSource, func(key, value string) error { - return addKeyFromLiteralToConfigMap(configMap, key, value) - }) -} - -// addKeyFromFileToConfigMap adds a key with the given name to a ConfigMap, populating -// the value with the content of the given file path, or returns an error. -func addKeyFromFileToConfigMap(configMap *v1.ConfigMap, keyName, filePath string) error { - data, err := ioutil.ReadFile(filePath) - if err != nil { - return err - } - return addKeyFromLiteralToConfigMap(configMap, keyName, string(data)) -} - -// addKeyFromLiteralToConfigMap adds the given key and data to the given config map, -// returning an error if the key is not valid or if the key already exists. -func addKeyFromLiteralToConfigMap(configMap *v1.ConfigMap, keyName, data string) error { - // Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys. - if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 { - return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ";")) - } - if _, entryExists := configMap.Data[keyName]; entryExists { - return fmt.Errorf("cannot add key %s, another key by that name already exists: %v", keyName, configMap.Data) - } - configMap.Data[keyName] = data - return nil -} diff --git a/pkg/configmapandsecret/util/env_file.go b/pkg/configmapandsecret/util/env_file.go index ef4183062..ffa875adf 100644 --- a/pkg/configmapandsecret/util/env_file.go +++ b/pkg/configmapandsecret/util/env_file.go @@ -69,9 +69,9 @@ func processEnvFileLine(line []byte, filePath string, return key, value, err } -// addFromEnvFile processes an env file allows a generic addTo to handle the +// AddFromEnvFile processes an env file allows a generic addTo to handle the // collection of key value pairs or returns an error. -func addFromEnvFile(filePath string, addTo func(key, value string) error) error { +func AddFromEnvFile(filePath string, addTo func(key, value string) error) error { f, err := os.Open(filePath) if err != nil { return err diff --git a/pkg/configmapandsecret/util/secret.go b/pkg/configmapandsecret/util/secret.go deleted file mode 100644 index c903802cf..000000000 --- a/pkg/configmapandsecret/util/secret.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2015 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 util - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/validation" -) - -// HandleFromLiteralSources adds the specified literal source information into the provided secret -func HandleFromLiteralSources(secret *v1.Secret, literalSources []string) error { - for _, literalSource := range literalSources { - keyName, value, err := ParseLiteralSource(literalSource) - if err != nil { - return err - } - if err = addKeyFromLiteralToSecret(secret, keyName, []byte(value)); err != nil { - return err - } - } - return nil -} - -// HandleFromFileSources adds the specified file source information into the provided secret -func HandleFromFileSources(secret *v1.Secret, fileSources []string) error { - for _, fileSource := range fileSources { - keyName, filePath, err := ParseFileSource(fileSource) - if err != nil { - return err - } - info, err := os.Stat(filePath) - if err != nil { - switch err := err.(type) { - case *os.PathError: - return fmt.Errorf("error reading %s: %v", filePath, err.Err) - default: - return fmt.Errorf("error reading %s: %v", filePath, err) - } - } - if info.IsDir() { - if strings.Contains(fileSource, "=") { - return fmt.Errorf("cannot give a key name for a directory path") - } - fileList, err := ioutil.ReadDir(filePath) - if err != nil { - return fmt.Errorf("error listing files in %s: %v", filePath, err) - } - for _, item := range fileList { - itemPath := path.Join(filePath, item.Name()) - if item.Mode().IsRegular() { - keyName = item.Name() - if err = addKeyFromFileToSecret(secret, keyName, itemPath); err != nil { - return err - } - } - } - } else { - if err := addKeyFromFileToSecret(secret, keyName, filePath); err != nil { - return err - } - } - } - - return nil -} - -// HandleFromEnvFileSource adds the specified env file source information -// into the provided secret -func HandleFromEnvFileSource(secret *v1.Secret, envFileSource string) error { - info, err := os.Stat(envFileSource) - if err != nil { - switch err := err.(type) { - case *os.PathError: - return fmt.Errorf("error reading %s: %v", envFileSource, err.Err) - default: - return fmt.Errorf("error reading %s: %v", envFileSource, err) - } - } - if info.IsDir() { - return fmt.Errorf("env secret file cannot be a directory") - } - - return addFromEnvFile(envFileSource, func(key, value string) error { - return addKeyFromLiteralToSecret(secret, key, []byte(value)) - }) -} - -func addKeyFromFileToSecret(secret *v1.Secret, keyName, filePath string) error { - data, err := ioutil.ReadFile(filePath) - if err != nil { - return err - } - return addKeyFromLiteralToSecret(secret, keyName, data) -} - -func addKeyFromLiteralToSecret(secret *v1.Secret, keyName string, data []byte) error { - if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 { - return fmt.Errorf("%q is not a valid key name for a Secret: %s", keyName, strings.Join(errs, ";")) - } - - if _, entryExists := secret.Data[keyName]; entryExists { - return fmt.Errorf("cannot add key %s, another key by that name already exists: %v", keyName, secret.Data) - } - secret.Data[keyName] = data - return nil -} diff --git a/pkg/configmapandsecret/util/util.go b/pkg/configmapandsecret/util/util.go index bb71aca20..1479cc279 100644 --- a/pkg/configmapandsecret/util/util.go +++ b/pkg/configmapandsecret/util/util.go @@ -18,38 +18,12 @@ limitations under the License. package util import ( - "crypto/sha256" "errors" "fmt" "path" "strings" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" ) -// ParseRFC3339 parses an RFC3339 date in either RFC3339Nano or RFC3339 format. -func ParseRFC3339(s string) (metav1.Time, error) { - if t, timeErr := time.Parse(time.RFC3339Nano, s); timeErr == nil { - return metav1.Time{Time: t}, nil - } - t, err := time.Parse(time.RFC3339, s) - if err != nil { - return metav1.Time{}, err - } - return metav1.Time{Time: t}, nil -} - -// HashObject encodes object using given codec and returns MD5 sum of the result. -func HashObject(obj runtime.Object, codec runtime.Encoder) (string, error) { - data, err := runtime.Encode(codec, obj) - if err != nil { - return "", err - } - return fmt.Sprintf("%x", sha256.Sum256(data)), nil -} - // ParseFileSource parses the source given. // // Acceptable formats include: