// reportPanic now only displays panics nicely; at some point, should // auto-collect panics for central logging func reportPanic() error { fmt.Fprintln(os.Stderr, terminal.Colorize(terminal.ColorError, "\nWe encountered an unexpected internal error.")) // PanicStack is nil, so don't try to write it. if cli.PanicStack == nil { return nil } fmt.Fprintln(os.Stderr, string(cli.PanicStack)) return nil }
// handleTypedErrors catches typed errors, such as ApiErrors, and prints // appropriate error messages. It also contains logic necessary for // reauthenticating and re-executing a command. func handleTypedErrors(cmd *cli.Cmd, err error) (code int) { code = 1 // Switch off the type of error received, if any. switch aerr := err.(type) { default: // Unknown error received. fmt.Fprintf(os.Stderr, terminal.Colorize(terminal.ColorError, ERROR_PREFIX+"%s\n"), aerr.Error()) fmt.Fprintf(os.Stdout, "Try `kurma-cli help` for more information.\n") return } }
func main() { var exitcode int // defer os.Exit first since it would cancel any previous defers. defer func() { os.Exit(exitcode) }() // Handle any panic not caught at command-level. defer func() { if r := recover(); r != nil { // Dump the stack with information about what command was being run. cli.PanicStack = make([]byte, 1<<20) // 1 MB runtime.Stack(cli.PanicStack, true) header := "Panic within CLI\n\n" // Extend to make room for header. cli.PanicStack = append(cli.PanicStack, make([]byte, len(header))...) copy(cli.PanicStack[len(header):], cli.PanicStack) copy(cli.PanicStack[:len(header)], header) reportPanic() } }() handleSignals() // Special handling of flags to avoid calling flag.Parse() here. Commands // use flag.Flagsets and we don't want to interfere. if len(os.Args) == 1 { printHelp(os.Stderr) exitcode = 1 return } else if len(os.Args) == 2 { switch os.Args[1] { case "-v", "--v", "-version", "--version": if _, ver, err := cli.NewCmd("version"); err == nil { ver.RunCli() } else { fmt.Fprintln(os.Stderr, "version command not defined") } return case "-h", "--h", "-help", "--help": printHelp(os.Stdout) exitcode = 0 return } } // Find the command cmdDef, cmd, err := cli.NewCmd(os.Args[1:]...) if err != nil { exitcode = 1 // Print help and error if command was found but args were incorrect. // e.g. any validation errors returned are surfaced here. if cmdDef != nil { cmdDef.PrintHelp(os.Stderr) // Hide any 'help requested' errors from bubbling up, but still show // usage errors. if err.Error() == "flag: help requested" { return } } fmt.Fprintf(os.Stderr, terminal.Colorize(terminal.ColorError, "Error (usage): %s\n"), err) exitcode = 1 return } // Catch any other '--version' flags. While instances of "apc --version", etc // will be caught above in our len(os.Args) case, we want to take example input // like "apc app --version" just in case. if cli.ShowVersion { if _, ver, err := cli.NewCmd("version"); err == nil { ver.RunCli() } else { fmt.Fprintln(os.Stderr, "version command not defined") } return } conn, err := grpc.Dial(determineKurmaHostPort()) if err != nil { fmt.Fprintf(os.Stderr, terminal.Colorize(terminal.ColorError, ERROR_PREFIX+"%s\n"), err.Error()) exitcode = 1 return } defer conn.Close() cmd.Client = pb.NewKurmaClient(conn) exitcode = runCommand(cmd) }