github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+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/requirements"
    21  	"code.cloudfoundry.org/cli/cf/terminal"
    22  	"code.cloudfoundry.org/cli/cf/trace"
    23  	"code.cloudfoundry.org/cli/plugin/rpc"
    24  	"code.cloudfoundry.org/cli/util/spellcheck"
    25  
    26  	netrpc "net/rpc"
    27  )
    28  
    29  var cmdRegistry = commandregistry.Commands
    30  
    31  func Main(traceEnv string, args []string) {
    32  
    33  	//handle `cf -v` for cf version
    34  	if len(args) == 2 && (args[1] == "-v" || args[1] == "--version") {
    35  		args[1] = "version"
    36  	}
    37  
    38  	//handles `cf`
    39  	if len(args) == 1 {
    40  		args = []string{args[0], "help"}
    41  	}
    42  
    43  	//handles `cf [COMMAND] -h ...`
    44  	//rearrange args to `cf help COMMAND` and let `command help` to print out usage
    45  	args = append([]string{args[0]}, handleHelp(args[1:])...)
    46  
    47  	newArgs, isVerbose := handleVerbose(args)
    48  	args = newArgs
    49  
    50  	errFunc := func(err error) {
    51  		if err != nil {
    52  			ui := terminal.NewUI(
    53  				os.Stdin,
    54  				Writer,
    55  				terminal.NewTeePrinter(Writer),
    56  				trace.NewLogger(Writer, isVerbose, traceEnv, ""),
    57  			)
    58  			ui.Failed(fmt.Sprintf("Config error: %s", err))
    59  			os.Exit(1)
    60  		}
    61  	}
    62  
    63  	// Only used to get Trace, so our errorHandler doesn't matter, since it's not used
    64  	configPath, err := confighelpers.DefaultFilePath()
    65  	if err != nil {
    66  		errFunc(err)
    67  	}
    68  	config := coreconfig.NewRepositoryFromFilepath(configPath, errFunc)
    69  	defer config.Close()
    70  
    71  	traceConfigVal := config.Trace()
    72  
    73  	// Writer is assigned in writer_unix.go/writer_windows.go
    74  	traceLogger := trace.NewLogger(Writer, isVerbose, traceEnv, traceConfigVal)
    75  
    76  	deps := commandregistry.NewDependency(Writer, traceLogger, os.Getenv("CF_DIAL_TIMEOUT"))
    77  	defer deps.Config.Close()
    78  
    79  	warningProducers := []net.WarningProducer{}
    80  	for _, warningProducer := range deps.Gateways {
    81  		warningProducers = append(warningProducers, warningProducer)
    82  	}
    83  
    84  	warningsCollector := net.NewWarningsCollector(deps.UI, warningProducers...)
    85  
    86  	commandsloader.Load()
    87  
    88  	//run core command
    89  	cmdName := args[1]
    90  	cmd := cmdRegistry.FindCommand(cmdName)
    91  	if cmd != nil {
    92  		meta := cmd.MetaData()
    93  		flagContext := flags.NewFlagContext(meta.Flags)
    94  		flagContext.SkipFlagParsing(meta.SkipFlagParsing)
    95  
    96  		cmdArgs := args[2:]
    97  		err = flagContext.Parse(cmdArgs...)
    98  		if err != nil {
    99  			usage := cmdRegistry.CommandUsage(cmdName)
   100  			deps.UI.Failed(T("Incorrect Usage") + "\n\n" + err.Error() + "\n\n" + usage)
   101  			os.Exit(1)
   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 -a'"))
   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 generateBacktrace() string {
   174  	stackByteCount := 0
   175  	stackSizeLimit := 1024 * 1024
   176  	var bytes []byte
   177  	for stackSize := 1024; (stackByteCount == 0 || stackByteCount == stackSize) && stackSize < stackSizeLimit; stackSize = 2 * stackSize {
   178  		bytes = make([]byte, stackSize)
   179  		stackByteCount = runtime.Stack(bytes, true)
   180  	}
   181  	stackTrace := "\t" + strings.Replace(string(bytes), "\n", "\n\t", -1)
   182  	return stackTrace
   183  }
   184  
   185  func handleHelp(args []string) []string {
   186  	hIndex := -1
   187  
   188  	for i, v := range args {
   189  		if v == "-h" || v == "--help" || v == "--h" {
   190  			hIndex = i
   191  			break
   192  		}
   193  	}
   194  
   195  	if hIndex == -1 {
   196  		return args
   197  	} else if len(args) > 1 {
   198  		if hIndex == 0 {
   199  			return []string{"help", args[1]}
   200  		}
   201  		return []string{"help", args[0]}
   202  	} else {
   203  		return []string{"help"}
   204  	}
   205  }
   206  
   207  func handleVerbose(args []string) ([]string, bool) {
   208  	var verbose bool
   209  	idx := -1
   210  
   211  	for i, arg := range args {
   212  		if arg == "-v" {
   213  			idx = i
   214  			break
   215  		}
   216  	}
   217  
   218  	if idx != -1 && len(args) > 1 {
   219  		verbose = true
   220  		args = append(args[:idx], args[idx+1:]...)
   221  	}
   222  
   223  	return args, verbose
   224  }