add StatusError to api package (#11054)

* 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>
This commit is contained in:
FFMMM 2021-09-20 14:04:13 -07:00 committed by GitHub
parent 9528a3bf1f
commit ae9e167096
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 14 deletions

View File

@ -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
@ -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
}
@ -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
@ -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) {

View File

@ -352,9 +352,10 @@ 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)
@ -362,12 +363,6 @@ func (op *Operator) AutopilotServerHealth(q *QueryOptions) (*OperatorHealthReply
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

View File

@ -1,6 +1,7 @@
package api
import (
"errors"
"testing"
"time"
@ -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")
}
}