Merge pull request #562 from jacksontj/issue_560

Add storage.Warnings to client
This commit is contained in:
Björn Rabenstein 2019-05-22 00:31:30 +02:00 committed by GitHub
commit b46e6ec51b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 226 additions and 154 deletions

View File

@ -25,6 +25,42 @@ import (
"time"
)
func NewErrorAPI(err error, warnings []string) Error {
if err == nil && warnings == nil {
return nil
}
return &ErrorAPI{err, warnings}
}
type ErrorAPI struct {
err error
warnings []string
}
func (w *ErrorAPI) Err() error {
return w.err
}
func (w *ErrorAPI) Error() string {
if w.err != nil {
return w.err.Error()
}
return "Warnings: " + strings.Join(w.warnings, " , ")
}
func (w *ErrorAPI) Warnings() []string {
return w.warnings
}
// Error encapsulates an error + warning
type Error interface {
error
// Err returns the underlying error.
Err() error
// Warnings returns a list of warnings.
Warnings() []string
}
// DefaultRoundTripper is used if no RoundTripper is set in Config.
var DefaultRoundTripper http.RoundTripper = &http.Transport{
Proxy: http.ProxyFromEnvironment,
@ -55,14 +91,14 @@ func (cfg *Config) roundTripper() http.RoundTripper {
// Client is the interface for an API client.
type Client interface {
URL(ep string, args map[string]string) *url.URL
Do(context.Context, *http.Request) (*http.Response, []byte, error)
Do(context.Context, *http.Request) (*http.Response, []byte, Error)
}
// DoGetFallback will attempt to do the request as-is, and on a 405 it will fallback to a GET request.
func DoGetFallback(c Client, ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, error) {
func DoGetFallback(c Client, ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, Error) {
req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(args.Encode()))
if err != nil {
return nil, nil, err
return nil, nil, NewErrorAPI(err, nil)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@ -71,11 +107,14 @@ func DoGetFallback(c Client, ctx context.Context, u *url.URL, args url.Values) (
u.RawQuery = args.Encode()
req, err = http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return nil, nil, err
return nil, nil, NewErrorAPI(err, nil)
}
} else {
return resp, body, err
if err != nil {
return resp, body, NewErrorAPI(err, nil)
}
return resp, body, nil
}
return c.Do(ctx, req)
}
@ -115,7 +154,7 @@ func (c *httpClient) URL(ep string, args map[string]string) *url.URL {
return &u
}
func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, Error) {
if ctx != nil {
req = req.WithContext(ctx)
}
@ -127,7 +166,7 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response,
}()
if err != nil {
return nil, nil, err
return nil, nil, NewErrorAPI(err, nil)
}
var body []byte
@ -147,5 +186,5 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response,
case <-done:
}
return resp, body, err
return resp, body, NewErrorAPI(err, nil)
}

View File

