fix(cli): Status code on api error

When using the json output of the cli (`-format=json`), a successful
call to the controller API would place the HTTP status code in the JSON
as `status_code`. However, if there was an error from the API the status
code would be placed in the JSON as `status`. In order to be consistent,
this adds a `status_code` field to the error response case. For
backwards compatibility it still populates `status` in the error case.

This inconsistency was missed partially due to a bug in the CLI tests.
The assertion on status code was not valid and would always be
successful. Thus this also fixes the assertions and tests.
pull/2887/head
Timothy Messier 3 years ago
parent 99a8875d3e
commit e66ea15fef
No known key found for this signature in database
GPG Key ID: EFD2F184F7600572

@ -34,6 +34,12 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
subcommand, will have the subtype set to `vault-generic`. The deprecated
subtype and subcommands will be removed in boundary 0.14.0, at which point
`vault-generic` must be used.
* In Boundary 0.1.8 using the `-format=json` option with the cli would provide
a `status_code` for successful API requests from the cli. However, in the
case where an error was returned, the JSON would use `status` instead. This
inconsistency has been fixed, with `status_code` being used in both cases.
For error cases `status` will still be populated, but is deprecated and will
be removed in 0.14.0.
### New and Improved
@ -68,6 +74,11 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
* data warehouse: Fix bug that caused credential dimensions to not get
associated with session facts ([PR](https://github.com/hashicorp/boundary/pull/2787)).
* sessions: Fix two authorizeSession race conditions in handleProxy. ([PR](https://github.com/hashicorp/boundary/pull/2795))
* cli: When using `-format=json` the JSON was inconsistent in how it reported
status codes. In successful cases it would use `status_code`, but in error
cases it would use `status`. Now `status_code` is used in both cases. In
error cases `status` is still populated, see the deprecations above for
more details. ([PR](https://github.com/hashicorp/boundary/pull/2887))
## 0.11.2 (2022/12/09)

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/plugins"
"github.com/hashicorp/boundary/api/scopes"
"github.com/hashicorp/boundary/version"
"github.com/mitchellh/cli"
"github.com/mitchellh/go-wordwrap"
"github.com/pkg/errors"
@ -181,16 +182,32 @@ func (c *Command) PrintApiError(in *api.Error, contextStr string, opt ...Option)
opts := getOpts(opt...)
switch Format(c.UI) {
case "json":
output := struct {
Context string `json:"context,omitempty"`
Status int `json:"status"`
ApiError json.RawMessage `json:"api_error"`
}{
Context: contextStr,
Status: in.Response().StatusCode(),
ApiError: in.Response().Body.Bytes(),
var b []byte
if version.SupportsFeature(version.Binary, version.IncludeStatusInCli) {
output := struct {
Context string `json:"context,omitempty"`
StatusCode int `json:"status_code"`
Status int `json:"status"`
ApiError json.RawMessage `json:"api_error"`
}{
Context: contextStr,
StatusCode: in.Response().StatusCode(),
Status: in.Response().StatusCode(),
ApiError: in.Response().Body.Bytes(),
}
b, _ = JsonFormatter{}.Format(output)
} else {
output := struct {
Context string `json:"context,omitempty"`
StatusCode int `json:"status_code"`
ApiError json.RawMessage `json:"api_error"`
}{
Context: contextStr,
StatusCode: in.Response().StatusCode(),
ApiError: in.Response().Body.Bytes(),
}
b, _ = JsonFormatter{}.Format(output)
}
b, _ := JsonFormatter{}.Format(output)
c.UI.Error(string(b))
default:

@ -8,7 +8,7 @@ function read_account() {
}
function delete_account() {
boundary accounts delete -id $1
boundary accounts delete -id $1 -format json
}
function list_accounts() {

@ -20,7 +20,7 @@ function read_credential_library() {
}
function delete_credential_library() {
boundary credential-libraries delete -id $1
boundary credential-libraries delete -id $1 -format json
}
function list_credential_libraries() {

@ -19,7 +19,7 @@ function read_credential_store() {
}
function delete_credential_store() {
boundary credential-stores delete -id $1
boundary credential-stores delete -id $1 -format json
}
function list_credential_stores() {

@ -5,7 +5,7 @@ function create_username_password_credential() {
local sid=$2
local user=$3
local pass=$4
export BP="${pass}"
boundary credentials create username-password \
-name $name \
@ -32,7 +32,7 @@ function read_credential() {
}
function delete_credential() {
boundary credentials delete -id $1
boundary credentials delete -id $1 -format json
}
function list_credentials() {
@ -42,6 +42,6 @@ function list_credentials() {
function credential_id() {
local name=$1
local sid=$2
strip $(list_credentials $sid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]")
}

@ -9,7 +9,7 @@ function read_group() {
}
function delete_group() {
boundary groups delete -id $1
boundary groups delete -id $1 -format json
}
function list_groups() {
@ -27,7 +27,7 @@ function group_id() {
function group_member_ids() {
local gid=$1
boundary groups read -id $gid -format json | jq '.item["members"][]["id"]'
boundary groups read -id $gid -format json | jq '.item["members"][]["id"]'
}
function group_has_member_id() {
@ -36,10 +36,10 @@ function group_has_member_id() {
ids=$(group_member_ids $gid)
for id in $ids; do
if [ $(strip "$id") == "$mid" ]; then
return 0
return 0
fi
done
return 1
return 1
}
function has_default_group_actions() {
@ -51,6 +51,6 @@ function has_default_group_actions() {
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
}
done
}

@ -24,9 +24,8 @@ function strip_all() {
function has_status_code() {
local json=$1
local code=$2
if [ echo "$json"|jq -c ".status_code == $code" ]; then
return 1
fi
echo "checking .status_code == $code in $json"
echo "$json" | jq -e ".status_code == $code"
}
diag() {

@ -15,7 +15,7 @@ function read_host_catalog() {
}
function delete_host_catalog() {
boundary host-catalogs delete -id $1
boundary host-catalogs delete -id $1 -format json
}
function list_host_catalogs() {
@ -35,7 +35,7 @@ function has_default_host_catalog_actions() {
for action in ${actions[@]}; do
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
return 1
}
done
}

@ -3,7 +3,7 @@ load _authorized_actions
function create_host() {
local name=$1
local hcid=$2
boundary hosts create static \
-name $name \
-description 'test host' \
@ -16,7 +16,7 @@ function read_host() {
}
function delete_host() {
boundary hosts delete -id $1
boundary hosts delete -id $1 -format json
}
function list_hosts() {
@ -26,7 +26,7 @@ function list_hosts() {
function host_id() {
local name=$1
local hcid=$2
strip $(list_hosts $hcid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]")
}
@ -37,7 +37,7 @@ function has_default_host_actions() {
for action in ${actions[@]}; do
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
return 1
}
done
}

@ -4,7 +4,7 @@ function create_role() {
local sid=$1
local name=$2
local gsid=$3
boundary roles create \
-scope-id $sid \
-name $name \
@ -17,7 +17,7 @@ function read_role() {
}
function delete_role() {
boundary roles delete -id $1
boundary roles delete -id $1 -format json
}
function list_roles() {
@ -27,28 +27,28 @@ function list_roles() {
function assoc_role_grant() {
local grant=$1
local id=$2
boundary roles add-grants -grant $grant -id $id
}
function assoc_role_principal() {
local principal=$1
local id=$2
boundary roles add-principals -principal $principal -id $id
boundary roles add-principals -principal $principal -id $id
}
function remove_role_grant() {
local grant=$1
local id=$2
boundary roles remove-grants -grant $grant -id $id
}
function remove_role_principal() {
local principal=$1
local id=$2
boundary roles remove-principals -principal $principal -id $id
}
@ -60,7 +60,7 @@ function role_id() {
function role_principal_ids() {
local rid=$1
strip $(read_role $rid | jq '.item["principals"][]["id"]')
strip $(read_role $rid | jq '.item["principals"][]["id"]')
}
function role_has_principal_id() {
@ -69,10 +69,10 @@ function role_has_principal_id() {
local ids=$(role_principal_ids $rid)
for id in $ids; do
if [ $(strip "$id") == "$pid" ]; then
return 0
return 0
fi
done
return 1
return 1
}
function role_grants() {
@ -86,10 +86,10 @@ function role_has_grant() {
local hasgrants=$(role_grants $rid)
for grant in $hasgrants; do
if [ $(strip_all "$grant") == "$g" ]; then
return 0
return 0
fi
done
return 1
return 1
}
function has_default_role_actions() {
@ -99,7 +99,7 @@ function has_default_role_actions() {
for action in ${actions[@]}; do
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
return 1
}
done
}

@ -16,7 +16,7 @@ function read_scope() {
function delete_scope() {
local sid=$1
boundary scopes delete -id $sid
boundary scopes delete -id $sid -format json
}
function list_scopes() {
@ -26,7 +26,7 @@ function list_scopes() {
function scope_id() {
local name=$1
local sid=$2
strip $(list_scopes $sid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]")
}
@ -37,7 +37,7 @@ function has_default_scope_actions() {
for action in ${actions[@]}; do
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
return 1
}
done
}

@ -32,7 +32,7 @@ function read_target() {
}
function delete_target() {
boundary targets delete -id $1
boundary targets delete -id $1 -format json
}
function list_targets() {
@ -65,20 +65,20 @@ function target_id_from_name() {
function target_host_source_ids() {
local tid=$1
boundary targets read -id $tid -format json | jq '.item.host_sources[].id'
boundary targets read -id $tid -format json | jq '.item.host_sources[].id'
}
function target_has_host_source_id() {
local tid=$1
local hsid=$2
ids=$(target_host_source_ids $tid)
ids=$(target_host_source_ids $tid)
for id in $ids; do
if [ $(strip "$id") == "$hsid" ]; then
return 0
return 0
fi
done
return 1
return 1
}
function has_default_target_actions() {
@ -88,7 +88,7 @@ function has_default_target_actions() {
for action in ${actions[@]}; do
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
return 1
}
done
}

@ -9,7 +9,7 @@ function read_user() {
}
function delete_user() {
boundary users delete -id $1
boundary users delete -id $1 -format json
}
function list_users() {
@ -28,8 +28,8 @@ function has_default_user_actions() {
for action in ${actions[@]}; do
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
return 1
}
done
}

@ -15,7 +15,7 @@ function update_worker() {
}
function delete_worker() {
boundary workers delete -id $1
boundary workers delete -id $1 -format json
}
function list_workers() {
@ -34,7 +34,7 @@ function has_default_worker_actions() {
for action in ${actions[@]}; do
$(has_authorized_action "$out" "$action") || {
echo "failed to find $action action in output: $out"
return 1
}
return 1
}
done
}

@ -73,6 +73,13 @@ export NEW_VAULT_LIB="test_vault"
[ "$status" -eq 0 ]
run has_status_code "$output" "204"
[ "$status" -eq 0 ]
run delete_credential_library $clid
echo "$output"
[ "$status" -eq 1 ]
run has_status_code "$output" "404"
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credential-libraries: can create $NEW_VAULT_LIB vault library in credential store $NEW_STORE deprecated subcommand" {

@ -23,6 +23,7 @@ type Feature int
const (
UnknownFeature Feature = iota
MultiHopSessionFeature
IncludeStatusInCli
)
var featureMap map[Feature]MetadataConstraint
@ -52,6 +53,10 @@ func init() {
Constraints: mustNewConstraints(">= 0.1.0"), // This feature exists at 0.1.0 and above
}
*/
featureMap[IncludeStatusInCli] = MetadataConstraint{
MetaInfo: []Metadata{OSS, HCP},
Constraints: mustNewConstraints("< 0.14.0"),
}
}
func metadataStringToMetadata(m string) Metadata {

Loading…
Cancel
Save