diff --git a/go.mod b/go.mod index fc71c92764..4854f48966 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/fatih/color v1.9.0 github.com/fatih/structs v1.1.0 github.com/favadi/protoc-go-inject-tag v1.1.0 - github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 + github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect github.com/go-bindata/go-bindata/v3 v3.1.3 github.com/go-swagger/go-swagger v0.25.0 github.com/golang-migrate/migrate/v4 v4.12.2 @@ -48,7 +48,6 @@ require ( github.com/pires/go-proxyproto v0.1.3 github.com/pkg/errors v0.9.1 github.com/posener/complete v1.2.3 - github.com/ryanuber/columnize v2.1.0+incompatible github.com/ryanuber/go-glob v1.0.0 github.com/stretchr/testify v1.6.1 github.com/zalando/go-keyring v0.1.0 diff --git a/internal/cmd/base/base.go b/internal/cmd/base/base.go index 829bde2697..ab1e4eda5e 100644 --- a/internal/cmd/base/base.go +++ b/internal/cmd/base/base.go @@ -65,7 +65,6 @@ type Command struct { flagTLSInsecure bool flagFormat string - flagField string FlagTokenName string FlagRecoveryConfig string flagOutputCurlString bool diff --git a/internal/cmd/base/format.go b/internal/cmd/base/format.go index 82938acd30..a0188ea48e 100644 --- a/internal/cmd/base/format.go +++ b/internal/cmd/base/format.go @@ -4,21 +4,12 @@ import ( "encoding/json" "fmt" "os" - "sort" "strings" "unicode" "unicode/utf8" - "github.com/ghodss/yaml" "github.com/mitchellh/cli" "github.com/mitchellh/go-wordwrap" - "github.com/ryanuber/columnize" -) - -const ( - // hopeDelim is the delimiter to use when splitting columns. We call it a - // hopeDelim because we hope that it's never contained in a secret. - hopeDelim = "♨" ) // This is adapted from the code in the strings package for TrimSpace @@ -77,10 +68,6 @@ func WrapMap(prefixSpaces, maxLengthOverride int, input map[string]interface{}) return strings.Join(ret, "\n") } -type FormatOptions struct { - Format string -} - // An output formatter for json output of an object type JsonFormatter struct{} @@ -88,337 +75,6 @@ func (j JsonFormatter) Format(data interface{}) ([]byte, error) { return json.Marshal(data) } -/* -func (j JsonFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error { - b, err := j.Format(data) - if err != nil { - return err - } - ui.Output(string(b)) - return nil -} -*/ - -// An output formatter for yaml output format of an object -type YamlFormatter struct{} - -func (y YamlFormatter) Format(data interface{}) ([]byte, error) { - return yaml.Marshal(data) -} - -/* -func (y YamlFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error { - b, err := y.Format(data) - if err == nil { - ui.Output(strings.TrimSpace(string(b))) - } - return err -} -*/ - -// An output formatter for table output of an object -type TableFormatter struct{} - -// We don't use this -func (t TableFormatter) Format(data interface{}) ([]byte, error) { - return nil, nil -} - -/* -func (t TableFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error { - 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) - case map[string]interface{}: - return t.OutputMap(ui, data.(map[string]interface{})) - default: - return errors.New("cannot use the table formatter for this type") - } -} - -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 { - typed, ok := v.(string) - if !ok { - return fmt.Errorf("%v is not a string", v) - } - keys[i] = typed - } - sort.Strings(keys) - - // Prepend the header - keys = append([]string{"Keys"}, keys...) - - ui.Output(tableOutput(keys, &columnize.Config{ - Delim: hopeDelim, - })) - } - - return nil -} - -// printWarnings prints any warnings in the secret. -func (t TableFormatter) printWarnings(ui cli.Ui, secret *api.Secret) { - if secret != nil && len(secret.Warnings) > 0 { - ui.Warn("WARNING! The following warnings were returned from Vault:\n") - for _, warning := range secret.Warnings { - ui.Warn(WrapAtLengthWithPadding(fmt.Sprintf("* %s", warning), 2)) - ui.Warn("") - } - } -} - -func (t TableFormatter) OutputSecret(ui cli.Ui, secret *api.Secret) error { - if secret == nil { - return nil - } - - t.printWarnings(ui, secret) - - out := make([]string, 0, 8) - if secret.LeaseDuration > 0 { - if secret.LeaseID != "" { - out = append(out, fmt.Sprintf("lease_id %s %s", hopeDelim, secret.LeaseID)) - out = append(out, fmt.Sprintf("lease_duration %s %v", hopeDelim, humanDurationInt(secret.LeaseDuration))) - out = append(out, fmt.Sprintf("lease_renewable %s %t", hopeDelim, secret.Renewable)) - } else { - // This is probably the generic secret backend which has leases, but we - // print them as refresh_interval to reduce confusion. - out = append(out, fmt.Sprintf("refresh_interval %s %v", hopeDelim, humanDurationInt(secret.LeaseDuration))) - } - } - - if secret.Auth != nil { - out = append(out, fmt.Sprintf("token %s %s", hopeDelim, secret.Auth.ClientToken)) - out = append(out, fmt.Sprintf("token_accessor %s %s", hopeDelim, secret.Auth.Accessor)) - // If the lease duration is 0, it's likely a root token, so output the - // duration as "infinity" to clear things up. - if secret.Auth.LeaseDuration == 0 { - out = append(out, fmt.Sprintf("token_duration %s %s", hopeDelim, "∞")) - } else { - out = append(out, fmt.Sprintf("token_duration %s %v", hopeDelim, humanDurationInt(secret.Auth.LeaseDuration))) - } - out = append(out, fmt.Sprintf("token_renewable %s %t", hopeDelim, secret.Auth.Renewable)) - out = append(out, fmt.Sprintf("token_policies %s %q", hopeDelim, secret.Auth.TokenPolicies)) - out = append(out, fmt.Sprintf("identity_policies %s %q", hopeDelim, secret.Auth.IdentityPolicies)) - out = append(out, fmt.Sprintf("policies %s %q", hopeDelim, secret.Auth.Policies)) - for k, v := range secret.Auth.Metadata { - out = append(out, fmt.Sprintf("token_meta_%s %s %v", k, hopeDelim, v)) - } - } - - if secret.WrapInfo != nil { - out = append(out, fmt.Sprintf("wrapping_token: %s %s", hopeDelim, secret.WrapInfo.Token)) - out = append(out, fmt.Sprintf("wrapping_accessor: %s %s", hopeDelim, secret.WrapInfo.Accessor)) - out = append(out, fmt.Sprintf("wrapping_token_ttl: %s %v", hopeDelim, humanDurationInt(secret.WrapInfo.TTL))) - out = append(out, fmt.Sprintf("wrapping_token_creation_time: %s %s", hopeDelim, secret.WrapInfo.CreationTime.String())) - out = append(out, fmt.Sprintf("wrapping_token_creation_path: %s %s", hopeDelim, secret.WrapInfo.CreationPath)) - if secret.WrapInfo.WrappedAccessor != "" { - out = append(out, fmt.Sprintf("wrapped_accessor: %s %s", hopeDelim, secret.WrapInfo.WrappedAccessor)) - } - } - - if len(secret.Data) > 0 { - keys := make([]string, 0, len(secret.Data)) - for k := range secret.Data { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := secret.Data[k] - - // If the field "looks" like a TTL, print it as a time duration instead. - if looksLikeDuration(k) { - v = humanDurationInt(v) - } - - out = append(out, fmt.Sprintf("%s %s %v", k, hopeDelim, v)) - } - } - - // If we got this far and still don't have any data, there's nothing to print, - // sorry. - if len(out) == 0 { - return nil - } - - // Prepend the header - out = append([]string{"Key" + hopeDelim + "Value"}, out...) - - ui.Output(tableOutput(out, &columnize.Config{ - Delim: hopeDelim, - })) - return nil -} -*/ - -func (t TableFormatter) OutputMap(ui cli.Ui, data map[string]interface{}) error { - out := make([]string, 0, len(data)+1) - if len(data) > 0 { - keys := make([]string, 0, len(data)) - for k := range data { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := data[k] - - // If the field "looks" like a TTL, print it as a time duration instead. - if looksLikeDuration(k) { - v = humanDurationInt(v) - } - - out = append(out, fmt.Sprintf("%s %s %v", k, hopeDelim, v)) - } - } - - // If we got this far and still don't have any data, there's nothing to print, - // sorry. - if len(out) == 0 { - return nil - } - - // Prepend the header - out = append([]string{"Key" + hopeDelim + "Value"}, out...) - - ui.Output(tableOutput(out, &columnize.Config{ - Delim: hopeDelim, - })) - 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 " - } - - out := []string{} - out = append(out, "Key | Value") - out = append(out, fmt.Sprintf("%sSeal Type | %s", sealPrefix, status.Type)) - out = append(out, fmt.Sprintf("Initialized | %t", status.Initialized)) - out = append(out, fmt.Sprintf("Sealed | %t", status.Sealed)) - out = append(out, fmt.Sprintf("Total %sShares | %d", sealPrefix, status.N)) - out = append(out, fmt.Sprintf("Threshold | %d", status.T)) - - if status.Sealed { - out = append(out, fmt.Sprintf("Unseal Progress | %d/%d", status.Progress, status.T)) - out = append(out, fmt.Sprintf("Unseal Nonce | %s", status.Nonce)) - } - - if status.Migration { - out = append(out, fmt.Sprintf("Seal Migration in Progress | %t", status.Migration)) - } - - out = append(out, fmt.Sprintf("Version | %s", status.Version)) - - if status.ClusterName != "" && status.ClusterID != "" { - out = append(out, fmt.Sprintf("Cluster Name | %s", status.ClusterName)) - out = append(out, fmt.Sprintf("Cluster ID | %s", status.ClusterID)) - } - - // Mask the 'Vault is sealed' error, since this means HA is enabled, but that - // we cannot query for the leader since we are sealed. - leaderStatus, err := client.Sys().Leader() - if err != nil && strings.Contains(err.Error(), "Vault is sealed") { - leaderStatus = &api.LeaderResponse{HAEnabled: true} - err = nil - } - if err != nil { - ui.Error(fmt.Sprintf("Error checking leader status: %s", err)) - return 1 - } - - // Output if HA is enabled - out = append(out, fmt.Sprintf("HA Enabled | %t", leaderStatus.HAEnabled)) - if leaderStatus.HAEnabled { - mode := "sealed" - if !status.Sealed { - out = append(out, fmt.Sprintf("HA Cluster | %s", leaderStatus.LeaderClusterAddress)) - mode = "standby" - showLeaderAddr := false - if leaderStatus.IsSelf { - mode = "active" - } else { - if leaderStatus.LeaderAddress == "" { - leaderStatus.LeaderAddress = "" - } - showLeaderAddr = true - } - out = append(out, fmt.Sprintf("HA Mode | %s", mode)) - - // This is down here just to keep ordering consistent - if showLeaderAddr { - out = append(out, fmt.Sprintf("Active Node Address | %s", leaderStatus.LeaderAddress)) - } - - if leaderStatus.PerfStandby { - out = append(out, fmt.Sprintf("Performance Standby Node | %t", leaderStatus.PerfStandby)) - out = append(out, fmt.Sprintf("Performance Standby Last Remote WAL | %d", leaderStatus.PerfStandbyLastRemoteWAL)) - } - } - } - - if leaderStatus.LastWAL != 0 { - out = append(out, fmt.Sprintf("Last WAL | %d", leaderStatus.LastWAL)) - } - - ui.Output(tableOutput(out, nil)) - return 0 -} -*/ - -// looksLikeDuration checks if the given key "k" looks like a duration value. -// This is used to pretty-format duration values in responses, especially from -// plugins. -func looksLikeDuration(k string) bool { - return k == "period" || strings.HasSuffix(k, "_period") || - k == "ttl" || strings.HasSuffix(k, "_ttl") || - k == "duration" || strings.HasSuffix(k, "_duration") || - k == "lease_max" || k == "ttl_max" -} - -type Formatter interface { - //Output(ui cli.Ui, secret *api.Secret, data interface{}) error - Format(data interface{}) ([]byte, error) -} - -var Formatters = map[string]Formatter{ - "json": JsonFormatter{}, - "table": TableFormatter{}, - "yaml": YamlFormatter{}, - "yml": YamlFormatter{}, -} - func Format(ui cli.Ui) string { switch t := ui.(type) { case *BoundaryUI: diff --git a/internal/cmd/base/helpers.go b/internal/cmd/base/helpers.go index cca0697c7b..5840075f62 100644 --- a/internal/cmd/base/helpers.go +++ b/internal/cmd/base/helpers.go @@ -1,64 +1,11 @@ package base import ( - "encoding/json" "strings" - "time" "github.com/kr/text" - "github.com/ryanuber/columnize" ) -// columnOuput prints the list of items as a table with no headers. -func columnOutput(list []string, c *columnize.Config) string { - if len(list) == 0 { - return "" - } - - if c == nil { - c = &columnize.Config{} - } - if c.Glue == "" { - c.Glue = " " - } - if c.Empty == "" { - c.Empty = "n/a" - } - - return columnize.Format(list, c) -} - -// tableOutput prints the list of items as columns, where the first row is -// the list of headers. -func tableOutput(list []string, c *columnize.Config) string { - if len(list) == 0 { - return "" - } - - delim := "|" - if c != nil && c.Delim != "" { - delim = c.Delim - } - - underline := "" - headers := strings.Split(list[0], delim) - for i, h := range headers { - h = strings.TrimSpace(h) - u := strings.Repeat("-", len(h)) - - underline = underline + u - if i != len(headers)-1 { - underline = underline + delim - } - } - - list = append(list, "") - copy(list[2:], list[1:]) - list[1] = underline - - return columnOutput(list, c) -} - // WrapAtLengthWithPadding wraps the given text at the maxLineLength, taking // into account any provided left padding. func WrapAtLengthWithPadding(s string, pad int) string { @@ -74,37 +21,3 @@ func WrapAtLengthWithPadding(s string, pad int) string { func WrapAtLength(s string) string { return WrapAtLengthWithPadding(s, 0) } - -// humanDuration prints the time duration without those pesky zeros. -func humanDuration(d time.Duration) string { - if d == 0 { - return "0s" - } - - s := d.String() - if strings.HasSuffix(s, "m0s") { - s = s[:len(s)-2] - } - if idx := strings.Index(s, "h0m"); idx > 0 { - s = s[:idx+1] + s[idx+3:] - } - return s -} - -// humanDurationInt prints the given int as if it were a time.Duration number -// of seconds. -func humanDurationInt(i interface{}) interface{} { - switch t := i.(type) { - case int: - return humanDuration(time.Duration(t) * time.Second) - case int64: - return humanDuration(time.Duration(t) * time.Second) - case json.Number: - if i, err := t.Int64(); err == nil { - return humanDuration(time.Duration(i) * time.Second) - } - } - - // If we don't know what type it is, just return the original value - return i -} diff --git a/internal/cmd/commands/controller/controller.go b/internal/cmd/commands/controller/controller.go index 71a1cb7b3a..ca51a75cb2 100644 --- a/internal/cmd/commands/controller/controller.go +++ b/internal/cmd/commands/controller/controller.go @@ -4,7 +4,6 @@ import ( "fmt" "runtime" "strings" - "sync" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/auth/password" @@ -31,8 +30,6 @@ type Command struct { ReloadedCh chan struct{} SigUSR2Ch chan struct{} - cleanupGuard sync.Once - Config *config.Config controller *controller.Controller diff --git a/internal/cmd/main.go b/internal/cmd/main.go index e65b574db4..5789961407 100644 --- a/internal/cmd/main.go +++ b/internal/cmd/main.go @@ -145,7 +145,9 @@ func RunCustom(args []string, runOpts *RunOptions) int { Format: format, } - if _, ok := base.Formatters[format]; !ok { + switch format { + case "table", "json": + default: ui.Error(fmt.Sprintf("Invalid output format: %s", format)) return 1 }