@ -22,6 +22,7 @@ import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/prometheus/client_golang/api"
@ -95,13 +96,26 @@ const (
// Error is an error returned by the API.
type Error struct {
Type ErrorType
Msg string
Detail string
Type ErrorType
Msg string
Detail string
warnings []string
}
func (e *Error) Error() string {
return fmt.Sprintf("%s: %s", e.Type, e.Msg)
if e.Type != "" || e.Msg != "" {
return fmt.Sprintf("%s: %s", e.Type, e.Msg)
}
return "Warnings: " + strings.Join(e.warnings, " , ")
}
func (w *Error) Err() error {
return w
}
func (w *Error) Warnings() []string {
return w.warnings
}
// Range represents a sliced time range.
@ -115,32 +129,32 @@ type Range struct {
// API provides bindings for Prometheus's v1 API.
type API interface {
// Alerts returns a list of all active alerts.
Alerts(ctx context.Context) (AlertsResult, error)
Alerts(ctx context.Context) (AlertsResult, api.Error)
// AlertManagers returns an overview of the current state of the Prometheus alert manager discovery.
AlertManagers(ctx context.Context) (AlertManagersResult, error)
AlertManagers(ctx context.Context) (AlertManagersResult, api.Error)
// CleanTombstones removes the deleted data from disk and cleans up the existing tombstones.
CleanTombstones(ctx context.Context) error
CleanTombstones(ctx context.Context) api.Error
// Config returns the current Prometheus configuration.
Config(ctx context.Context) (ConfigResult, error)
Config(ctx context.Context) (ConfigResult, api.Error)
// DeleteSeries deletes data for a selection of series in a time range.
DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error
DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) api.Error
// Flags returns the flag values that Prometheus was launched with.
Flags(ctx context.Context) (FlagsResult, error)
Flags(ctx context.Context) (FlagsResult, api.Error)
// LabelValues performs a query for the values of the given label.
LabelValues(ctx context.Context, label string) (model.LabelValues, error)
LabelValues(ctx context.Context, label string) (model.LabelValues, api.Error)
// Query performs a query for the given time.
Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Error)
// QueryRange performs a query for the given range.
QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
QueryRange(ctx context.Context, query string, r Range) (model.Value, api.Error)
// Series finds series by label matchers.
Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error)
Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Error)
// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
// under the TSDB's data directory and returns the directory as response.
Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error)
Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, api.Error)
// Rules returns a list of alerting and recording rules that are currently loaded.
Rules(ctx context.Context) (RulesResult, error)
Rules(ctx context.Context) (RulesResult, api.Error)
// Targets returns an overview of the current state of the Prometheus target discovery.
Targets(ctx context.Context) (TargetsResult, error)
Targets(ctx context.Context) (TargetsResult, api.Error)
}
// AlertsResult contains the result from querying the alerts endpoint.
@ -408,73 +422,73 @@ type httpAPI struct {
client api.Client
}
func (h *httpAPI) Alerts(ctx context.Context) (AlertsResult, error) {
func (h *httpAPI) Alerts(ctx context.Context) (AlertsResult, api.Error) {
u := h.client.URL(epAlerts, nil)
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return AlertsResult{}, err
return AlertsResult{}, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return AlertsResult{}, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return AlertsResult{}, apiErr
}
var res AlertsResult
err = json.Unmarshal(body, &res)
return res, err
return res, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) AlertManagers(ctx context.Context) (AlertManagersResult, error) {
func (h *httpAPI) AlertManagers(ctx context.Context) (AlertManagersResult, api.Error) {
u := h.client.URL(epAlertManagers, nil)
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return AlertManagersResult{}, err
return AlertManagersResult{}, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return AlertManagersResult{}, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return AlertManagersResult{}, apiErr
}
var res AlertManagersResult
err = json.Unmarshal(body, &res)
return res, err
return res, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) CleanTombstones(ctx context.Context) error {
func (h *httpAPI) CleanTombstones(ctx context.Context) api.Error {
u := h.client.URL(epCleanTombstones, nil)
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
if err != nil {
return err
return api.NewErrorAPI(err, nil)
}
_, _, err = h.client.Do(ctx, req)
return err
_, _, apiErr := h.client.Do(ctx, req)
return apiErr
}
func (h *httpAPI) Config(ctx context.Context) (ConfigResult, error) {
func (h *httpAPI) Config(ctx context.Context) (ConfigResult, api.Error) {
u := h.client.URL(epConfig, nil)
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return ConfigResult{}, err
return ConfigResult{}, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return ConfigResult{}, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return ConfigResult{}, apiErr
}
var res ConfigResult
err = json.Unmarshal(body, &res)
return res, err
return res, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error {
func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) api.Error {
u := h.client.URL(epDeleteSeries, nil)
q := u.Query()
@ -489,47 +503,47 @@ func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
if err != nil {
return err
return api.NewErrorAPI(err, nil)
}
_, _, err = h.client.Do(ctx, req)
return err
_, _, apiErr := h.client.Do(ctx, req)
return apiErr
}
func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, error) {
func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, api.Error) {
u := h.client.URL(epFlags, nil)
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return FlagsResult{}, err
return FlagsResult{}, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return FlagsResult{}, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return FlagsResult{}, apiErr
}
var res FlagsResult
err = json.Unmarshal(body, &res)
return res, err
return res, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, error) {
func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, api.Error) {
u := h.client.URL(epLabelValues, map[string]string{"name": label})
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return nil, err
return nil, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return nil, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return nil, apiErr
}
var labelValues model.LabelValues
err = json.Unmarshal(body, &labelValues)
return labelValues, err
return labelValues, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Error) {
u := h.client.URL(epQuery, nil)
q := u.Query()
@ -538,18 +552,16 @@ func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.
q.Set("time", ts.Format(time.RFC3339Nano))
}
_, body, err := api.DoGetFallback(h.client, ctx, u, q)
if err != nil {
return nil, err
_, body, apiErr := api.DoGetFallback(h.client, ctx, u, q)
if apiErr != nil {
return nil, apiErr
}
var qres queryResult
err = json.Unmarshal(body, &qres)
return model.Value(qres.v), err
return model.Value(qres.v), api.NewErrorAPI(json.Unmarshal(body, &qres), nil)
}
func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, api.Error) {
u := h.client.URL(epQueryRange, nil)
q := u.Query()
@ -564,18 +576,17 @@ func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.
q.Set("end", end)
q.Set("step", step)
_, body, err := api.DoGetFallback(h.client, ctx, u, q)
if err != nil {
return nil, err
_, body, apiErr := api.DoGetFallback(h.client, ctx, u, q)
if apiErr != nil {
return nil, apiErr
}
var qres queryResult
err = json.Unmarshal(body, &qres)
return model.Value(qres.v), err
return model.Value(qres.v), api.NewErrorAPI(json.Unmarshal(body, &qres), nil)
}
func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error) {
func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Error) {
u := h.client.URL(epSeries, nil)
q := u.Query()
@ -590,20 +601,20 @@ func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.T
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return nil, err
return nil, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return nil, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return nil, apiErr
}
var mset []model.LabelSet
err = json.Unmarshal(body, &mset)
return mset, err
return mset, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error) {
func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, api.Error) {
u := h.client.URL(epSnapshot, nil)
q := u.Query()
@ -613,53 +624,53 @@ func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult,
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
if err != nil {
return SnapshotResult{}, err
return SnapshotResult{}, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return SnapshotResult{}, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return SnapshotResult{}, apiErr
}
var res SnapshotResult
err = json.Unmarshal(body, &res)
return res, err
return res, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) Rules(ctx context.Context) (RulesResult, error) {
func (h *httpAPI) Rules(ctx context.Context) (RulesResult, api.Error) {
u := h.client.URL(epRules, nil)
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return RulesResult{}, err
return RulesResult{}, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return RulesResult{}, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return RulesResult{}, apiErr
}
var res RulesResult
err = json.Unmarshal(body, &res)
return res, err
return res, api.NewErrorAPI(err, nil)
}
func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, error) {
func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, api.Error) {
u := h.client.URL(epTargets, nil)
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return TargetsResult{}, err
return TargetsResult{}, api.NewErrorAPI(err, nil)
}
_, body, err := h.client.Do(ctx, req)
if err != nil {
return TargetsResult{}, err
_, body, apiErr := h.client.Do(ctx, req)
if apiErr != nil {
return TargetsResult{}, apiErr
}
var res TargetsResult
err = json.Unmarshal(body, &res)
return res, err
return res, api.NewErrorAPI(err, nil)
}
// apiClient wraps a regular client and processes successful API responses.
@ -673,6 +684,7 @@ type apiResponse struct {
Data json.RawMessage `json:"data"`
ErrorType ErrorType `json:"errorType"`
Error string `json:"error"`
Warnings []string `json:"warnings,omitempty"`
}
func apiError(code int) bool {
@ -690,14 +702,16 @@ func errorTypeAndMsgFor(resp *http.Response) (ErrorType, string) {
return ErrBadResponse, fmt.Sprintf("bad response code %d", resp.StatusCode)
}
func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
resp, body, err := c.Client.Do(ctx, req)
if err != nil {
return resp, body, err
func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Error) {
resp, body, apiErr := c.Client.Do(ctx, req)
if apiErr != nil {
return resp, body, apiErr
}
code := resp.StatusCode
var err api.Error
if code/100 != 2 && !apiError(code) {
errorType, errorMsg := errorTypeAndMsgFor(resp)
return resp, body, &Error{
@ -710,27 +724,30 @@ func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, [
var result apiResponse
if http.StatusNoContent != code {
if err = json.Unmarshal(body, &result); err != nil {
if jsonErr := json.Unmarshal(body, &result); jsonErr != nil {
return resp, body, &Error{
Type: ErrBadResponse,
Msg: err.Error(),
Msg: jsonErr.Error(),
}
}
}
if apiError(code) != (result.Status == "error") {
err = &Error{
Type: ErrBadResponse,
Msg: "inconsistent body for response code",
Type: ErrBadResponse,
Msg: "inconsistent body for response code",
warnings: result.Warnings,
}
}
if apiError(code) && result.Status == "error" {
err = &Error{
Type: result.ErrorType,
Msg: result.Error,
Type: result.ErrorType,
Msg: result.Error,
warnings: result.Warnings,
}
}
return resp, []byte(result.Data), err
}

View File

@ -25,11 +25,14 @@ import (
"testing"
"time"
"github.com/prometheus/client_golang/api"
"github.com/prometheus/common/model"
"github.com/prometheus/tsdb/testutil"
)
type apiTest struct {
do func() (interface{}, error)
do func() (interface{}, api.Error)
inWarnings []string
inErr error
inStatusCode int
inRes interface{}
@ -58,7 +61,7 @@ func (c *apiTestClient) URL(ep string, args map[string]string) *url.URL {
return u
}
func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Error) {
test := c.curTest
@ -83,7 +86,7 @@ func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Respon
resp.StatusCode = http.StatusOK
}
return resp, b, test.inErr
return resp, b, api.NewErrorAPI(test.inErr, test.inWarnings)
}
func TestAPIs(t *testing.T) {
@ -96,74 +99,74 @@ func TestAPIs(t *testing.T) {
client: client,
}
doAlertManagers := func() func() (interface{}, error) {
return func() (interface{}, error) {
doAlertManagers := func() func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.AlertManagers(context.Background())
}
}
doCleanTombstones := func() func() (interface{}, error) {
return func() (interface{}, error) {
doCleanTombstones := func() func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return nil, promAPI.CleanTombstones(context.Background())
}
}
doConfig := func() func() (interface{}, error) {
return func() (interface{}, error) {
doConfig := func() func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.Config(context.Background())
}
}
doDeleteSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, error) {
return func() (interface{}, error) {
doDeleteSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return nil, promAPI.DeleteSeries(context.Background(), []string{matcher}, startTime, endTime)
}
}
doFlags := func() func() (interface{}, error) {
return func() (interface{}, error) {
doFlags := func() func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.Flags(context.Background())
}
}
doLabelValues := func(label string) func() (interface{}, error) {
return func() (interface{}, error) {
doLabelValues := func(label string) func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.LabelValues(context.Background(), label)
}
}
doQuery := func(q string, ts time.Time) func() (interface{}, error) {
return func() (interface{}, error) {
doQuery := func(q string, ts time.Time) func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.Query(context.Background(), q, ts)
}
}
doQueryRange := func(q string, rng Range) func() (interface{}, error) {
return func() (interface{}, error) {
doQueryRange := func(q string, rng Range) func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.QueryRange(context.Background(), q, rng)
}
}
doSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, error) {
return func() (interface{}, error) {
doSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.Series(context.Background(), []string{matcher}, startTime, endTime)
}
}
doSnapshot := func(skipHead bool) func() (interface{}, error) {
return func() (interface{}, error) {
doSnapshot := func(skipHead bool) func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.Snapshot(context.Background(), skipHead)
}
}
doRules := func() func() (interface{}, error) {
return func() (interface{}, error) {
doRules := func() func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.Rules(context.Background())
}
}
doTargets := func() func() (interface{}, error) {
return func() (interface{}, error) {
doTargets := func() func() (interface{}, api.Error) {
return func() (interface{}, api.Error) {
return promAPI.Targets(context.Background())
}
}
@ -693,7 +696,7 @@ func (c *testClient) URL(ep string, args map[string]string) *url.URL {
return nil
}
func (c *testClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
func (c *testClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Error) {
if ctx == nil {
c.Fatalf("context was not passed down")
}
@ -829,6 +832,21 @@ func TestAPIClientDo(t *testing.T) {
Msg: "inconsistent body for response code",
},
},
{
code: http.StatusOK,
response: &apiResponse{
Status: "error",
Data: json.RawMessage(`"test"`),
ErrorType: ErrTimeout,
Error: "timed out",
Warnings: []string{"a"},
},
expectedErr: &Error{
Type: ErrBadResponse,
Msg: "inconsistent body for response code",
warnings: []string{"a"},
},
},
}
tc := &testClient{
@ -846,28 +864,20 @@ func TestAPIClientDo(t *testing.T) {
_, body, err := client.Do(context.Background(), tc.req)
if test.expectedErr != nil {
if err == nil {
t.Fatalf("expected error %q but got none", test.expectedErr)
}
if test.expectedErr.Error() != err.Error() {
t.Errorf("unexpected error: want %q, got %q", test.expectedErr, err)
}
testutil.NotOk(t, err)
testutil.Equals(t, test.expectedErr.Error(), err.Error())
if test.expectedErr.Detail != "" {
apiErr := err.(*Error)
if apiErr.Detail != test.expectedErr.Detail {
t.Errorf("unexpected error details: want %q, got %q", test.expectedErr.Detail, apiErr.Detail)
}
testutil.Equals(t, apiErr.Detail, test.expectedErr.Detail)
}
testutil.Equals(t, test.expectedErr.Warnings(), err.Warnings())
return
}
if err != nil {
t.Fatalf("unexpeceted error %s", err)
}
testutil.Ok(t, err)
want, got := test.expectedBody, string(body)
if want != got {
t.Errorf("unexpected body: want %q, got %q", want, got)
}
testutil.Equals(t, test.expectedBody, string(body))
})
}

6
go.sum
View File

@ -1,3 +1,4 @@
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -5,7 +6,9 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
@ -30,6 +33,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
@ -45,8 +49,10 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=