mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-10 08:20:59 +00:00
configMap factory refactor for #86
This commit is contained in:
@@ -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")
|
||||
}
|
||||
@@ -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"},
|
||||
},
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
192
pkg/configmapandsecret/configmapfactory.go
Normal file
192
pkg/configmapandsecret/configmapfactory.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user