diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go index 5c4713286e6..ae662903a78 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go @@ -32,6 +32,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" cliflag "k8s.io/component-base/cli/flag" + "k8s.io/klog/v2" "k8s.io/kubectl/pkg/cmd/annotate" "k8s.io/kubectl/pkg/cmd/apiresources" "k8s.io/kubectl/pkg/cmd/apply" @@ -401,14 +402,19 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { // 2) Adds CommandHeaderRoundTripper as a wrapper around the standard // RoundTripper. CommandHeaderRoundTripper adds X-Headers then delegates // to standard RoundTripper. -// For alpha, these hooks are only updated if the KUBECTL_COMMAND_HEADERS -// environment variable is set. +// For beta, these hooks are updated unless the KUBECTL_COMMAND_HEADERS environment variable +// is set, and the value of the env var is false (or zero). // See SIG CLI KEP 859 for more information: // https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/859-kubectl-headers func addCmdHeaderHooks(cmds *cobra.Command, kubeConfigFlags *genericclioptions.ConfigFlags) { - if _, exists := os.LookupEnv(kubectlCmdHeaders); !exists { - return + // If the feature gate env var is set to "false", then do no add kubectl command headers. + if value, exists := os.LookupEnv(kubectlCmdHeaders); exists { + if value == "false" || value == "0" { + klog.V(5).Infoln("kubectl command headers turned off") + return + } } + klog.V(5).Infoln("kubectl command headers turned on") crt := &genericclioptions.CommandHeaderRoundTripper{} existingPreRunE := cmds.PersistentPreRunE // Add command parsing to the existing persistent pre-run function. diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/cmd_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/cmd_test.go index f2a19179751..d01cf04fc8d 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/cmd_test.go @@ -23,6 +23,7 @@ import ( "reflect" "testing" + "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" cmdutil "k8s.io/kubectl/pkg/cmd/util" ) @@ -158,3 +159,50 @@ func (h *testPluginHandler) Execute(executablePath string, cmdArgs, env []string h.withEnv = env return nil } + +func TestKubectlCommandHeadersHooks(t *testing.T) { + tests := map[string]struct { + envVar string + addsHooks bool + }{ + "empty environment variable; hooks added": { + envVar: "", + addsHooks: true, + }, + "random env var value; hooks added": { + envVar: "foo", + addsHooks: true, + }, + "true env var value; hooks added": { + envVar: "true", + addsHooks: true, + }, + "false env var value; hooks NOT added": { + envVar: "false", + addsHooks: false, + }, + "zero env var value; hooks NOT added": { + envVar: "0", + addsHooks: false, + }, + } + + for name, testCase := range tests { + t.Run(name, func(t *testing.T) { + cmds := &cobra.Command{} + kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() + if kubeConfigFlags.WrapConfigFn != nil { + t.Fatal("expected initial nil WrapConfigFn") + } + os.Setenv(kubectlCmdHeaders, testCase.envVar) + addCmdHeaderHooks(cmds, kubeConfigFlags) + // Valdidate whether the hooks were added. + if testCase.addsHooks && kubeConfigFlags.WrapConfigFn == nil { + t.Error("after adding kubectl command header, expecting non-nil WrapConfigFn") + } + if !testCase.addsHooks && kubeConfigFlags.WrapConfigFn != nil { + t.Error("env var feature gate should have blocked setting WrapConfigFn") + } + }) + } +}