From b088e65007761bda1aee0b5fb92f79d1aae82c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arda=20G=C3=BC=C3=A7l=C3=BC?= Date: Thu, 23 Nov 2023 15:32:32 +0300 Subject: [PATCH 1/3] kubectl config set-credentials: Add InteractiveMode and ProvideClusterInfo flags `set-credentials` command can also be used to config kubeconfig with credentials exec plugins. However, there is no flag provided by this command to set `InteractiveMode` and `ProvideClusterInfo` fields in credentials exec plugins. Since `InteractiveMode` is required field, this makes the command's output is incomplete and invalid. This PR introduces 2 new flags in set-credentials command to let users can configure these 2 fields in credentials exec plugins, especially the one `InteractiveMode` which is required. --- .../kubectl/pkg/cmd/config/config_test.go | 85 +++++++++++++++++++ .../kubectl/pkg/cmd/config/set_credentials.go | 42 +++++++-- 2 files changed, 118 insertions(+), 9 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/config_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/config_test.go index 611b85e4dad..28603f261e0 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/config_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/config_test.go @@ -279,6 +279,91 @@ func TestEmbedClientCert(t *testing.T) { test.run(t) } +func TestExecPlugin(t *testing.T) { + fakeCertFile, _ := os.CreateTemp(os.TempDir(), "") + defer utiltesting.CloseAndRemove(t, fakeCertFile) + fakeData := []byte("fake-data") + err := os.WriteFile(fakeCertFile.Name(), fakeData, 0600) + if err != nil { + t.Errorf("unexpected error %v", err) + } + expectedConfig := newRedFederalCowHammerConfig() + authInfo := clientcmdapi.NewAuthInfo() + authInfo.Exec = &clientcmdapi.ExecConfig{ + Command: "example-client-go-exec-plugin", + Args: []string{"arg1", "arg2"}, + Env: []clientcmdapi.ExecEnvVar{ + { + Name: "FOO", + Value: "bar", + }, + }, + APIVersion: "client.authentication.k8s.io/v1", + ProvideClusterInfo: false, + InteractiveMode: "Never", + } + expectedConfig.AuthInfos["cred-exec-user"] = authInfo + + test := configCommandTest{ + args: []string{ + "set-credentials", + "cred-exec-user", + "--exec-api-version=client.authentication.k8s.io/v1", + "--exec-command=example-client-go-exec-plugin", + "--exec-arg=arg1,arg2", + "--exec-env=FOO=bar", + "--exec-interactive-mode=Never", + }, + startingConfig: newRedFederalCowHammerConfig(), + expectedConfig: expectedConfig, + } + + test.run(t) +} + +func TestExecPluginWithProveClusterInfo(t *testing.T) { + fakeCertFile, _ := os.CreateTemp(os.TempDir(), "") + defer utiltesting.CloseAndRemove(t, fakeCertFile) + fakeData := []byte("fake-data") + err := os.WriteFile(fakeCertFile.Name(), fakeData, 0600) + if err != nil { + t.Errorf("unexpected error %v", err) + } + expectedConfig := newRedFederalCowHammerConfig() + authInfo := clientcmdapi.NewAuthInfo() + authInfo.Exec = &clientcmdapi.ExecConfig{ + Command: "example-client-go-exec-plugin", + Args: []string{"arg1", "arg2"}, + Env: []clientcmdapi.ExecEnvVar{ + { + Name: "FOO", + Value: "bar", + }, + }, + APIVersion: "client.authentication.k8s.io/v1", + ProvideClusterInfo: true, + InteractiveMode: "Always", + } + expectedConfig.AuthInfos["cred-exec-user"] = authInfo + + test := configCommandTest{ + args: []string{ + "set-credentials", + "cred-exec-user", + "--exec-api-version=client.authentication.k8s.io/v1", + "--exec-command=example-client-go-exec-plugin", + "--exec-arg=arg1,arg2", + "--exec-env=FOO=bar", + "--exec-interactive-mode=Always", + "--exec-provide-cluster-info=true", + }, + startingConfig: newRedFederalCowHammerConfig(), + expectedConfig: expectedConfig, + } + + test.run(t) +} + func TestEmbedClientKey(t *testing.T) { fakeKeyFile, _ := os.CreateTemp(os.TempDir(), "") defer utiltesting.CloseAndRemove(t, fakeKeyFile) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go index 5bfd1186b5d..e982c518a9a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go @@ -48,21 +48,25 @@ type setCredentialsOptions struct { authProviderArgs map[string]string authProviderArgsToRemove []string - execCommand cliflag.StringFlag - execAPIVersion cliflag.StringFlag - execArgs []string - execEnv map[string]string - execEnvToRemove []string + execCommand cliflag.StringFlag + execAPIVersion cliflag.StringFlag + execInteractiveMode cliflag.StringFlag + execProvideClusterInfo bool + 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" + flagExecCommand = "exec-command" + flagExecAPIVersion = "exec-api-version" + flagExecArg = "exec-arg" + flagExecEnv = "exec-env" + flagExecInteractiveMode = "exec-interactive-mode" + flagExecProvideClusterInfo = "exec-provide-cluster-info" ) var ( @@ -105,6 +109,9 @@ var ( # 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/v1beta1 + # Enable new exec auth plugin for the "cluster-admin" entry with interactive mode + kubectl config set-credentials cluster-admin --exec-command=/path/to/the/executable --exec-api-version=client.authentication.k8s.io/v1beta1 --exec-interactive-mode=Never + # Define new exec auth plugin arguments for the "cluster-admin" entry kubectl config set-credentials cluster-admin --exec-arg=arg1 --exec-arg=arg2 @@ -179,6 +186,8 @@ func newCmdConfigSetCredentials(out io.Writer, options *setCredentialsOptions) * 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().Var(&options.execInteractiveMode, flagExecInteractiveMode, "InteractiveMode of the exec credentials plugin for the user entry in kubeconfig") + cmd.Flags().BoolVar(&options.execProvideClusterInfo, flagExecProvideClusterInfo, options.execProvideClusterInfo, "ProvideClusterInfo of the exec credentials 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") @@ -293,6 +302,8 @@ func (o *setCredentialsOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.Aut // explicitly reset exec arguments modifiedAuthInfo.Exec.Args = nil } + + modifiedAuthInfo.Exec.ProvideClusterInfo = o.execProvideClusterInfo } // modify next values only if Exec exists, ignore these changes otherwise @@ -306,6 +317,10 @@ func (o *setCredentialsOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.Aut modifiedAuthInfo.Exec.Args = o.execArgs } + if o.execInteractiveMode.Provided() { + modifiedAuthInfo.Exec.InteractiveMode = clientcmdapi.ExecInteractiveMode(o.execInteractiveMode.Value()) + } + // iterate over the existing exec env values and remove the specified if o.execEnvToRemove != nil { newExecEnv := []clientcmdapi.ExecEnvVar{} @@ -437,5 +452,14 @@ func (o setCredentialsOptions) validate() error { } } + if o.execInteractiveMode.Provided() { + interactiveMode := o.execInteractiveMode.Value() + if interactiveMode != string(clientcmdapi.IfAvailableExecInteractiveMode) && + interactiveMode != string(clientcmdapi.AlwaysExecInteractiveMode) && + interactiveMode != string(clientcmdapi.NeverExecInteractiveMode) { + return fmt.Errorf("invalid interactive mode type, can be only IfAvailable, Never, Always") + } + } + return nil } From 5e5f8b1dd29d1c4f983620dd818b1bc1f00d7319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arda=20G=C3=BC=C3=A7l=C3=BC?= Date: Tue, 28 Nov 2023 08:54:54 +0300 Subject: [PATCH 2/3] move provideclusterinfo assignment into correct place --- staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go index e982c518a9a..adc1cee3eca 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go @@ -302,8 +302,6 @@ func (o *setCredentialsOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.Aut // explicitly reset exec arguments modifiedAuthInfo.Exec.Args = nil } - - modifiedAuthInfo.Exec.ProvideClusterInfo = o.execProvideClusterInfo } // modify next values only if Exec exists, ignore these changes otherwise @@ -321,6 +319,8 @@ func (o *setCredentialsOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.Aut modifiedAuthInfo.Exec.InteractiveMode = clientcmdapi.ExecInteractiveMode(o.execInteractiveMode.Value()) } + modifiedAuthInfo.Exec.ProvideClusterInfo = o.execProvideClusterInfo + // iterate over the existing exec env values and remove the specified if o.execEnvToRemove != nil { newExecEnv := []clientcmdapi.ExecEnvVar{} From 38c67af789bc62b88275e6a12facfe3f7881208c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arda=20G=C3=BC=C3=A7l=C3=BC?= Date: Wed, 29 Nov 2023 08:58:59 +0300 Subject: [PATCH 3/3] Use tristate for provideclusterinfo to manage provided flag --- .../src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go index adc1cee3eca..2bc6896b16d 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/set_credentials.go @@ -51,7 +51,7 @@ type setCredentialsOptions struct { execCommand cliflag.StringFlag execAPIVersion cliflag.StringFlag execInteractiveMode cliflag.StringFlag - execProvideClusterInfo bool + execProvideClusterInfo cliflag.Tristate execArgs []string execEnv map[string]string execEnvToRemove []string @@ -187,7 +187,8 @@ func newCmdConfigSetCredentials(out io.Writer, options *setCredentialsOptions) * 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().Var(&options.execInteractiveMode, flagExecInteractiveMode, "InteractiveMode of the exec credentials plugin for the user entry in kubeconfig") - cmd.Flags().BoolVar(&options.execProvideClusterInfo, flagExecProvideClusterInfo, options.execProvideClusterInfo, "ProvideClusterInfo of the exec credentials plugin for the user entry in kubeconfig") + flagClusterInfo := cmd.Flags().VarPF(&options.execProvideClusterInfo, flagExecProvideClusterInfo, "", "ProvideClusterInfo of the exec credentials plugin for the user entry in kubeconfig") + flagClusterInfo.NoOptDefVal = "true" 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") @@ -319,7 +320,9 @@ func (o *setCredentialsOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.Aut modifiedAuthInfo.Exec.InteractiveMode = clientcmdapi.ExecInteractiveMode(o.execInteractiveMode.Value()) } - modifiedAuthInfo.Exec.ProvideClusterInfo = o.execProvideClusterInfo + if o.execProvideClusterInfo.Provided() { + modifiedAuthInfo.Exec.ProvideClusterInfo = o.execProvideClusterInfo.Value() + } // iterate over the existing exec env values and remove the specified if o.execEnvToRemove != nil {