From acd4241740a7c394f9841b1e6b947d0bde617a14 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Mon, 4 Sep 2017 23:50:13 -0400 Subject: [PATCH] Drop cli and meta packages This centralizes all command-related things in the command package --- cli/main.go | 53 -------- cli/help.go => command/main.go | 82 ++++++++---- main.go | 4 +- meta/meta.go | 233 --------------------------------- meta/meta_test.go | 41 ------ 5 files changed, 61 insertions(+), 352 deletions(-) delete mode 100644 cli/main.go rename cli/help.go => command/main.go (50%) delete mode 100644 meta/meta.go delete mode 100644 meta/meta_test.go diff --git a/cli/main.go b/cli/main.go deleted file mode 100644 index 000e1e9a4..000000000 --- a/cli/main.go +++ /dev/null @@ -1,53 +0,0 @@ -package cli - -import ( - "fmt" - "os" - - "github.com/mitchellh/cli" -) - -func Run(args []string) int { - return RunCustom(args, Commands(nil)) -} - -func RunCustom(args []string, commands map[string]cli.CommandFactory) int { - // Get the command line args. We shortcut "--version" and "-v" to - // just show the version. - for _, arg := range args { - if arg == "-v" || arg == "-version" || arg == "--version" { - newArgs := make([]string, len(args)+1) - newArgs[0] = "version" - copy(newArgs[1:], args) - args = newArgs - break - } - } - - // Build the commands to include in the help now. This is pretty... - // tedious, but we don't have a better way at the moment. - commandsInclude := make([]string, 0, len(commands)) - for k, _ := range commands { - switch k { - case "token-disk": - default: - commandsInclude = append(commandsInclude, k) - } - } - - cli := &cli.CLI{ - Args: args, - Commands: commands, - Name: "vault", - Autocomplete: true, - HelpFunc: cli.FilteredHelpFunc(commandsInclude, HelpFunc), - } - - exitCode, err := cli.Run() - if err != nil { - fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error()) - return 1 - } - - return exitCode -} diff --git a/cli/help.go b/command/main.go similarity index 50% rename from cli/help.go rename to command/main.go index bd66e335a..731deb227 100644 --- a/cli/help.go +++ b/command/main.go @@ -1,51 +1,87 @@ -package cli +package command import ( "bytes" "fmt" + "os" "sort" "strings" "github.com/mitchellh/cli" ) -// HelpFunc is a cli.HelpFunc that can is used to output the help for Vault. -func HelpFunc(commands map[string]cli.CommandFactory) string { +func Run(args []string) int { + // Handle -v shorthand + for _, arg := range args { + if arg == "--" { + break + } + + if arg == "-v" || arg == "-version" || arg == "--version" { + args = []string{"version"} + break + } + } + + cli := &cli.CLI{ + Name: "vault", + Args: args, + Commands: Commands, + HelpFunc: helpFunc, + + Autocomplete: true, + AutocompleteNoDefaultFlags: true, + } + + exitCode, err := cli.Run() + if err != nil { + fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error()) + return 1 + } + + return exitCode +} + +// helpFunc is a cli.HelpFunc that can is used to output the help for Vault. +func helpFunc(commands map[string]cli.CommandFactory) string { commonNames := map[string]struct{}{ - "delete": struct{}{}, - "path-help": struct{}{}, - "read": struct{}{}, - "renew": struct{}{}, - "revoke": struct{}{}, - "write": struct{}{}, - "server": struct{}{}, - "status": struct{}{}, - "unwrap": struct{}{}, + "delete": struct{}{}, + "read": struct{}{}, + "renew": struct{}{}, + "revoke": struct{}{}, + "server": struct{}{}, + "status": struct{}{}, + "unwrap": struct{}{}, + "write": struct{}{}, } // Determine the maximum key length, and classify based on type commonCommands := make(map[string]cli.CommandFactory) otherCommands := make(map[string]cli.CommandFactory) - maxKeyLen := 0 - for key, f := range commands { - if len(key) > maxKeyLen { - maxKeyLen = len(key) - } + commonKeyLen, otherKeyLen := 0, 0 + for key, f := range commands { if _, ok := commonNames[key]; ok { + if len(key) > commonKeyLen { + commonKeyLen = len(key) + } commonCommands[key] = f } else { + if len(key) > otherKeyLen { + otherKeyLen = len(key) + } otherCommands[key] = f } } var buf bytes.Buffer - buf.WriteString("usage: vault [-version] [-help] [args]\n\n") - buf.WriteString("Common commands:\n") - buf.WriteString(listCommands(commonCommands, maxKeyLen)) - buf.WriteString("\nAll other commands:\n") - buf.WriteString(listCommands(otherCommands, maxKeyLen)) - return buf.String() + buf.WriteString("Usage: vault [args]\n\n") + buf.WriteString("Common commands:\n\n") + buf.WriteString(listCommands(commonCommands, commonKeyLen)) + buf.WriteString("\n") + buf.WriteString("Other commands:\n\n") + buf.WriteString(listCommands(otherCommands, otherKeyLen)) + return strings.TrimSpace(buf.String()) } // listCommands just lists the commands in the map with the diff --git a/main.go b/main.go index 6cd34fe36..7e4b1c9d2 100644 --- a/main.go +++ b/main.go @@ -3,9 +3,9 @@ package main // import "github.com/hashicorp/vault" import ( "os" - "github.com/hashicorp/vault/cli" + "github.com/hashicorp/vault/command" ) func main() { - os.Exit(cli.Run(os.Args[1:])) + os.Exit(command.Run(os.Args[1:])) } diff --git a/meta/meta.go b/meta/meta.go deleted file mode 100644 index d75f1054c..000000000 --- a/meta/meta.go +++ /dev/null @@ -1,233 +0,0 @@ -package meta - -import ( - "bufio" - "flag" - "io" - "os" - - "github.com/hashicorp/errwrap" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/command/token" - "github.com/hashicorp/vault/helper/flag-slice" - "github.com/mitchellh/cli" -) - -// FlagSetFlags is an enum to define what flags are present in the -// default FlagSet returned by Meta.FlagSet. -type FlagSetFlags uint - -type TokenHelperFunc func() (token.TokenHelper, error) - -const ( - FlagSetNone FlagSetFlags = 0 - FlagSetServer FlagSetFlags = 1 << iota - FlagSetDefault = FlagSetServer -) - -var ( - additionalOptionsUsage = func() string { - return ` - -wrap-ttl="" Indicates that the response should be wrapped in a - cubbyhole token with the requested TTL. The response - can be fetched by calling the "sys/wrapping/unwrap" - endpoint, passing in the wrapping token's ID. This - is a numeric string with an optional suffix - "s", "m", or "h"; if no suffix is specified it will - be parsed as seconds. May also be specified via - VAULT_WRAP_TTL. - - -policy-override Indicates that any soft-mandatory Sentinel policies - be overridden. -` - } -) - -// Meta contains the meta-options and functionality that nearly every -// Vault command inherits. -type Meta struct { - ClientToken string - Ui cli.Ui - - // The things below can be set, but aren't common - ForceAddress string // Address to force for API clients - - // These are set by the command line flags. - flagAddress string - flagCACert string - flagCAPath string - flagClientCert string - flagClientKey string - flagWrapTTL string - flagInsecure bool - flagMFA []string - flagPolicyOverride bool - - // Queried if no token can be found - TokenHelper TokenHelperFunc -} - -func (m *Meta) DefaultWrappingLookupFunc(operation, path string) string { - if m.flagWrapTTL != "" { - return m.flagWrapTTL - } - - return api.DefaultWrappingLookupFunc(operation, path) -} - -// Client returns the API client to a Vault server given the configured -// flag settings for this command. -func (m *Meta) Client() (*api.Client, error) { - config := api.DefaultConfig() - - err := config.ReadEnvironment() - if err != nil { - return nil, errwrap.Wrapf("error reading environment: {{err}}", err) - } - - if m.flagAddress != "" { - config.Address = m.flagAddress - } - if m.ForceAddress != "" { - config.Address = m.ForceAddress - } - // If we need custom TLS configuration, then set it - if m.flagCACert != "" || m.flagCAPath != "" || m.flagClientCert != "" || m.flagClientKey != "" || m.flagInsecure { - t := &api.TLSConfig{ - CACert: m.flagCACert, - CAPath: m.flagCAPath, - ClientCert: m.flagClientCert, - ClientKey: m.flagClientKey, - TLSServerName: "", - Insecure: m.flagInsecure, - } - config.ConfigureTLS(t) - } - - // Build the client - client, err := api.NewClient(config) - if err != nil { - return nil, err - } - - client.SetWrappingLookupFunc(m.DefaultWrappingLookupFunc) - - var mfaCreds []string - - // Extract the MFA credentials from environment variable first - if os.Getenv(api.EnvVaultMFA) != "" { - mfaCreds = []string{os.Getenv(api.EnvVaultMFA)} - } - - // If CLI MFA flags were supplied, prefer that over environment variable - if len(m.flagMFA) != 0 { - mfaCreds = m.flagMFA - } - - client.SetMFACreds(mfaCreds) - - client.SetPolicyOverride(m.flagPolicyOverride) - - // If we have a token directly, then set that - token := m.ClientToken - - // Try to set the token to what is already stored - if token == "" { - token = client.Token() - } - - // If we don't have a token, check the token helper - if token == "" { - if m.TokenHelper != nil { - // If we have a token, then set that - tokenHelper, err := m.TokenHelper() - if err != nil { - return nil, err - } - token, err = tokenHelper.Get() - if err != nil { - return nil, err - } - } - } - - // Set the token - if token != "" { - client.SetToken(token) - } - - return client, nil -} - -// FlagSet returns a FlagSet with the common flags that every -// command implements. The exact behavior of FlagSet can be configured -// using the flags as the second parameter, for example to disable -// server settings on the commands that don't talk to a server. -func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet { - f := flag.NewFlagSet(n, flag.ContinueOnError) - - // FlagSetServer tells us to enable the settings for selecting - // the server information. - if fs&FlagSetServer != 0 { - f.StringVar(&m.flagAddress, "address", "", "") - f.StringVar(&m.flagCACert, "ca-cert", "", "") - f.StringVar(&m.flagCAPath, "ca-path", "", "") - f.StringVar(&m.flagClientCert, "client-cert", "", "") - f.StringVar(&m.flagClientKey, "client-key", "", "") - f.StringVar(&m.flagWrapTTL, "wrap-ttl", "", "") - f.BoolVar(&m.flagInsecure, "insecure", false, "") - f.BoolVar(&m.flagInsecure, "tls-skip-verify", false, "") - f.BoolVar(&m.flagPolicyOverride, "policy-override", false, "") - f.Var((*sliceflag.StringFlag)(&m.flagMFA), "mfa", "") - } - - // Create an io.Writer that writes to our Ui properly for errors. - // This is kind of a hack, but it does the job. Basically: create - // a pipe, use a scanner to break it into lines, and output each line - // to the UI. Do this forever. - errR, errW := io.Pipe() - errScanner := bufio.NewScanner(errR) - go func() { - for errScanner.Scan() { - m.Ui.Error(errScanner.Text()) - } - }() - f.SetOutput(errW) - - return f -} - -// GeneralOptionsUsage returns the usage documentation for commonly -// available options -func GeneralOptionsUsage() string { - general := ` - -address=addr The address of the Vault server. - Overrides the VAULT_ADDR environment variable if set. - - -ca-cert=path Path to a PEM encoded CA cert file to use to - verify the Vault server SSL certificate. - Overrides the VAULT_CACERT environment variable if set. - - -ca-path=path Path to a directory of PEM encoded CA cert files - to verify the Vault server SSL certificate. If both - -ca-cert and -ca-path are specified, -ca-cert is used. - Overrides the VAULT_CAPATH environment variable if set. - - -client-cert=path Path to a PEM encoded client certificate for TLS - authentication to the Vault server. Must also specify - -client-key. Overrides the VAULT_CLIENT_CERT - environment variable if set. - - -client-key=path Path to an unencrypted PEM encoded private key - matching the client certificate from -client-cert. - Overrides the VAULT_CLIENT_KEY environment variable - if set. - - -tls-skip-verify Do not verify TLS certificate. This is highly - not recommended. Verification will also be skipped - if VAULT_SKIP_VERIFY is set. -` - - general += additionalOptionsUsage() - return general -} diff --git a/meta/meta_test.go b/meta/meta_test.go deleted file mode 100644 index 99a294d24..000000000 --- a/meta/meta_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package meta - -import ( - "flag" - "reflect" - "sort" - "testing" -) - -func TestFlagSet(t *testing.T) { - cases := []struct { - Flags FlagSetFlags - Expected []string - }{ - { - FlagSetNone, - []string{}, - }, - { - FlagSetServer, - []string{"address", "ca-cert", "ca-path", "client-cert", "client-key", "insecure", "mfa", "policy-override", "tls-skip-verify", "wrap-ttl"}, - }, - } - - for i, tc := range cases { - var m Meta - fs := m.FlagSet("foo", tc.Flags) - - actual := make([]string, 0, 0) - fs.VisitAll(func(f *flag.Flag) { - actual = append(actual, f.Name) - }) - sort.Strings(actual) - sort.Strings(tc.Expected) - - if !reflect.DeepEqual(actual, tc.Expected) { - t.Fatalf("%d: flags: %#v\n\nExpected: %#v\nGot: %#v", - i, tc.Flags, tc.Expected, actual) - } - } -}