diff --git a/kustomize/commands/edit/remove/removeconfigmap.go b/kustomize/commands/edit/remove/removeconfigmap.go index 7119519900..7eeedb7d0f 100644 --- a/kustomize/commands/edit/remove/removeconfigmap.go +++ b/kustomize/commands/edit/remove/removeconfigmap.go @@ -13,33 +13,49 @@ import ( "sigs.k8s.io/kustomize/api/konfig" "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" ) type removeConfigMapOptions struct { configMapNamesToRemove []string + namespace string } // newCmdRemoveConfigMap removes configMapGenerator(s) with the specified name(s). func newCmdRemoveConfigMap(fSys filesys.FileSystem) *cobra.Command { - var o removeConfigMapOptions + var flags removeConfigMapOptions cmd := &cobra.Command{ - Use: "configmap", + Use: "configmap NAME [,NAME] [--namespace=namespace-name]", Short: "Removes the specified configmap(s) from " + konfig.DefaultKustomizationFileName(), - Long: "", + Long: `Removes the specified configmap(s) from the ` + konfig.DefaultKustomizationFileName() + ` file in the specified namespace. +If multiple configmap names are specified, the command will not fail on secret names that were not found in the file, +but will issue a warning for each name that wasn't found.`, Example: ` - remove configmap my-configmap - `, + # Removes a single configmap named 'my-configmap' in the default namespace from the ` + konfig.DefaultKustomizationFileName() + ` file + kustomize edit remove configmap my-configmap + + # Removes configmaps named 'my-configmap' and 'other-configmap' in namespace 'test-namespace' from the ` + konfig.DefaultKustomizationFileName() + ` file + kustomize edit remove configmap my-configmap,other-configmap --namespace=test-namespace +`, RunE: func(cmd *cobra.Command, args []string) error { - err := o.Validate(args) + err := flags.Validate(args) if err != nil { return err } - return o.RunRemoveConfigMap(fSys) + return flags.RunRemoveConfigMap(fSys) }, } + + cmd.Flags().StringVar( + &flags.namespace, + util.NamespaceFlag, + "", + "Namespace to remove ConfigMap(s) from", + ) + return cmd } @@ -69,14 +85,16 @@ func (o *removeConfigMapOptions) RunRemoveConfigMap(fSys filesys.FileSystem) err } foundConfigMaps := make(map[string]struct{}) + remainingConfigMaps := make([]types.ConfigMapArgs, 0, len(m.ConfigMapGenerator)) - newConfigMaps := make([]types.ConfigMapArgs, 0, len(m.ConfigMapGenerator)) for _, currentConfigMap := range m.ConfigMapGenerator { - if kustfile.StringInSlice(currentConfigMap.Name, o.configMapNamesToRemove) { + if kustfile.StringInSlice(currentConfigMap.Name, o.configMapNamesToRemove) && + util.NamespaceEqual(currentConfigMap.Namespace, o.namespace) { foundConfigMaps[currentConfigMap.Name] = struct{}{} continue } - newConfigMaps = append(newConfigMaps, currentConfigMap) + + remainingConfigMaps = append(remainingConfigMaps, currentConfigMap) } if len(foundConfigMaps) == 0 { @@ -90,7 +108,7 @@ func (o *removeConfigMapOptions) RunRemoveConfigMap(fSys filesys.FileSystem) err } } - m.ConfigMapGenerator = newConfigMaps + m.ConfigMapGenerator = remainingConfigMaps err = mf.Write(m) if err != nil { return fmt.Errorf("failed to write kustomization file: %w", err) diff --git a/kustomize/commands/edit/remove/removeconfigmap_test.go b/kustomize/commands/edit/remove/removeconfigmap_test.go index d1ac37c232..fe03b21a94 100644 --- a/kustomize/commands/edit/remove/removeconfigmap_test.go +++ b/kustomize/commands/edit/remove/removeconfigmap_test.go @@ -1,10 +1,9 @@ // Copyright 2023 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package remove //nolint:testpackage +package remove import ( - "fmt" "testing" "github.com/stretchr/testify/require" @@ -13,84 +12,188 @@ import ( ) func TestRemoveConfigMap(t *testing.T) { - const configMapName01 = "example-configmap-01" - const configMapName02 = "example-configmap-02" - tests := map[string]struct { input string args []string expectedOutput string + wantErr bool expectedErr string }{ - "happy path": { - input: fmt.Sprintf(` + "removes a configmap successfully": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization configMapGenerator: -- name: %s +- name: test-cm-1 files: - application.properties -`, configMapName01), - args: []string{configMapName01}, +`, + args: []string{"test-cm-1"}, expectedOutput: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization `, }, - "multiple": { - input: fmt.Sprintf(` + "removes multiple configmaps successfully": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization configMapGenerator: -- name: %s +- name: test-cm-1 files: - application.properties -- name: %s +- name: test-cm-2 files: - application.properties -`, configMapName01, configMapName02), - args: []string{ - fmt.Sprintf("%s,%s", configMapName01, configMapName02), - }, +`, + args: []string{"test-cm-1,test-cm-2"}, expectedOutput: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization `, }, - "miss": { - input: fmt.Sprintf(` + "removes a configmap successfully when single configmap name is specified, it exists, but is not the only configmap present": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization configMapGenerator: -- name: %s +- name: test-cm-1 + namespace: test-ns files: - application.properties -`, configMapName01), - args: []string{"foo"}, - expectedErr: "no specified configmap(s) were found", +- name: test-cm-2 + namespace: default + literals: + - test-key=test-value +`, + args: []string{"test-cm-1", "--namespace=test-ns"}, + wantErr: false, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: +- literals: + - test-key=test-value + name: test-cm-2 + namespace: default +`, }, - "no configmap name specified": { - args: []string{}, - expectedErr: "at least one configmap name must be specified", + "succeeds when one configmap name exists and one doesn't exist": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: +- name: test-cm-1 + files: + - application.properties +`, + args: []string{"test-cm-1,foo"}, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`, }, - "too many configmap names specified": { - args: []string{"test1", "test2"}, - expectedErr: "too many arguments", + "succeeds when one configmap name exists in the specified namespace": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: +- name: test-cm + namespace: test-ns + files: + - application.properties +`, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`, + wantErr: false, + args: []string{"test-cm", "--namespace=test-ns"}, }, - "one existing and one non-existing": { - input: fmt.Sprintf(` + "handles empty namespace as default in the args": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization configMapGenerator: -- name: %s +- name: test-cm + namespace: default files: - application.properties -`, configMapName01), - args: []string{fmt.Sprintf("%s,%s", configMapName01, "foo")}, +`, expectedOutput: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization `, + wantErr: false, + args: []string{"test-cm"}, + }, + "handles empty namespace as default in the kustomization file": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: +- name: test-cm + files: + - application.properties +`, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`, + wantErr: false, + args: []string{"test-cm", "--namespace=default"}, + }, + "fails when single configmap name is specified and doesn't exist": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: +- name: test-cm-1 + files: + - application.properties +`, + args: []string{"foo"}, + wantErr: true, + expectedErr: "no specified configmap(s) were found", + }, + "fails when single configmap name is specified and doesn't exist in the specified namespace": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: +- name: test-cm-1 + namespace: test-ns + files: + - application.properties +`, + args: []string{"test-cm-1"}, + wantErr: true, + expectedErr: "no specified configmap(s) were found", + }, + + "fails when single configmap name is specified and doesn't exist in the specified namespace, and neither namespace is the default one": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: +- name: test-cm-1 + namespace: test-ns + files: + - application.properties +`, + args: []string{"test-cm-1", "--namespace=other-ns"}, + wantErr: true, + expectedErr: "no specified configmap(s) were found", + }, + "fails when no configmap name is specified": { + args: []string{}, + wantErr: true, + expectedErr: "at least one configmap name must be specified", + }, + "fails when too many configmap names are specified": { + args: []string{"test1", "test2"}, + wantErr: true, + expectedErr: "too many arguments", }, } @@ -99,9 +202,10 @@ kind: Kustomization fSys := filesys.MakeFsInMemory() testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input)) cmd := newCmdRemoveConfigMap(fSys) - err := cmd.RunE(cmd, tc.args) + cmd.SetArgs(tc.args) + err := cmd.Execute() - if tc.expectedErr != "" { + if tc.wantErr { require.Error(t, err) require.Contains(t, err.Error(), tc.expectedErr) return diff --git a/kustomize/commands/edit/remove/removesecret.go b/kustomize/commands/edit/remove/removesecret.go index 9151b58022..89a8af2306 100644 --- a/kustomize/commands/edit/remove/removesecret.go +++ b/kustomize/commands/edit/remove/removesecret.go @@ -13,33 +13,50 @@ import ( "sigs.k8s.io/kustomize/api/konfig" "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" ) type removeSecretOptions struct { secretNamesToRemove []string + namespace string } // newCmdRemoveSecret removes secretGenerator(s) with the specified name(s). func newCmdRemoveSecret(fSys filesys.FileSystem) *cobra.Command { - var o removeSecretOptions + var flags removeSecretOptions cmd := &cobra.Command{ - Use: "secret", + Use: "secret NAME [,NAME] [--namespace=namespace-name]", Short: "Removes the specified secret(s) from " + konfig.DefaultKustomizationFileName(), - Long: "", + Long: `Removes the specified secret(s) from the ` + konfig.DefaultKustomizationFileName() + ` file in the specified namespace. +If multiple secret names are specified, the command will not fail on secret names that were not found in the file, +but will issue a warning for each name that wasn't found.`, Example: ` - remove secret my-secret - `, + # Removes a single secret named 'my-secret' in the default namespace from the ` + konfig.DefaultKustomizationFileName() + ` file + kustomize edit remove secret my-secret + + # Removes secrets named 'my-secret' and 'other-secret' in namespace 'test-namespace' from the ` + konfig.DefaultKustomizationFileName() + ` file + kustomize edit remove secret my-secret,other-secret --namespace=test-namespace +`, + RunE: func(cmd *cobra.Command, args []string) error { - err := o.Validate(args) + err := flags.Validate(args) if err != nil { return err } - return o.RunRemoveSecret(fSys) + return flags.RunRemoveSecret(fSys) }, } + + cmd.Flags().StringVar( + &flags.namespace, + util.NamespaceFlag, + "", + "Namespace to remove Secret(s) from", + ) + return cmd } @@ -69,14 +86,15 @@ func (o *removeSecretOptions) RunRemoveSecret(fSys filesys.FileSystem) error { } foundSecrets := make(map[string]struct{}) + remainingSecrets := make([]types.SecretArgs, 0, len(m.SecretGenerator)) - newSecrets := make([]types.SecretArgs, 0, len(m.SecretGenerator)) for _, currentSecret := range m.SecretGenerator { - if kustfile.StringInSlice(currentSecret.Name, o.secretNamesToRemove) { + if kustfile.StringInSlice(currentSecret.Name, o.secretNamesToRemove) && + util.NamespaceEqual(currentSecret.Namespace, o.namespace) { foundSecrets[currentSecret.Name] = struct{}{} continue } - newSecrets = append(newSecrets, currentSecret) + remainingSecrets = append(remainingSecrets, currentSecret) } if len(foundSecrets) == 0 { @@ -89,8 +107,8 @@ func (o *removeSecretOptions) RunRemoveSecret(fSys filesys.FileSystem) error { log.Printf("secret %s doesn't exist in kustomization file", name) } } - m.SecretGenerator = newSecrets + m.SecretGenerator = remainingSecrets err = mf.Write(m) if err != nil { return fmt.Errorf("failed to write kustomization file: %w", err) diff --git a/kustomize/commands/edit/remove/removesecret_test.go b/kustomize/commands/edit/remove/removesecret_test.go index 6426d816ce..f0790a64a4 100644 --- a/kustomize/commands/edit/remove/removesecret_test.go +++ b/kustomize/commands/edit/remove/removesecret_test.go @@ -1,10 +1,9 @@ // Copyright 2023 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package remove //nolint:testpackage +package remove import ( - "fmt" "testing" "github.com/stretchr/testify/require" @@ -13,84 +12,187 @@ import ( ) func TestRemoveSecret(t *testing.T) { - const secretName01 = "example-secret-01" - const secretName02 = "example-secret-02" - tests := map[string]struct { input string args []string expectedOutput string + wantErr bool expectedErr string }{ - "happy path": { - input: fmt.Sprintf(` + "removes a secret successfully": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization secretGenerator: -- name: %s +- name: test-secret-1 files: - longsecret.txt -`, secretName01), - args: []string{secretName01}, +`, + args: []string{"test-secret-1"}, expectedOutput: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization `, }, - "multiple": { - input: fmt.Sprintf(` + "removes multiple secrets successfully": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization secretGenerator: -- name: %s +- name: test-secret-1 files: - longsecret.txt -- name: %s +- name: test-secret-2 files: - longsecret.txt -`, secretName01, secretName02), - args: []string{ - fmt.Sprintf("%s,%s", secretName01, secretName02), - }, +`, + args: []string{"test-secret-1,test-secret-2"}, expectedOutput: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization `, }, - "miss": { - input: fmt.Sprintf(` + "removes a secret successfully when single secret name is specified, it exists, but is not the only secret present": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization secretGenerator: -- name: %s +- name: test-secret-1 + namespace: test-ns files: - longsecret.txt -`, secretName01), - args: []string{"foo"}, - expectedErr: "no specified secret(s) were found", +- name: test-secret-2 + namespace: default + literals: + - test-key=test-secret +`, + args: []string{"test-secret-1", "--namespace=test-ns"}, + wantErr: false, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +secretGenerator: +- literals: + - test-key=test-secret + name: test-secret-2 + namespace: default +`, }, - "no secret name specified": { - args: []string{}, - expectedErr: "at least one secret name must be specified", + "succeeds when one secret name exists and one doesn't exist": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +secretGenerator: +- name: test-secret-1 + files: + - application.properties +`, + args: []string{"test-secret-1,test-secret-2"}, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`, }, - "too many secret names specified": { - args: []string{"test1", "test2"}, - expectedErr: "too many arguments", + "succeeds when one secret name exists in the specified namespace": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +secretGenerator: +- name: test-secret + namespace: test-ns + files: + - application.properties +`, + args: []string{"test-secret", "--namespace=test-ns"}, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`, }, - "one existing and one non-existing": { - input: fmt.Sprintf(` + "handles empty namespace as default in the args": { + input: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization secretGenerator: -- name: %s +- name: test-secret + namespace: default files: - application.properties -`, secretName01), - args: []string{fmt.Sprintf("%s,%s", secretName01, "foo")}, +`, expectedOutput: ` apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization `, + wantErr: false, + args: []string{"test-secret"}, + }, + "handles empty namespace as default in the kustomization file": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +secretGenerator: +- name: test-secret + files: + - application.properties +`, + expectedOutput: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`, + wantErr: false, + args: []string{"test-secret", "--namespace=default"}, + }, + "fails when single secret name is specified and doesn't exist": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +secretGenerator: +- name: test-secret-1 + files: + - longsecret.txt +`, + args: []string{"foo"}, + wantErr: true, + expectedErr: "no specified secret(s) were found", + }, + "fails when single secret name is specified and doesn't exist in the specified namespace": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +secretGenerator: +- name: test-secret-1 + namespace: test-ns + files: + - longsecret.txt +`, + args: []string{"test-secret-1"}, + wantErr: true, + expectedErr: "no specified secret(s) were found", + }, + + "fails when single secret name is specified and doesn't exist in the specified namespace, and neither namespace is the default one": { + input: ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +secretGenerator: +- name: test-secret-1 + namespace: test-ns + files: + - longsecret.txt +`, + args: []string{"test-secret-1", "--namespace=other-ns"}, + wantErr: true, + expectedErr: "no specified secret(s) were found", + }, + "fails when no secret name is specified": { + args: []string{}, + wantErr: true, + expectedErr: "at least one secret name must be specified", + }, + "fails when too many secret names are specified": { + args: []string{"test1", "test2"}, + wantErr: true, + expectedErr: "too many arguments", }, } @@ -99,9 +201,10 @@ kind: Kustomization fSys := filesys.MakeFsInMemory() testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input)) cmd := newCmdRemoveSecret(fSys) - err := cmd.RunE(cmd, tc.args) + cmd.SetArgs(tc.args) + err := cmd.Execute() - if tc.expectedErr != "" { + if tc.wantErr { require.Error(t, err) require.Contains(t, err.Error(), tc.expectedErr) return