From 7f4279992ed68ae6a4a00cf4f28ea75b22ec5566 Mon Sep 17 00:00:00 2001 From: Joe Elliott Date: Tue, 10 Dec 2019 15:16:44 -0500 Subject: [PATCH] First pass unravel Signed-off-by: Joe Elliott --- api/client.go | 33 +++---------------- api/prometheus/v1/api.go | 69 ++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/api/client.go b/api/client.go index 53b87ae..6a2fe2c 100644 --- a/api/client.go +++ b/api/client.go @@ -57,32 +57,7 @@ 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, Warnings, 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, Warnings, error) { - req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(args.Encode())) - if err != nil { - return nil, nil, nil, err - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - resp, body, warnings, err := c.Do(ctx, req) - if resp != nil && resp.StatusCode == http.StatusMethodNotAllowed { - u.RawQuery = args.Encode() - req, err = http.NewRequest(http.MethodGet, u.String(), nil) - if err != nil { - return nil, nil, warnings, err - } - - } else { - if err != nil { - return resp, body, warnings, err - } - return resp, body, warnings, nil - } - return c.Do(ctx, req) + Do(context.Context, *http.Request) (*http.Response, []byte, error) } // NewClient returns a new Client. @@ -120,7 +95,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, Warnings, error) { +func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) { if ctx != nil { req = req.WithContext(ctx) } @@ -132,7 +107,7 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, }() if err != nil { - return nil, nil, nil, err + return nil, nil, err } var body []byte @@ -152,5 +127,5 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, case <-done: } - return resp, body, nil, err + return resp, body, err } diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index 00cd76e..154e4a8 100644 --- a/api/prometheus/v1/api.go +++ b/api/prometheus/v1/api.go @@ -21,7 +21,9 @@ import ( "fmt" "math" "net/http" + "net/url" "strconv" + "strings" "time" "unsafe" @@ -515,7 +517,7 @@ func (qr *queryResult) UnmarshalJSON(b []byte) error { // // It is safe to use the returned API from multiple goroutines. func NewAPI(c api.Client) API { - return &httpAPI{client: apiClient{c}} + return &httpAPI{client: c} } type httpAPI struct { @@ -530,7 +532,7 @@ func (h *httpAPI) Alerts(ctx context.Context) (AlertsResult, error) { return AlertsResult{}, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return AlertsResult{}, err } @@ -547,7 +549,7 @@ func (h *httpAPI) AlertManagers(ctx context.Context) (AlertManagersResult, error return AlertManagersResult{}, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return AlertManagersResult{}, err } @@ -564,7 +566,7 @@ func (h *httpAPI) CleanTombstones(ctx context.Context) error { return err } - _, _, _, err = h.client.Do(ctx, req) + _, _, _, err = h.Do(ctx, req) return err } @@ -576,7 +578,7 @@ func (h *httpAPI) Config(ctx context.Context) (ConfigResult, error) { return ConfigResult{}, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return ConfigResult{}, err } @@ -603,7 +605,7 @@ func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime return err } - _, _, _, err = h.client.Do(ctx, req) + _, _, _, err = h.Do(ctx, req) return err } @@ -615,7 +617,7 @@ func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, error) { return FlagsResult{}, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return FlagsResult{}, err } @@ -630,7 +632,7 @@ func (h *httpAPI) LabelNames(ctx context.Context) ([]string, api.Warnings, error if err != nil { return nil, nil, err } - _, body, w, err := h.client.Do(ctx, req) + _, body, w, err := h.Do(ctx, req) if err != nil { return nil, w, err } @@ -644,7 +646,7 @@ func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelVal if err != nil { return nil, nil, err } - _, body, w, err := h.client.Do(ctx, req) + _, body, w, err := h.Do(ctx, req) if err != nil { return nil, w, err } @@ -661,7 +663,7 @@ func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model. q.Set("time", formatTime(ts)) } - _, body, warnings, err := api.DoGetFallback(h.client, ctx, u, q) + _, body, warnings, err := h.DoGetFallback(ctx, u, q) if err != nil { return nil, warnings, err } @@ -679,7 +681,7 @@ func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model. q.Set("end", formatTime(r.End)) q.Set("step", strconv.FormatFloat(r.Step.Seconds(), 'f', -1, 64)) - _, body, warnings, err := api.DoGetFallback(h.client, ctx, u, q) + _, body, warnings, err := h.DoGetFallback(ctx, u, q) if err != nil { return nil, warnings, err } @@ -707,7 +709,7 @@ func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.T return nil, nil, err } - _, body, warnings, err := h.client.Do(ctx, req) + _, body, warnings, err := h.Do(ctx, req) if err != nil { return nil, warnings, err } @@ -729,7 +731,7 @@ func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, return SnapshotResult{}, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return SnapshotResult{}, err } @@ -746,7 +748,7 @@ func (h *httpAPI) Rules(ctx context.Context) (RulesResult, error) { return RulesResult{}, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return RulesResult{}, err } @@ -763,7 +765,7 @@ func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, error) { return TargetsResult{}, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return TargetsResult{}, err } @@ -787,7 +789,7 @@ func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metri return nil, err } - _, body, _, err := h.client.Do(ctx, req) + _, body, _, err := h.Do(ctx, req) if err != nil { return nil, err } @@ -796,12 +798,6 @@ func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metri return res, json.Unmarshal(body, &res) } -// apiClient wraps a regular client and processes successful API responses. -// Successful also includes responses that errored at the API level. -type apiClient struct { - api.Client -} - type apiResponse struct { Status string `json:"status"` Data json.RawMessage `json:"data"` @@ -825,8 +821,8 @@ 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, api.Warnings, error) { - resp, body, warnings, err := c.Client.Do(ctx, req) +func (h *httpAPI) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Warnings, error) { + resp, body, warnings, err := h.Do(ctx, req) if err != nil { return resp, body, warnings, err } @@ -871,6 +867,31 @@ func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, [ } +// DoGetFallback will attempt to do the request as-is, and on a 405 it will fallback to a GET request. +func (h *httpAPI) DoGetFallback(ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, api.Warnings, error) { + req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(args.Encode())) + if err != nil { + return nil, nil, nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, body, warnings, err := h.Do(ctx, req) + if resp != nil && resp.StatusCode == http.StatusMethodNotAllowed { + u.RawQuery = args.Encode() + req, err = http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, nil, warnings, err + } + + } else { + if err != nil { + return resp, body, warnings, err + } + return resp, body, warnings, nil + } + return h.Do(ctx, req) +} + func formatTime(t time.Time) string { return strconv.FormatFloat(float64(t.Unix())+float64(t.Nanosecond())/1e9, 'f', -1, 64) }