Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CLI error codes to be consistent with connect #976

Merged
merged 5 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,25 @@ All of these changes are from [PR
and `ResponseBody()`, resources simply expose `Response()`. This higher-level
response object contains the map and body, and also exposes `StatusCode()` in
place of indivdidual resources.
[PR](https://github.com/hashicorp/boundary/pull/962)
* cli: In `json` output format, a resource item is now an object under the
top-level key `item`; a list of resource items is now an list of objects under
the top-level key `items`. This preserves the top level for putting in other
useful information later on (and the HTTP status code is included now).
[PR](https://github.com/hashicorp/boundary/pull/962)
* cli: In `json` output format, errors are now serialized as a JSON object with
an `error` key instead of outputting normal text
[PR](https://github.com/hashicorp/boundary/pull/962)
* cli: All errors, including API errors, are now written to `stderr`. Previously
in the default table format, API errors would be written to `stdout`.
[PR](https://github.com/hashicorp/boundary/pull/962)
* cli: Error return codes have been standardized across CLI commands. An error
code of `1` indicates an error generated from the actual controller API; an
error code of `2` is an error encountered due to the CLI command's logic; and
an error code of `3` indicates an error that was caused due to user input to
the command. (There is some nuance sometimes whether an error is really due to
user input or not, but we attempt to be consistent.)
[PR](https://github.com/hashicorp/boundary/pull/976)

### New and Improved

Expand Down
7 changes: 7 additions & 0 deletions internal/cmd/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ import (
zkeyring "github.com/zalando/go-keyring"
)

const (
CommandSuccess int = iota
CommandApiError
CommandCliError
CommandUserError
)

const (
// maxLineLength is the maximum width of any line.
maxLineLength int = 78
Expand Down
12 changes: 6 additions & 6 deletions internal/cmd/base/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (c *Command) PrintCliError(err error) {
}

// PrintJsonItem prints the given item to the UI in JSON format
func (c *Command) PrintJsonItem(result api.GenericResult, item interface{}) int {
func (c *Command) PrintJsonItem(result api.GenericResult, item interface{}) bool {
output := struct {
StatusCode int `json:"status_code"`
Item interface{} `json:"item"`
Expand All @@ -240,14 +240,14 @@ func (c *Command) PrintJsonItem(result api.GenericResult, item interface{}) int
b, err := JsonFormatter{}.Format(output)
if err != nil {
c.PrintCliError(err)
return 1
return false
}
c.UI.Output(string(b))
return 0
return true
}

// PrintJsonItems prints the given items to the UI in JSON format
func (c *Command) PrintJsonItems(result api.GenericListResult, items []interface{}) int {
func (c *Command) PrintJsonItems(result api.GenericListResult, items []interface{}) bool {
output := struct {
StatusCode int `json:"status_code"`
Items []interface{} `json:"items"`
Expand All @@ -258,10 +258,10 @@ func (c *Command) PrintJsonItems(result api.GenericListResult, items []interface
b, err := JsonFormatter{}.Format(output)
if err != nil {
c.PrintCliError(err)
return 1
return false
}
c.UI.Output(string(b))
return 0
return true
}

// An output formatter for json output of an object
Expand Down
36 changes: 20 additions & 16 deletions internal/cmd/commands/accountscmd/accounts.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions internal/cmd/commands/accountscmd/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,25 +115,25 @@ func extraFlagsFuncImpl(c *Command, _ *base.FlagSets, f *base.FlagSet) {
}
}

func extraFlagsHandlingFuncImpl(c *Command, opts *[]accounts.Option) int {
func extraFlagsHandlingFuncImpl(c *Command, opts *[]accounts.Option) bool {
if strutil.StrListContains(flagsMap[c.Func], "password") && c.flagPassword == "" {
fmt.Print("Password is not set as flag, please enter it now (will be hidden): ")
value, err := password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
return false
}
fmt.Print("Please enter it one more time for confirmation: ")
confirmation, err := password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
return false
}
if strings.TrimSpace(value) != strings.TrimSpace(confirmation) {
c.UI.Error("Entered password and confirmation value did not match.")
return 2
return false
}
c.flagPassword = strings.TrimSpace(value)
}
Expand All @@ -144,18 +144,18 @@ func extraFlagsHandlingFuncImpl(c *Command, opts *[]accounts.Option) int {
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
return false
}
fmt.Print("Please enter it one more time for confirmation: ")
confirmation, err := password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
return false
}
if strings.TrimSpace(value) != strings.TrimSpace(confirmation) {
c.UI.Error("Entered password and confirmation value did not match.")
return 2
return false
}
c.flagCurrentPassword = strings.TrimSpace(value)
}
Expand All @@ -166,12 +166,12 @@ func extraFlagsHandlingFuncImpl(c *Command, opts *[]accounts.Option) int {
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
return false
}
c.flagNewPassword = strings.TrimSpace(value)
}

return 0
return true
}

func executeExtraActionsImpl(c *Command, origResult api.GenericResult, origError error, accountClient *accounts.Client, version uint32, opts []accounts.Option) (api.GenericResult, error) {
Expand Down
28 changes: 15 additions & 13 deletions internal/cmd/commands/accountscmd/password_accounts.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions internal/cmd/commands/accountscmd/password_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ func extraPasswordFlagsFuncImpl(c *PasswordCommand, set *base.FlagSets, f *base.
}
}

func extraPasswordFlagsHandlingFuncImpl(c *PasswordCommand, opts *[]accounts.Option) int {
func extraPasswordFlagsHandlingFuncImpl(c *PasswordCommand, opts *[]accounts.Option) bool {
if c.Func == "create" && c.flagLoginName == "" {
c.UI.Error("Login Name must be passed in via -login-name")
return 1
return false
}

switch c.flagLoginName {
Expand All @@ -100,24 +100,24 @@ func extraPasswordFlagsHandlingFuncImpl(c *PasswordCommand, opts *[]accounts.Opt
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
return false
}
fmt.Print("Please enter it one more time for confirmation: ")
confirmation, err := password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return 2
return false
}
if strings.TrimSpace(value) != strings.TrimSpace(confirmation) {
c.UI.Error("Entered password and confirmation value did not match.")
return 2
return false
}
*opts = append(*opts, accounts.WithPasswordAccountPassword(strings.TrimSpace(value)))
default:
*opts = append(*opts, accounts.WithPasswordAccountPassword(c.flagPassword))
}
}

return 0
return true
}
Loading