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 }