Skip to content

Commit

Permalink
add StatusError to api package (#11054)
Browse files Browse the repository at this point in the history
* add require http codes in api and use in operator_autopilot health check

* add StatusError type in api package

Signed-off-by: FFMMM <FFMMM@users.noreply.github.com>
  • Loading branch information
FFMMM authored Sep 20, 2021
1 parent 9592990 commit 377c3a9
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 14 deletions.
31 changes: 26 additions & 5 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ const (
HTTPPartitionEnvName = "CONSUL_PARTITION"
)

type StatusError struct {
Code int
Body string
}

func (e StatusError) Error() string {
return fmt.Sprintf("Unexpected response code: %d (%s)", e.Code, e.Body)
}

// QueryOptions are used to parameterize a query
type QueryOptions struct {
// Namespace overrides the `default` namespace
Expand Down Expand Up @@ -972,7 +981,7 @@ func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
func (c *Client) query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) {
r := c.newRequest("GET", endpoint)
r.setQueryOptions(q)
rtt, resp, err := c.doRequest(r)
rtt, resp, err := requireOK(c.doRequest(r))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1090,16 +1099,28 @@ func encodeBody(obj interface{}) (io.Reader, error) {

// requireOK is used to wrap doRequest and check for a 200
func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
return requireHttpCodes(d, resp, e, 200)
}

// requireHttpCodes checks for the "allowable" http codes for a response
func requireHttpCodes(d time.Duration, resp *http.Response, e error, httpCodes ...int) (time.Duration, *http.Response, error) {
if e != nil {
if resp != nil {
closeResponseBody(resp)
}
return d, nil, e
}
if resp.StatusCode != 200 {
return d, nil, generateUnexpectedResponseCodeError(resp)

// if there is an http code that we require, return w no error
for _, httpCode := range httpCodes {
if resp.StatusCode == httpCode {
return d, resp, nil
}
}
return d, resp, nil

// if we reached here, then none of the http codes in resp matched any that we expected
// so err out
return d, nil, generateUnexpectedResponseCodeError(resp)
}

// closeResponseBody reads resp.Body until EOF, and then closes it. The read
Expand Down Expand Up @@ -1127,7 +1148,7 @@ func generateUnexpectedResponseCodeError(resp *http.Response) error {
closeResponseBody(resp)

trimmed := strings.TrimSpace(string(buf.Bytes()))
return fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, trimmed)
return StatusError{Code: resp.StatusCode, Body: trimmed}
}

func requireNotFoundOrOK(d time.Duration, resp *http.Response, e error) (bool, time.Duration, *http.Response, error) {
Expand Down
13 changes: 4 additions & 9 deletions api/operator_autopilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,22 +352,17 @@ func (op *Operator) AutopilotServerHealth(q *QueryOptions) (*OperatorHealthReply
r := op.c.newRequest("GET", "/v1/operator/autopilot/health")
r.setQueryOptions(q)

// we cannot just use requireOK because this endpoint might use a 429 status to indicate
// that unhealthiness
_, resp, err := op.c.doRequest(r)
// we use 429 status to indicate unhealthiness
d, resp, err := op.c.doRequest(r)
_, resp, err = requireHttpCodes(d, resp, err, 200, 429)

if err != nil {
if resp != nil {
closeResponseBody(resp)
}
return nil, err
}

// these are the only 2 status codes that would indicate that we should
// expect the body to contain the right format.
if resp.StatusCode != 200 && resp.StatusCode != 429 {
return nil, generateUnexpectedResponseCodeError(resp)
}

defer closeResponseBody(resp)

var out OperatorHealthReply
Expand Down
12 changes: 12 additions & 0 deletions api/operator_autopilot_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"errors"
"testing"
"time"

Expand Down Expand Up @@ -181,4 +182,15 @@ func TestAPI_OperatorAutopilotServerHealth_429(t *testing.T) {
out, err := client.Operator().AutopilotServerHealth(nil)
require.NoError(t, err)
require.Equal(t, &reply, out)

mapi.withReply("GET", "/v1/operator/autopilot/health", nil, 500, nil).Once()
_, err = client.Operator().AutopilotServerHealth(nil)

var statusE StatusError
if errors.As(err, &statusE) {
require.Equal(t, 500, statusE.Code)
} else {
t.Error("Failed to unwrap error as StatusError")
}

}

0 comments on commit 377c3a9

Please sign in to comment.