code.cloudfoundry.org/cli@v7.1.0+incompatible/cf/cmd/cmd.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"runtime"
     7  	"strings"
     8  
     9  	"path/filepath"
    10  
    11  	"code.cloudfoundry.org/cli/cf/commandregistry"
    12  	"code.cloudfoundry.org/cli/cf/commandsloader"
    13  	"code.cloudfoundry.org/cli/cf/configuration"
    14  	"code.cloudfoundry.org/cli/cf/configuration/confighelpers"
    15  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    16  	"code.cloudfoundry.org/cli/cf/configuration/pluginconfig"
    17  	"code.cloudfoundry.org/cli/cf/errors"
    18  	"code.cloudfoundry.org/cli/cf/flags"
    19  	. "code.cloudfoundry.org/cli/cf/i18n"
    20  	"code.cloudfoundry.org/cli/cf/net"
    21  	"code.cloudfoundry.org/cli/cf/requirements"
    22  	"code.cloudfoundry.org/cli/cf/terminal"
    23  	"code.cloudfoundry.org/cli/cf/trace"
    24  	"code.cloudfoundry.org/cli/cf/util/spellcheck"
    25  	"code.cloudfoundry.org/cli/plugin/rpc"
    26  
    27  	netrpc "net/rpc"
    28  )
    29  
    30  var cmdRegistry = commandregistry.Commands
    31  
    32  func Main(traceEnv string, args []string) {
    33  
    34  	//handle `cf -v` for cf version
    35  	if len(args) == 2 && (args[1] == "-v" || args[1] == "--version") {
    36  		args[1] = "version"
    37  	}
    38  
    39  	//handles `cf`
    40  	if len(args) == 1 {
    41  		args = []string{args[0], "help"}
    42  	}
    43  
    44  	//handles `cf [COMMAND] -h ...`
    45  	//rearrange args to `cf help COMMAND` and let `command help` to print out usage
    46  	args = append([]string{args[0]}, handleHelp(args[1:])...)
    47  
    48  	newArgs, isVerbose := handleVerbose(args)
    49  	args = newArgs
    50  
    51  	errFunc := func(err error) {
    52  		if err != nil {
    53  			ui := terminal.NewUI(
    54  				os.Stdin,
    55  				Writer,
    56  				terminal.NewTeePrinter(Writer),
    57  				trace.NewLogger(Writer, isVerbose, traceEnv, ""),
    58  			)
    59  			ui.Failed(fmt.Sprintf("Config error: %s", err))
    60  			os.Exit(1)
    61  		}
    62  	}
    63  
    64  	// Only used to get Trace, so our errorHandler doesn't matter, since it's not used
    65  	configPath, err := confighelpers.DefaultFilePath()
    66  	if err != nil {
    67  		errFunc(err)
    68  	}
    69  	config := coreconfig.NewRepositoryFromFilepath(configPath, errFunc)
    70  	defer config.Close()
    71  
    72  	traceConfigVal := config.Trace()
    73  
    74  	// Writer is assigned in writer_unix.go/writer_windows.go
    75  	traceLogger := trace.NewLogger(Writer, isVerbose, traceEnv, traceConfigVal)
    76  
    77  	deps := commandregistry.NewDependency(Writer, traceLogger, os.Getenv("CF_DIAL_TIMEOUT"))
    78  	defer deps.Config.Close()
    79  
    80  	warningProducers := []net.WarningProducer{}
    81  	for _, warningProducer := range deps.Gateways {
    82  		warningProducers = append(warningProducers, warningProducer)
    83  	}
    84  
    85  	warningsCollector := net.NewWarningsCollector(deps.UI, warningProducers...)
    86  
    87  	commandsloader.Load()
    88  
    89  	//run core command
    90  	cmdName := args[1]
    91  	cmd := cmdRegistry.FindCommand(cmdName)
    92  	if cmd != nil {
    93  		meta := cmd.MetaData()
    94  		flagContext := flags.NewFlagContext(meta.Flags)
    95  		flagContext.SkipFlagParsing(meta.SkipFlagParsing)
    96  
    97  		cmdArgs := args[2:]
    98  		err = flagContext.Parse(cmdArgs...)
    99  		if err != nil {
   100  			usage := cmdRegistry.CommandUsage(cmdName)
   101  			deps.UI.Failed(T("Incorrect Usage") + "\n\n" + err.Error() + "\n\n" + usage)
   102  			os.Exit(1)
   103  		}
   104  
   105  		cmd = cmd.SetDependency(deps, false)
   106  		cmdRegistry.SetCommand(cmd)
   107  
   108  		requirementsFactory := requirements.NewFactory(deps.Config, deps.RepoLocator)
   109  		reqs, reqErr := cmd.Requirements(requirementsFactory, flagContext)
   110  		if reqErr != nil {
   111  			os.Exit(1)
   112  		}
   113  
   114  		for _, req := range reqs {
   115  			err = req.Execute()
   116  			if err != nil {
   117  				deps.UI.Failed(err.Error())
   118  				os.Exit(1)
   119  			}
   120  		}
   121  
   122  		err = cmd.Execute(flagContext)
   123  		if err != nil {
   124  			deps.UI.Failed(err.Error())
   125  			if _, ok := err.(*errors.CurlHTTPError); ok {
   126  				os.Exit(22)
   127  			}
   128  			os.Exit(1)
   129  		}
   130  
   131  		err = warningsCollector.PrintWarnings()
   132  		if err != nil {
   133  			deps.UI.Failed(err.Error())
   134  			os.Exit(1)
   135  		}
   136  
   137  		os.Exit(0)
   138  	}
   139  
   140  	//non core command, try plugin command
   141  	server := netrpc.NewServer()
   142  	rpcService, err := rpc.NewRpcService(deps.TeePrinter, deps.TeePrinter, deps.Config, deps.RepoLocator, rpc.NewCommandRunner(), deps.Logger, Writer, server)
   143  	if err != nil {
   144  		deps.UI.Say(T("Error initializing RPC service: ") + err.Error())
   145  		os.Exit(1)
   146  	}
   147  
   148  	pluginPath := filepath.Join(confighelpers.PluginRepoDir(), ".cf", "plugins")
   149  	pluginConfig := pluginconfig.NewPluginConfig(
   150  		func(err error) {
   151  			deps.UI.Failed(fmt.Sprintf("Error read/writing plugin config: %s, ", err.Error()))
   152  		},
   153  		configuration.NewDiskPersistor(filepath.Join(pluginPath, "config.json")),
   154  		pluginPath,
   155  	)
   156  	pluginList := pluginConfig.Plugins()
   157  
   158  	ran := rpc.RunMethodIfExists(rpcService, args[1:], pluginList)
   159  	if !ran {
   160  		deps.UI.Say("'" + args[1] + T("' is not a registered command. See 'cf help -a'"))
   161  		suggestCommands(cmdName, deps.UI, append(cmdRegistry.ListCommands(), pluginConfig.ListCommands()...))
   162  		os.Exit(1)
   163  	}
   164  }
   165  
   166  func suggestCommands(cmdName string, ui terminal.UI, cmdsList []string) {
   167  	cmdSuggester := spellcheck.NewCommandSuggester(cmdsList)
   168  	recommendedCmds := cmdSuggester.Recommend(cmdName)
   169  	if len(recommendedCmds) != 0 {
   170  		ui.Say("\n" + T("Did you mean?"))
   171  		for _, suggestion := range recommendedCmds {
   172  			ui.Say("      " + suggestion)
   173  		}
   174  	}
   175  }
   176  
   177  func generateBacktrace() string {
   178  	stackByteCount := 0
   179  	stackSizeLimit := 1024 * 1024
   180  	var bytes []byte
   181  	for stackSize := 1024; (stackByteCount == 0 || stackByteCount == stackSize) && stackSize < stackSizeLimit; stackSize = 2 * stackSize {
   182  		bytes = make([]byte, stackSize)
   183  		stackByteCount = runtime.Stack(bytes, true)
   184  	}
   185  	stackTrace := "\t" + strings.Replace(string(bytes), "\n", "\n\t", -1)
   186  	return stackTrace
   187  }
   188  
   189  func handleHelp(args []string) []string {
   190  	hIndex := -1
   191  
   192  	for i, v := range args {
   193  		if v == "-h" || v == "--help" || v == "--h" {
   194  			hIndex = i
   195  			break
   196  		}
   197  	}
   198  
   199  	if hIndex == -1 {
   200  		return args
   201  	} else if len(args) > 1 {
   202  		if hIndex == 0 {
   203  			return []string{"help", args[1]}
   204  		}
   205  		return []string{"help", args[0]}
   206  	} else {
   207  		return []string{"help"}
   208  	}
   209  }
   210  
   211  func handleVerbose(args []string) ([]string, bool) {
   212  	var verbose bool
   213  	idx := -1
   214  
   215  	for i, arg := range args {
   216  		if arg == "-v" {
   217  			idx = i
   218  			break
   219  		}
   220  	}
   221  
   222  	if idx != -1 && len(args) > 1 {
   223  		verbose = true
   224  		args = append(args[:idx], args[idx+1:]...)
   225  	}
   226  
   227  	return args, verbose
   228  }