CLI Enhancements (#3897)
* Use Colored UI if stdout is a tty * Add format options to operator unseal * Add format test on operator unseal * Add -no-color output flag, and use BasicUi if no-color flag is provided * Move seal status formatting logic to OutputSealStatus * Apply no-color to warnings from DeprecatedCommands as well * Add OutputWithFormat to support arbitrary data, add format option to auth list * Add ability to output arbitrary list data on TableFormatter * Clear up switch logic on format * Add format option for list-related commands * Add format option to rest of commands that returns a client API response * Remove initOutputYAML and initOutputJSON, and use OutputWithFormat instead * Remove outputAsYAML and outputAsJSON, and use OutputWithFormat instead * Remove -no-color flag, use env var exclusively to toggle colored output * Fix compile * Remove -no-color flag in main.go * Add missing FlagSetOutputFormat * Fix generate-root/decode test * Migrate init functions to main.go * Add no-color flag back as hidden * Handle non-supported data types for TableFormatter.OutputList * Pull formatting much further up to remove the need to use c.flagFormat (#3950) * Pull formatting much further up to remove the need to use c.flagFormat Also remove OutputWithFormat as the logic can cause issues. * Use const for env var * Minor updates * Remove unnecessary check * Fix SSH output and some tests * Fix tests * Make race detector not run on generate root since it kills Travis these days * Update docs * Update docs * Address review feedback * Handle --format as well as -format
This commit is contained in:
parent
d2324066c4
commit
3189278c84
@ -99,11 +99,11 @@ func (c *Sys) generateRootUpdateCommon(path, shard, nonce string) (*GenerateRoot
|
||||
}
|
||||
|
||||
type GenerateRootStatusResponse struct {
|
||||
Nonce string
|
||||
Started bool
|
||||
Progress int
|
||||
Required int
|
||||
Complete bool
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
Progress int `json:"progress"`
|
||||
Required int `json:"required"`
|
||||
Complete bool `json:"complete"`
|
||||
EncodedToken string `json:"encoded_token"`
|
||||
EncodedRootToken string `json:"encoded_root_token"`
|
||||
PGPFingerprint string `json:"pgp_fingerprint"`
|
||||
|
@ -177,27 +177,27 @@ type RekeyInitRequest struct {
|
||||
}
|
||||
|
||||
type RekeyStatusResponse struct {
|
||||
Nonce string
|
||||
Started bool
|
||||
T int
|
||||
N int
|
||||
Progress int
|
||||
Required int
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
Required int `json:"required"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool
|
||||
Backup bool `json:"backup"`
|
||||
}
|
||||
|
||||
type RekeyUpdateResponse struct {
|
||||
Nonce string
|
||||
Complete bool
|
||||
Keys []string
|
||||
Nonce string `json:"nonce"`
|
||||
Complete bool `json:"complete"`
|
||||
Keys []string `json:"keys"`
|
||||
KeysB64 []string `json:"keys_base64"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool
|
||||
Backup bool `json:"backup"`
|
||||
}
|
||||
|
||||
type RekeyRetrieveResponse struct {
|
||||
Nonce string
|
||||
Keys map[string][]string
|
||||
Nonce string `json:"nonce"`
|
||||
Keys map[string][]string `json:"keys"`
|
||||
KeysB64 map[string][]string `json:"keys_base64"`
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ Usage: vault audit list [options]
|
||||
}
|
||||
|
||||
func (c *AuditListCommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP)
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
@ -99,13 +99,17 @@ func (c *AuditListCommand) Run(args []string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
if c.flagDetailed {
|
||||
c.UI.Output(tableOutput(c.detailedAudits(audits), nil))
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
if c.flagDetailed {
|
||||
c.UI.Output(tableOutput(c.detailedAudits(audits), nil))
|
||||
return 0
|
||||
}
|
||||
c.UI.Output(tableOutput(c.simpleAudits(audits), nil))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, audits)
|
||||
}
|
||||
|
||||
c.UI.Output(tableOutput(c.simpleAudits(audits), nil))
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *AuditListCommand) simpleAudits(audits map[string]*api.Audit) []string {
|
||||
|
@ -46,7 +46,7 @@ Usage: vault auth list [options]
|
||||
}
|
||||
|
||||
func (c *AuthListCommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP)
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
@ -55,7 +55,8 @@ func (c *AuthListCommand) Flags() *FlagSets {
|
||||
Target: &c.flagDetailed,
|
||||
Default: false,
|
||||
Usage: "Print detailed information such as configuration and replication " +
|
||||
"status about each auth method.",
|
||||
"status about each auth method. This option is only applicable to " +
|
||||
"table-formatted output.",
|
||||
})
|
||||
|
||||
return set
|
||||
@ -95,13 +96,17 @@ func (c *AuthListCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
if c.flagDetailed {
|
||||
c.UI.Output(tableOutput(c.detailedMounts(auths), nil))
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
if c.flagDetailed {
|
||||
c.UI.Output(tableOutput(c.detailedMounts(auths), nil))
|
||||
return 0
|
||||
}
|
||||
c.UI.Output(tableOutput(c.simpleMounts(auths), nil))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, auths)
|
||||
}
|
||||
|
||||
c.UI.Output(tableOutput(c.simpleMounts(auths), nil))
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *AuthListCommand) simpleMounts(auths map[string]*api.AuthMount) []string {
|
||||
|
@ -152,6 +152,11 @@ func (c *BaseCommand) flagSet(bit FlagSetBit) *FlagSets {
|
||||
c.flagsOnce.Do(func() {
|
||||
set := NewFlagSets(c.UI)
|
||||
|
||||
// These flag sets will apply to all leaf subcommands.
|
||||
// TODO: Optional, but FlagSetHTTP can be safely removed from the individual
|
||||
// Flags() subcommands.
|
||||
bit = bit | FlagSetHTTP
|
||||
|
||||
if bit&FlagSetHTTP != 0 {
|
||||
f := set.NewFlagSet("HTTP Options")
|
||||
|
||||
@ -260,7 +265,7 @@ func (c *BaseCommand) flagSet(bit FlagSetBit) *FlagSets {
|
||||
Name: "format",
|
||||
Target: &c.flagFormat,
|
||||
Default: "table",
|
||||
EnvVar: "VAULT_FORMAT",
|
||||
EnvVar: EnvVaultFormat,
|
||||
Completion: complete.PredictSet("table", "json", "yaml"),
|
||||
Usage: "Print the output in the given format. Valid formats " +
|
||||
"are \"table\", \"json\", or \"yaml\".",
|
||||
@ -317,6 +322,11 @@ func (f *FlagSets) Parse(args []string) error {
|
||||
return f.mainSet.Parse(args)
|
||||
}
|
||||
|
||||
// Parsed reports whether the command-line flags have been parsed.
|
||||
func (f *FlagSets) Parsed() bool {
|
||||
return f.mainSet.Parsed()
|
||||
}
|
||||
|
||||
// Args returns the remaining args after parsing.
|
||||
func (f *FlagSets) Args() []string {
|
||||
return f.mainSet.Args()
|
||||
|
@ -64,6 +64,13 @@ import (
|
||||
physZooKeeper "github.com/hashicorp/vault/physical/zookeeper"
|
||||
)
|
||||
|
||||
const (
|
||||
// EnvVaultCLINoColor is an env var that toggles colored UI output.
|
||||
EnvVaultCLINoColor = `VAULT_CLI_NO_COLOR`
|
||||
// EnvVaultFormat is the output format
|
||||
EnvVaultFormat = `VAULT_FORMAT`
|
||||
)
|
||||
|
||||
var (
|
||||
auditBackends = map[string]audit.Factory{
|
||||
"file": auditFile.Factory,
|
||||
@ -149,7 +156,9 @@ func (c *DeprecatedCommand) Help() string {
|
||||
|
||||
// Run wraps the embedded Run command and prints a warning about deprecation.
|
||||
func (c *DeprecatedCommand) Run(args []string) int {
|
||||
c.warn()
|
||||
if Format(c.UI) == "table" {
|
||||
c.warn()
|
||||
}
|
||||
return c.Command.Run(args)
|
||||
}
|
||||
|
||||
@ -166,24 +175,7 @@ func (c *DeprecatedCommand) warn() {
|
||||
var Commands map[string]cli.CommandFactory
|
||||
var DeprecatedCommands map[string]cli.CommandFactory
|
||||
|
||||
func init() {
|
||||
ui := &cli.ColoredUi{
|
||||
ErrorColor: cli.UiColorRed,
|
||||
WarnColor: cli.UiColorYellow,
|
||||
Ui: &cli.BasicUi{
|
||||
Writer: os.Stdout,
|
||||
ErrorWriter: os.Stderr,
|
||||
},
|
||||
}
|
||||
|
||||
serverCmdUi := &cli.ColoredUi{
|
||||
ErrorColor: cli.UiColorRed,
|
||||
WarnColor: cli.UiColorYellow,
|
||||
Ui: &cli.BasicUi{
|
||||
Writer: os.Stdout,
|
||||
},
|
||||
}
|
||||
|
||||
func initCommands(ui, serverCmdUi cli.Ui) {
|
||||
loginHandlers := map[string]LoginHandler{
|
||||
"aws": &credAws.CLIHandler{},
|
||||
"centrify": &credCentrify.CLIHandler{},
|
||||
@ -572,7 +564,7 @@ func init() {
|
||||
|
||||
// Deprecated commands
|
||||
//
|
||||
// TODO: Remove in 0.9.0
|
||||
// TODO: Remove not before 0.11.0
|
||||
DeprecatedCommands = map[string]cli.CommandFactory{
|
||||
"audit-disable": func() (cli.Command, error) {
|
||||
return &DeprecatedCommand{
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@ -19,29 +20,38 @@ const (
|
||||
hopeDelim = "♨"
|
||||
)
|
||||
|
||||
func OutputSecret(ui cli.Ui, format string, secret *api.Secret) int {
|
||||
return outputWithFormat(ui, format, secret, secret)
|
||||
type FormatOptions struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
func OutputList(ui cli.Ui, format string, secret *api.Secret) int {
|
||||
return outputWithFormat(ui, format, secret, secret.Data["keys"])
|
||||
func OutputSecret(ui cli.Ui, secret *api.Secret) int {
|
||||
return outputWithFormat(ui, secret, secret)
|
||||
}
|
||||
|
||||
func outputWithFormat(ui cli.Ui, format string, secret *api.Secret, data interface{}) int {
|
||||
// If we had a colored UI, pull out the nested ui so we don't add escape
|
||||
// sequences for outputting json, etc.
|
||||
colorUI, ok := ui.(*cli.ColoredUi)
|
||||
if ok {
|
||||
ui = colorUI.Ui
|
||||
func OutputList(ui cli.Ui, data interface{}) int {
|
||||
switch data.(type) {
|
||||
case *api.Secret:
|
||||
secret := data.(*api.Secret)
|
||||
return outputWithFormat(ui, secret, secret.Data["keys"])
|
||||
default:
|
||||
return outputWithFormat(ui, nil, data)
|
||||
}
|
||||
}
|
||||
|
||||
formatter, ok := Formatters[strings.ToLower(format)]
|
||||
func OutputData(ui cli.Ui, data interface{}) int {
|
||||
return outputWithFormat(ui, nil, data)
|
||||
}
|
||||
|
||||
func outputWithFormat(ui cli.Ui, secret *api.Secret, data interface{}) int {
|
||||
format := Format(ui)
|
||||
formatter, ok := Formatters[format]
|
||||
if !ok {
|
||||
ui.Error(fmt.Sprintf("Invalid output format: %s", format))
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := formatter.Output(ui, secret, data); err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not output secret: %s", err.Error()))
|
||||
ui.Error(fmt.Sprintf("Could not parse output: %s", err.Error()))
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
@ -58,6 +68,22 @@ var Formatters = map[string]Formatter{
|
||||
"yml": YamlFormatter{},
|
||||
}
|
||||
|
||||
func format() string {
|
||||
format := os.Getenv(EnvVaultFormat)
|
||||
if format == "" {
|
||||
format = "table"
|
||||
}
|
||||
return format
|
||||
}
|
||||
|
||||
func Format(ui cli.Ui) string {
|
||||
switch ui.(type) {
|
||||
case *VaultUI:
|
||||
return ui.(*VaultUI).format
|
||||
}
|
||||
return format()
|
||||
}
|
||||
|
||||
// An output formatter for json output of an object
|
||||
type JsonFormatter struct{}
|
||||
|
||||
@ -87,19 +113,32 @@ type TableFormatter struct {
|
||||
}
|
||||
|
||||
func (t TableFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error {
|
||||
// TODO: this should really use reflection like the other formatters do
|
||||
if s, ok := data.(*api.Secret); ok {
|
||||
return t.OutputSecret(ui, s)
|
||||
switch data.(type) {
|
||||
case *api.Secret:
|
||||
return t.OutputSecret(ui, secret)
|
||||
case []interface{}:
|
||||
return t.OutputList(ui, secret, data)
|
||||
case []string:
|
||||
return t.OutputList(ui, nil, data)
|
||||
default:
|
||||
return errors.New("Cannot use the table formatter for this type")
|
||||
}
|
||||
if s, ok := data.([]interface{}); ok {
|
||||
return t.OutputList(ui, secret, s)
|
||||
}
|
||||
return errors.New("Cannot use the table formatter for this type")
|
||||
}
|
||||
|
||||
func (t TableFormatter) OutputList(ui cli.Ui, secret *api.Secret, list []interface{}) error {
|
||||
func (t TableFormatter) OutputList(ui cli.Ui, secret *api.Secret, data interface{}) error {
|
||||
t.printWarnings(ui, secret)
|
||||
|
||||
switch data.(type) {
|
||||
case []interface{}:
|
||||
case []string:
|
||||
ui.Output(tableOutput(data.([]string), nil))
|
||||
return nil
|
||||
default:
|
||||
return errors.New("Error: table formatter cannot output list for this data type")
|
||||
}
|
||||
|
||||
list := data.([]interface{})
|
||||
|
||||
if len(list) > 0 {
|
||||
keys := make([]string, len(list))
|
||||
for i, v := range list {
|
||||
@ -208,7 +247,14 @@ func (t TableFormatter) OutputSecret(ui cli.Ui, secret *api.Secret) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OutputSealStatus will print *api.SealStatusResponse in the CLI according to the format provided
|
||||
func OutputSealStatus(ui cli.Ui, client *api.Client, status *api.SealStatusResponse) int {
|
||||
switch Format(ui) {
|
||||
case "table":
|
||||
default:
|
||||
return OutputData(ui, status)
|
||||
}
|
||||
|
||||
var sealPrefix string
|
||||
if status.RecoverySeal {
|
||||
sealPrefix = "Recovery "
|
||||
|
@ -1,6 +1,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -30,8 +31,9 @@ func (m mockUi) Error(s string) { m.t.Log(s) }
|
||||
func (m mockUi) Warn(s string) { m.t.Log(s) }
|
||||
|
||||
func TestJsonFormatter(t *testing.T) {
|
||||
os.Setenv(EnvVaultFormat, "json")
|
||||
ui := mockUi{t: t, SampleData: "something"}
|
||||
if err := outputWithFormat(ui, "json", nil, ui); err != 0 {
|
||||
if err := outputWithFormat(ui, nil, ui); err != 0 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var newUi mockUi
|
||||
@ -46,8 +48,9 @@ func TestJsonFormatter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestYamlFormatter(t *testing.T) {
|
||||
os.Setenv(EnvVaultFormat, "yaml")
|
||||
ui := mockUi{t: t, SampleData: "something"}
|
||||
if err := outputWithFormat(ui, "yaml", nil, ui); err != 0 {
|
||||
if err := outputWithFormat(ui, nil, ui); err != 0 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var newUi mockUi
|
||||
@ -63,12 +66,68 @@ func TestYamlFormatter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTableFormatter(t *testing.T) {
|
||||
os.Setenv(EnvVaultFormat, "table")
|
||||
ui := mockUi{t: t}
|
||||
s := api.Secret{Data: map[string]interface{}{"k": "something"}}
|
||||
if err := outputWithFormat(ui, "table", &s, &s); err != 0 {
|
||||
if err := outputWithFormat(ui, &s, &s); err != 0 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(output, "something") {
|
||||
t.Fatal("did not find 'something'")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Format_Parsing(t *testing.T) {
|
||||
defer func() {
|
||||
os.Setenv(EnvVaultCLINoColor, "")
|
||||
os.Setenv(EnvVaultFormat, "")
|
||||
}()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
args []string
|
||||
out string
|
||||
code int
|
||||
}{
|
||||
{
|
||||
"format",
|
||||
[]string{"-format", "json"},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{"-format", "nope-not-real"},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
// Login with the token so we can renew-self.
|
||||
token, _ := testTokenAndAccessor(t, client)
|
||||
client.SetToken(token)
|
||||
|
||||
ui, cmd := testTokenRenewCommand(t)
|
||||
cmd.client = client
|
||||
|
||||
tc.args = setupEnv(tc.args)
|
||||
|
||||
code := cmd.Run(tc.args)
|
||||
if code != tc.code {
|
||||
t.Errorf("expected %d to be %d", code, tc.code)
|
||||
}
|
||||
|
||||
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||
if !strings.Contains(combined, tc.out) {
|
||||
t.Errorf("expected %q to contain %q", combined, tc.out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -123,5 +123,5 @@ func (c *LeaseRenewCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
@ -74,18 +74,6 @@ func TestLeaseRenewCommand_Run(t *testing.T) {
|
||||
"foo",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format",
|
||||
[]string{"-format", "json"},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{"-format", "nope-not-real"},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("group", func(t *testing.T) {
|
||||
|
@ -89,7 +89,7 @@ func (c *ListCommand) Run(args []string) int {
|
||||
|
||||
// If the secret is wrapped, return the wrapped response.
|
||||
if secret.WrapInfo != nil && secret.WrapInfo.TTL != 0 {
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
||||
if _, ok := extractListData(secret); !ok {
|
||||
@ -97,5 +97,5 @@ func (c *ListCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
return OutputList(c.UI, c.flagFormat, secret)
|
||||
return OutputList(c.UI, secret)
|
||||
}
|
||||
|
@ -57,24 +57,6 @@ func TestListCommand_Run(t *testing.T) {
|
||||
"bar\nbaz\nfoo",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format",
|
||||
[]string{
|
||||
"-format", "json",
|
||||
"secret/list/",
|
||||
},
|
||||
"[",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{
|
||||
"-format", "nope-not-real",
|
||||
"secret/list/",
|
||||
},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("validations", func(t *testing.T) {
|
||||
|
@ -274,7 +274,7 @@ func (c *LoginCommand) Run(args []string) int {
|
||||
return PrintRawField(c.UI, secret, "wrapping_token")
|
||||
}
|
||||
if c.flagNoStore {
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,7 +308,7 @@ func (c *LoginCommand) Run(args []string) int {
|
||||
c.UI.Error(wrapAtLength(
|
||||
"Authentication was successful, but the token was not persisted. The "+
|
||||
"resulting token is shown below for your records.") + "\n")
|
||||
OutputSecret(c.UI, c.flagFormat, secret)
|
||||
OutputSecret(c.UI, secret)
|
||||
return 2
|
||||
}
|
||||
|
||||
@ -335,7 +335,7 @@ func (c *LoginCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
// Print some yay! text, but only in table mode.
|
||||
if c.flagFormat == "table" {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Output(wrapAtLength(
|
||||
"Success! You are now authenticated. The token information displayed "+
|
||||
"below is already stored in the token helper. You do NOT need to run "+
|
||||
@ -343,7 +343,7 @@ func (c *LoginCommand) Run(args []string) int {
|
||||
"this token.") + "\n")
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
||||
// extractToken extracts the token from the given secret, automatically
|
||||
|
108
command/main.go
108
command/main.go
@ -10,11 +10,36 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
func Run(args []string) int {
|
||||
// Handle -v shorthand
|
||||
type VaultUI struct {
|
||||
cli.Ui
|
||||
isTerminal bool
|
||||
format string
|
||||
}
|
||||
|
||||
func (u *VaultUI) Output(m string) {
|
||||
if u.isTerminal {
|
||||
u.Ui.Output(m)
|
||||
} else {
|
||||
getWriterFromUI(u.Ui).Write([]byte(m))
|
||||
}
|
||||
}
|
||||
|
||||
// setupEnv parses args and may replace them and sets some env vars to known
|
||||
// values based on format options
|
||||
func setupEnv(args []string) []string {
|
||||
var format string
|
||||
var nextArgFormat bool
|
||||
|
||||
for _, arg := range args {
|
||||
if nextArgFormat {
|
||||
nextArgFormat = false
|
||||
format = arg
|
||||
continue
|
||||
}
|
||||
|
||||
if arg == "--" {
|
||||
break
|
||||
}
|
||||
@ -23,8 +48,87 @@ func Run(args []string) int {
|
||||
args = []string{"version"}
|
||||
break
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
// Put back into the env for later
|
||||
os.Setenv(EnvVaultFormat, format)
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func Run(args []string) int {
|
||||
args = setupEnv(args)
|
||||
|
||||
// Don't use color if disabled
|
||||
color := true
|
||||
if os.Getenv(EnvVaultCLINoColor) != "" {
|
||||
color = false
|
||||
}
|
||||
|
||||
format := format()
|
||||
|
||||
isTerminal := terminal.IsTerminal(int(os.Stdout.Fd()))
|
||||
|
||||
ui := &VaultUI{
|
||||
Ui: &cli.BasicUi{
|
||||
Writer: os.Stdout,
|
||||
ErrorWriter: os.Stderr,
|
||||
},
|
||||
isTerminal: isTerminal,
|
||||
format: format,
|
||||
}
|
||||
serverCmdUi := &VaultUI{
|
||||
Ui: &cli.BasicUi{
|
||||
Writer: os.Stdout,
|
||||
},
|
||||
isTerminal: isTerminal,
|
||||
format: format,
|
||||
}
|
||||
|
||||
if _, ok := Formatters[format]; !ok {
|
||||
ui.Error(fmt.Sprintf("Invalid output format: %s", format))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Only use colored UI if stdoout is a tty, and not disabled
|
||||
if isTerminal && color && format == "table" {
|
||||
ui.Ui = &cli.ColoredUi{
|
||||
ErrorColor: cli.UiColorRed,
|
||||
WarnColor: cli.UiColorYellow,
|
||||
Ui: ui.Ui,
|
||||
}
|
||||
|
||||
serverCmdUi.Ui = &cli.ColoredUi{
|
||||
ErrorColor: cli.UiColorRed,
|
||||
WarnColor: cli.UiColorYellow,
|
||||
Ui: serverCmdUi.Ui,
|
||||
}
|
||||
}
|
||||
|
||||
initCommands(ui, serverCmdUi)
|
||||
|
||||
// Calculate hidden commands from the deprecated ones
|
||||
hiddenCommands := make([]string, 0, len(DeprecatedCommands)+1)
|
||||
for k := range DeprecatedCommands {
|
||||
|
@ -82,7 +82,7 @@ Usage: vault operator generate-root [options] [KEY]
|
||||
}
|
||||
|
||||
func (c *OperatorGenerateRootCommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP)
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
@ -337,7 +337,13 @@ func (c *OperatorGenerateRootCommand) init(client *api.Client, otp, pgpKey strin
|
||||
c.UI.Error(fmt.Sprintf("Error initializing root generation: %s", err))
|
||||
return 2
|
||||
}
|
||||
return c.printStatus(status)
|
||||
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
return c.printStatus(status)
|
||||
default:
|
||||
return OutputData(c.UI, status)
|
||||
}
|
||||
}
|
||||
|
||||
// provide prompts the user for the seal key and posts it to the update root
|
||||
@ -428,7 +434,12 @@ func (c *OperatorGenerateRootCommand) provide(client *api.Client, key string, dr
|
||||
c.UI.Error(fmt.Sprintf("Error posting unseal key: %s", err))
|
||||
return 2
|
||||
}
|
||||
return c.printStatus(status)
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
return c.printStatus(status)
|
||||
default:
|
||||
return OutputData(c.UI, status)
|
||||
}
|
||||
}
|
||||
|
||||
// cancel cancels the root token generation
|
||||
@ -456,7 +467,12 @@ func (c *OperatorGenerateRootCommand) status(client *api.Client, drToken bool) i
|
||||
c.UI.Error(fmt.Sprintf("Error getting root generation status: %s", err))
|
||||
return 2
|
||||
}
|
||||
return c.printStatus(status)
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
return c.printStatus(status)
|
||||
default:
|
||||
return OutputData(c.UI, status)
|
||||
}
|
||||
}
|
||||
|
||||
// printStatus dumps the status to output
|
||||
|
@ -1,7 +1,10 @@
|
||||
// +build !race
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -129,6 +132,14 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) {
|
||||
|
||||
ui, cmd := testOperatorGenerateRootCommand(t)
|
||||
|
||||
// Simulate piped output to print raw output
|
||||
old := os.Stdout
|
||||
_, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.Stdout = w
|
||||
|
||||
code := cmd.Run([]string{
|
||||
"-decode", encoded,
|
||||
"-otp", otp,
|
||||
@ -137,6 +148,9 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) {
|
||||
t.Errorf("expected %d to be %d", code, exp)
|
||||
}
|
||||
|
||||
w.Close()
|
||||
os.Stdout = old
|
||||
|
||||
expected := "5b54841c-c705-e59c-c6e4-a22b48e4b2cf"
|
||||
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||
if combined != expected {
|
||||
|
@ -1,13 +1,11 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault/helper/pgpkeys"
|
||||
"github.com/mitchellh/cli"
|
||||
@ -434,15 +432,10 @@ func (c *OperatorInitCommand) init(client *api.Client, req *api.InitRequest) int
|
||||
return 2
|
||||
}
|
||||
|
||||
switch c.flagFormat {
|
||||
case "yaml", "yml":
|
||||
return c.initOutputYAML(req, resp)
|
||||
case "json":
|
||||
return c.initOutputJSON(req, resp)
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
default:
|
||||
c.UI.Error(fmt.Sprintf("Unknown format: %s", c.flagFormat))
|
||||
return 1
|
||||
return OutputData(c.UI, newMachineInit(req, resp))
|
||||
}
|
||||
|
||||
for i, key := range resp.Keys {
|
||||
@ -503,26 +496,6 @@ func (c *OperatorInitCommand) init(client *api.Client, req *api.InitRequest) int
|
||||
return 0
|
||||
}
|
||||
|
||||
// initOutputYAML outputs the init output as YAML.
|
||||
func (c *OperatorInitCommand) initOutputYAML(req *api.InitRequest, resp *api.InitResponse) int {
|
||||
b, err := yaml.Marshal(newMachineInit(req, resp))
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error marshaling YAML: %s", err))
|
||||
return 2
|
||||
}
|
||||
return PrintRaw(c.UI, strings.TrimSpace(string(b)))
|
||||
}
|
||||
|
||||
// initOutputJSON outputs the init output as JSON.
|
||||
func (c *OperatorInitCommand) initOutputJSON(req *api.InitRequest, resp *api.InitResponse) int {
|
||||
b, err := json.Marshal(newMachineInit(req, resp))
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error marshaling JSON: %s", err))
|
||||
return 2
|
||||
}
|
||||
return PrintRaw(c.UI, strings.TrimSpace(string(b)))
|
||||
}
|
||||
|
||||
// status inspects the init status of vault and returns an appropriate error
|
||||
// code and message.
|
||||
func (c *OperatorInitCommand) status(client *api.Client) int {
|
||||
|
@ -32,7 +32,7 @@ Usage: vault operator key-status [options]
|
||||
}
|
||||
|
||||
func (c *OperatorKeyStatusCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
return c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
}
|
||||
|
||||
func (c *OperatorKeyStatusCommand) AutocompleteArgs() complete.Predictor {
|
||||
@ -69,6 +69,11 @@ func (c *OperatorKeyStatusCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
c.UI.Output(printKeyStatus(status))
|
||||
return 0
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(printKeyStatus(status))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, status)
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ Usage: vault rekey [options] [KEY]
|
||||
}
|
||||
|
||||
func (c *OperatorRekeyCommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP)
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Common Options")
|
||||
|
||||
@ -534,7 +534,7 @@ func (c *OperatorRekeyCommand) backupRetrieve(client *api.Client) int {
|
||||
Data: structs.New(storedKeys).Map(),
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, "table", secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
||||
// backupDelete deletes the stored backup keys.
|
||||
@ -579,11 +579,22 @@ func (c *OperatorRekeyCommand) printStatus(status *api.RekeyStatusResponse) int
|
||||
out = append(out, fmt.Sprintf("Backup | %t", status.Backup))
|
||||
}
|
||||
|
||||
c.UI.Output(tableOutput(out, nil))
|
||||
return 0
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(tableOutput(out, nil))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, status)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OperatorRekeyCommand) printUnsealKeys(status *api.RekeyStatusResponse, resp *api.RekeyUpdateResponse) int {
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
default:
|
||||
return OutputData(c.UI, resp)
|
||||
}
|
||||
|
||||
// Space between the key prompt, if any, and the output
|
||||
c.UI.Output("")
|
||||
|
||||
|
@ -50,7 +50,7 @@ Usage: vault operator unseal [options] [KEY]
|
||||
}
|
||||
|
||||
func (c *OperatorUnsealCommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP)
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -138,3 +140,36 @@ func TestOperatorUnsealCommand_Run(t *testing.T) {
|
||||
assertNoTabs(t, cmd)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOperatorUnsealCommand_Format(t *testing.T) {
|
||||
defer func() {
|
||||
os.Setenv(EnvVaultFormat, "")
|
||||
os.Setenv(EnvVaultCLINoColor, "")
|
||||
}()
|
||||
|
||||
client, keys, closer := testVaultServerUnseal(t)
|
||||
defer closer()
|
||||
|
||||
// Seal so we can unseal
|
||||
if err := client.Sys().Seal(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ui, cmd := testOperatorUnsealCommand(t)
|
||||
cmd.client = client
|
||||
cmd.testOutput = ioutil.Discard
|
||||
|
||||
args := setupEnv([]string{"-format", "json"})
|
||||
|
||||
// Unseal with one key
|
||||
code := cmd.Run(append(args, []string{
|
||||
keys[0],
|
||||
}...))
|
||||
if exp := 0; code != exp {
|
||||
t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
if !json.Valid(ui.OutputWriter.Bytes()) {
|
||||
t.Error("expected output to be valid JSON")
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ func (c *PoliciesDeprecatedCommand) Run(args []string) int {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
args = f.Args()
|
||||
|
||||
// Got an arg, this is trying to read a policy
|
||||
|
@ -31,7 +31,7 @@ Usage: vault policy list [options]
|
||||
}
|
||||
|
||||
func (c *PolicyListCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
return c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
}
|
||||
|
||||
func (c *PolicyListCommand) AutocompleteArgs() complete.Predictor {
|
||||
@ -68,9 +68,14 @@ func (c *PolicyListCommand) Run(args []string) int {
|
||||
c.UI.Error(fmt.Sprintf("Error listing policies: %s", err))
|
||||
return 2
|
||||
}
|
||||
for _, p := range policies {
|
||||
c.UI.Output(p)
|
||||
}
|
||||
|
||||
return 0
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
for _, p := range policies {
|
||||
c.UI.Output(p)
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, policies)
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ Usage: vault policy read [options] [NAME]
|
||||
}
|
||||
|
||||
func (c *PolicyReadCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
return c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
}
|
||||
|
||||
func (c *PolicyReadCommand) AutocompleteArgs() complete.Predictor {
|
||||
@ -81,7 +81,15 @@ func (c *PolicyReadCommand) Run(args []string) int {
|
||||
c.UI.Error(fmt.Sprintf("No policy named: %s", name))
|
||||
return 2
|
||||
}
|
||||
c.UI.Output(strings.TrimSpace(rules))
|
||||
|
||||
return 0
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(strings.TrimSpace(rules))
|
||||
return 0
|
||||
default:
|
||||
resp := map[string]string{
|
||||
"policy": rules,
|
||||
}
|
||||
return OutputData(c.UI, &resp)
|
||||
}
|
||||
}
|
||||
|
@ -90,5 +90,5 @@ func (c *ReadCommand) Run(args []string) int {
|
||||
return PrintRawField(c.UI, secret, c.flagField)
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
@ -69,24 +69,6 @@ func TestReadCommand_Run(t *testing.T) {
|
||||
"not present in secret",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"format",
|
||||
[]string{
|
||||
"-format", "json",
|
||||
"secret/read/foo",
|
||||
},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{
|
||||
"-format", "nope-not-real",
|
||||
"secret/read/foo",
|
||||
},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("validations", func(t *testing.T) {
|
||||
|
@ -44,7 +44,7 @@ Usage: vault rotate [options]
|
||||
}
|
||||
|
||||
func (c *OperatorRotateCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
return c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
}
|
||||
|
||||
func (c *OperatorRotateCommand) AutocompleteArgs() complete.Predictor {
|
||||
@ -89,8 +89,13 @@ func (c *OperatorRotateCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
c.UI.Output("Success! Rotated key")
|
||||
c.UI.Output("")
|
||||
c.UI.Output(printKeyStatus(status))
|
||||
return 0
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output("Success! Rotated key")
|
||||
c.UI.Output("")
|
||||
c.UI.Output(printKeyStatus(status))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, status)
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ Usage: vault secrets list [options]
|
||||
}
|
||||
|
||||
func (c *SecretsListCommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP)
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
@ -96,13 +96,17 @@ func (c *SecretsListCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
if c.flagDetailed {
|
||||
c.UI.Output(tableOutput(c.detailedMounts(mounts), nil))
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
if c.flagDetailed {
|
||||
c.UI.Output(tableOutput(c.detailedMounts(mounts), nil))
|
||||
return 0
|
||||
}
|
||||
c.UI.Output(tableOutput(c.simpleMounts(mounts), nil))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, mounts)
|
||||
}
|
||||
|
||||
c.UI.Output(tableOutput(c.simpleMounts(mounts), nil))
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *SecretsListCommand) simpleMounts(mounts map[string]*api.MountOutput) []string {
|
||||
|
@ -386,10 +386,10 @@ func (c *SSHCommand) handleTypeCA(username, hostname, ip string, sshArgs []strin
|
||||
|
||||
// Handle no-exec
|
||||
if c.flagNoExec {
|
||||
if c.flagFormat != "" {
|
||||
if c.flagField != "" {
|
||||
return PrintRawField(c.UI, secret, c.flagField)
|
||||
}
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
||||
// Extract public key
|
||||
@ -490,10 +490,10 @@ func (c *SSHCommand) handleTypeOTP(username, ip string, sshArgs []string) int {
|
||||
|
||||
// Handle no-exec
|
||||
if c.flagNoExec {
|
||||
if c.flagFormat != "" {
|
||||
if c.flagField != "" {
|
||||
return PrintRawField(c.UI, secret, c.flagField)
|
||||
}
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
@ -572,10 +572,10 @@ func (c *SSHCommand) handleTypeDynamic(username, ip string, sshArgs []string) in
|
||||
|
||||
// Handle no-exec
|
||||
if c.flagNoExec {
|
||||
if c.flagFormat != "" {
|
||||
if c.flagField != "" {
|
||||
return PrintRawField(c.UI, secret, c.flagField)
|
||||
}
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
||||
// Write the dynamic key to disk
|
||||
|
@ -39,7 +39,7 @@ Usage: vault status [options]
|
||||
}
|
||||
|
||||
func (c *StatusCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
return c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
}
|
||||
|
||||
func (c *StatusCommand) AutocompleteArgs() complete.Predictor {
|
||||
@ -77,13 +77,13 @@ func (c *StatusCommand) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Do not return the int here, since we want to return a custom error code
|
||||
// depending on the seal status.
|
||||
OutputSealStatus(c.UI, client, status)
|
||||
// Do not return the int here yet, since we may want to return a custom error
|
||||
// code depending on the seal status.
|
||||
code := OutputSealStatus(c.UI, client, status)
|
||||
|
||||
if status.Sealed {
|
||||
return 2
|
||||
}
|
||||
|
||||
return 0
|
||||
return code
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ Usage: vault token capabilities [options] [TOKEN] PATH
|
||||
}
|
||||
|
||||
func (c *TokenCapabilitiesCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP)
|
||||
return c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
}
|
||||
|
||||
func (c *TokenCapabilitiesCommand) AutocompleteArgs() complete.Predictor {
|
||||
@ -94,7 +94,12 @@ func (c *TokenCapabilitiesCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
sort.Strings(capabilities)
|
||||
c.UI.Output(strings.Join(capabilities, ", "))
|
||||
return 0
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
sort.Strings(capabilities)
|
||||
c.UI.Output(strings.Join(capabilities, ", "))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, capabilities)
|
||||
}
|
||||
}
|
||||
|
@ -246,5 +246,5 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
||||
return PrintRawField(c.UI, secret, c.flagField)
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
@ -68,22 +68,6 @@ func TestTokenCreateCommand_Run(t *testing.T) {
|
||||
"not present in secret",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"format",
|
||||
[]string{
|
||||
"-format", "json",
|
||||
},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{
|
||||
"-format", "nope-not-real",
|
||||
},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("validations", func(t *testing.T) {
|
||||
|
@ -124,5 +124,5 @@ func (c *TokenLookupCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
@ -45,18 +45,6 @@ func TestTokenLookupCommand_Run(t *testing.T) {
|
||||
"Too many arguments",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"format",
|
||||
[]string{"-format", "json"},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{"-format", "nope-not-real"},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("validations", func(t *testing.T) {
|
||||
|
@ -132,5 +132,5 @@ func (c *TokenRenewCommand) Run(args []string) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
@ -53,18 +53,6 @@ func TestTokenRenewCommand_Run(t *testing.T) {
|
||||
"",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format",
|
||||
[]string{"-format", "json"},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{"-format", "nope-not-real"},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("validations", func(t *testing.T) {
|
||||
|
@ -100,7 +100,7 @@ func (c *UnwrapCommand) Run(args []string) int {
|
||||
|
||||
// Check if the original was a list response and format as a list
|
||||
if _, ok := extractListData(secret); ok {
|
||||
return OutputList(c.UI, c.flagFormat, secret)
|
||||
return OutputList(c.UI, secret)
|
||||
}
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
@ -65,18 +65,6 @@ func TestUnwrapCommand_Run(t *testing.T) {
|
||||
"not present in secret",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"format",
|
||||
[]string{"-format", "json"},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{"-format", "nope-not-real"},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("validations", func(t *testing.T) {
|
||||
|
@ -115,6 +115,8 @@ func PrintRaw(ui cli.Ui, str string) int {
|
||||
// type, this falls back to os.Stdout.
|
||||
func getWriterFromUI(ui cli.Ui) io.Writer {
|
||||
switch t := ui.(type) {
|
||||
case *VaultUI:
|
||||
return getWriterFromUI(t.Ui)
|
||||
case *cli.BasicUi:
|
||||
return t.Writer
|
||||
case *cli.ColoredUi:
|
||||
|
@ -135,7 +135,7 @@ func (c *WriteCommand) Run(args []string) int {
|
||||
}
|
||||
if secret == nil {
|
||||
// Don't output anything unless using the "table" format
|
||||
if c.flagFormat == "table" {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path))
|
||||
}
|
||||
return 0
|
||||
@ -146,5 +146,5 @@ func (c *WriteCommand) Run(args []string) int {
|
||||
return PrintRawField(c.UI, secret, c.flagField)
|
||||
}
|
||||
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
return OutputSecret(c.UI, secret)
|
||||
}
|
||||
|
@ -37,5 +37,13 @@ file/ file n/a replicated file_path=/var/log/audit.log
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
### Command Options
|
||||
|
||||
- `-detailed` `(bool: false)` - Print detailed information such as options and
|
||||
replication status about each auth device.
|
||||
|
@ -39,5 +39,13 @@ userpass/ userpass auth_userpass_eea6507e n/a system syst
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
### Command Options
|
||||
|
||||
- `-detailed` `(bool: false)` - Print detailed information such as configuration
|
||||
and replication status about each auth method.
|
||||
|
@ -212,6 +212,10 @@ model](/docs/internals/security.html).
|
||||
|
||||
Name to use as the SNI host when connecting via TLS.
|
||||
|
||||
### `VAULT_CLI_NO_COLOR`
|
||||
|
||||
If provided, Vault output will not include ANSI color escape sequence characters.
|
||||
|
||||
### `VAULT_MFA`
|
||||
|
||||
**ENTERPRISE ONLY**
|
||||
|
@ -89,13 +89,13 @@ flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
- `-field` `(string: "")` - Print only the field with the given name. Specifying
|
||||
this option will take precedence over other formatting directives. The result
|
||||
will not have a trailing newline making it idea for piping to other processes.
|
||||
will not have a trailing newline making it ideal for piping to other processes.
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
## Command Options
|
||||
### Command Options
|
||||
|
||||
- `-method` `(string "token")` - Type of authentication to use such as
|
||||
"userpass" or "ldap". Note this corresponds to the TYPE, not the enabled path.
|
||||
|
@ -55,6 +55,14 @@ $ vault operator generate-root -otp="..."
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
### Command Options
|
||||
|
||||
- `-cancel` `(bool: false)` - Reset the root token generation progress. This
|
||||
will discard any submitted unseal keys or configuration.
|
||||
|
||||
|
@ -24,5 +24,11 @@ Install Time 01 Jan 17 12:30 UTC
|
||||
|
||||
## Usage
|
||||
|
||||
There are no flags beyond the [standard set of flags](/docs/commands/index.html)
|
||||
included on all commands.
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
@ -72,7 +72,13 @@ $ vault operator rekey -backup-delete
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
## Common Options
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
### Command Options
|
||||
|
||||
- `-cancel` `(bool: false)` - Reset the rekeying progress. This will discard any submitted unseal keys
|
||||
or configuration. The default is false.
|
||||
|
@ -32,5 +32,11 @@ Install Time 01 May 17 10:30 UTC
|
||||
|
||||
## Usage
|
||||
|
||||
There are no flags beyond the [standard set of flags](/docs/commands/index.html)
|
||||
included on all commands.
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
@ -50,5 +50,13 @@ Unseal Progress: 0
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
### Command Options
|
||||
|
||||
- `-reset` `(bool: false)` - Discard any previously entered keys to the unseal
|
||||
process.
|
||||
|
@ -24,5 +24,12 @@ root
|
||||
|
||||
## Usage
|
||||
|
||||
There are no flags beyond the [standard set of flags](/docs/commands/index.html)
|
||||
included on all commands.
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
|
@ -22,5 +22,11 @@ $ vault policy read my-policy
|
||||
|
||||
## Usage
|
||||
|
||||
There are no flags beyond the [standard set of flags](/docs/commands/index.html)
|
||||
included on all commands.
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
@ -37,7 +37,7 @@ flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
- `-field` `(string: "")` - Print only the field with the given name. Specifying
|
||||
this option will take precedence over other formatting directives. The result
|
||||
will not have a trailing newline making it idea for piping to other processes.
|
||||
will not have a trailing newline making it ideal for piping to other processes.
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
|
@ -45,5 +45,13 @@ sys/ system system_a9fd745d n/a n/a n/a
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
||||
|
||||
### Command Options
|
||||
|
||||
- `-detailed` `(bool: false)` - Print detailed information such as configuration
|
||||
and replication status about each secrets engine.
|
||||
|
@ -57,7 +57,7 @@ flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
- `-field` `(string: "")` - Print only the field with the given name. Specifying
|
||||
this option will take precedence over other formatting directives. The result
|
||||
will not have a trailing newline making it idea for piping to other processes.
|
||||
will not have a trailing newline making it ideal for piping to other processes.
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
|
@ -40,5 +40,11 @@ High-Availability Enabled: false
|
||||
|
||||
## Usage
|
||||
|
||||
There are no flags beyond the [standard set of flags](/docs/commands/index.html)
|
||||
included on all commands.
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
@ -35,5 +35,11 @@ deny
|
||||
|
||||
## Usage
|
||||
|
||||
There are no flags beyond the [standard set of flags](/docs/commands/index.html)
|
||||
included on all commands.
|
||||
The following flags are available in addition to the [standard set of
|
||||
flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
### Output Options
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
`VAULT_FORMAT` environment variable.
|
@ -64,7 +64,7 @@ flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
- `-field` `(string: "")` - Print only the field with the given name. Specifying
|
||||
this option will take precedence over other formatting directives. The result
|
||||
will not have a trailing newline making it idea for piping to other processes.
|
||||
will not have a trailing newline making it ideal for piping to other processes.
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
|
@ -39,7 +39,7 @@ flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
- `-field` `(string: "")` - Print only the field with the given name. Specifying
|
||||
this option will take precedence over other formatting directives. The result
|
||||
will not have a trailing newline making it idea for piping to other processes.
|
||||
will not have a trailing newline making it ideal for piping to other processes.
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
|
@ -56,7 +56,7 @@ flags](/docs/commands/index.html) included on all commands.
|
||||
|
||||
- `-field` `(string: "")` - Print only the field with the given name. Specifying
|
||||
this option will take precedence over other formatting directives. The result
|
||||
will not have a trailing newline making it idea for piping to other processes.
|
||||
will not have a trailing newline making it ideal for piping to other processes.
|
||||
|
||||
- `-format` `(string: "table")` - Print the output in the given format. Valid
|
||||
formats are "table", "json", or "yaml". This can also be specified via the
|
||||
|
Loading…
Reference in New Issue
Block a user