Skip to content

Commit

Permalink
refactor: change "add configmap/secret" commands to reuse code and im…
Browse files Browse the repository at this point in the history
…prove tests (kubernetes-sigs#5315)

* feat: minor refactoring + test improvements

* Refactor some bits of the edit add secret/configmap commands to reuse code.
* Improve test coverage for both these commands by invoking the cobra command
  directly.
* Add some reusable constants for both these commands and their tests.

* fix: changes from code review

* Update formatting as requested in code review.
* Rename flagsAndArgs to configmapSecretFlagsAndArgs: change the file and struct
  names to reflect the real usage of this code.
* Remove excessive newlines from the imports block.
* Replace all usages of assert with require and fix newlines in imports.
  • Loading branch information
stormqueen1990 authored Oct 2, 2023
1 parent 45e57f0 commit fb7ee2f
Show file tree
Hide file tree
Showing 6 changed files with 479 additions and 162 deletions.
123 changes: 61 additions & 62 deletions kustomize/commands/edit/add/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package add

import (
"fmt"

"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resource"
Expand All @@ -16,8 +18,9 @@ import (
func newCmdAddConfigMap(
fSys filesys.FileSystem,
ldr ifc.KvLoader,
rf *resource.Factory) *cobra.Command {
var flags flagsAndArgs
rf *resource.Factory,
) *cobra.Command {
var flags configmapSecretFlagsAndArgs
cmd := &cobra.Command{
Use: "configmap NAME [--behavior={create|merge|replace}] [--from-file=[key=]source] [--from-literal=key1=value1]",
Short: "Adds a configmap to the kustomization file",
Expand All @@ -36,63 +39,35 @@ func newCmdAddConfigMap(
kustomize edit add configmap my-configmap --behavior=merge --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.
err = addConfigMap(ldr, kustomization, flags, rf)
if err != nil {
return err
}

// Write out the kustomization file with added configmap.
return mf.Write(kustomization)
return runEditAddConfigMap(flags, fSys, args, ldr, rf)
},
}

cmd.Flags().StringSliceVar(
&flags.FileSources,
"from-file",
fromFileFlag,
[]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(
&flags.LiteralSources,
"from-literal",
fromLiteralFlag,
[]string{},
"Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
cmd.Flags().StringVar(
&flags.EnvFileSource,
"from-env-file",
fromEnvFileFlag,
"",
"Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
cmd.Flags().BoolVar(
&flags.DisableNameSuffixHash,
"disableNameSuffixHash",
flagDisableNameSuffixHash,
false,
"Disable the name suffix for the configmap")
cmd.Flags().StringVar(
&flags.Behavior,
"behavior",
flagBehavior,
"",
"Specify the behavior for config map generation, i.e whether to create a new configmap (the default), "+
"to merge with a previously defined one, or to replace an existing one. Merge and replace should be used only "+
Expand All @@ -101,15 +76,60 @@ func newCmdAddConfigMap(
return cmd
}

func runEditAddConfigMap(
flags configmapSecretFlagsAndArgs,
fSys filesys.FileSystem,
args []string,
ldr ifc.KvLoader,
rf *resource.Factory,
) error {
err := flags.ExpandFileSource(fSys)
if err != nil {
return fmt.Errorf("failed to expand file source: %w", err)
}

err = flags.Validate(args)
if err != nil {
return fmt.Errorf("failed to validate flags: %w", err)
}

// Load the kustomization file.
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return fmt.Errorf("failed to load kustomization file: %w", err)
}

kustomization, err := mf.Read()
if err != nil {
return fmt.Errorf("failed to read kustomization file: %w", err)
}

// Add the configmapSecretFlagsAndArgs map to the kustomization file.
err = addConfigMap(ldr, kustomization, flags, rf)
if err != nil {
return fmt.Errorf("failed to create configmap: %w", err)
}

// Write out the kustomization file with added configmap.
err = mf.Write(kustomization)
if err != nil {
return fmt.Errorf("failed to write kustomization file: %w", err)
}

return nil
}

// 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(
ldr ifc.KvLoader,
k *types.Kustomization,
flags flagsAndArgs, rf *resource.Factory) error {
flags configmapSecretFlagsAndArgs,
rf *resource.Factory,
) error {
args := findOrMakeConfigMapArgs(k, flags.Name)
mergeFlagsIntoCmArgs(args, flags)
mergeFlagsIntoGeneratorArgs(&args.GeneratorArgs, flags)
// Validate by trying to create corev1.configmap.
args.Options = types.MergeGlobalOptionsIntoLocal(
args.Options, k.GeneratorOptions)
Expand All @@ -124,30 +144,9 @@ func findOrMakeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigM
}
}
// config map not found, create new one and add it to the kustomization file.
cm := &types.ConfigMapArgs{GeneratorArgs: types.GeneratorArgs{Name: name}}
cm := &types.ConfigMapArgs{
GeneratorArgs: types.GeneratorArgs{Name: name},
}
m.ConfigMapGenerator = append(m.ConfigMapGenerator, *cm)
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
}

func mergeFlagsIntoCmArgs(args *types.ConfigMapArgs, flags flagsAndArgs) {
if len(flags.LiteralSources) > 0 {
args.LiteralSources = append(
args.LiteralSources, flags.LiteralSources...)
}
if len(flags.FileSources) > 0 {
args.FileSources = append(
args.FileSources, flags.FileSources...)
}
if flags.EnvFileSource != "" {
args.EnvSources = append(
args.EnvSources, flags.EnvFileSource)
}
if flags.DisableNameSuffixHash {
args.Options = &types.GeneratorOptions{
DisableNameSuffixHash: true,
}
}
if flags.Behavior != "" {
args.Behavior = flags.Behavior
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@ import (
"sigs.k8s.io/kustomize/kyaml/filesys"
)

// flagsAndArgs encapsulates the options for add secret/configmap commands.
type flagsAndArgs struct {
const (
fromFileFlag = "from-file"
fromLiteralFlag = "from-literal"
fromEnvFileFlag = "from-env-file"
flagDisableNameSuffixHash = "disableNameSuffixHash"
flagBehavior = "behavior"
flagFormat = "--%s=%s"
)

// configmapSecretFlagsAndArgs encapsulates the options for add secret/configmap commands.
type configmapSecretFlagsAndArgs struct {
// Name of configMap/Secret (required)
Name string
// FileSources to derive the configMap/Secret from (optional)
Expand All @@ -35,7 +44,7 @@ type flagsAndArgs struct {
}

// Validate validates required fields are set to support structured generation.
func (a *flagsAndArgs) Validate(args []string) error {
func (a *configmapSecretFlagsAndArgs) Validate(args []string) error {
if len(args) != 1 {
return fmt.Errorf("name must be specified once")
}
Expand Down Expand Up @@ -71,7 +80,7 @@ func (a *flagsAndArgs) Validate(args []string) error {
// and the key, if missing, is the same as the value.
// In the case where the key is explicitly declared,
// the globbing, if present, must have exactly one match.
func (a *flagsAndArgs) ExpandFileSource(fSys filesys.FileSystem) error {
func (a *configmapSecretFlagsAndArgs) ExpandFileSource(fSys filesys.FileSystem) error {
var results []string
for _, pattern := range a.FileSources {
var patterns []string
Expand Down Expand Up @@ -105,3 +114,26 @@ func (a *flagsAndArgs) ExpandFileSource(fSys filesys.FileSystem) error {
a.FileSources = results
return nil
}

func mergeFlagsIntoGeneratorArgs(args *types.GeneratorArgs, flags configmapSecretFlagsAndArgs) {
if len(flags.LiteralSources) > 0 {
args.LiteralSources = append(
args.LiteralSources, flags.LiteralSources...)
}
if len(flags.FileSources) > 0 {
args.FileSources = append(
args.FileSources, flags.FileSources...)
}
if flags.EnvFileSource != "" {
args.EnvSources = append(
args.EnvSources, flags.EnvFileSource)
}
if flags.DisableNameSuffixHash {
args.Options = &types.GeneratorOptions{
DisableNameSuffixHash: true,
}
}
if flags.Behavior != "" {
args.Behavior = flags.Behavior
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,68 +7,67 @@ import (
"reflect"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/kyaml/filesys"
)

func TestDataValidation_NoName(t *testing.T) {
fa := flagsAndArgs{}
assert.Error(t, fa.Validate([]string{}))
fa := configmapSecretFlagsAndArgs{}
require.Error(t, fa.Validate([]string{}))
}

func TestDataValidation_MoreThanOneName(t *testing.T) {
fa := flagsAndArgs{}
fa := configmapSecretFlagsAndArgs{}

assert.Error(t, fa.Validate([]string{"name", "othername"}))
require.Error(t, fa.Validate([]string{"name", "othername"}))
}

func TestDataConfigValidation_Flags(t *testing.T) {
tests := []struct {
name string
fa flagsAndArgs
fa configmapSecretFlagsAndArgs
shouldFail bool
}{
{
name: "env-file-source and literal are both set",
fa: flagsAndArgs{
fa: configmapSecretFlagsAndArgs{
LiteralSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "env-file-source and from-file are both set",
fa: flagsAndArgs{
fa: configmapSecretFlagsAndArgs{
FileSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "we don't have any option set",
fa: flagsAndArgs{},
fa: configmapSecretFlagsAndArgs{},
shouldFail: true,
},
{
name: "we have from-file and literal ",
fa: flagsAndArgs{
fa: configmapSecretFlagsAndArgs{
LiteralSources: []string{"one", "two"},
FileSources: []string{"three", "four"},
},
shouldFail: false,
},
{
name: "correct behavior",
fa: flagsAndArgs{
fa: configmapSecretFlagsAndArgs{
EnvFileSource: "foo",
Behavior: "merge",
},
shouldFail: false,
},
{
name: "incorrect behavior",
fa: flagsAndArgs{
fa: configmapSecretFlagsAndArgs{
EnvFileSource: "foo",
Behavior: "merge-unknown",
},
Expand All @@ -79,9 +78,9 @@ func TestDataConfigValidation_Flags(t *testing.T) {
for _, test := range tests {
err := test.fa.Validate([]string{"name"})
if test.shouldFail {
assert.Error(t, err)
require.Error(t, err)
} else {
assert.NoError(t, err)
require.NoError(t, err)
}
}
}
Expand All @@ -94,7 +93,7 @@ func TestExpandFileSource(t *testing.T) {
require.NoError(t, err)
_, err = fSys.Create("dir/readme")
require.NoError(t, err)
fa := flagsAndArgs{
fa := configmapSecretFlagsAndArgs{
FileSources: []string{"dir/fa*"},
}
err = fa.ExpandFileSource(fSys)
Expand All @@ -118,7 +117,7 @@ func TestExpandFileSourceWithKey(t *testing.T) {
require.NoError(t, err)
_, err = fSys.Create("dir/readme")
require.NoError(t, err)
fa := flagsAndArgs{
fa := configmapSecretFlagsAndArgs{
FileSources: []string{"foo-key=dir/fa*", "bar-key=dir/foobar", "dir/simplebar"},
}
err = fa.ExpandFileSource(fSys)
Expand All @@ -141,7 +140,7 @@ func TestExpandFileSourceWithKeyAndError(t *testing.T) {
require.NoError(t, err)
_, err = fSys.Create("dir/readme")
require.NoError(t, err)
fa := flagsAndArgs{
fa := configmapSecretFlagsAndArgs{
FileSources: []string{"foo-key=dir/fa*"},
}
err = fa.ExpandFileSource(fSys)
Expand Down
Loading

0 comments on commit fb7ee2f

Please sign in to comment.