mirror of https://github.com/k3s-io/k3s
Allow to define exec credential plugin config options from kubectl
This commit adds support of setting config options to the exec plugin from cli. Next options are added: * --exec-command new command for the exec credential plugin * --exec-api-version API version of the exec credential plugin. * --exec-arg new arguments for the exec credential plugin command * --exec-env add, update or remove environment values for the exec credential plugink3s-v1.15.3
parent
7a8e11c16d
commit
a556e4fcd1
|
@ -48,11 +48,22 @@ type createAuthInfoOptions struct {
|
|||
|
||||
authProviderArgs map[string]string
|
||||
authProviderArgsToRemove []string
|
||||
|
||||
execCommand cliflag.StringFlag
|
||||
execAPIVersion cliflag.StringFlag
|
||||
execArgs []string
|
||||
execEnv map[string]string
|
||||
execEnvToRemove []string
|
||||
}
|
||||
|
||||
const (
|
||||
flagAuthProvider = "auth-provider"
|
||||
flagAuthProviderArg = "auth-provider-arg"
|
||||
|
||||
flagExecCommand = "exec-command"
|
||||
flagExecAPIVersion = "exec-api-version"
|
||||
flagExecArg = "exec-arg"
|
||||
flagExecEnv = "exec-env"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -90,7 +101,19 @@ var (
|
|||
kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-id=foo --auth-provider-arg=client-secret=bar
|
||||
|
||||
# Remove the "client-secret" config value for the OpenID Connect auth provider for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-secret-`)
|
||||
kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-secret-
|
||||
|
||||
# Enable new exec auth plugin for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --exec-command=/path/to/the/executable --exec-api-version=client.authentication.k8s.io/v1beta
|
||||
|
||||
# Define new exec auth plugin args for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --exec-arg=arg1 --exec-arg=arg2
|
||||
|
||||
# Create or update exec auth plugin environment variables for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --exec-env=key1=val1 --exec-env=key2=val2
|
||||
|
||||
# Remove exec auth plugin environment variables for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --exec-env=var-to-remove-`)
|
||||
)
|
||||
|
||||
// NewCmdConfigSetAuthInfo returns an Command option instance for 'config set-credentials' sub command
|
||||
|
@ -101,7 +124,30 @@ func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess)
|
|||
|
||||
func newCmdConfigSetAuthInfo(out io.Writer, options *createAuthInfoOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: fmt.Sprintf("set-credentials NAME [--%v=path/to/certfile] [--%v=path/to/keyfile] [--%v=bearer_token] [--%v=basic_user] [--%v=basic_password] [--%v=provider_name] [--%v=key=value]", clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword, flagAuthProvider, flagAuthProviderArg),
|
||||
Use: fmt.Sprintf(
|
||||
"set-credentials NAME [--%v=path/to/certfile] "+
|
||||
"[--%v=path/to/keyfile] "+
|
||||
"[--%v=bearer_token] "+
|
||||
"[--%v=basic_user] "+
|
||||
"[--%v=basic_password] "+
|
||||
"[--%v=provider_name] "+
|
||||
"[--%v=key=value] "+
|
||||
"[--%v=exec_command] "+
|
||||
"[--%v=exec_api_version] "+
|
||||
"[--%v=arg] "+
|
||||
"[--%v=key=value]",
|
||||
clientcmd.FlagCertFile,
|
||||
clientcmd.FlagKeyFile,
|
||||
clientcmd.FlagBearerToken,
|
||||
clientcmd.FlagUsername,
|
||||
clientcmd.FlagPassword,
|
||||
flagAuthProvider,
|
||||
flagAuthProviderArg,
|
||||
flagExecCommand,
|
||||
flagExecAPIVersion,
|
||||
flagExecArg,
|
||||
flagExecEnv,
|
||||
),
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Sets a user entry in kubeconfig"),
|
||||
Long: createAuthInfoLong,
|
||||
|
@ -126,6 +172,10 @@ func newCmdConfigSetAuthInfo(out io.Writer, options *createAuthInfoOptions) *cob
|
|||
cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in kubeconfig")
|
||||
cmd.Flags().Var(&options.authProvider, flagAuthProvider, "Auth provider for the user entry in kubeconfig")
|
||||
cmd.Flags().StringSlice(flagAuthProviderArg, nil, "'key=value' arguments for the auth provider")
|
||||
cmd.Flags().Var(&options.execCommand, flagExecCommand, "Command for the exec credential plugin for the user entry in kubeconfig")
|
||||
cmd.Flags().Var(&options.execAPIVersion, flagExecAPIVersion, "API version of the exec credential plugin for the user entry in kubeconfig")
|
||||
cmd.Flags().StringSlice(flagExecArg, nil, "New arguments for the exec credential plugin command for the user entry in kubeconfig")
|
||||
cmd.Flags().StringArray(flagExecEnv, nil, "'key=value' environment values for the exec credential plugin")
|
||||
f := cmd.Flags().VarPF(&options.embedCertData, clientcmd.FlagEmbedCerts, "", "Embed client cert/key for the user entry in kubeconfig")
|
||||
f.NoOptDefVal = "true"
|
||||
|
||||
|
@ -226,6 +276,72 @@ func (o *createAuthInfoOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.Aut
|
|||
}
|
||||
}
|
||||
|
||||
if o.execCommand.Provided() {
|
||||
newExecCommand := o.execCommand.Value()
|
||||
|
||||
// create new Exec if doesn't exist, otherwise just modify the command
|
||||
if modifiedAuthInfo.Exec == nil {
|
||||
modifiedAuthInfo.Exec = &clientcmdapi.ExecConfig{
|
||||
Command: newExecCommand,
|
||||
}
|
||||
} else {
|
||||
modifiedAuthInfo.Exec.Command = newExecCommand
|
||||
// explicitly reset exec arguments
|
||||
modifiedAuthInfo.Exec.Args = nil
|
||||
}
|
||||
}
|
||||
|
||||
// modify next values only if Exec exists, ignore these changes otherwise
|
||||
if modifiedAuthInfo.Exec != nil {
|
||||
if o.execAPIVersion.Provided() {
|
||||
modifiedAuthInfo.Exec.APIVersion = o.execAPIVersion.Value()
|
||||
}
|
||||
|
||||
// rewrite exec arguments list with new values
|
||||
if o.execArgs != nil {
|
||||
modifiedAuthInfo.Exec.Args = o.execArgs
|
||||
}
|
||||
|
||||
// iterate over the existing exec env values and remove the specified
|
||||
if o.execEnvToRemove != nil {
|
||||
newExecEnv := []clientcmdapi.ExecEnvVar{}
|
||||
for _, value := range modifiedAuthInfo.Exec.Env {
|
||||
needToRemove := false
|
||||
for _, elemToRemove := range o.execEnvToRemove {
|
||||
if value.Name == elemToRemove {
|
||||
needToRemove = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needToRemove {
|
||||
newExecEnv = append(newExecEnv, value)
|
||||
}
|
||||
}
|
||||
modifiedAuthInfo.Exec.Env = newExecEnv
|
||||
}
|
||||
|
||||
// update or create specified environment variables for the exec plugin
|
||||
if o.execEnv != nil {
|
||||
newEnv := []clientcmdapi.ExecEnvVar{}
|
||||
for newEnvName, newEnvValue := range o.execEnv {
|
||||
needToCreate := true
|
||||
for i := 0; i < len(modifiedAuthInfo.Exec.Env); i++ {
|
||||
if modifiedAuthInfo.Exec.Env[i].Name == newEnvName {
|
||||
// update the existing value
|
||||
needToCreate = false
|
||||
modifiedAuthInfo.Exec.Env[i].Value = newEnvValue
|
||||
break
|
||||
}
|
||||
}
|
||||
if needToCreate {
|
||||
// create a new env value
|
||||
newEnv = append(newEnv, clientcmdapi.ExecEnvVar{Name: newEnvName, Value: newEnvValue})
|
||||
}
|
||||
}
|
||||
modifiedAuthInfo.Exec.Env = append(modifiedAuthInfo.Exec.Env, newEnv...)
|
||||
}
|
||||
}
|
||||
|
||||
// If any auth info was set, make sure any other existing auth types are cleared
|
||||
if setToken || setBasic {
|
||||
if !setToken {
|
||||
|
@ -260,6 +376,27 @@ func (o *createAuthInfoOptions) complete(cmd *cobra.Command, out io.Writer) erro
|
|||
o.authProviderArgsToRemove = removePairs
|
||||
}
|
||||
|
||||
execArgs, err := cmd.Flags().GetStringSlice(flagExecArg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: %s", err)
|
||||
}
|
||||
if len(execArgs) > 0 {
|
||||
o.execArgs = execArgs
|
||||
}
|
||||
|
||||
execEnv, err := cmd.Flags().GetStringArray(flagExecEnv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: %s", err)
|
||||
}
|
||||
if len(execEnv) > 0 {
|
||||
newPairs, removePairs, err := cmdutil.ParsePairs(execEnv, flagExecEnv, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: %s", err)
|
||||
}
|
||||
o.execEnv = newPairs
|
||||
o.execEnvToRemove = removePairs
|
||||
}
|
||||
|
||||
o.name = args[0]
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -156,6 +156,48 @@ func TestCreateAuthInfoOptions(t *testing.T) {
|
|||
},
|
||||
wantCompleteErr: true,
|
||||
},
|
||||
{
|
||||
name: "test10",
|
||||
flags: []string{
|
||||
"--exec-command=example-client-go-exec-plugin",
|
||||
"me",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
execCommand: stringFlagFor("example-client-go-exec-plugin"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test11",
|
||||
flags: []string{
|
||||
"--exec-command=example-client-go-exec-plugin",
|
||||
"--exec-arg=arg1",
|
||||
"--exec-arg=arg2",
|
||||
"me",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
execCommand: stringFlagFor("example-client-go-exec-plugin"),
|
||||
execArgs: []string{"arg1", "arg2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test12",
|
||||
flags: []string{
|
||||
"--exec-command=example-client-go-exec-plugin",
|
||||
"--exec-env=key1=val1",
|
||||
"--exec-env=key2=val2",
|
||||
"--exec-env=env-remove1-",
|
||||
"--exec-env=env-remove2-",
|
||||
"me",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
execCommand: stringFlagFor("example-client-go-exec-plugin"),
|
||||
execEnv: map[string]string{"key1": "val1", "key2": "val2"},
|
||||
execEnvToRemove: []string{"env-remove1", "env-remove2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@ -205,6 +247,185 @@ func TestCreateAuthInfoOptions(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestModifyExistingAuthInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
flags []string
|
||||
wantParseErr bool
|
||||
wantCompleteErr bool
|
||||
wantValidateErr bool
|
||||
|
||||
existingAuthInfo clientcmdapi.AuthInfo
|
||||
wantAuthInfo clientcmdapi.AuthInfo
|
||||
}{
|
||||
{
|
||||
name: "1. create new exec config",
|
||||
flags: []string{
|
||||
"--exec-command=example-client-go-exec-plugin",
|
||||
"--exec-api-version=client.authentication.k8s.io/v1",
|
||||
"me",
|
||||
},
|
||||
existingAuthInfo: clientcmdapi.AuthInfo{},
|
||||
wantAuthInfo: clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "example-client-go-exec-plugin",
|
||||
APIVersion: "client.authentication.k8s.io/v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "2. redefine exec args",
|
||||
flags: []string{
|
||||
"--exec-arg=new-arg1",
|
||||
"--exec-arg=new-arg2",
|
||||
"me",
|
||||
},
|
||||
existingAuthInfo: clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "example-client-go-exec-plugin",
|
||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||
Args: []string{"existing-arg1", "existing-arg2"},
|
||||
},
|
||||
},
|
||||
wantAuthInfo: clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "example-client-go-exec-plugin",
|
||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||
Args: []string{"new-arg1", "new-arg2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "3. reset exec args",
|
||||
flags: []string{
|
||||
"--exec-command=example-client-go-exec-plugin",
|
||||
"me",
|
||||
},
|
||||
existingAuthInfo: clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "example-client-go-exec-plugin",
|
||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||
Args: []string{"existing-arg1", "existing-arg2"},
|
||||
},
|
||||
},
|
||||
wantAuthInfo: clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "example-client-go-exec-plugin",
|
||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "4. modify exec env variables",
|
||||
flags: []string{
|
||||
"--exec-command=example-client-go-exec-plugin",
|
||||
"--exec-env=name1=value1000",
|
||||
"--exec-env=name3=value3",
|
||||
"--exec-env=name2-",
|
||||
"--exec-env=non-existing-",
|
||||
"me",
|
||||
},
|
||||
existingAuthInfo: clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "existing-command",
|
||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||
Env: []clientcmdapi.ExecEnvVar{
|
||||
{Name: "name1", Value: "value1"},
|
||||
{Name: "name2", Value: "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantAuthInfo: clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "example-client-go-exec-plugin",
|
||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||
Env: []clientcmdapi.ExecEnvVar{
|
||||
{Name: "name1", Value: "value1000"},
|
||||
{Name: "name3", Value: "value3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "5. modify auth provider arguments",
|
||||
flags: []string{
|
||||
"--auth-provider=new-auth-provider",
|
||||
"--auth-provider-arg=key1=val1000",
|
||||
"--auth-provider-arg=key3=val3",
|
||||
"--auth-provider-arg=key2-",
|
||||
"--auth-provider-arg=non-existing-",
|
||||
"me",
|
||||
},
|
||||
existingAuthInfo: clientcmdapi.AuthInfo{
|
||||
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
||||
Name: "auth-provider",
|
||||
Config: map[string]string{
|
||||
"key1": "val1",
|
||||
"key2": "val2",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantAuthInfo: clientcmdapi.AuthInfo{
|
||||
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
||||
Name: "new-auth-provider",
|
||||
Config: map[string]string{
|
||||
"key1": "val1000",
|
||||
"key3": "val3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buff := new(bytes.Buffer)
|
||||
|
||||
opts := new(createAuthInfoOptions)
|
||||
cmd := newCmdConfigSetAuthInfo(buff, opts)
|
||||
if err := cmd.ParseFlags(tt.flags); err != nil {
|
||||
if !tt.wantParseErr {
|
||||
t.Errorf("case %s: parsing error for flags %q: %v: %s", tt.name, tt.flags, err, buff)
|
||||
}
|
||||
return
|
||||
}
|
||||
if tt.wantParseErr {
|
||||
t.Errorf("case %s: expected parsing error for flags %q: %s", tt.name, tt.flags, buff)
|
||||
return
|
||||
}
|
||||
|
||||
if err := opts.complete(cmd, buff); err != nil {
|
||||
if !tt.wantCompleteErr {
|
||||
t.Errorf("case %s: complete() error for flags %q: %s", tt.name, tt.flags, buff)
|
||||
}
|
||||
return
|
||||
}
|
||||
if tt.wantCompleteErr {
|
||||
t.Errorf("case %s: complete() expected errors for flags %q: %s", tt.name, tt.flags, buff)
|
||||
return
|
||||
}
|
||||
|
||||
if err := opts.validate(); err != nil {
|
||||
if !tt.wantValidateErr {
|
||||
t.Errorf("case %s: flags %q: validate failed: %v", tt.name, tt.flags, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if tt.wantValidateErr {
|
||||
t.Errorf("case %s: flags %q: expected validate to fail", tt.name, tt.flags)
|
||||
return
|
||||
}
|
||||
|
||||
modifiedAuthInfo := opts.modifyAuthInfo(tt.existingAuthInfo)
|
||||
|
||||
if !reflect.DeepEqual(modifiedAuthInfo, tt.wantAuthInfo) {
|
||||
t.Errorf("case %s: flags %q: mis-matched auth info,\nwanted=%#v\ngot= %#v", tt.name, tt.flags, tt.wantAuthInfo, modifiedAuthInfo)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type createAuthInfoTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config
|
||||
|
|
Loading…
Reference in New Issue