mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-03 20:17:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			267 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package command
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
	"text/tabwriter"
 | 
						|
 | 
						|
	"github.com/fatih/color"
 | 
						|
	"github.com/hashicorp/vault/api"
 | 
						|
	"github.com/hashicorp/vault/command/token"
 | 
						|
	colorable "github.com/mattn/go-colorable"
 | 
						|
	"github.com/mitchellh/cli"
 | 
						|
)
 | 
						|
 | 
						|
type VaultUI struct {
 | 
						|
	cli.Ui
 | 
						|
	format string
 | 
						|
}
 | 
						|
 | 
						|
// setupEnv parses args and may replace them and sets some env vars to known
 | 
						|
// values based on format options
 | 
						|
func setupEnv(args []string) (retArgs []string, format string, outputCurlString bool) {
 | 
						|
	var nextArgFormat bool
 | 
						|
 | 
						|
	for _, arg := range args {
 | 
						|
		if nextArgFormat {
 | 
						|
			nextArgFormat = false
 | 
						|
			format = arg
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if arg == "--" {
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		if len(args) == 1 && (arg == "-v" || arg == "-version" || arg == "--version") {
 | 
						|
			args = []string{"version"}
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		if arg == "-output-curl-string" {
 | 
						|
			outputCurlString = true
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// Parse a given flag here, which overrides the env var
 | 
						|
		if strings.HasPrefix(arg, "--format=") {
 | 
						|
			format = strings.TrimPrefix(arg, "--format=")
 | 
						|
		}
 | 
						|
		if strings.HasPrefix(arg, "-format=") {
 | 
						|
			format = strings.TrimPrefix(arg, "-format=")
 | 
						|
		}
 | 
						|
		// For backwards compat, it could be specified without an equal sign
 | 
						|
		if arg == "-format" || arg == "--format" {
 | 
						|
			nextArgFormat = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	envVaultFormat := os.Getenv(EnvVaultFormat)
 | 
						|
	// If we did not parse a value, fetch the env var
 | 
						|
	if format == "" && envVaultFormat != "" {
 | 
						|
		format = envVaultFormat
 | 
						|
	}
 | 
						|
	// Lowercase for consistency
 | 
						|
	format = strings.ToLower(format)
 | 
						|
	if format == "" {
 | 
						|
		format = "table"
 | 
						|
	}
 | 
						|
 | 
						|
	return args, format, outputCurlString
 | 
						|
}
 | 
						|
 | 
						|
type RunOptions struct {
 | 
						|
	TokenHelper token.TokenHelper
 | 
						|
	Stdout      io.Writer
 | 
						|
	Stderr      io.Writer
 | 
						|
	Address     string
 | 
						|
	Client      *api.Client
 | 
						|
}
 | 
						|
 | 
						|
func Run(args []string) int {
 | 
						|
	return RunCustom(args, nil)
 | 
						|
}
 | 
						|
 | 
						|
// RunCustom allows passing in a base command template to pass to other
 | 
						|
// commands. Currently, this is only used for setting a custom token helper.
 | 
						|
func RunCustom(args []string, runOpts *RunOptions) int {
 | 
						|
	if runOpts == nil {
 | 
						|
		runOpts = &RunOptions{}
 | 
						|
	}
 | 
						|
 | 
						|
	var format string
 | 
						|
	var outputCurlString bool
 | 
						|
	args, format, outputCurlString = setupEnv(args)
 | 
						|
 | 
						|
	// Don't use color if disabled
 | 
						|
	useColor := true
 | 
						|
	if os.Getenv(EnvVaultCLINoColor) != "" || color.NoColor {
 | 
						|
		useColor = false
 | 
						|
	}
 | 
						|
 | 
						|
	if runOpts.Stdout == nil {
 | 
						|
		runOpts.Stdout = os.Stdout
 | 
						|
	}
 | 
						|
	if runOpts.Stderr == nil {
 | 
						|
		runOpts.Stderr = os.Stderr
 | 
						|
	}
 | 
						|
 | 
						|
	// Only use colored UI if stdout is a tty, and not disabled
 | 
						|
	if useColor && format == "table" {
 | 
						|
		if f, ok := runOpts.Stdout.(*os.File); ok {
 | 
						|
			runOpts.Stdout = colorable.NewColorable(f)
 | 
						|
		}
 | 
						|
		if f, ok := runOpts.Stderr.(*os.File); ok {
 | 
						|
			runOpts.Stderr = colorable.NewColorable(f)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		runOpts.Stdout = colorable.NewNonColorable(runOpts.Stdout)
 | 
						|
		runOpts.Stderr = colorable.NewNonColorable(runOpts.Stderr)
 | 
						|
	}
 | 
						|
 | 
						|
	uiErrWriter := runOpts.Stderr
 | 
						|
	if outputCurlString {
 | 
						|
		uiErrWriter = ioutil.Discard
 | 
						|
	}
 | 
						|
 | 
						|
	ui := &VaultUI{
 | 
						|
		Ui: &cli.ColoredUi{
 | 
						|
			ErrorColor: cli.UiColorRed,
 | 
						|
			WarnColor:  cli.UiColorYellow,
 | 
						|
			Ui: &cli.BasicUi{
 | 
						|
				Reader:      bufio.NewReader(os.Stdin),
 | 
						|
				Writer:      runOpts.Stdout,
 | 
						|
				ErrorWriter: uiErrWriter,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		format: format,
 | 
						|
	}
 | 
						|
 | 
						|
	serverCmdUi := &VaultUI{
 | 
						|
		Ui: &cli.ColoredUi{
 | 
						|
			ErrorColor: cli.UiColorRed,
 | 
						|
			WarnColor:  cli.UiColorYellow,
 | 
						|
			Ui: &cli.BasicUi{
 | 
						|
				Reader: bufio.NewReader(os.Stdin),
 | 
						|
				Writer: runOpts.Stdout,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		format: format,
 | 
						|
	}
 | 
						|
 | 
						|
	if _, ok := Formatters[format]; !ok {
 | 
						|
		ui.Error(fmt.Sprintf("Invalid output format: %s", format))
 | 
						|
		return 1
 | 
						|
	}
 | 
						|
 | 
						|
	initCommands(ui, serverCmdUi, runOpts)
 | 
						|
 | 
						|
	hiddenCommands := []string{"version"}
 | 
						|
 | 
						|
	cli := &cli.CLI{
 | 
						|
		Name:     "vault",
 | 
						|
		Args:     args,
 | 
						|
		Commands: Commands,
 | 
						|
		HelpFunc: groupedHelpFunc(
 | 
						|
			cli.BasicHelpFunc("vault"),
 | 
						|
		),
 | 
						|
		HelpWriter:                 runOpts.Stderr,
 | 
						|
		HiddenCommands:             hiddenCommands,
 | 
						|
		Autocomplete:               true,
 | 
						|
		AutocompleteNoDefaultFlags: true,
 | 
						|
	}
 | 
						|
 | 
						|
	exitCode, err := cli.Run()
 | 
						|
	if outputCurlString {
 | 
						|
		if exitCode == 0 {
 | 
						|
			fmt.Fprint(runOpts.Stderr, "Could not generate cURL command")
 | 
						|
			return 1
 | 
						|
		} else {
 | 
						|
			if api.LastOutputStringError == nil {
 | 
						|
				if exitCode == 127 {
 | 
						|
					// Usage, just pass it through
 | 
						|
					return exitCode
 | 
						|
				}
 | 
						|
				fmt.Fprint(runOpts.Stderr, "cURL command not set by API operation; run without -output-curl-string to see the generated error\n")
 | 
						|
				return exitCode
 | 
						|
			}
 | 
						|
			if api.LastOutputStringError.Error() != api.ErrOutputStringRequest {
 | 
						|
				runOpts.Stdout.Write([]byte(fmt.Sprintf("Error creating request string: %s\n", api.LastOutputStringError.Error())))
 | 
						|
				return 1
 | 
						|
			}
 | 
						|
			runOpts.Stdout.Write([]byte(fmt.Sprintf("%s\n", api.LastOutputStringError.CurlString())))
 | 
						|
			return 0
 | 
						|
		}
 | 
						|
	} else if err != nil {
 | 
						|
		fmt.Fprintf(runOpts.Stderr, "Error executing CLI: %s\n", err.Error())
 | 
						|
		return 1
 | 
						|
	}
 | 
						|
 | 
						|
	return exitCode
 | 
						|
}
 | 
						|
 | 
						|
var commonCommands = []string{
 | 
						|
	"read",
 | 
						|
	"write",
 | 
						|
	"delete",
 | 
						|
	"list",
 | 
						|
	"login",
 | 
						|
	"agent",
 | 
						|
	"server",
 | 
						|
	"status",
 | 
						|
	"unwrap",
 | 
						|
}
 | 
						|
 | 
						|
func groupedHelpFunc(f cli.HelpFunc) cli.HelpFunc {
 | 
						|
	return func(commands map[string]cli.CommandFactory) string {
 | 
						|
		var b bytes.Buffer
 | 
						|
		tw := tabwriter.NewWriter(&b, 0, 2, 6, ' ', 0)
 | 
						|
 | 
						|
		fmt.Fprintf(tw, "Usage: vault <command> [args]\n\n")
 | 
						|
		fmt.Fprintf(tw, "Common commands:\n")
 | 
						|
		for _, v := range commonCommands {
 | 
						|
			printCommand(tw, v, commands[v])
 | 
						|
		}
 | 
						|
 | 
						|
		otherCommands := make([]string, 0, len(commands))
 | 
						|
		for k := range commands {
 | 
						|
			found := false
 | 
						|
			for _, v := range commonCommands {
 | 
						|
				if k == v {
 | 
						|
					found = true
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if !found {
 | 
						|
				otherCommands = append(otherCommands, k)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		sort.Strings(otherCommands)
 | 
						|
 | 
						|
		fmt.Fprintf(tw, "\n")
 | 
						|
		fmt.Fprintf(tw, "Other commands:\n")
 | 
						|
		for _, v := range otherCommands {
 | 
						|
			printCommand(tw, v, commands[v])
 | 
						|
		}
 | 
						|
 | 
						|
		tw.Flush()
 | 
						|
 | 
						|
		return strings.TrimSpace(b.String())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func printCommand(w io.Writer, name string, cmdFn cli.CommandFactory) {
 | 
						|
	cmd, err := cmdFn()
 | 
						|
	if err != nil {
 | 
						|
		panic(fmt.Sprintf("failed to load %q command: %s", name, err))
 | 
						|
	}
 | 
						|
	fmt.Fprintf(w, "    %s\t%s\n", name, cmd.Synopsis())
 | 
						|
}
 |