mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
feat: edit set secret
* Add new functionality to allow editing a secret in a kustomization file. Initial implementation supports only --from-literal option. * Refactor edit set configmap to reuse some testing bits.
This commit is contained in:
@@ -27,12 +27,19 @@ func NewCmdSet(
|
|||||||
|
|
||||||
# Sets the namesuffix field
|
# Sets the namesuffix field
|
||||||
kustomize edit set namesuffix <suffix-value>
|
kustomize edit set namesuffix <suffix-value>
|
||||||
|
|
||||||
|
# Edits a field in an existing configmap in the kustomization file
|
||||||
|
kustomize edit set configmap my-configmap --from-literal=key1=value1
|
||||||
|
|
||||||
|
# Edits a field in an existing secret in the kustomization file
|
||||||
|
kustomize edit set secret my-secret --from-literal=key1=value1
|
||||||
`,
|
`,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
c.AddCommand(
|
c.AddCommand(
|
||||||
newCmdSetConfigMap(fSys, ldr, rf),
|
newCmdSetConfigMap(fSys, ldr, rf),
|
||||||
|
newCmdSetSecret(fSys, ldr, rf),
|
||||||
newCmdSetNamePrefix(fSys),
|
newCmdSetNamePrefix(fSys),
|
||||||
newCmdSetNameSuffix(fSys),
|
newCmdSetNameSuffix(fSys),
|
||||||
newCmdSetNamespace(fSys, v),
|
newCmdSetNamespace(fSys, v),
|
||||||
|
|||||||
@@ -1,245 +1,204 @@
|
|||||||
// Copyright 2023 The Kubernetes Authors.
|
// Copyright 2023 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// The duplicate linter is reporting that setsecret_test.go and this file are duplicates, which is not true.
|
||||||
|
// Disabling lint for these two files specifically to work around that.
|
||||||
|
//
|
||||||
|
//nolint:dupl
|
||||||
package set
|
package set
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/kv"
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v5/commands/internal/configmapsecret"
|
||||||
"sigs.k8s.io/kustomize/api/pkg/loader"
|
|
||||||
"sigs.k8s.io/kustomize/api/provider"
|
|
||||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
|
|
||||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v5/commands/internal/testutils"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEditSetConfigMapError(t *testing.T) {
|
func TestErrorCasesEditSetConfigMap(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
testCases := []testutils_test.FailureCase{
|
||||||
pvd := provider.NewDefaultDepProvider()
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
args []string
|
|
||||||
expectedErrorMsg string
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
name: "fails to set a value because key doesn't exist",
|
Name: "fails to set a value because key doesn't exist",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
- test=value
|
- test-cm-key=value
|
||||||
- key1=val1
|
- key1=val1
|
||||||
name: test-cm
|
name: test-cm
|
||||||
|
- literals:
|
||||||
|
- key3=val1
|
||||||
|
name: test-cm-2
|
||||||
|
namespace: test-ns
|
||||||
`,
|
`,
|
||||||
args: []string{"test-cm", "--from-literal=key3=val2"},
|
Args: []string{"test-cm", "--from-literal=test-key=test-value"},
|
||||||
expectedErrorMsg: "key 'key3' not found in resource",
|
ExpectedErrorMsg: "key 'test-key' not found in resource",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fails to set a value because configmap doesn't exist",
|
Name: "fails to set a value because configmap doesn't exist",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
- test=value
|
|
||||||
- key1=val1
|
- key1=val1
|
||||||
|
- key2=val2
|
||||||
name: test-cm
|
name: test-cm
|
||||||
`,
|
`,
|
||||||
args: []string{"test-cm2", "--from-literal=key3=val2"},
|
Args: []string{"test-cm2", "--from-literal=test-key=test-value"},
|
||||||
expectedErrorMsg: "unable to find ConfigMap with name '\"test-cm2\"'",
|
ExpectedErrorMsg: "unable to find ConfigMap with name '\"test-cm2\"'",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fails validation because no attributes are being changed",
|
Name: "fails validation because no attributes are being changed",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
- test=value
|
- test-key=test-value
|
||||||
- key1=val1
|
|
||||||
name: test-cm
|
name: test-cm
|
||||||
namespace: test-ns
|
namespace: test-ns
|
||||||
`,
|
`,
|
||||||
args: []string{"test-cm", "--namespace=test-ns"},
|
Args: []string{"test-cm", "--namespace=test-ns"},
|
||||||
expectedErrorMsg: "at least one of [--from-literal, --new-namespace] must be specified",
|
ExpectedErrorMsg: "at least one of [--from-literal, --new-namespace] must be specified",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fails when a literal source doesn't have a key",
|
Name: "fails when a literal source doesn't have a key",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
- test=value
|
- test-key=test-value
|
||||||
- key1=val1
|
- key3=other-value
|
||||||
name: test-cm
|
name: test-cm
|
||||||
`,
|
`,
|
||||||
args: []string{"test-cm", "--from-literal=value"},
|
Args: []string{"test-cm", "--from-literal=value"},
|
||||||
expectedErrorMsg: "literal values must be specified in the key=value format",
|
ExpectedErrorMsg: "literal values must be specified in the key=value format",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
cmd := newCmdSetConfigMap(
|
kustomization, err := testutils_test.SetupEditSetConfigMapSecretTest(t, newCmdSetConfigMap, tc.KustomizationFileContent, tc.Args)
|
||||||
fSys,
|
|
||||||
kv.NewLoader(
|
|
||||||
loader.NewFileLoaderAtCwd(fSys),
|
|
||||||
pvd.GetFieldValidator()),
|
|
||||||
pvd.GetResourceFactory(),
|
|
||||||
)
|
|
||||||
|
|
||||||
testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input))
|
|
||||||
|
|
||||||
cmd.SetArgs(tc.args)
|
|
||||||
err := cmd.Execute()
|
|
||||||
|
|
||||||
|
require.Nil(t, kustomization)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), tc.expectedErrorMsg)
|
require.Contains(t, err.Error(), tc.ExpectedErrorMsg)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEditSetConfigMapSuccess(t *testing.T) {
|
func TestSuccessCasesEditSetConfigMap(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
testCases := []testutils_test.SuccessCase{
|
||||||
pvd := provider.NewDefaultDepProvider()
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
args []string
|
|
||||||
expectedLiterals []string
|
|
||||||
expectedNamespace string
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
name: "set a value successfully",
|
Name: "set a value successfully",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
|
- a-key=a-value
|
||||||
- key1=val1
|
- key1=val1
|
||||||
- test=value
|
|
||||||
name: test-cm
|
name: test-cm
|
||||||
|
- literals:
|
||||||
|
- another-key=another-value
|
||||||
|
- key1=value-from-cm-2
|
||||||
|
name: test-cm-2
|
||||||
|
namespace: another-ns
|
||||||
`,
|
`,
|
||||||
expectedLiterals: []string{"key1=val2", "test=value"},
|
ExpectedLiterals: []string{"a-key=a-value", "key1=val2"},
|
||||||
args: []string{"test-cm", "--from-literal=key1=val2"},
|
Args: []string{"test-cm", "--from-literal=key1=val2"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "successfully update namespace of target configmap",
|
Name: "successfully update namespace of target configmap",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
- test=value
|
- yet-another-key=value
|
||||||
- key1=val1
|
|
||||||
name: test-cm
|
name: test-cm
|
||||||
namespace: test-ns
|
namespace: test-ns
|
||||||
`,
|
`,
|
||||||
args: []string{"test-cm", "--namespace=test-ns", "--new-namespace=test-new-ns"},
|
Args: []string{"test-cm", "--namespace=test-ns", "--new-namespace=test-new-ns"},
|
||||||
expectedNamespace: "test-new-ns",
|
ExpectedNamespace: "test-new-ns",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "successfully update namespace of target configmap with empty namespace in file and namespace specified in command",
|
Name: "successfully update namespace of target configmap with empty namespace in file and namespace specified in command",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
- test=value
|
- found-key=found-value
|
||||||
- key1=val1
|
- a-key=a-value
|
||||||
name: test-cm
|
name: test-cm
|
||||||
`,
|
`,
|
||||||
args: []string{"test-cm", "--namespace=default", "--new-namespace=test-new-ns"},
|
Args: []string{"test-cm", "--namespace=default", "--new-namespace=test-new-ns"},
|
||||||
expectedNamespace: "test-new-ns",
|
ExpectedNamespace: "test-new-ns",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "successfully update namespace of target configmap with default namespace and no namespace specified in command",
|
Name: "successfully update namespace of target configmap with default namespace and no namespace specified in command",
|
||||||
input: `---
|
KustomizationFileContent: `
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- literals:
|
- literals:
|
||||||
- test=value
|
- a-different-key=a-different-value
|
||||||
|
- key2=value
|
||||||
|
name: test-cm
|
||||||
|
namespace: default
|
||||||
|
`,
|
||||||
|
Args: []string{"test-cm", "--new-namespace=test-new-ns"},
|
||||||
|
ExpectedNamespace: "test-new-ns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "successfully update literal source of target configmap with empty namespace in file and namespace specified in command",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
configMapGenerator:
|
||||||
|
- literals:
|
||||||
|
- key1=value
|
||||||
|
- a-separate-key=a-separate-value
|
||||||
|
name: test-cm
|
||||||
|
`,
|
||||||
|
Args: []string{"test-cm", "--namespace=default", "--from-literal=key1=val2"},
|
||||||
|
ExpectedLiterals: []string{"key1=val2", "a-separate-key=a-separate-value"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "successfully update namespace of target configmap with default namespace and no namespace specified in command",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
configMapGenerator:
|
||||||
|
- literals:
|
||||||
|
- a-random-key=a-random-value
|
||||||
- key1=val1
|
- key1=val1
|
||||||
name: test-cm
|
name: test-cm
|
||||||
namespace: default
|
namespace: default
|
||||||
`,
|
`,
|
||||||
args: []string{"test-cm", "--new-namespace=test-new-ns"},
|
Args: []string{"test-cm", "--from-literal=key1=val2"},
|
||||||
expectedNamespace: "test-new-ns",
|
ExpectedLiterals: []string{"a-random-key=a-random-value", "key1=val2"},
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "successfully update literal source of target configmap with empty namespace in file and namespace specified in command",
|
|
||||||
input: `---
|
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
configMapGenerator:
|
|
||||||
- literals:
|
|
||||||
- test=value
|
|
||||||
- key1=val1
|
|
||||||
name: test-cm
|
|
||||||
`,
|
|
||||||
args: []string{"test-cm", "--namespace=default", "--from-literal=key1=val2"},
|
|
||||||
expectedLiterals: []string{"test=value", "key1=val2"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "successfully update namespace of target configmap with default namespace and no namespace specified in command",
|
|
||||||
input: `---
|
|
||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
configMapGenerator:
|
|
||||||
- literals:
|
|
||||||
- test=value
|
|
||||||
- key1=val1
|
|
||||||
name: test-cm
|
|
||||||
namespace: default
|
|
||||||
`,
|
|
||||||
args: []string{"test-cm", "--namespace=default", "--from-literal=key1=val2"},
|
|
||||||
expectedLiterals: []string{"test=value", "key1=val2"},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
cmd := newCmdSetConfigMap(
|
kustomization, err := testutils_test.SetupEditSetConfigMapSecretTest(t, newCmdSetConfigMap, tc.KustomizationFileContent, tc.Args)
|
||||||
fSys,
|
|
||||||
kv.NewLoader(
|
|
||||||
loader.NewFileLoaderAtCwd(fSys),
|
|
||||||
pvd.GetFieldValidator()),
|
|
||||||
pvd.GetResourceFactory(),
|
|
||||||
)
|
|
||||||
|
|
||||||
testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input))
|
|
||||||
|
|
||||||
cmd.SetArgs(tc.args)
|
|
||||||
err := cmd.Execute()
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = testutils_test.ReadTestKustomization(fSys)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mf, err := kustfile.NewKustomizationFile(fSys)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
kustomization, err := mf.Read()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.NotNil(t, kustomization)
|
require.NotNil(t, kustomization)
|
||||||
require.NotEmpty(t, kustomization.ConfigMapGenerator)
|
require.NotEmpty(t, kustomization.ConfigMapGenerator)
|
||||||
require.Greater(t, len(kustomization.ConfigMapGenerator), 0)
|
require.Greater(t, len(kustomization.ConfigMapGenerator), 0)
|
||||||
|
|
||||||
if tc.expectedNamespace != "" {
|
if tc.ExpectedNamespace != "" {
|
||||||
require.Equal(t, tc.expectedNamespace, kustomization.ConfigMapGenerator[0].Namespace)
|
require.Equal(t, tc.ExpectedNamespace, kustomization.ConfigMapGenerator[0].Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(tc.expectedLiterals) > 0 {
|
if len(tc.ExpectedLiterals) > 0 {
|
||||||
require.ElementsMatch(t, tc.expectedLiterals, kustomization.ConfigMapGenerator[0].LiteralSources)
|
require.ElementsMatch(t, tc.ExpectedLiterals, kustomization.ConfigMapGenerator[0].LiteralSources)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
152
kustomize/commands/edit/set/setsecret.go
Normal file
152
kustomize/commands/edit/set/setsecret.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// Copyright 2023 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/util"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newCmdSetSecret(
|
||||||
|
fSys filesys.FileSystem,
|
||||||
|
ldr ifc.KvLoader,
|
||||||
|
rf *resource.Factory,
|
||||||
|
) *cobra.Command {
|
||||||
|
var flags util.ConfigMapSecretFlagsAndArgs
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "secret NAME [--from-literal=key1=value1] [--namespace=namespace-name] [--new-namespace=new-namespace-name]",
|
||||||
|
Short: fmt.Sprintf("Edits the value for an existing key for a secret in the %s file", konfig.DefaultKustomizationFileName()),
|
||||||
|
Long: fmt.Sprintf(`Edits the value for an existing key in an existing secret in the %s file.
|
||||||
|
Both secret name and key name must exist for this command to succeed.`, konfig.DefaultKustomizationFileName()),
|
||||||
|
Example: fmt.Sprintf(`
|
||||||
|
# Edits an existing secret in the %[1]s file, changing the value of key1 to 2
|
||||||
|
kustomize edit set secret my-secret --from-literal=key1=2
|
||||||
|
|
||||||
|
# Edits an existing secret in the %[1]s file, changing namespace to 'new-namespace'
|
||||||
|
kustomize edit set secret my-secret --namespace=current-namespace --new-namespace=new-namespace
|
||||||
|
`, konfig.DefaultKustomizationFileName()),
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
return runEditSetSecret(flags, fSys, args, ldr, rf)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringArrayVar(
|
||||||
|
&flags.LiteralSources,
|
||||||
|
util.FromLiteralFlag,
|
||||||
|
[]string{},
|
||||||
|
"Specify an existing key and a new value to update a Secret (i.e. mykey=newvalue)")
|
||||||
|
cmd.Flags().StringVar(
|
||||||
|
&flags.Namespace,
|
||||||
|
util.NamespaceFlag,
|
||||||
|
"",
|
||||||
|
"Current namespace of the target Secret")
|
||||||
|
cmd.Flags().StringVar(
|
||||||
|
&flags.NewNamespace,
|
||||||
|
util.NewNamespaceFlag,
|
||||||
|
"",
|
||||||
|
"New namespace value for the target Secret")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runEditSetSecret(
|
||||||
|
flags util.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.ValidateSet(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the existing Secret
|
||||||
|
err = setSecret(ldr, kustomization, flags, rf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create secret: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the kustomization file with added secret.
|
||||||
|
err = mf.Write(kustomization)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write kustomization file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSecret(
|
||||||
|
ldr ifc.KvLoader,
|
||||||
|
k *types.Kustomization,
|
||||||
|
flags util.ConfigMapSecretFlagsAndArgs,
|
||||||
|
rf *resource.Factory,
|
||||||
|
) error {
|
||||||
|
args, err := findSecretArgs(k, flags.Name, flags.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not set new Secret value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(flags.LiteralSources) > 0 {
|
||||||
|
err := util.UpdateLiteralSources(&args.GeneratorArgs, flags)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update literal sources: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update namespace to new one
|
||||||
|
if flags.NewNamespace != "" {
|
||||||
|
args.Namespace = flags.NewNamespace
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate by trying to create corev1.secret.
|
||||||
|
args.Options = types.MergeGlobalOptionsIntoLocal(
|
||||||
|
args.Options, k.GeneratorOptions)
|
||||||
|
|
||||||
|
_, err = rf.MakeSecret(ldr, args)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to validate Secret structure: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findSecretArgs finds the generator arguments corresponding to the specified
|
||||||
|
// Secret name. Secret must exist for this command to be successful.
|
||||||
|
func findSecretArgs(m *types.Kustomization, name, namespace string) (*types.SecretArgs, error) {
|
||||||
|
cmIndex := slices.IndexFunc(m.SecretGenerator, func(cmArgs types.SecretArgs) bool {
|
||||||
|
return name == cmArgs.Name && util.NamespaceEqual(namespace, cmArgs.Namespace)
|
||||||
|
})
|
||||||
|
|
||||||
|
if cmIndex == -1 {
|
||||||
|
return nil, fmt.Errorf("unable to find Secret with name '%q'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m.SecretGenerator[cmIndex], nil
|
||||||
|
}
|
||||||
213
kustomize/commands/edit/set/setsecret_test.go
Normal file
213
kustomize/commands/edit/set/setsecret_test.go
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
// Copyright 2023 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// The duplicate linter is reporting that setconfigmap_test.go and this file are duplicates, which is not true.
|
||||||
|
// Disabling lint for these two files specifically to work around that.
|
||||||
|
//
|
||||||
|
//nolint:dupl
|
||||||
|
package set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v5/commands/internal/configmapsecret"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFailureCasesEditSetSecret(t *testing.T) {
|
||||||
|
testCases := []testutils_test.FailureCase{
|
||||||
|
{
|
||||||
|
Name: "fails to set a value because key doesn't exist",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- key1=val1
|
||||||
|
name: test-secret
|
||||||
|
type: Opaque
|
||||||
|
- literals:
|
||||||
|
- key3=val1
|
||||||
|
name: test-secret-2
|
||||||
|
namespace: test-ns
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--from-literal=key3=val2"},
|
||||||
|
ExpectedErrorMsg: "key 'key3' not found in resource",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "fails to set a value because secret doesn't exist",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- key1=val1
|
||||||
|
name: test-secret
|
||||||
|
type: Opaque
|
||||||
|
- literals:
|
||||||
|
- key2=value
|
||||||
|
name: another-secret
|
||||||
|
namespace: a-namespace
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret2", "--from-literal=key3=val2"},
|
||||||
|
ExpectedErrorMsg: "unable to find Secret with name '\"test-secret2\"'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "fails validation because no attributes are being changed",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- a-test-key=a-test-value
|
||||||
|
name: test-secret
|
||||||
|
namespace: test-ns
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--namespace=test-ns"},
|
||||||
|
ExpectedErrorMsg: "at least one of [--from-literal, --new-namespace] must be specified",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "fails when a literal source doesn't have a key",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- some-key=some-value
|
||||||
|
name: test-secret
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--from-literal=value"},
|
||||||
|
ExpectedErrorMsg: "literal values must be specified in the key=value format",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
kustomization, err := testutils_test.SetupEditSetConfigMapSecretTest(t, newCmdSetSecret, tc.KustomizationFileContent, tc.Args)
|
||||||
|
|
||||||
|
require.Nil(t, kustomization)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), tc.ExpectedErrorMsg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuccessCasesEditSetSecret(t *testing.T) {
|
||||||
|
testCases := []testutils_test.SuccessCase{
|
||||||
|
{
|
||||||
|
Name: "set a value successfully",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- random-key=random-value
|
||||||
|
- key1=value
|
||||||
|
name: test-secret
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
ExpectedLiterals: []string{"key1=val2", "random-key=random-value"},
|
||||||
|
Args: []string{"test-secret", "--from-literal=key1=val2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "successfully update namespace of target secret",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- a-key=test
|
||||||
|
- another-key=value
|
||||||
|
name: test-secret
|
||||||
|
namespace: test-ns
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--namespace=test-ns", "--new-namespace=test-new-ns"},
|
||||||
|
ExpectedNamespace: "test-new-ns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "successfully update namespace of target secret with empty namespace in file and namespace specified in command",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- key1=value1
|
||||||
|
- another-key=another-value
|
||||||
|
name: test-secret
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--namespace=default", "--new-namespace=test-new-ns"},
|
||||||
|
ExpectedNamespace: "test-new-ns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "successfully update namespace of target secret with default namespace and no namespace specified in command",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- random-key=random-value
|
||||||
|
name: test-secret
|
||||||
|
namespace: default
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--new-namespace=test-new-ns"},
|
||||||
|
ExpectedNamespace: "test-new-ns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "successfully update literal source of target secret with empty namespace in file and namespace specified in command",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- a-key=a-value
|
||||||
|
- key1=value
|
||||||
|
name: test-secret
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--namespace=default", "--from-literal=key1=val2"},
|
||||||
|
ExpectedLiterals: []string{"a-key=a-value", "key1=val2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "successfully update namespace of target secret with default namespace and no namespace specified in command",
|
||||||
|
KustomizationFileContent: `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
secretGenerator:
|
||||||
|
- literals:
|
||||||
|
- test-secret-key=value
|
||||||
|
- key1=val1
|
||||||
|
name: test-secret
|
||||||
|
namespace: default
|
||||||
|
type: Opaque
|
||||||
|
`,
|
||||||
|
Args: []string{"test-secret", "--from-literal=key1=val2"},
|
||||||
|
ExpectedLiterals: []string{"test-secret-key=value", "key1=val2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
kustomization, err := testutils_test.SetupEditSetConfigMapSecretTest(t, newCmdSetSecret, tc.KustomizationFileContent, tc.Args)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, kustomization)
|
||||||
|
require.NotEmpty(t, kustomization.SecretGenerator)
|
||||||
|
require.Greater(t, len(kustomization.SecretGenerator), 0)
|
||||||
|
|
||||||
|
if tc.ExpectedNamespace != "" {
|
||||||
|
require.Equal(t, tc.ExpectedNamespace, kustomization.SecretGenerator[0].Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tc.ExpectedLiterals) > 0 {
|
||||||
|
require.ElementsMatch(t, tc.ExpectedLiterals, kustomization.SecretGenerator[0].LiteralSources)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2023 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package configmapsecret_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
|
"sigs.k8s.io/kustomize/api/pkg/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/provider"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
|
||||||
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v5/commands/internal/testutils"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FailureCase specifies a test case for a failure in the 'edit set configmap'/'edit set secret' commands.
|
||||||
|
type FailureCase struct {
|
||||||
|
// The name of the test case
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// The kustomization file content for the test case in YAML format
|
||||||
|
KustomizationFileContent string
|
||||||
|
|
||||||
|
// Arguments passed to the test case command
|
||||||
|
Args []string
|
||||||
|
|
||||||
|
// The expected error message for the test case
|
||||||
|
ExpectedErrorMsg string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuccessCase specifies a test case for a success in the 'edit set configmap'/'edit set secret' commands.
|
||||||
|
type SuccessCase struct {
|
||||||
|
// The name of the test case
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// The kustomization file content for the test case in YAML format
|
||||||
|
KustomizationFileContent string
|
||||||
|
|
||||||
|
// Arguments passed to the test case command
|
||||||
|
Args []string
|
||||||
|
|
||||||
|
// List of expected literals for the result of the test case
|
||||||
|
ExpectedLiterals []string
|
||||||
|
|
||||||
|
// The expected namespace for the result of the test case
|
||||||
|
ExpectedNamespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupEditSetConfigMapSecretTest(
|
||||||
|
t *testing.T,
|
||||||
|
command func(filesys.FileSystem, ifc.KvLoader, *resource.Factory) *cobra.Command,
|
||||||
|
input string,
|
||||||
|
args []string,
|
||||||
|
) (*types.Kustomization, error) {
|
||||||
|
t.Helper()
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
pvd := provider.NewDefaultDepProvider()
|
||||||
|
|
||||||
|
cmd := command(
|
||||||
|
fSys,
|
||||||
|
kv.NewLoader(
|
||||||
|
loader.NewFileLoaderAtCwd(fSys),
|
||||||
|
pvd.GetFieldValidator()),
|
||||||
|
pvd.GetResourceFactory(),
|
||||||
|
)
|
||||||
|
|
||||||
|
testutils_test.WriteTestKustomizationWith(fSys, []byte(input))
|
||||||
|
|
||||||
|
cmd.SetArgs(args)
|
||||||
|
err := cmd.Execute()
|
||||||
|
|
||||||
|
//nolint: wrapcheck
|
||||||
|
// this needs to be bubbled up for checking in the test
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = testutils_test.ReadTestKustomization(fSys)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mf, err := kustfile.NewKustomizationFile(fSys)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
kustomization, err := mf.Read()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return kustomization, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user