...

Source file src/k8s.io/kubectl/pkg/cmd/cmd.go

Documentation: k8s.io/kubectl/pkg/cmd

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cmd
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"os"
    23  	"os/exec"
    24  	"path/filepath"
    25  	"runtime"
    26  	"strings"
    27  	"syscall"
    28  
    29  	"github.com/spf13/cobra"
    30  
    31  	"k8s.io/cli-runtime/pkg/genericiooptions"
    32  	"k8s.io/client-go/rest"
    33  	"k8s.io/client-go/tools/clientcmd"
    34  	cliflag "k8s.io/component-base/cli/flag"
    35  	"k8s.io/klog/v2"
    36  	"k8s.io/kubectl/pkg/cmd/annotate"
    37  	"k8s.io/kubectl/pkg/cmd/apiresources"
    38  	"k8s.io/kubectl/pkg/cmd/apply"
    39  	"k8s.io/kubectl/pkg/cmd/attach"
    40  	"k8s.io/kubectl/pkg/cmd/auth"
    41  	"k8s.io/kubectl/pkg/cmd/autoscale"
    42  	"k8s.io/kubectl/pkg/cmd/certificates"
    43  	"k8s.io/kubectl/pkg/cmd/clusterinfo"
    44  	"k8s.io/kubectl/pkg/cmd/completion"
    45  	cmdconfig "k8s.io/kubectl/pkg/cmd/config"
    46  	"k8s.io/kubectl/pkg/cmd/cp"
    47  	"k8s.io/kubectl/pkg/cmd/create"
    48  	"k8s.io/kubectl/pkg/cmd/debug"
    49  	"k8s.io/kubectl/pkg/cmd/delete"
    50  	"k8s.io/kubectl/pkg/cmd/describe"
    51  	"k8s.io/kubectl/pkg/cmd/diff"
    52  	"k8s.io/kubectl/pkg/cmd/drain"
    53  	"k8s.io/kubectl/pkg/cmd/edit"
    54  	"k8s.io/kubectl/pkg/cmd/events"
    55  	cmdexec "k8s.io/kubectl/pkg/cmd/exec"
    56  	"k8s.io/kubectl/pkg/cmd/explain"
    57  	"k8s.io/kubectl/pkg/cmd/expose"
    58  	"k8s.io/kubectl/pkg/cmd/get"
    59  	"k8s.io/kubectl/pkg/cmd/label"
    60  	"k8s.io/kubectl/pkg/cmd/logs"
    61  	"k8s.io/kubectl/pkg/cmd/options"
    62  	"k8s.io/kubectl/pkg/cmd/patch"
    63  	"k8s.io/kubectl/pkg/cmd/plugin"
    64  	"k8s.io/kubectl/pkg/cmd/portforward"
    65  	"k8s.io/kubectl/pkg/cmd/proxy"
    66  	"k8s.io/kubectl/pkg/cmd/replace"
    67  	"k8s.io/kubectl/pkg/cmd/rollout"
    68  	"k8s.io/kubectl/pkg/cmd/run"
    69  	"k8s.io/kubectl/pkg/cmd/scale"
    70  	"k8s.io/kubectl/pkg/cmd/set"
    71  	"k8s.io/kubectl/pkg/cmd/taint"
    72  	"k8s.io/kubectl/pkg/cmd/top"
    73  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    74  	"k8s.io/kubectl/pkg/cmd/version"
    75  	"k8s.io/kubectl/pkg/cmd/wait"
    76  	utilcomp "k8s.io/kubectl/pkg/util/completion"
    77  	"k8s.io/kubectl/pkg/util/i18n"
    78  	"k8s.io/kubectl/pkg/util/templates"
    79  	"k8s.io/kubectl/pkg/util/term"
    80  
    81  	"k8s.io/cli-runtime/pkg/genericclioptions"
    82  	"k8s.io/kubectl/pkg/cmd/kustomize"
    83  )
    84  
    85  const kubectlCmdHeaders = "KUBECTL_COMMAND_HEADERS"
    86  
    87  type KubectlOptions struct {
    88  	PluginHandler PluginHandler
    89  	Arguments     []string
    90  	ConfigFlags   *genericclioptions.ConfigFlags
    91  
    92  	genericiooptions.IOStreams
    93  }
    94  
    95  func defaultConfigFlags() *genericclioptions.ConfigFlags {
    96  	return genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag().WithDiscoveryBurst(300).WithDiscoveryQPS(50.0)
    97  }
    98  
    99  // NewDefaultKubectlCommand creates the `kubectl` command with default arguments
   100  func NewDefaultKubectlCommand() *cobra.Command {
   101  	ioStreams := genericiooptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
   102  	return NewDefaultKubectlCommandWithArgs(KubectlOptions{
   103  		PluginHandler: NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes),
   104  		Arguments:     os.Args,
   105  		ConfigFlags:   defaultConfigFlags().WithWarningPrinter(ioStreams),
   106  		IOStreams:     ioStreams,
   107  	})
   108  }
   109  
   110  // NewDefaultKubectlCommandWithArgs creates the `kubectl` command with arguments
   111  func NewDefaultKubectlCommandWithArgs(o KubectlOptions) *cobra.Command {
   112  	cmd := NewKubectlCommand(o)
   113  
   114  	if o.PluginHandler == nil {
   115  		return cmd
   116  	}
   117  
   118  	if len(o.Arguments) > 1 {
   119  		cmdPathPieces := o.Arguments[1:]
   120  
   121  		// only look for suitable extension executables if
   122  		// the specified command does not already exist
   123  		if foundCmd, foundArgs, err := cmd.Find(cmdPathPieces); err != nil {
   124  			// Also check the commands that will be added by Cobra.
   125  			// These commands are only added once rootCmd.Execute() is called, so we
   126  			// need to check them explicitly here.
   127  			var cmdName string // first "non-flag" arguments
   128  			for _, arg := range cmdPathPieces {
   129  				if !strings.HasPrefix(arg, "-") {
   130  					cmdName = arg
   131  					break
   132  				}
   133  			}
   134  
   135  			switch cmdName {
   136  			case "help", cobra.ShellCompRequestCmd, cobra.ShellCompNoDescRequestCmd:
   137  				// Don't search for a plugin
   138  			default:
   139  				if err := HandlePluginCommand(o.PluginHandler, cmdPathPieces, false); err != nil {
   140  					fmt.Fprintf(o.IOStreams.ErrOut, "Error: %v\n", err)
   141  					os.Exit(1)
   142  				}
   143  			}
   144  		} else if err == nil {
   145  			if !cmdutil.CmdPluginAsSubcommand.IsDisabled() {
   146  				// Command exists(e.g. kubectl create), but it is not certain that
   147  				// subcommand also exists (e.g. kubectl create networkpolicy)
   148  				// we also have to eliminate kubectl create -f
   149  				if IsSubcommandPluginAllowed(foundCmd.Name()) && len(foundArgs) >= 1 && !strings.HasPrefix(foundArgs[0], "-") {
   150  					subcommand := foundArgs[0]
   151  					builtinSubcmdExist := false
   152  					for _, subcmd := range foundCmd.Commands() {
   153  						if subcmd.Name() == subcommand {
   154  							builtinSubcmdExist = true
   155  							break
   156  						}
   157  					}
   158  
   159  					if !builtinSubcmdExist {
   160  						if err := HandlePluginCommand(o.PluginHandler, cmdPathPieces, true); err != nil {
   161  							fmt.Fprintf(o.IOStreams.ErrOut, "Error: %v\n", err)
   162  							os.Exit(1)
   163  						}
   164  					}
   165  				}
   166  			}
   167  		}
   168  	}
   169  
   170  	return cmd
   171  }
   172  
   173  // IsSubcommandPluginAllowed returns the given command is allowed
   174  // to use plugin as subcommand if the subcommand does not exist as builtin.
   175  func IsSubcommandPluginAllowed(foundCmd string) bool {
   176  	allowedCmds := map[string]struct{}{"create": {}}
   177  	_, ok := allowedCmds[foundCmd]
   178  	return ok
   179  }
   180  
   181  // PluginHandler is capable of parsing command line arguments
   182  // and performing executable filename lookups to search
   183  // for valid plugin files, and execute found plugins.
   184  type PluginHandler interface {
   185  	// exists at the given filename, or a boolean false.
   186  	// Lookup will iterate over a list of given prefixes
   187  	// in order to recognize valid plugin filenames.
   188  	// The first filepath to match a prefix is returned.
   189  	Lookup(filename string) (string, bool)
   190  	// Execute receives an executable's filepath, a slice
   191  	// of arguments, and a slice of environment variables
   192  	// to relay to the executable.
   193  	Execute(executablePath string, cmdArgs, environment []string) error
   194  }
   195  
   196  // DefaultPluginHandler implements PluginHandler
   197  type DefaultPluginHandler struct {
   198  	ValidPrefixes []string
   199  }
   200  
   201  // NewDefaultPluginHandler instantiates the DefaultPluginHandler with a list of
   202  // given filename prefixes used to identify valid plugin filenames.
   203  func NewDefaultPluginHandler(validPrefixes []string) *DefaultPluginHandler {
   204  	return &DefaultPluginHandler{
   205  		ValidPrefixes: validPrefixes,
   206  	}
   207  }
   208  
   209  // Lookup implements PluginHandler
   210  func (h *DefaultPluginHandler) Lookup(filename string) (string, bool) {
   211  	for _, prefix := range h.ValidPrefixes {
   212  		path, err := exec.LookPath(fmt.Sprintf("%s-%s", prefix, filename))
   213  		if shouldSkipOnLookPathErr(err) || len(path) == 0 {
   214  			continue
   215  		}
   216  		return path, true
   217  	}
   218  	return "", false
   219  }
   220  
   221  func Command(name string, arg ...string) *exec.Cmd {
   222  	cmd := &exec.Cmd{
   223  		Path: name,
   224  		Args: append([]string{name}, arg...),
   225  	}
   226  	if filepath.Base(name) == name {
   227  		lp, err := exec.LookPath(name)
   228  		if lp != "" && !shouldSkipOnLookPathErr(err) {
   229  			// Update cmd.Path even if err is non-nil.
   230  			// If err is ErrDot (especially on Windows), lp may include a resolved
   231  			// extension (like .exe or .bat) that should be preserved.
   232  			cmd.Path = lp
   233  		}
   234  	}
   235  	return cmd
   236  }
   237  
   238  // Execute implements PluginHandler
   239  func (h *DefaultPluginHandler) Execute(executablePath string, cmdArgs, environment []string) error {
   240  
   241  	// Windows does not support exec syscall.
   242  	if runtime.GOOS == "windows" {
   243  		cmd := Command(executablePath, cmdArgs...)
   244  		cmd.Stdout = os.Stdout
   245  		cmd.Stderr = os.Stderr
   246  		cmd.Stdin = os.Stdin
   247  		cmd.Env = environment
   248  		err := cmd.Run()
   249  		if err == nil {
   250  			os.Exit(0)
   251  		}
   252  		return err
   253  	}
   254  
   255  	// invoke cmd binary relaying the environment and args given
   256  	// append executablePath to cmdArgs, as execve will make first argument the "binary name".
   257  	return syscall.Exec(executablePath, append([]string{executablePath}, cmdArgs...), environment)
   258  }
   259  
   260  // HandlePluginCommand receives a pluginHandler and command-line arguments and attempts to find
   261  // a plugin executable on the PATH that satisfies the given arguments.
   262  func HandlePluginCommand(pluginHandler PluginHandler, cmdArgs []string, exactMatch bool) error {
   263  	var remainingArgs []string // all "non-flag" arguments
   264  	for _, arg := range cmdArgs {
   265  		if strings.HasPrefix(arg, "-") {
   266  			break
   267  		}
   268  		remainingArgs = append(remainingArgs, strings.Replace(arg, "-", "_", -1))
   269  	}
   270  
   271  	if len(remainingArgs) == 0 {
   272  		// the length of cmdArgs is at least 1
   273  		return fmt.Errorf("flags cannot be placed before plugin name: %s", cmdArgs[0])
   274  	}
   275  
   276  	foundBinaryPath := ""
   277  
   278  	// attempt to find binary, starting at longest possible name with given cmdArgs
   279  	for len(remainingArgs) > 0 {
   280  		path, found := pluginHandler.Lookup(strings.Join(remainingArgs, "-"))
   281  		if !found {
   282  			if exactMatch {
   283  				// if exactMatch is true, we shouldn't continue searching with shorter names.
   284  				// this is especially for not searching kubectl-create plugin
   285  				// when kubectl-create-foo plugin is not found.
   286  				break
   287  			}
   288  			remainingArgs = remainingArgs[:len(remainingArgs)-1]
   289  			continue
   290  		}
   291  
   292  		foundBinaryPath = path
   293  		break
   294  	}
   295  
   296  	if len(foundBinaryPath) == 0 {
   297  		return nil
   298  	}
   299  
   300  	// invoke cmd binary relaying the current environment and args given
   301  	if err := pluginHandler.Execute(foundBinaryPath, cmdArgs[len(remainingArgs):], os.Environ()); err != nil {
   302  		return err
   303  	}
   304  
   305  	return nil
   306  }
   307  
   308  // NewKubectlCommand creates the `kubectl` command and its nested children.
   309  func NewKubectlCommand(o KubectlOptions) *cobra.Command {
   310  	warningHandler := rest.NewWarningWriter(o.IOStreams.ErrOut, rest.WarningWriterOptions{Deduplicate: true, Color: term.AllowsColorOutput(o.IOStreams.ErrOut)})
   311  	warningsAsErrors := false
   312  	// Parent command to which all subcommands are added.
   313  	cmds := &cobra.Command{
   314  		Use:   "kubectl",
   315  		Short: i18n.T("kubectl controls the Kubernetes cluster manager"),
   316  		Long: templates.LongDesc(`
   317        kubectl controls the Kubernetes cluster manager.
   318  
   319        Find more information at:
   320              https://kubernetes.io/docs/reference/kubectl/`),
   321  		Run: runHelp,
   322  		// Hook before and after Run initialize and write profiles to disk,
   323  		// respectively.
   324  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   325  			rest.SetDefaultWarningHandler(warningHandler)
   326  
   327  			if cmd.Name() == cobra.ShellCompRequestCmd {
   328  				// This is the __complete or __completeNoDesc command which
   329  				// indicates shell completion has been requested.
   330  				plugin.SetupPluginCompletion(cmd, args)
   331  			}
   332  
   333  			return initProfiling()
   334  		},
   335  		PersistentPostRunE: func(*cobra.Command, []string) error {
   336  			if err := flushProfiling(); err != nil {
   337  				return err
   338  			}
   339  			if warningsAsErrors {
   340  				count := warningHandler.WarningCount()
   341  				switch count {
   342  				case 0:
   343  					// no warnings
   344  				case 1:
   345  					return fmt.Errorf("%d warning received", count)
   346  				default:
   347  					return fmt.Errorf("%d warnings received", count)
   348  				}
   349  			}
   350  			return nil
   351  		},
   352  	}
   353  	// From this point and forward we get warnings on flags that contain "_" separators
   354  	// when adding them with hyphen instead of the original name.
   355  	cmds.SetGlobalNormalizationFunc(cliflag.WarnWordSepNormalizeFunc)
   356  
   357  	flags := cmds.PersistentFlags()
   358  
   359  	addProfilingFlags(flags)
   360  
   361  	flags.BoolVar(&warningsAsErrors, "warnings-as-errors", warningsAsErrors, "Treat warnings received from the server as errors and exit with a non-zero exit code")
   362  
   363  	kubeConfigFlags := o.ConfigFlags
   364  	if kubeConfigFlags == nil {
   365  		kubeConfigFlags = defaultConfigFlags().WithWarningPrinter(o.IOStreams)
   366  	}
   367  	kubeConfigFlags.AddFlags(flags)
   368  	matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
   369  	matchVersionKubeConfigFlags.AddFlags(flags)
   370  	// Updates hooks to add kubectl command headers: SIG CLI KEP 859.
   371  	addCmdHeaderHooks(cmds, kubeConfigFlags)
   372  
   373  	f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
   374  
   375  	// Proxy command is incompatible with CommandHeaderRoundTripper, so
   376  	// clear the WrapConfigFn before running proxy command.
   377  	proxyCmd := proxy.NewCmdProxy(f, o.IOStreams)
   378  	proxyCmd.PreRun = func(cmd *cobra.Command, args []string) {
   379  		kubeConfigFlags.WrapConfigFn = nil
   380  	}
   381  
   382  	// Avoid import cycle by setting ValidArgsFunction here instead of in NewCmdGet()
   383  	getCmd := get.NewCmdGet("kubectl", f, o.IOStreams)
   384  	getCmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f)
   385  
   386  	groups := templates.CommandGroups{
   387  		{
   388  			Message: "Basic Commands (Beginner):",
   389  			Commands: []*cobra.Command{
   390  				create.NewCmdCreate(f, o.IOStreams),
   391  				expose.NewCmdExposeService(f, o.IOStreams),
   392  				run.NewCmdRun(f, o.IOStreams),
   393  				set.NewCmdSet(f, o.IOStreams),
   394  			},
   395  		},
   396  		{
   397  			Message: "Basic Commands (Intermediate):",
   398  			Commands: []*cobra.Command{
   399  				explain.NewCmdExplain("kubectl", f, o.IOStreams),
   400  				getCmd,
   401  				edit.NewCmdEdit(f, o.IOStreams),
   402  				delete.NewCmdDelete(f, o.IOStreams),
   403  			},
   404  		},
   405  		{
   406  			Message: "Deploy Commands:",
   407  			Commands: []*cobra.Command{
   408  				rollout.NewCmdRollout(f, o.IOStreams),
   409  				scale.NewCmdScale(f, o.IOStreams),
   410  				autoscale.NewCmdAutoscale(f, o.IOStreams),
   411  			},
   412  		},
   413  		{
   414  			Message: "Cluster Management Commands:",
   415  			Commands: []*cobra.Command{
   416  				certificates.NewCmdCertificate(f, o.IOStreams),
   417  				clusterinfo.NewCmdClusterInfo(f, o.IOStreams),
   418  				top.NewCmdTop(f, o.IOStreams),
   419  				drain.NewCmdCordon(f, o.IOStreams),
   420  				drain.NewCmdUncordon(f, o.IOStreams),
   421  				drain.NewCmdDrain(f, o.IOStreams),
   422  				taint.NewCmdTaint(f, o.IOStreams),
   423  			},
   424  		},
   425  		{
   426  			Message: "Troubleshooting and Debugging Commands:",
   427  			Commands: []*cobra.Command{
   428  				describe.NewCmdDescribe("kubectl", f, o.IOStreams),
   429  				logs.NewCmdLogs(f, o.IOStreams),
   430  				attach.NewCmdAttach(f, o.IOStreams),
   431  				cmdexec.NewCmdExec(f, o.IOStreams),
   432  				portforward.NewCmdPortForward(f, o.IOStreams),
   433  				proxyCmd,
   434  				cp.NewCmdCp(f, o.IOStreams),
   435  				auth.NewCmdAuth(f, o.IOStreams),
   436  				debug.NewCmdDebug(f, o.IOStreams),
   437  				events.NewCmdEvents(f, o.IOStreams),
   438  			},
   439  		},
   440  		{
   441  			Message: "Advanced Commands:",
   442  			Commands: []*cobra.Command{
   443  				diff.NewCmdDiff(f, o.IOStreams),
   444  				apply.NewCmdApply("kubectl", f, o.IOStreams),
   445  				patch.NewCmdPatch(f, o.IOStreams),
   446  				replace.NewCmdReplace(f, o.IOStreams),
   447  				wait.NewCmdWait(f, o.IOStreams),
   448  				kustomize.NewCmdKustomize(o.IOStreams),
   449  			},
   450  		},
   451  		{
   452  			Message: "Settings Commands:",
   453  			Commands: []*cobra.Command{
   454  				label.NewCmdLabel(f, o.IOStreams),
   455  				annotate.NewCmdAnnotate("kubectl", f, o.IOStreams),
   456  				completion.NewCmdCompletion(o.IOStreams.Out, ""),
   457  			},
   458  		},
   459  	}
   460  	groups.Add(cmds)
   461  
   462  	filters := []string{"options"}
   463  
   464  	// Hide the "alpha" subcommand if there are no alpha commands in this build.
   465  	alpha := NewCmdAlpha(f, o.IOStreams)
   466  	if !alpha.HasSubCommands() {
   467  		filters = append(filters, alpha.Name())
   468  	}
   469  
   470  	// Add plugin command group to the list of command groups.
   471  	// The commands are only injected for the scope of showing help and completion, they are not
   472  	// invoked directly.
   473  	pluginCommandGroup := plugin.GetPluginCommandGroup(cmds)
   474  	groups = append(groups, pluginCommandGroup)
   475  
   476  	templates.ActsAsRootCommand(cmds, filters, groups...)
   477  
   478  	utilcomp.SetFactoryForCompletion(f)
   479  	registerCompletionFuncForGlobalFlags(cmds, f)
   480  
   481  	cmds.AddCommand(alpha)
   482  	cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), o.IOStreams))
   483  	cmds.AddCommand(plugin.NewCmdPlugin(o.IOStreams))
   484  	cmds.AddCommand(version.NewCmdVersion(f, o.IOStreams))
   485  	cmds.AddCommand(apiresources.NewCmdAPIVersions(f, o.IOStreams))
   486  	cmds.AddCommand(apiresources.NewCmdAPIResources(f, o.IOStreams))
   487  	cmds.AddCommand(options.NewCmdOptions(o.IOStreams.Out))
   488  
   489  	// Stop warning about normalization of flags. That makes it possible to
   490  	// add the klog flags later.
   491  	cmds.SetGlobalNormalizationFunc(cliflag.WordSepNormalizeFunc)
   492  	return cmds
   493  }
   494  
   495  // addCmdHeaderHooks performs updates on two hooks:
   496  //  1. Modifies the passed "cmds" persistent pre-run function to parse command headers.
   497  //     These headers will be subsequently added as X-headers to every
   498  //     REST call.
   499  //  2. Adds CommandHeaderRoundTripper as a wrapper around the standard
   500  //     RoundTripper. CommandHeaderRoundTripper adds X-Headers then delegates
   501  //     to standard RoundTripper.
   502  //
   503  // For beta, these hooks are updated unless the KUBECTL_COMMAND_HEADERS environment variable
   504  // is set, and the value of the env var is false (or zero).
   505  // See SIG CLI KEP 859 for more information:
   506  //
   507  //	https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/859-kubectl-headers
   508  func addCmdHeaderHooks(cmds *cobra.Command, kubeConfigFlags *genericclioptions.ConfigFlags) {
   509  	// If the feature gate env var is set to "false", then do no add kubectl command headers.
   510  	if value, exists := os.LookupEnv(kubectlCmdHeaders); exists {
   511  		if value == "false" || value == "0" {
   512  			klog.V(5).Infoln("kubectl command headers turned off")
   513  			return
   514  		}
   515  	}
   516  	klog.V(5).Infoln("kubectl command headers turned on")
   517  	crt := &genericclioptions.CommandHeaderRoundTripper{}
   518  	existingPreRunE := cmds.PersistentPreRunE
   519  	// Add command parsing to the existing persistent pre-run function.
   520  	cmds.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
   521  		crt.ParseCommandHeaders(cmd, args)
   522  		return existingPreRunE(cmd, args)
   523  	}
   524  	wrapConfigFn := kubeConfigFlags.WrapConfigFn
   525  	// Wraps CommandHeaderRoundTripper around standard RoundTripper.
   526  	kubeConfigFlags.WrapConfigFn = func(c *rest.Config) *rest.Config {
   527  		if wrapConfigFn != nil {
   528  			c = wrapConfigFn(c)
   529  		}
   530  		c.Wrap(func(rt http.RoundTripper) http.RoundTripper {
   531  			// Must be separate RoundTripper; not "crt" closure.
   532  			// Fixes: https://github.com/kubernetes/kubectl/issues/1098
   533  			return &genericclioptions.CommandHeaderRoundTripper{
   534  				Delegate: rt,
   535  				Headers:  crt.Headers,
   536  			}
   537  		})
   538  		return c
   539  	}
   540  }
   541  
   542  func runHelp(cmd *cobra.Command, args []string) {
   543  	cmd.Help()
   544  }
   545  
   546  func registerCompletionFuncForGlobalFlags(cmd *cobra.Command, f cmdutil.Factory) {
   547  	cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
   548  		"namespace",
   549  		func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   550  			return utilcomp.CompGetResource(f, "namespace", toComplete), cobra.ShellCompDirectiveNoFileComp
   551  		}))
   552  	cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
   553  		"context",
   554  		func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   555  			return utilcomp.ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp
   556  		}))
   557  	cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
   558  		"cluster",
   559  		func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   560  			return utilcomp.ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp
   561  		}))
   562  	cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
   563  		"user",
   564  		func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   565  			return utilcomp.ListUsersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp
   566  		}))
   567  }
   568  

View as plain text