github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/meta.go (about) 1 package command 2 3 import ( 4 "flag" 5 "os" 6 "strings" 7 8 "github.com/hashicorp/nomad/api" 9 colorable "github.com/mattn/go-colorable" 10 "github.com/mitchellh/cli" 11 "github.com/mitchellh/colorstring" 12 "github.com/posener/complete" 13 "golang.org/x/crypto/ssh/terminal" 14 ) 15 16 const ( 17 // Constants for CLI identifier length 18 shortId = 8 19 fullId = 36 20 ) 21 22 // FlagSetFlags is an enum to define what flags are present in the 23 // default FlagSet returned by Meta.FlagSet. 24 type FlagSetFlags uint 25 26 const ( 27 FlagSetNone FlagSetFlags = 0 28 FlagSetClient FlagSetFlags = 1 << iota 29 FlagSetDefault = FlagSetClient 30 ) 31 32 // Meta contains the meta-options and functionality that nearly every 33 // Nomad command inherits. 34 type Meta struct { 35 Ui cli.Ui 36 37 // These are set by the command line flags. 38 flagAddress string 39 40 // Whether to not-colorize output 41 noColor bool 42 43 // Whether to force colorized output 44 forceColor bool 45 46 // The region to send API requests 47 region string 48 49 // namespace to send API requests 50 namespace string 51 52 // token is used for ACLs to access privileged information 53 token string 54 55 caCert string 56 caPath string 57 clientCert string 58 clientKey string 59 tlsServerName string 60 insecure bool 61 } 62 63 // FlagSet returns a FlagSet with the common flags that every 64 // command implements. The exact behavior of FlagSet can be configured 65 // using the flags as the second parameter, for example to disable 66 // server settings on the commands that don't talk to a server. 67 func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet { 68 f := flag.NewFlagSet(n, flag.ContinueOnError) 69 70 // FlagSetClient is used to enable the settings for specifying 71 // client connectivity options. 72 if fs&FlagSetClient != 0 { 73 f.StringVar(&m.flagAddress, "address", "", "") 74 f.StringVar(&m.region, "region", "", "") 75 f.StringVar(&m.namespace, "namespace", "", "") 76 f.BoolVar(&m.noColor, "no-color", false, "") 77 f.BoolVar(&m.forceColor, "force-color", false, "") 78 f.StringVar(&m.caCert, "ca-cert", "", "") 79 f.StringVar(&m.caPath, "ca-path", "", "") 80 f.StringVar(&m.clientCert, "client-cert", "", "") 81 f.StringVar(&m.clientKey, "client-key", "", "") 82 f.BoolVar(&m.insecure, "insecure", false, "") 83 f.StringVar(&m.tlsServerName, "tls-server-name", "", "") 84 f.BoolVar(&m.insecure, "tls-skip-verify", false, "") 85 f.StringVar(&m.token, "token", "", "") 86 87 } 88 89 f.SetOutput(&uiErrorWriter{ui: m.Ui}) 90 91 return f 92 } 93 94 // AutocompleteFlags returns a set of flag completions for the given flag set. 95 func (m *Meta) AutocompleteFlags(fs FlagSetFlags) complete.Flags { 96 if fs&FlagSetClient == 0 { 97 return nil 98 } 99 100 return complete.Flags{ 101 "-address": complete.PredictAnything, 102 "-region": complete.PredictAnything, 103 "-namespace": NamespacePredictor(m.Client, nil), 104 "-no-color": complete.PredictNothing, 105 "-force-color": complete.PredictNothing, 106 "-ca-cert": complete.PredictFiles("*"), 107 "-ca-path": complete.PredictDirs("*"), 108 "-client-cert": complete.PredictFiles("*"), 109 "-client-key": complete.PredictFiles("*"), 110 "-insecure": complete.PredictNothing, 111 "-tls-server-name": complete.PredictNothing, 112 "-tls-skip-verify": complete.PredictNothing, 113 "-token": complete.PredictAnything, 114 } 115 } 116 117 // ApiClientFactory is the signature of a API client factory 118 type ApiClientFactory func() (*api.Client, error) 119 120 // Client is used to initialize and return a new API client using 121 // the default command line arguments and env vars. 122 func (m *Meta) clientConfig() *api.Config { 123 config := api.DefaultConfig() 124 125 if m.flagAddress != "" { 126 config.Address = m.flagAddress 127 } 128 if m.region != "" { 129 config.Region = m.region 130 } 131 if m.namespace != "" { 132 config.Namespace = m.namespace 133 } 134 135 if m.token != "" { 136 config.SecretID = m.token 137 } 138 139 // Override TLS configuration fields we may have received from env vars with 140 // flag arguments from the user only if they're provided. 141 if m.caCert != "" { 142 config.TLSConfig.CACert = m.caCert 143 } 144 145 if m.caPath != "" { 146 config.TLSConfig.CAPath = m.caPath 147 } 148 149 if m.clientCert != "" { 150 config.TLSConfig.ClientCert = m.clientCert 151 } 152 153 if m.clientKey != "" { 154 config.TLSConfig.ClientKey = m.clientKey 155 } 156 157 if m.tlsServerName != "" { 158 config.TLSConfig.TLSServerName = m.tlsServerName 159 } 160 161 if m.insecure { 162 config.TLSConfig.Insecure = m.insecure 163 } 164 165 return config 166 } 167 168 func (m *Meta) Client() (*api.Client, error) { 169 return api.NewClient(m.clientConfig()) 170 } 171 172 func (m *Meta) allNamespaces() bool { 173 return m.clientConfig().Namespace == api.AllNamespacesNamespace 174 } 175 176 func (m *Meta) Colorize() *colorstring.Colorize { 177 _, coloredUi := m.Ui.(*cli.ColoredUi) 178 179 return &colorstring.Colorize{ 180 Colors: colorstring.DefaultColors, 181 Disable: !coloredUi, 182 Reset: true, 183 } 184 } 185 186 func (m *Meta) SetupUi(args []string) { 187 noColor := os.Getenv(EnvNomadCLINoColor) != "" 188 forceColor := os.Getenv(EnvNomadCLIForceColor) != "" 189 190 for _, arg := range args { 191 // Check if color is set 192 if arg == "-no-color" || arg == "--no-color" { 193 noColor = true 194 } else if arg == "-force-color" || arg == "--force-color" { 195 forceColor = true 196 } 197 } 198 199 m.Ui = &cli.BasicUi{ 200 Reader: os.Stdin, 201 Writer: colorable.NewColorableStdout(), 202 ErrorWriter: colorable.NewColorableStderr(), 203 } 204 205 // Only use colored UI if not disabled and stdout is a tty or colors are 206 // forced. 207 isTerminal := terminal.IsTerminal(int(os.Stdout.Fd())) 208 useColor := !noColor && (isTerminal || forceColor) 209 if useColor { 210 m.Ui = &cli.ColoredUi{ 211 ErrorColor: cli.UiColorRed, 212 WarnColor: cli.UiColorYellow, 213 InfoColor: cli.UiColorGreen, 214 Ui: m.Ui, 215 } 216 } 217 } 218 219 type usageOptsFlags uint8 220 221 const ( 222 usageOptsDefault usageOptsFlags = 0 223 usageOptsNoNamespace = 1 << iota 224 ) 225 226 // generalOptionsUsage returns the help string for the global options. 227 func generalOptionsUsage(usageOpts usageOptsFlags) string { 228 229 helpText := ` 230 -address=<addr> 231 The address of the Nomad server. 232 Overrides the NOMAD_ADDR environment variable if set. 233 Default = http://127.0.0.1:4646 234 235 -region=<region> 236 The region of the Nomad servers to forward commands to. 237 Overrides the NOMAD_REGION environment variable if set. 238 Defaults to the Agent's local region. 239 ` 240 241 namespaceText := ` 242 -namespace=<namespace> 243 The target namespace for queries and actions bound to a namespace. 244 Overrides the NOMAD_NAMESPACE environment variable if set. 245 If set to '*', subcommands which support this functionality query 246 all namespaces authorized to user. 247 Defaults to the "default" namespace. 248 ` 249 250 // note: that although very few commands use color explicitly, all of them 251 // return red-colored text on error so we want the color flags to always be 252 // present in the help messages. 253 remainingText := ` 254 -no-color 255 Disables colored command output. Alternatively, NOMAD_CLI_NO_COLOR may be 256 set. This option takes precedence over -force-color. 257 258 -force-color 259 Forces colored command output. This can be used in cases where the usual 260 terminal detection fails. Alternatively, NOMAD_CLI_FORCE_COLOR may be set. 261 This option has no effect if -no-color is also used. 262 263 -ca-cert=<path> 264 Path to a PEM encoded CA cert file to use to verify the 265 Nomad server SSL certificate. Overrides the NOMAD_CACERT 266 environment variable if set. 267 268 -ca-path=<path> 269 Path to a directory of PEM encoded CA cert files to verify 270 the Nomad server SSL certificate. If both -ca-cert and 271 -ca-path are specified, -ca-cert is used. Overrides the 272 NOMAD_CAPATH environment variable if set. 273 274 -client-cert=<path> 275 Path to a PEM encoded client certificate for TLS authentication 276 to the Nomad server. Must also specify -client-key. Overrides 277 the NOMAD_CLIENT_CERT environment variable if set. 278 279 -client-key=<path> 280 Path to an unencrypted PEM encoded private key matching the 281 client certificate from -client-cert. Overrides the 282 NOMAD_CLIENT_KEY environment variable if set. 283 284 -tls-server-name=<value> 285 The server name to use as the SNI host when connecting via 286 TLS. Overrides the NOMAD_TLS_SERVER_NAME environment variable if set. 287 288 -tls-skip-verify 289 Do not verify TLS certificate. This is highly not recommended. Verification 290 will also be skipped if NOMAD_SKIP_VERIFY is set. 291 292 -token 293 The SecretID of an ACL token to use to authenticate API requests with. 294 Overrides the NOMAD_TOKEN environment variable if set. 295 ` 296 297 if usageOpts&usageOptsNoNamespace == 0 { 298 helpText = helpText + namespaceText 299 } 300 301 helpText = helpText + remainingText 302 return strings.TrimSpace(helpText) 303 } 304 305 // funcVar is a type of flag that accepts a function that is the string given 306 // by the user. 307 type funcVar func(s string) error 308 309 func (f funcVar) Set(s string) error { return f(s) } 310 func (f funcVar) String() string { return "" } 311 func (f funcVar) IsBoolFlag() bool { return false }