github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+incompatible/main.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "reflect" 8 "strings" 9 10 "code.cloudfoundry.org/cli/cf/cmd" 11 "code.cloudfoundry.org/cli/command" 12 "code.cloudfoundry.org/cli/command/common" 13 "code.cloudfoundry.org/cli/command/flag" 14 "code.cloudfoundry.org/cli/command/translatableerror" 15 "code.cloudfoundry.org/cli/util/configv3" 16 "code.cloudfoundry.org/cli/util/panichandler" 17 "code.cloudfoundry.org/cli/util/ui" 18 "github.com/jessevdk/go-flags" 19 log "github.com/sirupsen/logrus" 20 "golang.org/x/crypto/ssh" 21 ) 22 23 type UI interface { 24 DisplayError(err error) 25 DisplayWarning(template string, templateValues ...map[string]interface{}) 26 DisplayText(template string, templateValues ...map[string]interface{}) 27 } 28 29 type DisplayUsage interface { 30 DisplayUsage() 31 } 32 33 type TriggerLegacyMain interface { 34 LegacyMain() 35 error 36 } 37 38 var ErrFailed = errors.New("command failed") 39 var ParseErr = errors.New("incorrect type for arg") 40 41 func main() { 42 defer panichandler.HandlePanic() 43 exitStatus := parse(os.Args[1:]) 44 if exitStatus != 0 { 45 os.Exit(exitStatus) 46 } 47 } 48 49 func parse(args []string) int { 50 parser := flags.NewParser(&common.Commands, flags.HelpFlag) 51 parser.CommandHandler = executionWrapper 52 extraArgs, err := parser.ParseArgs(args) 53 if err == nil { 54 return 0 55 } 56 57 if flagErr, ok := err.(*flags.Error); ok { 58 return handleFlagErrorAndCommandHelp(flagErr, parser, extraArgs, args) 59 } else if err == ErrFailed { 60 return 1 61 } else if err == ParseErr { 62 fmt.Println() 63 parse([]string{"help", args[0]}) 64 return 1 65 } else if exitError, ok := err.(*ssh.ExitError); ok { 66 return exitError.ExitStatus() 67 } 68 69 fmt.Fprintf(os.Stderr, "Unexpected error: %s\n", err.Error()) 70 return 1 71 } 72 73 func handleFlagErrorAndCommandHelp(flagErr *flags.Error, parser *flags.Parser, extraArgs []string, originalArgs []string) int { 74 switch flagErr.Type { 75 case flags.ErrHelp, flags.ErrUnknownFlag, flags.ErrExpectedArgument, flags.ErrInvalidChoice: 76 _, found := reflect.TypeOf(common.Commands).FieldByNameFunc( 77 func(fieldName string) bool { 78 field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName) 79 return parser.Active != nil && parser.Active.Name == field.Tag.Get("command") 80 }, 81 ) 82 83 if found && flagErr.Type == flags.ErrUnknownFlag && (parser.Active.Name == "set-env" || parser.Active.Name == "v3-set-env") { 84 newArgs := []string{} 85 for _, arg := range originalArgs { 86 if arg[0] == '-' { 87 newArgs = append(newArgs, fmt.Sprintf("%s%s", flag.WorkAroundPrefix, arg)) 88 } else { 89 newArgs = append(newArgs, arg) 90 } 91 } 92 parse(newArgs) 93 return 0 94 } 95 96 if flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice { 97 fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error()) 98 } 99 100 var helpErrored int 101 if found { 102 helpErrored = parse([]string{"help", parser.Active.Name}) 103 } else { 104 switch len(extraArgs) { 105 case 0: 106 helpErrored = parse([]string{"help"}) 107 case 1: 108 if !isOption(extraArgs[0]) || (len(originalArgs) > 1 && extraArgs[0] == "-a") { 109 helpErrored = parse([]string{"help", extraArgs[0]}) 110 } else { 111 helpErrored = parse([]string{"help"}) 112 } 113 default: 114 if isCommand(extraArgs[0]) { 115 helpErrored = parse([]string{"help", extraArgs[0]}) 116 } else { 117 helpErrored = parse(extraArgs[1:]) 118 } 119 } 120 } 121 122 if helpErrored > 0 || flagErr.Type == flags.ErrUnknownFlag || flagErr.Type == flags.ErrExpectedArgument || flagErr.Type == flags.ErrInvalidChoice { 123 return 1 124 } 125 case flags.ErrRequired, flags.ErrMarshal: 126 fmt.Fprintf(os.Stderr, "Incorrect Usage: %s\n\n", flagErr.Error()) 127 parse([]string{"help", originalArgs[0]}) 128 return 1 129 case flags.ErrUnknownCommand: 130 cmd.Main(os.Getenv("CF_TRACE"), os.Args) 131 case flags.ErrCommandRequired: 132 if common.Commands.VerboseOrVersion { 133 parse([]string{"version"}) 134 } else { 135 parse([]string{"help"}) 136 } 137 default: 138 fmt.Fprintf(os.Stderr, "Unexpected flag error\ntype: %s\nmessage: %s\n", flagErr.Type, flagErr.Error()) 139 } 140 return 0 141 } 142 143 func isCommand(s string) bool { 144 _, found := reflect.TypeOf(common.Commands).FieldByNameFunc( 145 func(fieldName string) bool { 146 field, _ := reflect.TypeOf(common.Commands).FieldByName(fieldName) 147 return s == field.Tag.Get("command") || s == field.Tag.Get("alias") 148 }) 149 150 return found 151 } 152 153 func isOption(s string) bool { 154 return strings.HasPrefix(s, "-") 155 } 156 157 func executionWrapper(cmd flags.Commander, args []string) error { 158 cfConfig, configErr := configv3.LoadConfig(configv3.FlagOverride{ 159 Verbose: common.Commands.VerboseOrVersion, 160 }) 161 if configErr != nil { 162 if _, ok := configErr.(translatableerror.EmptyConfigError); !ok { 163 return configErr 164 } 165 } 166 167 commandUI, err := ui.NewUI(cfConfig) 168 if err != nil { 169 return err 170 } 171 172 err = cfConfig.CreatePluginHome() 173 if err != nil { 174 return err 175 } 176 177 // TODO: when the line in the old code under `cf` which calls 178 // configv3.LoadConfig() is finally removed, then we should replace the code 179 // path above with the following: 180 // 181 // var configErrTemplate string 182 // if configErr != nil { 183 // if ce, ok := configErr.(translatableerror.EmptyConfigError); ok { 184 // configErrTemplate = ce.Error() 185 // } else { 186 // return configErr 187 // } 188 // } 189 190 // commandUI, err := ui.NewUI(cfConfig) 191 // if err != nil { 192 // return err 193 // } 194 195 // if configErr != nil { 196 // commandUI.DisplayWarning(configErrTemplate, map[string]interface{}{ 197 // "FilePath": configv3.ConfigFilePath(), 198 // }) 199 // } 200 201 defer func() { 202 configWriteErr := configv3.WriteConfig(cfConfig) 203 if configWriteErr != nil { 204 fmt.Fprintf(os.Stderr, "Error writing config: %s", configWriteErr.Error()) 205 } 206 }() 207 208 if extendedCmd, ok := cmd.(command.ExtendedCommander); ok { 209 log.SetOutput(os.Stderr) 210 log.SetLevel(log.Level(cfConfig.LogLevel())) 211 212 err = extendedCmd.Setup(cfConfig, commandUI) 213 if err != nil { 214 return handleError(err, commandUI) 215 } 216 return handleError(extendedCmd.Execute(args), commandUI) 217 } 218 219 return fmt.Errorf("command does not conform to ExtendedCommander") 220 } 221 222 func handleError(passedErr error, commandUI UI) error { 223 if passedErr == nil { 224 return nil 225 } 226 227 translatedErr := translatableerror.ConvertToTranslatableError(passedErr) 228 229 switch typedErr := translatedErr.(type) { 230 case TriggerLegacyMain: 231 if typedErr.Error() != "" { 232 commandUI.DisplayWarning("") 233 commandUI.DisplayWarning(typedErr.Error()) 234 } 235 236 // TODO: Replace this section with os.Args when v2-push has been replaced 237 // var args []string 238 // for _, arg := range os.Args { 239 // if arg == "v2-push" { 240 // args = append(args, "push") 241 // } else { 242 // args = append(args, arg) 243 // } 244 // } 245 cmd.Main(os.Getenv("CF_TRACE"), os.Args) 246 case *ssh.ExitError: 247 exitStatus := typedErr.ExitStatus() 248 if sig := typedErr.Signal(); sig != "" { 249 commandUI.DisplayText("Process terminated by signal: {{.Signal}}. Exited with {{.ExitCode}}", map[string]interface{}{ 250 "Signal": sig, 251 "ExitCode": exitStatus, 252 }) 253 } 254 return passedErr 255 } 256 257 commandUI.DisplayError(translatedErr) 258 259 if _, ok := translatedErr.(DisplayUsage); ok { 260 return ParseErr 261 } 262 263 return ErrFailed 264 }