mirror of
https://github.com/kubernetes/kubectl.git
synced 2026-04-15 22:00:02 -04:00
* Add <...> kuberc set --section=credentialplugin The requirement that the `--command` option be provided is no longer valid; a solution will need to be found. Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> * Remove required mark on `--command` flag Move the validation logic to the Validate() function Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> * Remove requirement for `--command` Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> * Add cmd test cases for kuberc - Verify all error messages added/changed by this PR - Verify changes are effected by at least one valid `kubectl kuberc set` invocation with --section=credentialplugin Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> * Add unit tests for --section=credentialplugin Additionally: - Update allowlist-entry validation logic to check for empty string in value of k/v pairs - Update kuberc cmd test with this case as well Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> * Address linter complaints Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> * Update staging/src/k8s.io/kubectl/pkg/cmd/kuberc/set.go Co-authored-by: Maciej Szulik <soltysh@gmail.com> * Add two new test cases Additionally: - update error message when policy is invalid - use utilerrors.NewAggregate instead of custom code - add --section=credentialplugin example to cli help - fix copypasted doc comments - use more descriptive variable names in for loops - avoid confusion due to variable shadowing - fix capitalization typo - check both validate and run errors in credentialplugin tests Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> * Improve cli help for policy flag Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> --------- Signed-off-by: Peter Engelbert <pmengelbert@gmail.com> Co-authored-by: Maciej Szulik <soltysh@gmail.com> Kubernetes-commit: b4220f8b08949c115258abed0398512bf54ffc48
1087 lines
27 KiB
Go
1087 lines
27 KiB
Go
/*
|
|
Copyright 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 kuberc
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/cli-runtime/pkg/genericiooptions"
|
|
"k8s.io/kubectl/pkg/config/v1beta1"
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
func TestSetOptions_Run_Defaults(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
existingKuberc string
|
|
options SetOptions
|
|
expectedPref *v1beta1.Preference
|
|
expectError bool
|
|
errorContains string
|
|
}{
|
|
{
|
|
name: "create new defaults",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionDefaults,
|
|
Command: "get",
|
|
Options: []string{"output=wide"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Defaults: []v1beta1.CommandDefaults{
|
|
{
|
|
Command: "get",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "wide",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "add defaults to existing file",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
defaults:
|
|
- command: get
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionDefaults,
|
|
Command: "create",
|
|
Options: []string{"output=yaml"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Defaults: []v1beta1.CommandDefaults{
|
|
{
|
|
Command: "get",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "wide",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Command: "create",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "yaml",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite existing defaults",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
defaults:
|
|
- command: get
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionDefaults,
|
|
Command: "get",
|
|
Options: []string{"output=json"},
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Defaults: []v1beta1.CommandDefaults{
|
|
{
|
|
Command: "get",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "json",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite without options preserves existing options",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
defaults:
|
|
- command: get
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionDefaults,
|
|
Command: "get",
|
|
Options: []string{}, // no options provided
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Defaults: []v1beta1.CommandDefaults{
|
|
{
|
|
Command: "get",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "wide",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite with single option replaces all options",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
defaults:
|
|
- command: get
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
- name: show-labels
|
|
default: "true"
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionDefaults,
|
|
Command: "get",
|
|
Options: []string{"output=json"},
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Defaults: []v1beta1.CommandDefaults{
|
|
{
|
|
Command: "get",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "json",
|
|
},
|
|
// show-labels option is overwritten and thus not present
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "error without overwrite",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
defaults:
|
|
- command: get
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionDefaults,
|
|
Command: "get",
|
|
Options: []string{"output=json"},
|
|
Overwrite: false,
|
|
},
|
|
expectError: true,
|
|
errorContains: "already exist",
|
|
},
|
|
{
|
|
name: "subcommand with multiple options",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionDefaults,
|
|
Command: "set env",
|
|
Options: []string{"output=yaml", "local=true"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Defaults: []v1beta1.CommandDefaults{
|
|
{
|
|
Command: "set env",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "yaml",
|
|
},
|
|
{
|
|
Name: "local",
|
|
Default: "true",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, err := os.MkdirTemp("", "kuberc-set-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer func() {
|
|
os.RemoveAll(tmpDir) // nolint:errcheck
|
|
}()
|
|
|
|
kubercPath := filepath.Join(tmpDir, "kuberc")
|
|
if tt.existingKuberc != "" {
|
|
if err := os.WriteFile(kubercPath, []byte(tt.existingKuberc), 0644); err != nil {
|
|
t.Fatalf("failed to write existing kuberc file: %v", err)
|
|
}
|
|
}
|
|
|
|
streams, _, out, _ := genericiooptions.NewTestIOStreams()
|
|
tt.options.KubeRCFile = kubercPath
|
|
tt.options.IOStreams = streams
|
|
|
|
err = tt.options.Run()
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Fatalf("expected error but got none")
|
|
}
|
|
if !strings.Contains(err.Error(), tt.errorContains) {
|
|
t.Errorf("expected error to contain %q, got: %v", tt.errorContains, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("Run() unexpected error = %v", err)
|
|
}
|
|
|
|
// Verify the file was written
|
|
data, err := os.ReadFile(kubercPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read written kuberc file: %v", err)
|
|
}
|
|
|
|
var actualPref v1beta1.Preference
|
|
if err := yaml.Unmarshal(data, &actualPref); err != nil {
|
|
t.Fatalf("failed to unmarshal actual output: %v", err)
|
|
}
|
|
|
|
if diff := cmp.Diff(tt.expectedPref, &actualPref); diff != "" {
|
|
t.Errorf("Run() output mismatch (-expected +got):\n%s", diff)
|
|
}
|
|
|
|
// Verify output message
|
|
if !strings.Contains(out.String(), "Updated") {
|
|
t.Errorf("expected output to contain 'Updated', got: %s", out.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetOptions_Run_Aliases(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
existingKuberc string
|
|
options SetOptions
|
|
expectedPref *v1beta1.Preference
|
|
expectError bool
|
|
errorContains string
|
|
}{
|
|
{
|
|
name: "create new alias",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"nodes"},
|
|
Options: []string{"output=wide"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"nodes"},
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "wide",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "add alias to existing file",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
prependArgs:
|
|
- nodes
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getp",
|
|
Command: "get",
|
|
PrependArgs: []string{"pods"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"nodes"},
|
|
},
|
|
{
|
|
Name: "getp",
|
|
Command: "get",
|
|
PrependArgs: []string{"pods"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite existing alias",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
prependArgs:
|
|
- nodes
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"namespaces"},
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"namespaces"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite alias without options preserves existing options",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
Options: []string{}, // no options provided
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
PrependArgs: nil,
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "wide",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite alias without prependArgs preserves existing prependArgs",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
prependArgs:
|
|
- nodes
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{}, // no prependArgs provided
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"nodes"},
|
|
Options: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "overwrite alias without appendArgs preserves existing appendArgs",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
prependArgs:
|
|
- nodes
|
|
appendArgs:
|
|
- --
|
|
- custom-arg
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
AppendArgs: []string{}, // no appendArgs provided
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"nodes"},
|
|
AppendArgs: []string{"--", "custom-arg"},
|
|
Options: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update multiple fields simultaneously",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
prependArgs:
|
|
- nodes
|
|
appendArgs:
|
|
- --watch
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"pods"},
|
|
Options: []string{"output=json"},
|
|
AppendArgs: []string{}, // no appendArgs provided
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"pods"},
|
|
AppendArgs: []string{"--watch"},
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "json",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite with single option replaces all options",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
options:
|
|
- name: output
|
|
default: wide
|
|
- name: show-labels
|
|
default: "true"
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
Options: []string{"output=json"},
|
|
Overwrite: true,
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "getn",
|
|
Command: "get",
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "output",
|
|
Default: "json",
|
|
},
|
|
// show-labels option is overwritten and thus not present
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "error without overwrite",
|
|
existingKuberc: `apiVersion: kubectl.config.k8s.io/v1beta1
|
|
kind: Preference
|
|
aliases:
|
|
- name: getn
|
|
command: get
|
|
prependArgs:
|
|
- nodes
|
|
`,
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "getn",
|
|
Command: "get",
|
|
PrependArgs: []string{"namespaces"},
|
|
Overwrite: false,
|
|
},
|
|
expectError: true,
|
|
errorContains: "already exists",
|
|
},
|
|
{
|
|
name: "alias with append args",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionAliases,
|
|
AliasName: "runx",
|
|
Command: "run",
|
|
AppendArgs: []string{"--", "custom-arg1"},
|
|
Options: []string{"image=nginx"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
Aliases: []v1beta1.AliasOverride{
|
|
{
|
|
Name: "runx",
|
|
Command: "run",
|
|
AppendArgs: []string{"--", "custom-arg1"},
|
|
Options: []v1beta1.CommandOptionDefault{
|
|
{
|
|
Name: "image",
|
|
Default: "nginx",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, err := os.MkdirTemp("", "kuberc-set-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer func() {
|
|
os.RemoveAll(tmpDir) // nolint:errcheck
|
|
}()
|
|
|
|
kubercPath := filepath.Join(tmpDir, "kuberc")
|
|
if tt.existingKuberc != "" {
|
|
if err := os.WriteFile(kubercPath, []byte(tt.existingKuberc), 0644); err != nil {
|
|
t.Fatalf("failed to write existing kuberc file: %v", err)
|
|
}
|
|
}
|
|
|
|
streams, _, out, _ := genericiooptions.NewTestIOStreams()
|
|
tt.options.KubeRCFile = kubercPath
|
|
tt.options.IOStreams = streams
|
|
|
|
err = tt.options.Run()
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Fatalf("expected error but got none")
|
|
}
|
|
if !strings.Contains(err.Error(), tt.errorContains) {
|
|
t.Errorf("expected error to contain %q, got: %v", tt.errorContains, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("Run() unexpected error = %v", err)
|
|
}
|
|
|
|
// Verify the file was written
|
|
data, err := os.ReadFile(kubercPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read written kuberc file: %v", err)
|
|
}
|
|
|
|
var actualPref v1beta1.Preference
|
|
if err := yaml.Unmarshal(data, &actualPref); err != nil {
|
|
t.Fatalf("failed to unmarshal actual output: %v", err)
|
|
}
|
|
|
|
if diff := cmp.Diff(tt.expectedPref, &actualPref); diff != "" {
|
|
t.Errorf("Run() output mismatch (-expected +got):\n%s", diff)
|
|
}
|
|
|
|
// Verify output message
|
|
if !strings.Contains(out.String(), "Updated") {
|
|
t.Errorf("expected output to contain 'Updated', got: %s", out.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetOptions_Run_CredentialPlugin(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
existingKuberc string
|
|
options SetOptions
|
|
expectedPref *v1beta1.Preference
|
|
expectError bool
|
|
errorContains string
|
|
}{
|
|
{
|
|
name: "plugin policy AllowAll",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowAll),
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowAll,
|
|
},
|
|
},
|
|
{
|
|
name: "plugin policy DenyAll",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyDenyAll),
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyDenyAll,
|
|
},
|
|
},
|
|
{
|
|
name: "plugin policy Allowlist with single valid entry",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"command=foobar"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowlist,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "foobar"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "plugin policy Allowlist with multiple valid entries",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"command=foobar", "command=barbaz"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowlist,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "foobar"},
|
|
{Command: "barbaz"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "plugin policy Allowlist with no entries",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowlist,
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "plugin policy Allowlist with one invalid entry",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"calvinball=asdf"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowlist,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "hello"},
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "plugin policy Allowlist with empty command",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"command="},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowlist,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: ""},
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "plugin policy Allowlist with both valid and invalid entries",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"command=hello", "calvinball=asdf"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowlist,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "hello"},
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid plugin policy",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: "Foo",
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.CredentialPluginPolicy("Foo"),
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "empty plugin policy",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: "",
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "allowlist entries with AllowAll policy",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowAll),
|
|
AllowlistEntries: []string{"command=foo"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowAll,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "foo"},
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "allowlist entries with DenyAll policy",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyDenyAll),
|
|
AllowlistEntries: []string{"command=foo"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyDenyAll,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "foo"},
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "use of deprecated name field",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"name=foo"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyDenyAll,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "foo"},
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "improperly formatted allowlist entry",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"command:foo"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyDenyAll,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "foo"},
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "improperly formatted allowlist entry",
|
|
existingKuberc: "",
|
|
options: SetOptions{
|
|
Section: sectionCredentialPlugin,
|
|
PluginPolicy: string(v1beta1.PluginPolicyAllowlist),
|
|
AllowlistEntries: []string{"command=foo,command=bar"},
|
|
},
|
|
expectedPref: &v1beta1.Preference{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "kubectl.config.k8s.io/v1beta1",
|
|
Kind: "Preference",
|
|
},
|
|
CredentialPluginPolicy: v1beta1.PluginPolicyAllowlist,
|
|
CredentialPluginAllowlist: []v1beta1.AllowlistEntry{
|
|
{Command: "foo"},
|
|
{Command: "bar"},
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, err := os.MkdirTemp("", "kuberc-set-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer func() {
|
|
os.RemoveAll(tmpDir) // nolint:errcheck
|
|
}()
|
|
|
|
kubercPath := filepath.Join(tmpDir, "kuberc")
|
|
if tt.existingKuberc != "" {
|
|
if err := os.WriteFile(kubercPath, []byte(tt.existingKuberc), 0644); err != nil {
|
|
t.Fatalf("failed to write existing kuberc file: %v", err)
|
|
}
|
|
}
|
|
|
|
streams, _, out, _ := genericiooptions.NewTestIOStreams()
|
|
tt.options.KubeRCFile = kubercPath
|
|
tt.options.IOStreams = streams
|
|
|
|
validationErr := tt.options.Validate()
|
|
runErr := tt.options.Run()
|
|
if tt.expectError {
|
|
if validationErr == nil && runErr == nil {
|
|
t.Fatalf("expected error but got none")
|
|
}
|
|
|
|
firstErr := validationErr
|
|
if firstErr == nil {
|
|
firstErr = runErr
|
|
}
|
|
|
|
if !strings.Contains(firstErr.Error(), tt.errorContains) {
|
|
t.Errorf("expected firstError to contain %q, got: %v", tt.errorContains, firstErr)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("Run() unexpected error = %v", err)
|
|
}
|
|
|
|
// Verify the file was written
|
|
data, err := os.ReadFile(kubercPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read written kuberc file: %v", err)
|
|
}
|
|
|
|
var actualPref v1beta1.Preference
|
|
if err := yaml.Unmarshal(data, &actualPref); err != nil {
|
|
t.Fatalf("failed to unmarshal actual output: %v", err)
|
|
}
|
|
|
|
if diff := cmp.Diff(tt.expectedPref, &actualPref); diff != "" {
|
|
t.Errorf("Run() output mismatch (-expected +got):\n%s", diff)
|
|
}
|
|
|
|
// Verify output message
|
|
if !strings.Contains(out.String(), "Updated") {
|
|
t.Errorf("expected output to contain 'Updated', got: %s", out.String())
|
|
}
|
|
})
|
|
}
|
|
}
|