github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+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 }