Change:
Now it's possible to configure third-party auth providers using command with option. It's nice to have the same possibility for the recently . Example: To specify the executable path we can run This will generate a new config for the user: Various auxiliary configuration options are also could be . For instance: to define an argument for the executable; to set an environment variable; to specify a desired API version. /kind feature
I'm going to start working on this when the API is promoted to beta.
/sig auth
/assign
Can someone share why you can't do : Is there a way to set map values (e.g. something that is -name: value ; versus something that is -context: )
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
I would like to work on it, if this is still persist.
cc
This still seems useful and is outstanding. any objections to taking it over?
No, it's okay, I'm working on it. I'll post the code this week, sorry for the delay
I think this would help out with generating valid kubectl configurations for our EKS developers. I'm using Pulumi to build EKS environments and if all the flags are available, I think I can generate the necessary entries to assume a role. Thanks! Ex: name: k8s.developer user: exec: apiVersion: args: token -i cluster-eksCluster- -r arn:aws:iam:::role/k8sDevAdminRole- command: aws-iam-authenticator env: null
done :)
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
/priority important-longterm

Discussion:
authProviderArgs         map[string]string authProviderArgsToRemove []string <ins> execCommand     cliflag.StringFlag execAPIVersion  cliflag.StringFlag execArgs        []string execEnv         map[string]string execEnvToRemove []string </ins> } const ( flagAuthProvider    = "auth-provider" flagAuthProviderArg = "auth-provider-arg" <ins> flagExecCommand    = "exec-command" flagExecAPIVersion = "exec-api-version" flagExecArg        = "exec-arg" flagExecEnv        = "exec-env" </ins> ) 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 <del> kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-secret-`) </del> <ins> 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-`) </ins> ) // NewCmdConfigSetAuthInfo returns an Command option instance for 'config set-credentials' sub command

func newCmdConfigSetAuthInfo(out io.Writer, options *createAuthInfoOptions) *cobra.Command { cmd := &cobra.Command{ <del> 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), </del> <ins> 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, ), </ins> DisableFlagsInUseLine: true, Short:                 i18n.T("Sets a user entry in kubeconfig"), Long:                  createAuthInfoLong,

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") <ins> 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") </ins> f := cmd.Flags().VarPF(&options.embedCertData, clientcmd.FlagEmbedCerts, "", "Embed client cert/key for the user entry in kubeconfig") f.NoOptDefVal = "true"

} } <ins> 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...) } } </ins> // If any auth info was set, make sure any other existing auth types are cleared if setToken || setBasic { if !setToken {

o.authProviderArgsToRemove = removePairs } <ins> 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 } </ins> o.name = args[0] return nil }

}, wantCompleteErr: true, }, <ins> { 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"}, }, }, </ins> } for _, tt := range tests {

} } <ins> 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=%#vngot=   %#v", tt.name, tt.flags, tt.wantAuthInfo, modifiedAuthInfo) } }) } } </ins> type createAuthInfoTest struct { description    string config         clientcmdapi.Config