github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cli/cli.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package cli 12 13 import ( 14 "context" 15 "flag" 16 "fmt" 17 "math/rand" 18 "os" 19 "strings" 20 "text/tabwriter" 21 22 _ "github.com/benesch/cgosymbolizer" // calls runtime.SetCgoTraceback on import 23 "github.com/cockroachdb/cockroach/pkg/build" 24 "github.com/cockroachdb/cockroach/pkg/util/log" 25 "github.com/cockroachdb/cockroach/pkg/util/log/logflags" 26 "github.com/cockroachdb/cockroach/pkg/util/randutil" 27 // intentionally not all the workloads in pkg/ccl/workloadccl/allccl 28 _ "github.com/cockroachdb/cockroach/pkg/workload/bank" // registers workloads 29 _ "github.com/cockroachdb/cockroach/pkg/workload/bulkingest" // registers workloads 30 workloadcli "github.com/cockroachdb/cockroach/pkg/workload/cli" 31 _ "github.com/cockroachdb/cockroach/pkg/workload/examples" // registers workloads 32 _ "github.com/cockroachdb/cockroach/pkg/workload/kv" // registers workloads 33 _ "github.com/cockroachdb/cockroach/pkg/workload/movr" // registers workloads 34 _ "github.com/cockroachdb/cockroach/pkg/workload/tpcc" // registers workloads 35 _ "github.com/cockroachdb/cockroach/pkg/workload/tpch" // registers workloads 36 _ "github.com/cockroachdb/cockroach/pkg/workload/ycsb" // registers workloads 37 "github.com/cockroachdb/errors" 38 "github.com/spf13/cobra" 39 ) 40 41 // Main is the entry point for the cli, with a single line calling it intended 42 // to be the body of an action package main `main` func elsewhere. It is 43 // abstracted for reuse by duplicated `main` funcs in different distributions. 44 func Main() { 45 // Seed the math/rand RNG from crypto/rand. 46 rand.Seed(randutil.NewPseudoSeed()) 47 48 if len(os.Args) == 1 { 49 os.Args = append(os.Args, "help") 50 } 51 52 // Change the logging defaults for the main cockroach binary. 53 // The value is overridden after command-line parsing. 54 if err := flag.Lookup(logflags.LogToStderrName).Value.Set("NONE"); err != nil { 55 panic(err) 56 } 57 58 cmdName := commandName(os.Args[1:]) 59 60 log.SetupCrashReporter( 61 context.Background(), 62 cmdName, 63 ) 64 65 defer log.RecoverAndReportPanic(context.Background(), &serverCfg.Settings.SV) 66 67 err := Run(os.Args[1:]) 68 69 errCode := 0 70 if err != nil { 71 // Display the error and its details/hints. 72 cliOutputError(stderr, err, true /*showSeverity*/, false /*verbose*/) 73 74 // Remind the user of which command was being run. 75 fmt.Fprintf(stderr, "Failed running %q\n", cmdName) 76 77 // Finally, extract the error code, as optionally specified 78 // by the sub-command. 79 errCode = 1 80 var cliErr *cliError 81 if errors.As(err, &cliErr) { 82 errCode = cliErr.exitCode 83 } 84 } 85 86 os.Exit(errCode) 87 } 88 89 // commandName computes the name of the command that args would invoke. For 90 // example, the full name of "cockroach debug zip" is "debug zip". If args 91 // specify a nonexistent command, commandName returns "cockroach". 92 func commandName(args []string) string { 93 rootName := cockroachCmd.CommandPath() 94 // Ask Cobra to find the command so that flags and their arguments are 95 // ignored. The name of "cockroach --log-dir foo start" is "start", not 96 // "--log-dir" or "foo". 97 if cmd, _, _ := cockroachCmd.Find(os.Args[1:]); cmd != nil { 98 return strings.TrimPrefix(cmd.CommandPath(), rootName+" ") 99 } 100 return rootName 101 } 102 103 type cliError struct { 104 exitCode int 105 severity log.Severity 106 cause error 107 } 108 109 func (e *cliError) Error() string { return e.cause.Error() } 110 111 // Cause implements causer. 112 func (e *cliError) Cause() error { return e.cause } 113 114 // Format implements fmt.Formatter. 115 func (e *cliError) Format(s fmt.State, verb rune) { errors.FormatError(e, s, verb) } 116 117 // FormatError implements errors.Formatter. 118 func (e *cliError) FormatError(p errors.Printer) error { 119 if p.Detail() { 120 p.Printf("error with exit code: %d", e.exitCode) 121 } 122 return e.cause 123 } 124 125 // stderr aliases log.OrigStderr; we use an alias here so that tests 126 // in this package can redirect the output of CLI commands to stdout 127 // to be captured. 128 var stderr = log.OrigStderr 129 130 // stdin aliases os.Stdin; we use an alias here so that tests in this 131 // package can redirect the input of the CLI shell. 132 var stdin = os.Stdin 133 134 var versionCmd = &cobra.Command{ 135 Use: "version", 136 Short: "output version information", 137 Long: ` 138 Output build version information. 139 `, 140 Args: cobra.NoArgs, 141 RunE: func(cmd *cobra.Command, args []string) error { 142 info := build.GetInfo() 143 tw := tabwriter.NewWriter(os.Stdout, 2, 1, 2, ' ', 0) 144 fmt.Fprintf(tw, "Build Tag: %s\n", info.Tag) 145 fmt.Fprintf(tw, "Build Time: %s\n", info.Time) 146 fmt.Fprintf(tw, "Distribution: %s\n", info.Distribution) 147 fmt.Fprintf(tw, "Platform: %s", info.Platform) 148 if info.CgoTargetTriple != "" { 149 fmt.Fprintf(tw, " (%s)", info.CgoTargetTriple) 150 } 151 fmt.Fprintln(tw) 152 fmt.Fprintf(tw, "Go Version: %s\n", info.GoVersion) 153 fmt.Fprintf(tw, "C Compiler: %s\n", info.CgoCompiler) 154 fmt.Fprintf(tw, "Build SHA-1: %s\n", info.Revision) 155 fmt.Fprintf(tw, "Build Type: %s\n", info.Type) 156 return tw.Flush() 157 }, 158 } 159 160 var cockroachCmd = &cobra.Command{ 161 Use: "cockroach [command] (flags)", 162 Short: "CockroachDB command-line interface and server", 163 // TODO(cdo): Add a pointer to the docs in Long. 164 Long: `CockroachDB command-line interface and server.`, 165 // Disable automatic printing of usage information whenever an error 166 // occurs. Many errors are not the result of a bad command invocation, 167 // e.g. attempting to start a node on an in-use port, and printing the 168 // usage information in these cases obscures the cause of the error. 169 // Commands should manually print usage information when the error is, 170 // in fact, a result of a bad invocation, e.g. too many arguments. 171 SilenceUsage: true, 172 // Disable automatic printing of the error. We want to also print 173 // details and hints, which cobra does not do for us. Instead 174 // we do the printing in Main(). 175 SilenceErrors: true, 176 } 177 178 func init() { 179 cobra.EnableCommandSorting = false 180 181 // Set an error function for flag parsing which prints the usage message. 182 cockroachCmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error { 183 if err := c.Usage(); err != nil { 184 return err 185 } 186 fmt.Fprintln(c.OutOrStderr()) // provide a line break between usage and error 187 return err 188 }) 189 190 cockroachCmd.AddCommand( 191 startCmd, 192 startSingleNodeCmd, 193 initCmd, 194 certCmd, 195 quitCmd, 196 197 sqlShellCmd, 198 authCmd, 199 nodeCmd, 200 dumpCmd, 201 nodeLocalCmd, 202 203 // Miscellaneous commands. 204 // TODO(pmattis): stats 205 demoCmd, 206 genCmd, 207 versionCmd, 208 DebugCmd, 209 sqlfmtCmd, 210 workloadcli.WorkloadCmd(true /* userFacing */), 211 systemBenchCmd, 212 ) 213 } 214 215 // AddCmd adds a command to the cli. 216 func AddCmd(c *cobra.Command) { 217 cockroachCmd.AddCommand(c) 218 } 219 220 // Run ... 221 func Run(args []string) error { 222 cockroachCmd.SetArgs(args) 223 return cockroachCmd.Execute() 224 } 225 226 // usageAndErr informs the user about the usage of the command 227 // and returns an error. This ensures that the top-level command 228 // has a suitable exit status. 229 func usageAndErr(cmd *cobra.Command, args []string) error { 230 if err := cmd.Usage(); err != nil { 231 return err 232 } 233 return fmt.Errorf("unknown sub-command: %q", strings.Join(args, " ")) 234 }