diff --git a/cmd/clicheck/check_cli_conventions.go b/cmd/clicheck/check_cli_conventions.go index e24d6930a12..5c43c363a20 100644 --- a/cmd/clicheck/check_cli_conventions.go +++ b/cmd/clicheck/check_cli_conventions.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" + "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/kubectl/pkg/cmd" cmdsanity "k8s.io/kubectl/pkg/cmd/util/sanity" ) @@ -28,7 +29,7 @@ import ( func main() { var errorCount int - kubectl := cmd.NewKubectlCommand(os.Stdin, ioutil.Discard, ioutil.Discard) + kubectl := cmd.NewKubectlCommand(cmd.KubectlOptions{IOStreams: genericclioptions.IOStreams{In: os.Stdin, Out: ioutil.Discard, ErrOut: ioutil.Discard}}) errors := cmdsanity.RunCmdChecks(kubectl, cmdsanity.AllCmdChecks, []string{}) for _, err := range errors { errorCount++ diff --git a/cmd/gendocs/gen_kubectl_docs.go b/cmd/gendocs/gen_kubectl_docs.go index 6effecf714b..9eb38245995 100644 --- a/cmd/gendocs/gen_kubectl_docs.go +++ b/cmd/gendocs/gen_kubectl_docs.go @@ -23,6 +23,7 @@ import ( "os" "github.com/spf13/cobra/doc" + "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/kubectl/pkg/cmd" "k8s.io/kubernetes/cmd/genutils" ) @@ -46,6 +47,6 @@ func main() { // Set environment variables used by kubectl so the output is consistent, // regardless of where we run. os.Setenv("HOME", "/home/username") - kubectl := cmd.NewKubectlCommand(bytes.NewReader(nil), ioutil.Discard, ioutil.Discard) + kubectl := cmd.NewKubectlCommand(cmd.KubectlOptions{IOStreams: genericclioptions.IOStreams{In: bytes.NewReader(nil), Out: ioutil.Discard, ErrOut: ioutil.Discard}}) doc.GenMarkdownTree(kubectl, outDir) } diff --git a/cmd/genman/gen_kube_man.go b/cmd/genman/gen_kube_man.go index 5034bc978b5..6373f4f3cb4 100644 --- a/cmd/genman/gen_kube_man.go +++ b/cmd/genman/gen_kube_man.go @@ -26,6 +26,7 @@ import ( mangen "github.com/cpuguy83/go-md2man/v2/md2man" "github.com/spf13/cobra" "github.com/spf13/pflag" + "k8s.io/cli-runtime/pkg/genericclioptions" kubectlcmd "k8s.io/kubectl/pkg/cmd" "k8s.io/kubernetes/cmd/genutils" apiservapp "k8s.io/kubernetes/cmd/kube-apiserver/app" @@ -96,7 +97,7 @@ func main() { } case "kubectl": // generate manpage for kubectl - kubectl := kubectlcmd.NewKubectlCommand(bytes.NewReader(nil), ioutil.Discard, ioutil.Discard) + kubectl := kubectlcmd.NewKubectlCommand(kubectlcmd.KubectlOptions{IOStreams: genericclioptions.IOStreams{In: bytes.NewReader(nil), Out: ioutil.Discard, ErrOut: ioutil.Discard}}) genMarkdown(kubectl, "", outDir) for _, c := range kubectl.Commands() { genMarkdown(c, "kubectl", outDir) diff --git a/cmd/genyaml/gen_kubectl_yaml.go b/cmd/genyaml/gen_kubectl_yaml.go index 4f3c2159cb1..30a584bae0c 100644 --- a/cmd/genyaml/gen_kubectl_yaml.go +++ b/cmd/genyaml/gen_kubectl_yaml.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" "gopkg.in/yaml.v2" + "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/kubectl/pkg/cmd" "k8s.io/kubernetes/cmd/genutils" ) @@ -65,7 +66,7 @@ func main() { // Set environment variables used by kubectl so the output is consistent, // regardless of where we run. os.Setenv("HOME", "/home/username") - kubectl := cmd.NewKubectlCommand(bytes.NewReader(nil), ioutil.Discard, ioutil.Discard) + kubectl := cmd.NewKubectlCommand(cmd.KubectlOptions{IOStreams: genericclioptions.IOStreams{In: bytes.NewReader(nil), Out: ioutil.Discard, ErrOut: ioutil.Discard}}) genYaml(kubectl, "", outDir) for _, c := range kubectl.Commands() { genYaml(c, "kubectl", outDir) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go b/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go index 2b857e0fded..d75eda9e18e 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd.go +++ b/staging/src/k8s.io/kubectl/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. 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 f64d84365b1..acea4813067 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/cmd_test.go +++ b/staging/src/k8s.io/kubectl/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) }