github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cli/flags.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 "flag" 15 "net" 16 "path/filepath" 17 "regexp" 18 "strings" 19 "time" 20 21 "github.com/cockroachdb/cockroach/pkg/base" 22 "github.com/cockroachdb/cockroach/pkg/cli/cliflags" 23 "github.com/cockroachdb/cockroach/pkg/security" 24 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 25 "github.com/cockroachdb/cockroach/pkg/util/envutil" 26 "github.com/cockroachdb/cockroach/pkg/util/humanizeutil" 27 "github.com/cockroachdb/cockroach/pkg/util/log" 28 "github.com/cockroachdb/cockroach/pkg/util/log/logflags" 29 "github.com/cockroachdb/cockroach/pkg/util/netutil" 30 "github.com/cockroachdb/errors" 31 "github.com/spf13/cobra" 32 "github.com/spf13/pflag" 33 ) 34 35 // special global variables used by flag variable definitions below. 36 // These do not correspond directly to the configuration parameters 37 // used as input by the CLI commands (these are defined in context 38 // structs in context.go). Instead, they are used at the *end* of 39 // command-line parsing to override the defaults in the context 40 // structs. 41 // 42 // Corollaries: 43 // - it would be a programming error to access these variables directly 44 // outside of this file (flags.go) 45 // - the underlying context parameters must receive defaults in 46 // initCLIDefaults() even when they are otherwise overridden by the 47 // flags logic, because some tests to not use the flag logic at all. 48 var serverListenPort, serverSocketDir string 49 var serverAdvertiseAddr, serverAdvertisePort string 50 var serverSQLAddr, serverSQLPort string 51 var serverSQLAdvertiseAddr, serverSQLAdvertisePort string 52 var serverHTTPAddr, serverHTTPPort string 53 var localityAdvertiseHosts localityList 54 55 // initPreFlagsDefaults initializes the values of the global variables 56 // defined above. 57 func initPreFlagsDefaults() { 58 serverListenPort = base.DefaultPort 59 serverSocketDir = "" 60 serverAdvertiseAddr = "" 61 serverAdvertisePort = "" 62 63 serverSQLAddr = "" 64 serverSQLPort = "" 65 serverSQLAdvertiseAddr = "" 66 serverSQLAdvertisePort = "" 67 68 serverHTTPAddr = "" 69 serverHTTPPort = base.DefaultHTTPPort 70 71 localityAdvertiseHosts = localityList{} 72 } 73 74 // AddPersistentPreRunE add 'fn' as a persistent pre-run function to 'cmd'. 75 // If the command has an existing pre-run function, it is saved and will be called 76 // at the beginning of 'fn'. 77 // This allows an arbitrary number of pre-run functions with ordering based 78 // on the order in which AddPersistentPreRunE is called (usually package init order). 79 func AddPersistentPreRunE(cmd *cobra.Command, fn func(*cobra.Command, []string) error) { 80 // Save any existing hooks. 81 wrapped := cmd.PersistentPreRunE 82 83 cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { 84 // Run the previous hook if it exists. 85 if wrapped != nil { 86 if err := wrapped(cmd, args); err != nil { 87 return err 88 } 89 } 90 91 // Now we can call the new function. 92 return fn(cmd, args) 93 } 94 } 95 96 // StringFlag creates a string flag and registers it with the FlagSet. 97 func StringFlag(f *pflag.FlagSet, valPtr *string, flagInfo cliflags.FlagInfo, defaultVal string) { 98 f.StringVarP(valPtr, flagInfo.Name, flagInfo.Shorthand, defaultVal, flagInfo.Usage()) 99 100 registerEnvVarDefault(f, flagInfo) 101 } 102 103 // IntFlag creates an int flag and registers it with the FlagSet. 104 func IntFlag(f *pflag.FlagSet, valPtr *int, flagInfo cliflags.FlagInfo, defaultVal int) { 105 f.IntVarP(valPtr, flagInfo.Name, flagInfo.Shorthand, defaultVal, flagInfo.Usage()) 106 107 registerEnvVarDefault(f, flagInfo) 108 } 109 110 // BoolFlag creates a bool flag and registers it with the FlagSet. 111 func BoolFlag(f *pflag.FlagSet, valPtr *bool, flagInfo cliflags.FlagInfo, defaultVal bool) { 112 f.BoolVarP(valPtr, flagInfo.Name, flagInfo.Shorthand, defaultVal, flagInfo.Usage()) 113 114 registerEnvVarDefault(f, flagInfo) 115 } 116 117 // DurationFlag creates a duration flag and registers it with the FlagSet. 118 func DurationFlag( 119 f *pflag.FlagSet, valPtr *time.Duration, flagInfo cliflags.FlagInfo, defaultVal time.Duration, 120 ) { 121 f.DurationVarP(valPtr, flagInfo.Name, flagInfo.Shorthand, defaultVal, flagInfo.Usage()) 122 123 registerEnvVarDefault(f, flagInfo) 124 } 125 126 // VarFlag creates a custom-variable flag and registers it with the FlagSet. 127 func VarFlag(f *pflag.FlagSet, value pflag.Value, flagInfo cliflags.FlagInfo) { 128 f.VarP(value, flagInfo.Name, flagInfo.Shorthand, flagInfo.Usage()) 129 130 registerEnvVarDefault(f, flagInfo) 131 } 132 133 // StringSlice creates a string slice flag and registers it with the FlagSet. 134 func StringSlice( 135 f *pflag.FlagSet, valPtr *[]string, flagInfo cliflags.FlagInfo, defaultVal []string, 136 ) { 137 f.StringSliceVar(valPtr, flagInfo.Name, defaultVal, flagInfo.Usage()) 138 registerEnvVarDefault(f, flagInfo) 139 } 140 141 // aliasStrVar wraps a string configuration option and is meant 142 // to be used in addition to / next to another flag that targets the 143 // same option. It does not implement "default values" so that the 144 // main flag can perform the default logic. 145 type aliasStrVar struct{ p *string } 146 147 // String implements the pflag.Value interface. 148 func (a aliasStrVar) String() string { return "" } 149 150 // Set implements the pflag.Value interface. 151 func (a aliasStrVar) Set(v string) error { 152 if v != "" { 153 *a.p = v 154 } 155 return nil 156 } 157 158 // Type implements the pflag.Value interface. 159 func (a aliasStrVar) Type() string { return "string" } 160 161 // addrSetter wraps a address/port configuration option pair and 162 // enables setting them both with a single command-line flag. 163 type addrSetter struct { 164 addr *string 165 port *string 166 } 167 168 // String implements the pflag.Value interface. 169 func (a addrSetter) String() string { 170 return net.JoinHostPort(*a.addr, *a.port) 171 } 172 173 // Type implements the pflag.Value interface. 174 func (a addrSetter) Type() string { return "<addr/host>[:<port>]" } 175 176 // Set implements the pflag.Value interface. 177 func (a addrSetter) Set(v string) error { 178 addr, port, err := netutil.SplitHostPort(v, *a.port) 179 if err != nil { 180 return err 181 } 182 *a.addr = addr 183 *a.port = port 184 return nil 185 } 186 187 // clusterNameSetter wraps the cluster name variable 188 // and verifies its format during configuration. 189 type clusterNameSetter struct { 190 clusterName *string 191 } 192 193 // String implements the pflag.Value interface. 194 func (a clusterNameSetter) String() string { return *a.clusterName } 195 196 // Type implements the pflag.Value interface. 197 func (a clusterNameSetter) Type() string { return "<identifier>" } 198 199 // Set implements the pflag.Value interface. 200 func (a clusterNameSetter) Set(v string) error { 201 if v == "" { 202 return errors.New("cluster name cannot be empty") 203 } 204 if len(v) > maxClusterNameLength { 205 return errors.Newf(`cluster name can contain at most %d characters`, maxClusterNameLength) 206 } 207 if !clusterNameRe.MatchString(v) { 208 return errClusterNameInvalidFormat 209 } 210 *a.clusterName = v 211 return nil 212 } 213 214 var errClusterNameInvalidFormat = errors.New(`cluster name must contain only letters, numbers or the "-" and "." characters`) 215 216 // clusterNameRe matches valid cluster names. 217 // For example, "a", "a123" and "a-b" are OK, 218 // but "0123", "a-" and "123a" are not OK. 219 var clusterNameRe = regexp.MustCompile(`^[a-zA-Z](?:[-a-zA-Z0-9]*[a-zA-Z0-9]|)$`) 220 221 const maxClusterNameLength = 256 222 223 const backgroundEnvVar = "COCKROACH_BACKGROUND_RESTART" 224 225 // flagSetForCmd is a replacement for cmd.Flag() that properly merges 226 // persistent and local flags, until the upstream bug 227 // https://github.com/spf13/cobra/issues/961 has been fixed. 228 func flagSetForCmd(cmd *cobra.Command) *pflag.FlagSet { 229 _ = cmd.LocalFlags() // force merge persistent+local flags 230 return cmd.Flags() 231 } 232 233 func init() { 234 initCLIDefaults() 235 defer func() { 236 if err := processEnvVarDefaults(); err != nil { 237 panic(err) 238 } 239 }() 240 241 // Every command but start will inherit the following setting. 242 AddPersistentPreRunE(cockroachCmd, func(cmd *cobra.Command, _ []string) error { 243 if err := extraClientFlagInit(); err != nil { 244 return err 245 } 246 return setDefaultStderrVerbosity(cmd, log.Severity_WARNING) 247 }) 248 249 // Add a pre-run command for `start` and `start-single-node`. 250 for _, cmd := range StartCmds { 251 AddPersistentPreRunE(cmd, func(cmd *cobra.Command, _ []string) error { 252 // Finalize the configuration of network and logging settings. 253 if err := extraServerFlagInit(cmd); err != nil { 254 return err 255 } 256 return setDefaultStderrVerbosity(cmd, log.Severity_INFO) 257 }) 258 } 259 260 // Map any flags registered in the standard "flag" package into the 261 // top-level cockroach command. 262 pf := cockroachCmd.PersistentFlags() 263 flag.VisitAll(func(f *flag.Flag) { 264 flag := pflag.PFlagFromGoFlag(f) 265 // TODO(peter): Decide if we want to make the lightstep flags visible. 266 if strings.HasPrefix(flag.Name, "lightstep_") { 267 flag.Hidden = true 268 } 269 if strings.HasPrefix(flag.Name, "httptest.") { 270 // If we test the cli commands in tests, we may end up transitively 271 // importing httptest, for example via `testify/assert`. Make sure 272 // it doesn't show up in the output or it will confuse tests. 273 flag.Hidden = true 274 } 275 switch flag.Name { 276 case logflags.NoRedirectStderrName: 277 flag.Hidden = true 278 case logflags.ShowLogsName: 279 flag.Hidden = true 280 case logflags.LogToStderrName: 281 // The actual default value for --logtostderr is overridden in 282 // cli.Main. We don't override it here as doing so would affect all of 283 // the cli tests and any package which depends on cli. The following line 284 // is only overriding the default value for the pflag package (and what 285 // is visible in help text), not the stdlib flag value. 286 flag.DefValue = "NONE" 287 case logflags.LogDirName, 288 logflags.LogFileMaxSizeName, 289 logflags.LogFilesCombinedMaxSizeName, 290 logflags.LogFileVerbosityThresholdName: 291 // The --log-dir* and --log-file* flags are specified only for the 292 // `start` and `demo` commands. 293 return 294 } 295 pf.AddFlag(flag) 296 }) 297 298 // When a flag is specified but without a value, pflag assigns its 299 // NoOptDefVal to it via Set(). This is also the value used to 300 // generate the implicit assigned value in the usage text 301 // (e.g. "--logtostderr[=XXXXX]"). We can't populate a real default 302 // unfortunately, because the default depends on which command is 303 // run (`start` vs. the rest), and pflag does not support 304 // per-command NoOptDefVals. So we need some sentinel value here 305 // that we can recognize when setDefaultStderrVerbosity() is called 306 // after argument parsing. We could use UNKNOWN, but to ensure that 307 // the usage text is somewhat less confusing to the user, we use the 308 // special severity value DEFAULT instead. 309 pf.Lookup(logflags.LogToStderrName).NoOptDefVal = log.Severity_DEFAULT.String() 310 311 // Remember we are starting in the background as the `start` command will 312 // avoid printing some messages to standard output in that case. 313 _, startCtx.inBackground = envutil.EnvString(backgroundEnvVar, 1) 314 315 for _, cmd := range StartCmds { 316 f := cmd.Flags() 317 318 // Server flags. 319 VarFlag(f, addrSetter{&startCtx.serverListenAddr, &serverListenPort}, cliflags.ListenAddr) 320 VarFlag(f, addrSetter{&serverAdvertiseAddr, &serverAdvertisePort}, cliflags.AdvertiseAddr) 321 VarFlag(f, addrSetter{&serverSQLAddr, &serverSQLPort}, cliflags.ListenSQLAddr) 322 VarFlag(f, addrSetter{&serverSQLAdvertiseAddr, &serverSQLAdvertisePort}, cliflags.SQLAdvertiseAddr) 323 VarFlag(f, addrSetter{&serverHTTPAddr, &serverHTTPPort}, cliflags.ListenHTTPAddr) 324 StringFlag(f, &serverSocketDir, cliflags.SocketDir, serverSocketDir) 325 // --socket is deprecated as of 20.1. 326 // TODO(knz): remove in 20.2. 327 StringFlag(f, &serverCfg.SocketFile, cliflags.Socket, serverCfg.SocketFile) 328 _ = f.MarkDeprecated(cliflags.Socket.Name, "use the --socket-dir and --listen-addr flags instead") 329 BoolFlag(f, &startCtx.unencryptedLocalhostHTTP, cliflags.UnencryptedLocalhostHTTP, startCtx.unencryptedLocalhostHTTP) 330 331 // Backward-compatibility flags. 332 333 // These are deprecated but until we have qualitatively new 334 // functionality in the flags above, there is no need to nudge the 335 // user away from them with a deprecation warning. So we keep 336 // them, but hidden from docs so that they don't appear as 337 // redundant with the main flags. 338 VarFlag(f, aliasStrVar{&startCtx.serverListenAddr}, cliflags.ServerHost) 339 _ = f.MarkHidden(cliflags.ServerHost.Name) 340 VarFlag(f, aliasStrVar{&serverListenPort}, cliflags.ServerPort) 341 _ = f.MarkHidden(cliflags.ServerPort.Name) 342 343 VarFlag(f, aliasStrVar{&serverAdvertiseAddr}, cliflags.AdvertiseHost) 344 _ = f.MarkHidden(cliflags.AdvertiseHost.Name) 345 VarFlag(f, aliasStrVar{&serverAdvertisePort}, cliflags.AdvertisePort) 346 _ = f.MarkHidden(cliflags.AdvertisePort.Name) 347 348 VarFlag(f, aliasStrVar{&serverHTTPAddr}, cliflags.ListenHTTPAddrAlias) 349 _ = f.MarkHidden(cliflags.ListenHTTPAddrAlias.Name) 350 VarFlag(f, aliasStrVar{&serverHTTPPort}, cliflags.ListenHTTPPort) 351 _ = f.MarkHidden(cliflags.ListenHTTPPort.Name) 352 353 // More server flags. 354 355 VarFlag(f, &localityAdvertiseHosts, cliflags.LocalityAdvertiseAddr) 356 357 StringFlag(f, &serverCfg.Attrs, cliflags.Attrs, serverCfg.Attrs) 358 VarFlag(f, &serverCfg.Locality, cliflags.Locality) 359 360 VarFlag(f, &serverCfg.Stores, cliflags.Store) 361 VarFlag(f, &serverCfg.StorageEngine, cliflags.StorageEngine) 362 VarFlag(f, &serverCfg.MaxOffset, cliflags.MaxOffset) 363 StringFlag(f, &serverCfg.ClockDevicePath, cliflags.ClockDevice, "") 364 365 StringFlag(f, &startCtx.listeningURLFile, cliflags.ListeningURLFile, startCtx.listeningURLFile) 366 367 StringFlag(f, &startCtx.pidFile, cliflags.PIDFile, startCtx.pidFile) 368 StringFlag(f, &startCtx.geoLibsDir, cliflags.GeoLibsDir, startCtx.geoLibsDir) 369 370 // Use a separate variable to store the value of ServerInsecure. 371 // We share the default with the ClientInsecure flag. 372 BoolFlag(f, &startCtx.serverInsecure, cliflags.ServerInsecure, startCtx.serverInsecure) 373 374 // Enable/disable various external storage endpoints. 375 serverCfg.ExternalIODirConfig = base.ExternalIODirConfig{} 376 BoolFlag(f, &serverCfg.ExternalIODirConfig.DisableHTTP, 377 cliflags.ExternalIODisableHTTP, false) 378 BoolFlag(f, &serverCfg.ExternalIODirConfig.DisableImplicitCredentials, 379 cliflags.ExtenralIODisableImplicitCredentials, false) 380 381 // Certificates directory. Use a server-specific flag and value to ignore environment 382 // variables, but share the same default. 383 StringFlag(f, &startCtx.serverSSLCertsDir, cliflags.ServerCertsDir, startCtx.serverSSLCertsDir) 384 385 // Certificate principal map. 386 StringSlice(f, &startCtx.serverCertPrincipalMap, 387 cliflags.CertPrincipalMap, startCtx.serverCertPrincipalMap) 388 389 // Cluster joining flags. We need to enable this both for 'start' 390 // and 'start-single-node' although the latter does not support 391 // --join, because it delegates its logic to that of 'start', and 392 // 'start' will check that the flag is properly defined. 393 VarFlag(f, &serverCfg.JoinList, cliflags.Join) 394 BoolFlag(f, &serverCfg.JoinPreferSRVRecords, cliflags.JoinPreferSRVRecords, serverCfg.JoinPreferSRVRecords) 395 VarFlag(f, clusterNameSetter{&baseCfg.ClusterName}, cliflags.ClusterName) 396 BoolFlag(f, &baseCfg.DisableClusterNameVerification, 397 cliflags.DisableClusterNameVerification, baseCfg.DisableClusterNameVerification) 398 if cmd == startSingleNodeCmd { 399 // Even though all server flags are supported for 400 // 'start-single-node', we intend that command to be used by 401 // beginners / developers running on a single machine. To 402 // enhance the UX, we hide the flags since they are not directly 403 // relevant when running a single node. 404 _ = f.MarkHidden(cliflags.Join.Name) 405 _ = f.MarkHidden(cliflags.ClusterName.Name) 406 _ = f.MarkHidden(cliflags.DisableClusterNameVerification.Name) 407 _ = f.MarkHidden(cliflags.MaxOffset.Name) 408 _ = f.MarkHidden(cliflags.LocalityAdvertiseAddr.Name) 409 _ = f.MarkHidden(cliflags.AdvertiseAddr.Name) 410 _ = f.MarkHidden(cliflags.SQLAdvertiseAddr.Name) 411 } 412 413 // Engine flags. 414 VarFlag(f, cacheSizeValue, cliflags.Cache) 415 VarFlag(f, sqlSizeValue, cliflags.SQLMem) 416 // N.B. diskTempStorageSizeValue.ResolvePercentage() will be called after 417 // the stores flag has been parsed and the storage device that a percentage 418 // refers to becomes known. 419 VarFlag(f, diskTempStorageSizeValue, cliflags.SQLTempStorage) 420 StringFlag(f, &startCtx.tempDir, cliflags.TempDir, startCtx.tempDir) 421 StringFlag(f, &startCtx.externalIODir, cliflags.ExternalIODir, startCtx.externalIODir) 422 423 VarFlag(f, serverCfg.AuditLogDirName, cliflags.SQLAuditLogDirName) 424 } 425 426 // Log flags. 427 logCmds := append(StartCmds, demoCmd) 428 logCmds = append(logCmds, demoCmd.Commands()...) 429 for _, cmd := range logCmds { 430 f := cmd.Flags() 431 VarFlag(f, &startCtx.logDir, cliflags.LogDir) 432 VarFlag(f, 433 pflag.PFlagFromGoFlag(flag.Lookup(logflags.LogFilesCombinedMaxSizeName)).Value, 434 cliflags.LogDirMaxSize) 435 VarFlag(f, 436 pflag.PFlagFromGoFlag(flag.Lookup(logflags.LogFileMaxSizeName)).Value, 437 cliflags.LogFileMaxSize) 438 VarFlag(f, 439 pflag.PFlagFromGoFlag(flag.Lookup(logflags.LogFileVerbosityThresholdName)).Value, 440 cliflags.LogFileVerbosity) 441 } 442 443 for _, cmd := range certCmds { 444 f := cmd.Flags() 445 // All certs commands need the certificate directory. 446 StringFlag(f, &baseCfg.SSLCertsDir, cliflags.CertsDir, baseCfg.SSLCertsDir) 447 // All certs commands get the certificate principal map. 448 StringSlice(f, &cliCtx.certPrincipalMap, 449 cliflags.CertPrincipalMap, cliCtx.certPrincipalMap) 450 } 451 452 for _, cmd := range []*cobra.Command{createCACertCmd, createClientCACertCmd} { 453 f := cmd.Flags() 454 // CA certificates have a longer expiration time. 455 DurationFlag(f, &caCertificateLifetime, cliflags.CertificateLifetime, defaultCALifetime) 456 // The CA key can be re-used if it exists. 457 BoolFlag(f, &allowCAKeyReuse, cliflags.AllowCAKeyReuse, false) 458 } 459 460 for _, cmd := range []*cobra.Command{createNodeCertCmd, createClientCertCmd} { 461 f := cmd.Flags() 462 DurationFlag(f, &certificateLifetime, cliflags.CertificateLifetime, defaultCertLifetime) 463 } 464 465 // The remaining flags are shared between all cert-generating functions. 466 for _, cmd := range []*cobra.Command{createCACertCmd, createClientCACertCmd, createNodeCertCmd, createClientCertCmd} { 467 f := cmd.Flags() 468 StringFlag(f, &baseCfg.SSLCAKey, cliflags.CAKey, baseCfg.SSLCAKey) 469 IntFlag(f, &keySize, cliflags.KeySize, defaultKeySize) 470 BoolFlag(f, &overwriteFiles, cliflags.OverwriteFiles, false) 471 } 472 // PKCS8 key format is only available for the client cert command. 473 BoolFlag(createClientCertCmd.Flags(), &generatePKCS8Key, cliflags.GeneratePKCS8Key, false) 474 475 clientCmds := []*cobra.Command{ 476 debugGossipValuesCmd, 477 debugTimeSeriesDumpCmd, 478 debugZipCmd, 479 dumpCmd, 480 genHAProxyCmd, 481 initCmd, 482 quitCmd, 483 sqlShellCmd, 484 /* StartCmds are covered above */ 485 } 486 clientCmds = append(clientCmds, authCmds...) 487 clientCmds = append(clientCmds, nodeCmds...) 488 clientCmds = append(clientCmds, systemBenchCmds...) 489 clientCmds = append(clientCmds, nodeLocalCmds...) 490 for _, cmd := range clientCmds { 491 f := cmd.PersistentFlags() 492 VarFlag(f, addrSetter{&cliCtx.clientConnHost, &cliCtx.clientConnPort}, cliflags.ClientHost) 493 StringFlag(f, &cliCtx.clientConnPort, cliflags.ClientPort, cliCtx.clientConnPort) 494 _ = f.MarkHidden(cliflags.ClientPort.Name) 495 496 BoolFlag(f, &baseCfg.Insecure, cliflags.ClientInsecure, baseCfg.Insecure) 497 498 // Certificate flags. 499 StringFlag(f, &baseCfg.SSLCertsDir, cliflags.CertsDir, baseCfg.SSLCertsDir) 500 // Certificate principal map. 501 StringSlice(f, &cliCtx.certPrincipalMap, 502 cliflags.CertPrincipalMap, cliCtx.certPrincipalMap) 503 } 504 505 // Auth commands. 506 { 507 f := loginCmd.Flags() 508 DurationFlag(f, &authCtx.validityPeriod, cliflags.AuthTokenValidityPeriod, authCtx.validityPeriod) 509 BoolFlag(f, &authCtx.onlyCookie, cliflags.OnlyCookie, authCtx.onlyCookie) 510 } 511 512 timeoutCmds := []*cobra.Command{ 513 statusNodeCmd, 514 lsNodesCmd, 515 debugZipCmd, 516 // If you add something here, make sure the actual implementation 517 // of the command uses `cmdTimeoutContext(.)` or it will ignore 518 // the timeout. 519 } 520 521 for _, cmd := range timeoutCmds { 522 DurationFlag(cmd.Flags(), &cliCtx.cmdTimeout, cliflags.Timeout, cliCtx.cmdTimeout) 523 } 524 525 // Node Status command. 526 { 527 f := statusNodeCmd.Flags() 528 BoolFlag(f, &nodeCtx.statusShowRanges, cliflags.NodeRanges, nodeCtx.statusShowRanges) 529 BoolFlag(f, &nodeCtx.statusShowStats, cliflags.NodeStats, nodeCtx.statusShowStats) 530 BoolFlag(f, &nodeCtx.statusShowAll, cliflags.NodeAll, nodeCtx.statusShowAll) 531 BoolFlag(f, &nodeCtx.statusShowDecommission, cliflags.NodeDecommission, nodeCtx.statusShowDecommission) 532 } 533 534 // HDD Bench command. 535 { 536 f := seqWriteBench.Flags() 537 VarFlag(f, humanizeutil.NewBytesValue(&systemBenchCtx.writeSize), cliflags.WriteSize) 538 VarFlag(f, humanizeutil.NewBytesValue(&systemBenchCtx.syncInterval), cliflags.SyncInterval) 539 } 540 541 // Network Bench command. 542 { 543 f := networkBench.Flags() 544 BoolFlag(f, &networkBenchCtx.server, cliflags.BenchServer, networkBenchCtx.server) 545 IntFlag(f, &networkBenchCtx.port, cliflags.BenchPort, networkBenchCtx.port) 546 StringSlice(f, &networkBenchCtx.addresses, cliflags.BenchAddresses, networkBenchCtx.addresses) 547 BoolFlag(f, &networkBenchCtx.latency, cliflags.BenchLatency, networkBenchCtx.latency) 548 } 549 550 // Bench command. 551 { 552 for _, cmd := range systemBenchCmds { 553 f := cmd.Flags() 554 IntFlag(f, &systemBenchCtx.concurrency, cliflags.BenchConcurrency, systemBenchCtx.concurrency) 555 DurationFlag(f, &systemBenchCtx.duration, cliflags.BenchDuration, systemBenchCtx.duration) 556 StringFlag(f, &systemBenchCtx.tempDir, cliflags.TempDir, systemBenchCtx.tempDir) 557 } 558 } 559 560 // Zip command. 561 { 562 f := debugZipCmd.Flags() 563 VarFlag(f, &zipCtx.nodes.inclusive, cliflags.ZipNodes) 564 VarFlag(f, &zipCtx.nodes.exclusive, cliflags.ZipExcludeNodes) 565 } 566 567 // Decommission command. 568 VarFlag(decommissionNodeCmd.Flags(), &nodeCtx.nodeDecommissionWait, cliflags.Wait) 569 570 // Quit and node drain commands. 571 for _, cmd := range []*cobra.Command{quitCmd, drainNodeCmd} { 572 f := cmd.Flags() 573 DurationFlag(f, &quitCtx.drainWait, cliflags.DrainWait, quitCtx.drainWait) 574 } 575 576 // SQL and demo commands. 577 for _, cmd := range append([]*cobra.Command{sqlShellCmd, demoCmd}, demoCmd.Commands()...) { 578 f := cmd.Flags() 579 VarFlag(f, &sqlCtx.setStmts, cliflags.Set) 580 VarFlag(f, &sqlCtx.execStmts, cliflags.Execute) 581 DurationFlag(f, &sqlCtx.repeatDelay, cliflags.Watch, sqlCtx.repeatDelay) 582 BoolFlag(f, &sqlCtx.safeUpdates, cliflags.SafeUpdates, sqlCtx.safeUpdates) 583 BoolFlag(f, &sqlCtx.debugMode, cliflags.CliDebugMode, sqlCtx.debugMode) 584 } 585 586 VarFlag(dumpCmd.Flags(), &dumpCtx.dumpMode, cliflags.DumpMode) 587 StringFlag(dumpCmd.Flags(), &dumpCtx.asOf, cliflags.DumpTime, dumpCtx.asOf) 588 BoolFlag(dumpCmd.Flags(), &dumpCtx.dumpAll, cliflags.DumpAll, dumpCtx.dumpAll) 589 590 // Commands that establish a SQL connection. 591 sqlCmds := []*cobra.Command{sqlShellCmd, dumpCmd, demoCmd} 592 sqlCmds = append(sqlCmds, authCmds...) 593 sqlCmds = append(sqlCmds, demoCmd.Commands()...) 594 sqlCmds = append(sqlCmds, nodeLocalCmds...) 595 for _, cmd := range sqlCmds { 596 f := cmd.Flags() 597 BoolFlag(f, &sqlCtx.echo, cliflags.EchoSQL, sqlCtx.echo) 598 599 if cmd != demoCmd { 600 VarFlag(f, urlParser{cmd, &cliCtx, false /* strictSSL */}, cliflags.URL) 601 StringFlag(f, &cliCtx.sqlConnUser, cliflags.User, cliCtx.sqlConnUser) 602 603 // Even though SQL commands take their connection parameters via 604 // --url / --user (see above), the urlParser{} struct internally 605 // needs the ClientHost and ClientPort flags to be defined - 606 // even if they are invisible - due to the way initialization from 607 // env vars is implemented. 608 // 609 // TODO(knz): if/when env var option initialization is deferred 610 // to parse time, this can be removed. 611 VarFlag(f, addrSetter{&cliCtx.clientConnHost, &cliCtx.clientConnPort}, cliflags.ClientHost) 612 _ = f.MarkHidden(cliflags.ClientHost.Name) 613 StringFlag(f, &cliCtx.clientConnPort, cliflags.ClientPort, cliCtx.clientConnPort) 614 _ = f.MarkHidden(cliflags.ClientPort.Name) 615 616 } 617 618 if cmd == sqlShellCmd { 619 StringFlag(f, &cliCtx.sqlConnDBName, cliflags.Database, cliCtx.sqlConnDBName) 620 } 621 } 622 623 // Make the non-SQL client commands also recognize --url in strict SSL mode 624 // and ensure they can connect to clusters that use a cluster-name. 625 for _, cmd := range clientCmds { 626 if f := flagSetForCmd(cmd).Lookup(cliflags.URL.Name); f != nil { 627 // --url already registered above, nothing to do. 628 continue 629 } 630 f := cmd.PersistentFlags() 631 VarFlag(f, urlParser{cmd, &cliCtx, true /* strictSSL */}, cliflags.URL) 632 VarFlag(f, clusterNameSetter{&baseCfg.ClusterName}, cliflags.ClusterName) 633 BoolFlag(f, &baseCfg.DisableClusterNameVerification, 634 cliflags.DisableClusterNameVerification, baseCfg.DisableClusterNameVerification) 635 } 636 637 // Commands that print tables. 638 tableOutputCommands := append( 639 []*cobra.Command{sqlShellCmd, genSettingsListCmd, demoCmd}, 640 demoCmd.Commands()...) 641 tableOutputCommands = append(tableOutputCommands, nodeCmds...) 642 tableOutputCommands = append(tableOutputCommands, authCmds...) 643 644 // By default, these commands print their output as pretty-formatted 645 // tables on terminals, and TSV when redirected to a file. The user 646 // can override with --format. 647 // By default, query times are not displayed. The default is overridden 648 // in the CLI shell. 649 for _, cmd := range tableOutputCommands { 650 f := cmd.PersistentFlags() 651 VarFlag(f, &cliCtx.tableDisplayFormat, cliflags.TableDisplayFormat) 652 } 653 654 // demo command. 655 demoFlags := demoCmd.PersistentFlags() 656 // We add this command as a persistent flag so you can do stuff like 657 // ./cockroach demo movr --nodes=3. 658 IntFlag(demoFlags, &demoCtx.nodes, cliflags.DemoNodes, demoCtx.nodes) 659 BoolFlag(demoFlags, &demoCtx.runWorkload, cliflags.RunDemoWorkload, demoCtx.runWorkload) 660 VarFlag(demoFlags, &demoCtx.localities, cliflags.DemoNodeLocality) 661 BoolFlag(demoFlags, &demoCtx.geoPartitionedReplicas, cliflags.DemoGeoPartitionedReplicas, demoCtx.geoPartitionedReplicas) 662 VarFlag(demoFlags, demoNodeSQLMemSizeValue, cliflags.DemoNodeSQLMemSize) 663 VarFlag(demoFlags, demoNodeCacheSizeValue, cliflags.DemoNodeCacheSize) 664 BoolFlag(demoFlags, &demoCtx.insecure, cliflags.ClientInsecure, demoCtx.insecure) 665 BoolFlag(demoFlags, &demoCtx.disableLicenseAcquisition, cliflags.DemoNoLicense, demoCtx.disableLicenseAcquisition) 666 // Mark the --global flag as hidden until we investigate it more. 667 BoolFlag(demoFlags, &demoCtx.simulateLatency, cliflags.Global, demoCtx.simulateLatency) 668 _ = demoFlags.MarkHidden(cliflags.Global.Name) 669 // The --empty flag is only valid for the top level demo command, 670 // so we use the regular flag set. 671 BoolFlag(demoCmd.Flags(), &demoCtx.useEmptyDatabase, cliflags.UseEmptyDatabase, demoCtx.useEmptyDatabase) 672 StringFlag(demoFlags, &demoCtx.geoLibsDir, cliflags.GeoLibsDir, demoCtx.geoLibsDir) 673 674 // sqlfmt command. 675 fmtFlags := sqlfmtCmd.Flags() 676 VarFlag(fmtFlags, &sqlfmtCtx.execStmts, cliflags.Execute) 677 cfg := tree.DefaultPrettyCfg() 678 IntFlag(fmtFlags, &sqlfmtCtx.len, cliflags.SQLFmtLen, cfg.LineWidth) 679 BoolFlag(fmtFlags, &sqlfmtCtx.useSpaces, cliflags.SQLFmtSpaces, !cfg.UseTabs) 680 IntFlag(fmtFlags, &sqlfmtCtx.tabWidth, cliflags.SQLFmtTabWidth, cfg.TabWidth) 681 BoolFlag(fmtFlags, &sqlfmtCtx.noSimplify, cliflags.SQLFmtNoSimplify, !cfg.Simplify) 682 BoolFlag(fmtFlags, &sqlfmtCtx.align, cliflags.SQLFmtAlign, (cfg.Align != tree.PrettyNoAlign)) 683 684 // Debug commands. 685 { 686 f := debugKeysCmd.Flags() 687 VarFlag(f, (*mvccKey)(&debugCtx.startKey), cliflags.From) 688 VarFlag(f, (*mvccKey)(&debugCtx.endKey), cliflags.To) 689 IntFlag(f, &debugCtx.maxResults, cliflags.Limit, debugCtx.maxResults) 690 BoolFlag(f, &debugCtx.values, cliflags.Values, debugCtx.values) 691 BoolFlag(f, &debugCtx.sizes, cliflags.Sizes, debugCtx.sizes) 692 } 693 { 694 f := debugRangeDataCmd.Flags() 695 BoolFlag(f, &debugCtx.replicated, cliflags.Replicated, debugCtx.replicated) 696 IntFlag(f, &debugCtx.maxResults, cliflags.Limit, debugCtx.maxResults) 697 } 698 { 699 f := debugGossipValuesCmd.Flags() 700 StringFlag(f, &debugCtx.inputFile, cliflags.GossipInputFile, debugCtx.inputFile) 701 BoolFlag(f, &debugCtx.printSystemConfig, cliflags.PrintSystemConfig, debugCtx.printSystemConfig) 702 } 703 { 704 f := debugBallastCmd.Flags() 705 VarFlag(f, &debugCtx.ballastSize, cliflags.Size) 706 } 707 } 708 709 // processEnvVarDefaults injects the current value of flag-related 710 // environment variables into the initial value of the settings linked 711 // to the flags, during initialization and before the command line is 712 // actually parsed. For example, it will inject the value of 713 // $COCKROACH_URL into the urlParser object linked to the --url flag. 714 func processEnvVarDefaults() error { 715 for _, d := range envVarDefaults { 716 f := d.flagSet.Lookup(d.flagName) 717 if f == nil { 718 panic(errors.AssertionFailedf("unknown flag: %s", d.flagName)) 719 } 720 var err error 721 if url, ok := f.Value.(urlParser); ok { 722 // URLs are a special case: they can emit a warning if there's 723 // excess configuration for certain commands. 724 // Since the env-var initialization is ran for all commands 725 // all the time, regardless of which particular command is 726 // currently active, we want to silence this warning here. 727 // 728 // TODO(knz): rework this code to only pull env var values 729 // for the current command. 730 err = url.setInternal(d.envValue, false /* warn */) 731 } else { 732 err = d.flagSet.Set(d.flagName, d.envValue) 733 } 734 if err != nil { 735 return errors.Wrapf(err, "setting --%s from %s", d.flagName, d.envVar) 736 } 737 } 738 return nil 739 } 740 741 // envVarDefault describes a delayed default initialization of the 742 // setting covered by a flag from the value of an environment 743 // variable. 744 type envVarDefault struct { 745 envVar string 746 envValue string 747 flagName string 748 flagSet *pflag.FlagSet 749 } 750 751 // envVarDefaults records the initializations from environment variables 752 // for processing at the end of initialization, before flag parsing. 753 var envVarDefaults []envVarDefault 754 755 // registerEnvVarDefault registers a deferred initialization of a flag 756 // from an environment variable. 757 func registerEnvVarDefault(f *pflag.FlagSet, flagInfo cliflags.FlagInfo) { 758 if flagInfo.EnvVar == "" { 759 return 760 } 761 value, set := envutil.EnvString(flagInfo.EnvVar, 2) 762 if !set { 763 // Env var not set. Nothing to do. 764 return 765 } 766 envVarDefaults = append(envVarDefaults, envVarDefault{ 767 envVar: flagInfo.EnvVar, 768 envValue: value, 769 flagName: flagInfo.Name, 770 flagSet: f, 771 }) 772 } 773 774 // extraServerFlagInit configures the server.Config based on the command-line flags. 775 // It is only called when the command being ran is one of the start commands. 776 func extraServerFlagInit(cmd *cobra.Command) error { 777 if err := security.SetCertPrincipalMap(startCtx.serverCertPrincipalMap); err != nil { 778 return err 779 } 780 serverCfg.User = security.NodeUser 781 serverCfg.Insecure = startCtx.serverInsecure 782 serverCfg.SSLCertsDir = startCtx.serverSSLCertsDir 783 784 // Construct the main RPC listen address. 785 serverCfg.Addr = net.JoinHostPort(startCtx.serverListenAddr, serverListenPort) 786 787 fs := flagSetForCmd(cmd) 788 789 // Construct the socket name, if requested. 790 if !fs.Lookup(cliflags.Socket.Name).Changed && fs.Lookup(cliflags.SocketDir.Name).Changed { 791 // If --socket (DEPRECATED) was set, then serverCfg.SocketFile is 792 // already set and we don't want to change it. 793 // However, if --socket-dir is set, then we'll use that. 794 // There are two cases: 795 // --socket-dir is set and is empty; in this case the user is telling us "disable the socket". 796 // is set and non-empty. Then it should be used as specified. 797 if serverSocketDir == "" { 798 serverCfg.SocketFile = "" 799 } else { 800 serverCfg.SocketFile = filepath.Join(serverSocketDir, ".s.PGSQL."+serverListenPort) 801 } 802 } 803 804 // Fill in the defaults for --advertise-addr. 805 if serverAdvertiseAddr == "" { 806 serverAdvertiseAddr = startCtx.serverListenAddr 807 } 808 if serverAdvertisePort == "" { 809 serverAdvertisePort = serverListenPort 810 } 811 serverCfg.AdvertiseAddr = net.JoinHostPort(serverAdvertiseAddr, serverAdvertisePort) 812 813 // Fill in the defaults for --sql-addr. 814 if serverSQLAddr == "" { 815 serverSQLAddr = startCtx.serverListenAddr 816 } 817 if serverSQLPort == "" { 818 serverSQLPort = serverListenPort 819 } 820 serverCfg.SQLAddr = net.JoinHostPort(serverSQLAddr, serverSQLPort) 821 serverCfg.SplitListenSQL = fs.Lookup(cliflags.ListenSQLAddr.Name).Changed 822 823 // Fill in the defaults for --advertise-sql-addr. 824 advSpecified := fs.Lookup(cliflags.AdvertiseAddr.Name).Changed || 825 fs.Lookup(cliflags.AdvertiseHost.Name).Changed 826 if serverSQLAdvertiseAddr == "" { 827 if advSpecified { 828 serverSQLAdvertiseAddr = serverAdvertiseAddr 829 } else { 830 serverSQLAdvertiseAddr = serverSQLAddr 831 } 832 } 833 if serverSQLAdvertisePort == "" { 834 if advSpecified && !serverCfg.SplitListenSQL { 835 serverSQLAdvertisePort = serverAdvertisePort 836 } else { 837 serverSQLAdvertisePort = serverSQLPort 838 } 839 } 840 serverCfg.SQLAdvertiseAddr = net.JoinHostPort(serverSQLAdvertiseAddr, serverSQLAdvertisePort) 841 842 // Fill in the defaults for --http-addr. 843 if serverHTTPAddr == "" { 844 serverHTTPAddr = startCtx.serverListenAddr 845 } 846 if startCtx.unencryptedLocalhostHTTP { 847 // If --unencrypted-localhost-http was specified, we want to 848 // override whatever was specified or derived from other flags for 849 // the host part of --http-addr. 850 // 851 // Before we do so, we'll check whether the user explicitly 852 // specified something contradictory, and tell them that's no 853 // good. 854 if (fs.Lookup(cliflags.ListenHTTPAddr.Name).Changed || 855 fs.Lookup(cliflags.ListenHTTPAddrAlias.Name).Changed) && 856 (serverHTTPAddr != "" && serverHTTPAddr != "localhost") { 857 return errors.WithHintf( 858 errors.Newf("--unencrypted-localhost-http is incompatible with --http-addr=%s:%s", 859 serverHTTPAddr, serverHTTPPort), 860 `When --unencrypted-localhost-http is specified, use --http-addr=:%s or omit --http-addr entirely.`, serverHTTPPort) 861 } 862 863 // Now do the override proper. 864 serverHTTPAddr = "localhost" 865 // We then also tell the server to disable TLS for the HTTP 866 // listener. 867 serverCfg.DisableTLSForHTTP = true 868 } 869 serverCfg.HTTPAddr = net.JoinHostPort(serverHTTPAddr, serverHTTPPort) 870 871 // Fill the advertise port into the locality advertise addresses. 872 for i, addr := range localityAdvertiseHosts { 873 host, port, err := netutil.SplitHostPort(addr.Address.AddressField, serverAdvertisePort) 874 if err != nil { 875 return err 876 } 877 localityAdvertiseHosts[i].Address.AddressField = net.JoinHostPort(host, port) 878 } 879 serverCfg.LocalityAddresses = localityAdvertiseHosts 880 881 return nil 882 } 883 884 func extraClientFlagInit() error { 885 if err := security.SetCertPrincipalMap(cliCtx.certPrincipalMap); err != nil { 886 return err 887 } 888 serverCfg.Addr = net.JoinHostPort(cliCtx.clientConnHost, cliCtx.clientConnPort) 889 serverCfg.AdvertiseAddr = serverCfg.Addr 890 serverCfg.SQLAddr = net.JoinHostPort(cliCtx.clientConnHost, cliCtx.clientConnPort) 891 serverCfg.SQLAdvertiseAddr = serverCfg.SQLAddr 892 if serverHTTPAddr == "" { 893 serverHTTPAddr = startCtx.serverListenAddr 894 } 895 serverCfg.HTTPAddr = net.JoinHostPort(serverHTTPAddr, serverHTTPPort) 896 897 // If CLI/SQL debug mode is requested, override the echo mode here, 898 // so that the initial client/server handshake reveals the SQL being 899 // sent. 900 if sqlCtx.debugMode { 901 sqlCtx.echo = true 902 } 903 return nil 904 } 905 906 func setDefaultStderrVerbosity(cmd *cobra.Command, defaultSeverity log.Severity) error { 907 vf := flagSetForCmd(cmd).Lookup(logflags.LogToStderrName) 908 909 // if `--logtostderr` was not specified and no log directory was 910 // set, or `--logtostderr` was specified but without explicit level, 911 // then set stderr logging to the level considered default by the 912 // specific command. 913 if (!vf.Changed && !log.DirSet()) || 914 (vf.Changed && vf.Value.String() == log.Severity_DEFAULT.String()) { 915 if err := vf.Value.Set(defaultSeverity.String()); err != nil { 916 return err 917 } 918 } 919 920 return nil 921 }