diff --git a/go.mod b/go.mod index 151d1d8dc..31d0a44c9 100644 --- a/go.mod +++ b/go.mod @@ -33,13 +33,13 @@ require ( gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.0.0-20211111072600-2d1f8bde0d9a k8s.io/apimachinery v0.0.0-20211111072039-19377c9f105d - k8s.io/cli-runtime v0.0.0-20211111084148-919d79b4776a + k8s.io/cli-runtime v0.0.0-20211112202458-df9f5587248d k8s.io/client-go v0.0.0-20211111073227-e627be7959e7 k8s.io/component-base v0.0.0-20211111075655-ae7620c245bc - k8s.io/component-helpers v0.0.0-20211111080017-45265b0beef7 + k8s.io/component-helpers v0.0.0-20211112155227-f6f6db02ccda k8s.io/klog/v2 v2.30.0 k8s.io/kube-openapi v0.0.0-20211105084753-ee342a809c29 - k8s.io/metrics v0.0.0-20211111083834-95851fdda3ca + k8s.io/metrics v0.0.0-20211111231549-670a06559996 k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b sigs.k8s.io/kustomize/kustomize/v4 v4.2.0 sigs.k8s.io/kustomize/kyaml v0.11.0 @@ -49,10 +49,10 @@ require ( replace ( k8s.io/api => k8s.io/api v0.0.0-20211111072600-2d1f8bde0d9a k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20211111072039-19377c9f105d - k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20211111084148-919d79b4776a + k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20211112202458-df9f5587248d k8s.io/client-go => k8s.io/client-go v0.0.0-20211111073227-e627be7959e7 k8s.io/code-generator => k8s.io/code-generator v0.0.0-20211111071655-7b5df4132daf k8s.io/component-base => k8s.io/component-base v0.0.0-20211111075655-ae7620c245bc - k8s.io/component-helpers => k8s.io/component-helpers v0.0.0-20211111080017-45265b0beef7 - k8s.io/metrics => k8s.io/metrics v0.0.0-20211111083834-95851fdda3ca + k8s.io/component-helpers => k8s.io/component-helpers v0.0.0-20211112155227-f6f6db02ccda + k8s.io/metrics => k8s.io/metrics v0.0.0-20211111231549-670a06559996 ) diff --git a/go.sum b/go.sum index bffd3e7d6..2f299fa9d 100644 --- a/go.sum +++ b/go.sum @@ -913,15 +913,15 @@ k8s.io/api v0.0.0-20211111072600-2d1f8bde0d9a h1:MbPJiiG2XCMeh1IySIFlmF9Eu2JksfH k8s.io/api v0.0.0-20211111072600-2d1f8bde0d9a/go.mod h1:E/YSMnKP4hG73T35MZ3pw2P2Hy9cFo7KBeUkEPevRjk= k8s.io/apimachinery v0.0.0-20211111072039-19377c9f105d h1:rhNn3FjXMzfNpZLAQSrkW0YQoBEKl+d69o5F2Qw6ONs= k8s.io/apimachinery v0.0.0-20211111072039-19377c9f105d/go.mod h1:/fTTuFZJpMy6M4dc6F6QbWWj88D/Yd/ZdqJMvTIcbkE= -k8s.io/cli-runtime v0.0.0-20211111084148-919d79b4776a h1:4OW7gCPnS6pmzrJ7tT3UwkFLeLevhNwxbO+87KmIYCo= -k8s.io/cli-runtime v0.0.0-20211111084148-919d79b4776a/go.mod h1:av2zR9Yv/we5rQoS0/qTvzhzXt/q4dGOJyTPmdRzSYQ= +k8s.io/cli-runtime v0.0.0-20211112202458-df9f5587248d h1:gR1idnDAzEeIiSay3/tF1yekMynx0qZjfCibytuO7Rk= +k8s.io/cli-runtime v0.0.0-20211112202458-df9f5587248d/go.mod h1:av2zR9Yv/we5rQoS0/qTvzhzXt/q4dGOJyTPmdRzSYQ= k8s.io/client-go v0.0.0-20211111073227-e627be7959e7 h1:M/jv2hr7VfZjKhWEs6oIDJgnolKw9IUMZIJowcQ3T9Q= k8s.io/client-go v0.0.0-20211111073227-e627be7959e7/go.mod h1:vbhSM158IevUkKoqIxagoDYdB9XfHacEyDtKBlug+3g= k8s.io/code-generator v0.0.0-20211111071655-7b5df4132daf/go.mod h1:elIIhU8sF9q1YQFV7vZBy0EXwIqmRQ1K0HjPRxMHdEQ= k8s.io/component-base v0.0.0-20211111075655-ae7620c245bc h1:KzQZPIniySd4WlVRJ2XOo71Dlagq4hwDAlr5SIgMpwg= k8s.io/component-base v0.0.0-20211111075655-ae7620c245bc/go.mod h1:1FNCGI8+JFGNYTnOJidyT0C6gBaT3hZp0P5Nv/9NKr8= -k8s.io/component-helpers v0.0.0-20211111080017-45265b0beef7 h1:ntBTsLR5AsN9aIwaw1xkZwrHDYMe25/BgQg0VbnmqBE= -k8s.io/component-helpers v0.0.0-20211111080017-45265b0beef7/go.mod h1:l09fxADyldPge7DGxb+fkLNcs3yj0OR9w/NVY76ejB8= +k8s.io/component-helpers v0.0.0-20211112155227-f6f6db02ccda h1:xRczbjh1GdYEWIB5fSfhtp0BrPxlgdTU5Kg1Qcis47g= +k8s.io/component-helpers v0.0.0-20211112155227-f6f6db02ccda/go.mod h1:l09fxADyldPge7DGxb+fkLNcs3yj0OR9w/NVY76ejB8= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= @@ -931,8 +931,8 @@ k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211105084753-ee342a809c29 h1:SgxutK76kGA2O/LIjRjoJ2ABggpGJlaJOiLyOdCjEsU= k8s.io/kube-openapi v0.0.0-20211105084753-ee342a809c29/go.mod h1:X90lRFlqk35/w9FG4WIvZqMPfG3WrZGzdlSaL6uh7rc= -k8s.io/metrics v0.0.0-20211111083834-95851fdda3ca h1:geQPH7wjlMCffvVLugzq+xTkWLj+4YdTlt30SYFXVwU= -k8s.io/metrics v0.0.0-20211111083834-95851fdda3ca/go.mod h1:U223BQblZweDx/GR/JnaB2RdAXVNsUY8w4LhxJdaNnY= +k8s.io/metrics v0.0.0-20211111231549-670a06559996 h1:FjoTIPhFePhKnhTxBjpeRr0Z5yu6w6FJxzjuE5vFpyo= +k8s.io/metrics v0.0.0-20211111231549-670a06559996/go.mod h1:U223BQblZweDx/GR/JnaB2RdAXVNsUY8w4LhxJdaNnY= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index fde8882a2..d75eda9e1 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -18,7 +18,6 @@ package cmd import ( "fmt" - "io" "net/http" "os" "os/exec" @@ -82,21 +81,34 @@ import ( const kubectlCmdHeaders = "KUBECTL_COMMAND_HEADERS" +type KubectlOptions struct { + PluginHandler PluginHandler + Arguments []string + ConfigFlags *genericclioptions.ConfigFlags + + genericclioptions.IOStreams +} + // NewDefaultKubectlCommand creates the `kubectl` command with default arguments func NewDefaultKubectlCommand() *cobra.Command { - return NewDefaultKubectlCommandWithArgs(NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args, os.Stdin, os.Stdout, os.Stderr) + return NewDefaultKubectlCommandWithArgs(KubectlOptions{ + PluginHandler: NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), + Arguments: os.Args, + ConfigFlags: genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag(), + IOStreams: genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}, + }) } // NewDefaultKubectlCommandWithArgs creates the `kubectl` command with arguments -func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string, in io.Reader, out, errout io.Writer) *cobra.Command { - cmd := NewKubectlCommand(in, out, errout) +func NewDefaultKubectlCommandWithArgs(o KubectlOptions) *cobra.Command { + cmd := NewKubectlCommand(o) - if pluginHandler == nil { + if o.PluginHandler == nil { return cmd } - if len(args) > 1 { - cmdPathPieces := args[1:] + if len(o.Arguments) > 1 { + cmdPathPieces := o.Arguments[1:] // only look for suitable extension executables if // the specified command does not already exist @@ -116,8 +128,8 @@ func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string case "help", cobra.ShellCompRequestCmd, cobra.ShellCompNoDescRequestCmd: // Don't search for a plugin default: - if err := HandlePluginCommand(pluginHandler, cmdPathPieces); err != nil { - fmt.Fprintf(errout, "Error: %v\n", err) + if err := HandlePluginCommand(o.PluginHandler, cmdPathPieces); err != nil { + fmt.Fprintf(o.IOStreams.ErrOut, "Error: %v\n", err) os.Exit(1) } } @@ -233,8 +245,8 @@ func HandlePluginCommand(pluginHandler PluginHandler, cmdArgs []string) error { } // NewKubectlCommand creates the `kubectl` command and its nested children. -func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { - warningHandler := rest.NewWarningWriter(err, rest.WarningWriterOptions{Deduplicate: true, Color: term.AllowsColorOutput(err)}) +func NewKubectlCommand(o KubectlOptions) *cobra.Command { + warningHandler := rest.NewWarningWriter(o.IOStreams.ErrOut, rest.WarningWriterOptions{Deduplicate: true, Color: term.AllowsColorOutput(o.IOStreams.ErrOut)}) warningsAsErrors := false // Parent command to which all subcommands are added. cmds := &cobra.Command{ @@ -280,7 +292,10 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { flags.BoolVar(&warningsAsErrors, "warnings-as-errors", warningsAsErrors, "Treat warnings received from the server as errors and exit with a non-zero exit code") - kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() + kubeConfigFlags := o.ConfigFlags + if kubeConfigFlags == nil { + kubeConfigFlags = genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() + } kubeConfigFlags.AddFlags(flags) matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags) matchVersionKubeConfigFlags.AddFlags(flags) @@ -296,11 +311,9 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { // the language, instead of just loading from the LANG env. variable. i18n.LoadTranslations("kubectl", nil) - ioStreams := genericclioptions.IOStreams{In: in, Out: out, ErrOut: err} - // Proxy command is incompatible with CommandHeaderRoundTripper, so // clear the WrapConfigFn before running proxy command. - proxyCmd := proxy.NewCmdProxy(f, ioStreams) + proxyCmd := proxy.NewCmdProxy(f, o.IOStreams) proxyCmd.PreRun = func(cmd *cobra.Command, args []string) { kubeConfigFlags.WrapConfigFn = nil } @@ -308,72 +321,72 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { { Message: "Basic Commands (Beginner):", Commands: []*cobra.Command{ - create.NewCmdCreate(f, ioStreams), - expose.NewCmdExposeService(f, ioStreams), - run.NewCmdRun(f, ioStreams), - set.NewCmdSet(f, ioStreams), + create.NewCmdCreate(f, o.IOStreams), + expose.NewCmdExposeService(f, o.IOStreams), + run.NewCmdRun(f, o.IOStreams), + set.NewCmdSet(f, o.IOStreams), }, }, { Message: "Basic Commands (Intermediate):", Commands: []*cobra.Command{ - explain.NewCmdExplain("kubectl", f, ioStreams), - get.NewCmdGet("kubectl", f, ioStreams), - edit.NewCmdEdit(f, ioStreams), - delete.NewCmdDelete(f, ioStreams), + explain.NewCmdExplain("kubectl", f, o.IOStreams), + get.NewCmdGet("kubectl", f, o.IOStreams), + edit.NewCmdEdit(f, o.IOStreams), + delete.NewCmdDelete(f, o.IOStreams), }, }, { Message: "Deploy Commands:", Commands: []*cobra.Command{ - rollout.NewCmdRollout(f, ioStreams), - scale.NewCmdScale(f, ioStreams), - autoscale.NewCmdAutoscale(f, ioStreams), + rollout.NewCmdRollout(f, o.IOStreams), + scale.NewCmdScale(f, o.IOStreams), + autoscale.NewCmdAutoscale(f, o.IOStreams), }, }, { Message: "Cluster Management Commands:", Commands: []*cobra.Command{ - certificates.NewCmdCertificate(f, ioStreams), - clusterinfo.NewCmdClusterInfo(f, ioStreams), - top.NewCmdTop(f, ioStreams), - drain.NewCmdCordon(f, ioStreams), - drain.NewCmdUncordon(f, ioStreams), - drain.NewCmdDrain(f, ioStreams), - taint.NewCmdTaint(f, ioStreams), + certificates.NewCmdCertificate(f, o.IOStreams), + clusterinfo.NewCmdClusterInfo(f, o.IOStreams), + top.NewCmdTop(f, o.IOStreams), + drain.NewCmdCordon(f, o.IOStreams), + drain.NewCmdUncordon(f, o.IOStreams), + drain.NewCmdDrain(f, o.IOStreams), + taint.NewCmdTaint(f, o.IOStreams), }, }, { Message: "Troubleshooting and Debugging Commands:", Commands: []*cobra.Command{ - describe.NewCmdDescribe("kubectl", f, ioStreams), - logs.NewCmdLogs(f, ioStreams), - attach.NewCmdAttach(f, ioStreams), - cmdexec.NewCmdExec(f, ioStreams), - portforward.NewCmdPortForward(f, ioStreams), + describe.NewCmdDescribe("kubectl", f, o.IOStreams), + logs.NewCmdLogs(f, o.IOStreams), + attach.NewCmdAttach(f, o.IOStreams), + cmdexec.NewCmdExec(f, o.IOStreams), + portforward.NewCmdPortForward(f, o.IOStreams), proxyCmd, - cp.NewCmdCp(f, ioStreams), - auth.NewCmdAuth(f, ioStreams), - debug.NewCmdDebug(f, ioStreams), + cp.NewCmdCp(f, o.IOStreams), + auth.NewCmdAuth(f, o.IOStreams), + debug.NewCmdDebug(f, o.IOStreams), }, }, { Message: "Advanced Commands:", Commands: []*cobra.Command{ - diff.NewCmdDiff(f, ioStreams), - apply.NewCmdApply("kubectl", f, ioStreams), - patch.NewCmdPatch(f, ioStreams), - replace.NewCmdReplace(f, ioStreams), - wait.NewCmdWait(f, ioStreams), - kustomize.NewCmdKustomize(ioStreams), + diff.NewCmdDiff(f, o.IOStreams), + apply.NewCmdApply("kubectl", f, o.IOStreams), + patch.NewCmdPatch(f, o.IOStreams), + replace.NewCmdReplace(f, o.IOStreams), + wait.NewCmdWait(f, o.IOStreams), + kustomize.NewCmdKustomize(o.IOStreams), }, }, { Message: "Settings Commands:", Commands: []*cobra.Command{ - label.NewCmdLabel(f, ioStreams), - annotate.NewCmdAnnotate("kubectl", f, ioStreams), - completion.NewCmdCompletion(ioStreams.Out, ""), + label.NewCmdLabel(f, o.IOStreams), + annotate.NewCmdAnnotate("kubectl", f, o.IOStreams), + completion.NewCmdCompletion(o.IOStreams.Out, ""), }, }, } @@ -382,7 +395,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { filters := []string{"options"} // Hide the "alpha" subcommand if there are no alpha commands in this build. - alpha := NewCmdAlpha(f, ioStreams) + alpha := NewCmdAlpha(f, o.IOStreams) if !alpha.HasSubCommands() { filters = append(filters, alpha.Name()) } @@ -393,12 +406,12 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { registerCompletionFuncForGlobalFlags(cmds, f) cmds.AddCommand(alpha) - cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), ioStreams)) - cmds.AddCommand(plugin.NewCmdPlugin(ioStreams)) - cmds.AddCommand(version.NewCmdVersion(f, ioStreams)) - cmds.AddCommand(apiresources.NewCmdAPIVersions(f, ioStreams)) - cmds.AddCommand(apiresources.NewCmdAPIResources(f, ioStreams)) - cmds.AddCommand(options.NewCmdOptions(ioStreams.Out)) + cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), o.IOStreams)) + cmds.AddCommand(plugin.NewCmdPlugin(o.IOStreams)) + cmds.AddCommand(version.NewCmdVersion(f, o.IOStreams)) + cmds.AddCommand(apiresources.NewCmdAPIVersions(f, o.IOStreams)) + cmds.AddCommand(apiresources.NewCmdAPIResources(f, o.IOStreams)) + cmds.AddCommand(options.NewCmdOptions(o.IOStreams.Out)) // Stop warning about normalization of flags. That makes it possible to // add the klog flags later. @@ -433,8 +446,12 @@ func addCmdHeaderHooks(cmds *cobra.Command, kubeConfigFlags *genericclioptions.C crt.ParseCommandHeaders(cmd, args) return existingPreRunE(cmd, args) } + wrapConfigFn := kubeConfigFlags.WrapConfigFn // Wraps CommandHeaderRoundTripper around standard RoundTripper. kubeConfigFlags.WrapConfigFn = func(c *rest.Config) *rest.Config { + if wrapConfigFn != nil { + c = wrapConfigFn(c) + } c.Wrap(func(rt http.RoundTripper) http.RoundTripper { // Must be separate RoundTripper; not "crt" closure. // Fixes: https://github.com/kubernetes/kubectl/issues/1098 diff --git a/pkg/cmd/cmd_test.go b/pkg/cmd/cmd_test.go index f64d84365..acea48130 100644 --- a/pkg/cmd/cmd_test.go +++ b/pkg/cmd/cmd_test.go @@ -25,12 +25,11 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" ) func TestNormalizationFuncGlobalExistence(t *testing.T) { // This test can be safely deleted when we will not support multiple flag formats - root := NewKubectlCommand(os.Stdin, os.Stdout, os.Stderr) + root := NewKubectlCommand(KubectlOptions{IOStreams: genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}}) if root.Parent() != nil { t.Fatal("We expect the root command to be returned") @@ -129,14 +128,9 @@ func TestKubectlCommandHandlesPlugins(t *testing.T) { pluginsHandler := &testPluginHandler{ pluginsDirectory: "plugin/testdata", } - _, in, out, errOut := genericclioptions.NewTestIOStreams() + ioStreams, _, _, _ := genericclioptions.NewTestIOStreams() - cmdutil.BehaviorOnFatal(func(str string, code int) { - errOut.Write([]byte(str)) - }) - - root := NewDefaultKubectlCommandWithArgs(pluginsHandler, test.args, in, out, errOut) - root.SetOut(out) + root := NewDefaultKubectlCommandWithArgs(KubectlOptions{PluginHandler: pluginsHandler, Arguments: test.args, IOStreams: ioStreams}) if err := root.Execute(); err != nil { t.Fatalf("unexpected error: %v", err) }