github.com/elopio/cli@v6.21.2-0.20160902224010-ea909d1fdb2f+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/flags"
    18  	. "code.cloudfoundry.org/cli/cf/i18n"
    19  	"code.cloudfoundry.org/cli/cf/net"
    20  	"code.cloudfoundry.org/cli/cf/panicprinter"
    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/plugin/rpc"
    25  	"code.cloudfoundry.org/cli/utils/spellcheck"
    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  		}
   103  
   104  		cmd = cmd.SetDependency(deps, false)
   105  		cmdRegistry.SetCommand(cmd)
   106  
   107  		requirementsFactory := requirements.NewFactory(deps.Config, deps.RepoLocator)
   108  		reqs, reqErr := cmd.Requirements(requirementsFactory, flagContext)
   109  		if reqErr != nil {
   110  			os.Exit(1)
   111  		}
   112  
   113  		for _, req := range reqs {
   114  			err = req.Execute()
   115  			if err != nil {
   116  				deps.UI.Failed(err.Error())
   117  				os.Exit(1)
   118  			}
   119  		}
   120  
   121  		err = cmd.Execute(flagContext)
   122  		if err != nil {
   123  			deps.UI.Failed(err.Error())
   124  			os.Exit(1)
   125  		}
   126  
   127  		err = warningsCollector.PrintWarnings()
   128  		if err != nil {
   129  			deps.UI.Failed(err.Error())
   130  			os.Exit(1)
   131  		}
   132  
   133  		os.Exit(0)
   134  	}
   135  
   136  	//non core command, try plugin command
   137  	server := netrpc.NewServer()
   138  	rpcService, err := rpc.NewRpcService(deps.TeePrinter, deps.TeePrinter, deps.Config, deps.RepoLocator, rpc.NewCommandRunner(), deps.Logger, Writer, server)
   139  	if err != nil {
   140  		deps.UI.Say(T("Error initializing RPC service: ") + err.Error())
   141  		os.Exit(1)
   142  	}
   143  
   144  	pluginPath := filepath.Join(confighelpers.PluginRepoDir(), ".cf", "plugins")
   145  	pluginConfig := pluginconfig.NewPluginConfig(
   146  		func(err error) {
   147  			deps.UI.Failed(fmt.Sprintf("Error read/writing plugin config: %s, ", err.Error()))
   148  		},
   149  		configuration.NewDiskPersistor(filepath.Join(pluginPath, "config.json")),
   150  		pluginPath,
   151  	)
   152  	pluginList := pluginConfig.Plugins()
   153  
   154  	ran := rpc.RunMethodIfExists(rpcService, args[1:], pluginList)
   155  	if !ran {
   156  		deps.UI.Say("'" + args[1] + T("' is not a registered command. See 'cf help'"))
   157  		suggestCommands(cmdName, deps.UI, append(cmdRegistry.ListCommands(), pluginConfig.ListCommands()...))
   158  		os.Exit(1)
   159  	}
   160  }
   161  
   162  func suggestCommands(cmdName string, ui terminal.UI, cmdsList []string) {
   163  	cmdSuggester := spellcheck.NewCommandSuggester(cmdsList)
   164  	recommendedCmds := cmdSuggester.Recommend(cmdName)
   165  	if len(recommendedCmds) != 0 {
   166  		ui.Say("\n" + T("Did you mean?"))
   167  		for _, suggestion := range recommendedCmds {
   168  			ui.Say("      " + suggestion)
   169  		}
   170  	}
   171  }
   172  
   173  func handlePanics(args []string, printer terminal.Printer, logger trace.Printer) {
   174  	panicPrinter := panicprinter.NewPanicPrinter(terminal.NewUI(os.Stdin, Writer, printer, logger))
   175  
   176  	commandArgs := strings.Join(args, " ")
   177  	stackTrace := generateBacktrace()
   178  
   179  	err := recover()
   180  	panicPrinter.DisplayCrashDialog(err, commandArgs, stackTrace)
   181  
   182  	if err != nil {
   183  		os.Exit(1)
   184  	}
   185  }
   186  
   187  func generateBacktrace() string {
   188  	stackByteCount := 0
   189  	stackSizeLimit := 1024 * 1024
   190  	var bytes []byte
   191  	for stackSize := 1024; (stackByteCount == 0 || stackByteCount == stackSize) && stackSize < stackSizeLimit; stackSize = 2 * stackSize {
   192  		bytes = make([]byte, stackSize)
   193  		stackByteCount = runtime.Stack(bytes, true)
   194  	}
   195  	stackTrace := "\t" + strings.Replace(string(bytes), "\n", "\n\t", -1)
   196  	return stackTrace
   197  }
   198  
   199  func handleHelp(args []string) []string {
   200  	hIndex := -1
   201  
   202  	for i, v := range args {
   203  		if v == "-h" || v == "--help" || v == "--h" {
   204  			hIndex = i
   205  			break
   206  		}
   207  	}
   208  
   209  	if hIndex == -1 {
   210  		return args
   211  	} else if len(args) > 1 {
   212  		if hIndex == 0 {
   213  			return []string{"help", args[1]}
   214  		}
   215  		return []string{"help", args[0]}
   216  	} else {
   217  		return []string{"help"}
   218  	}
   219  }
   220  
   221  func handleVerbose(args []string) ([]string, bool) {
   222  	var verbose bool
   223  	idx := -1
   224  
   225  	for i, arg := range args {
   226  		if arg == "-v" {
   227  			idx = i
   228  			break
   229  		}
   230  	}
   231  
   232  	if idx != -1 && len(args) > 1 {
   233  		verbose = true
   234  		args = append(args[:idx], args[idx+1:]...)
   235  	}
   236  
   237  	return args, verbose
   238  }