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 }