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 }