@@ -21,7 +21,9 @@ import (
21
21
"fmt"
22
22
"math"
23
23
"net/http"
24
+ "net/url"
24
25
"strconv"
26
+ "strings"
25
27
"time"
26
28
"unsafe"
27
29
@@ -228,15 +230,15 @@ type API interface {
228
230
// Flags returns the flag values that Prometheus was launched with.
229
231
Flags (ctx context.Context ) (FlagsResult , error )
230
232
// LabelNames returns all the unique label names present in the block in sorted order.
231
- LabelNames (ctx context.Context ) ([]string , api. Warnings , error )
233
+ LabelNames (ctx context.Context ) ([]string , Warnings , error )
232
234
// LabelValues performs a query for the values of the given label.
233
- LabelValues (ctx context.Context , label string ) (model.LabelValues , api. Warnings , error )
235
+ LabelValues (ctx context.Context , label string ) (model.LabelValues , Warnings , error )
234
236
// Query performs a query for the given time.
235
- Query (ctx context.Context , query string , ts time.Time ) (model.Value , api. Warnings , error )
237
+ Query (ctx context.Context , query string , ts time.Time ) (model.Value , Warnings , error )
236
238
// QueryRange performs a query for the given range.
237
- QueryRange (ctx context.Context , query string , r Range ) (model.Value , api. Warnings , error )
239
+ QueryRange (ctx context.Context , query string , r Range ) (model.Value , Warnings , error )
238
240
// Series finds series by label matchers.
239
- Series (ctx context.Context , matches []string , startTime time.Time , endTime time.Time ) ([]model.LabelSet , api. Warnings , error )
241
+ Series (ctx context.Context , matches []string , startTime time.Time , endTime time.Time ) ([]model.LabelSet , Warnings , error )
240
242
// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
241
243
// under the TSDB's data directory and returns the directory as response.
242
244
Snapshot (ctx context.Context , skipHead bool ) (SnapshotResult , error )
@@ -515,11 +517,15 @@ func (qr *queryResult) UnmarshalJSON(b []byte) error {
515
517
//
516
518
// It is safe to use the returned API from multiple goroutines.
517
519
func NewAPI (c api.Client ) API {
518
- return & httpAPI {client : apiClient {c }}
520
+ return & httpAPI {
521
+ client : & apiClientImpl {
522
+ client : c ,
523
+ },
524
+ }
519
525
}
520
526
521
527
type httpAPI struct {
522
- client api. Client
528
+ client apiClient
523
529
}
524
530
525
531
func (h * httpAPI ) Alerts (ctx context.Context ) (AlertsResult , error ) {
@@ -624,7 +630,7 @@ func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, error) {
624
630
return res , json .Unmarshal (body , & res )
625
631
}
626
632
627
- func (h * httpAPI ) LabelNames (ctx context.Context ) ([]string , api. Warnings , error ) {
633
+ func (h * httpAPI ) LabelNames (ctx context.Context ) ([]string , Warnings , error ) {
628
634
u := h .client .URL (epLabels , nil )
629
635
req , err := http .NewRequest (http .MethodGet , u .String (), nil )
630
636
if err != nil {
@@ -638,7 +644,7 @@ func (h *httpAPI) LabelNames(ctx context.Context) ([]string, api.Warnings, error
638
644
return labelNames , w , json .Unmarshal (body , & labelNames )
639
645
}
640
646
641
- func (h * httpAPI ) LabelValues (ctx context.Context , label string ) (model.LabelValues , api. Warnings , error ) {
647
+ func (h * httpAPI ) LabelValues (ctx context.Context , label string ) (model.LabelValues , Warnings , error ) {
642
648
u := h .client .URL (epLabelValues , map [string ]string {"name" : label })
643
649
req , err := http .NewRequest (http .MethodGet , u .String (), nil )
644
650
if err != nil {
@@ -652,7 +658,7 @@ func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelVal
652
658
return labelValues , w , json .Unmarshal (body , & labelValues )
653
659
}
654
660
655
- func (h * httpAPI ) Query (ctx context.Context , query string , ts time.Time ) (model.Value , api. Warnings , error ) {
661
+ func (h * httpAPI ) Query (ctx context.Context , query string , ts time.Time ) (model.Value , Warnings , error ) {
656
662
u := h .client .URL (epQuery , nil )
657
663
q := u .Query ()
658
664
@@ -661,7 +667,7 @@ func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.
661
667
q .Set ("time" , formatTime (ts ))
662
668
}
663
669
664
- _ , body , warnings , err := api . DoGetFallback ( h .client , ctx , u , q )
670
+ _ , body , warnings , err := h .client . DoGetFallback ( ctx , u , q )
665
671
if err != nil {
666
672
return nil , warnings , err
667
673
}
@@ -670,7 +676,7 @@ func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.
670
676
return model .Value (qres .v ), warnings , json .Unmarshal (body , & qres )
671
677
}
672
678
673
- func (h * httpAPI ) QueryRange (ctx context.Context , query string , r Range ) (model.Value , api. Warnings , error ) {
679
+ func (h * httpAPI ) QueryRange (ctx context.Context , query string , r Range ) (model.Value , Warnings , error ) {
674
680
u := h .client .URL (epQueryRange , nil )
675
681
q := u .Query ()
676
682
@@ -679,7 +685,7 @@ func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.
679
685
q .Set ("end" , formatTime (r .End ))
680
686
q .Set ("step" , strconv .FormatFloat (r .Step .Seconds (), 'f' , - 1 , 64 ))
681
687
682
- _ , body , warnings , err := api . DoGetFallback ( h .client , ctx , u , q )
688
+ _ , body , warnings , err := h .client . DoGetFallback ( ctx , u , q )
683
689
if err != nil {
684
690
return nil , warnings , err
685
691
}
@@ -689,7 +695,7 @@ func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.
689
695
return model .Value (qres .v ), warnings , json .Unmarshal (body , & qres )
690
696
}
691
697
692
- func (h * httpAPI ) Series (ctx context.Context , matches []string , startTime time.Time , endTime time.Time ) ([]model.LabelSet , api. Warnings , error ) {
698
+ func (h * httpAPI ) Series (ctx context.Context , matches []string , startTime time.Time , endTime time.Time ) ([]model.LabelSet , Warnings , error ) {
693
699
u := h .client .URL (epSeries , nil )
694
700
q := u .Query ()
695
701
@@ -796,10 +802,19 @@ func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metri
796
802
return res , json .Unmarshal (body , & res )
797
803
}
798
804
805
+ // Warnings is an array of non critical errors
806
+ type Warnings []string
807
+
799
808
// apiClient wraps a regular client and processes successful API responses.
800
809
// Successful also includes responses that errored at the API level.
801
- type apiClient struct {
802
- api.Client
810
+ type apiClient interface {
811
+ URL (ep string , args map [string ]string ) * url.URL
812
+ Do (context.Context , * http.Request ) (* http.Response , []byte , Warnings , error )
813
+ DoGetFallback (ctx context.Context , u * url.URL , args url.Values ) (* http.Response , []byte , Warnings , error )
814
+ }
815
+
816
+ type apiClientImpl struct {
817
+ client api.Client
803
818
}
804
819
805
820
type apiResponse struct {
@@ -825,17 +840,21 @@ func errorTypeAndMsgFor(resp *http.Response) (ErrorType, string) {
825
840
return ErrBadResponse , fmt .Sprintf ("bad response code %d" , resp .StatusCode )
826
841
}
827
842
828
- func (c apiClient ) Do (ctx context.Context , req * http.Request ) (* http.Response , []byte , api.Warnings , error ) {
829
- resp , body , warnings , err := c .Client .Do (ctx , req )
843
+ func (h * apiClientImpl ) URL (ep string , args map [string ]string ) * url.URL {
844
+ return h .client .URL (ep , args )
845
+ }
846
+
847
+ func (h * apiClientImpl ) Do (ctx context.Context , req * http.Request ) (* http.Response , []byte , Warnings , error ) {
848
+ resp , body , err := h .client .Do (ctx , req )
830
849
if err != nil {
831
- return resp , body , warnings , err
850
+ return resp , body , nil , err
832
851
}
833
852
834
853
code := resp .StatusCode
835
854
836
855
if code / 100 != 2 && ! apiError (code ) {
837
856
errorType , errorMsg := errorTypeAndMsgFor (resp )
838
- return resp , body , warnings , & Error {
857
+ return resp , body , nil , & Error {
839
858
Type : errorType ,
840
859
Msg : errorMsg ,
841
860
Detail : string (body ),
@@ -846,7 +865,7 @@ func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, [
846
865
847
866
if http .StatusNoContent != code {
848
867
if jsonErr := json .Unmarshal (body , & result ); jsonErr != nil {
849
- return resp , body , warnings , & Error {
868
+ return resp , body , nil , & Error {
850
869
Type : ErrBadResponse ,
851
870
Msg : jsonErr .Error (),
852
871
}
@@ -867,10 +886,35 @@ func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, [
867
886
}
868
887
}
869
888
870
- return resp , []byte (result .Data ), warnings , err
889
+ return resp , []byte (result .Data ), result . Warnings , err
871
890
872
891
}
873
892
893
+ // DoGetFallback will attempt to do the request as-is, and on a 405 it will fallback to a GET request.
894
+ func (h * apiClientImpl ) DoGetFallback (ctx context.Context , u * url.URL , args url.Values ) (* http.Response , []byte , Warnings , error ) {
895
+ req , err := http .NewRequest (http .MethodPost , u .String (), strings .NewReader (args .Encode ()))
896
+ if err != nil {
897
+ return nil , nil , nil , err
898
+ }
899
+ req .Header .Set ("Content-Type" , "application/x-www-form-urlencoded" )
900
+
901
+ resp , body , warnings , err := h .Do (ctx , req )
902
+ if resp != nil && resp .StatusCode == http .StatusMethodNotAllowed {
903
+ u .RawQuery = args .Encode ()
904
+ req , err = http .NewRequest (http .MethodGet , u .String (), nil )
905
+ if err != nil {
906
+ return nil , nil , warnings , err
907
+ }
908
+
909
+ } else {
910
+ if err != nil {
911
+ return resp , body , warnings , err
912
+ }
913
+ return resp , body , warnings , nil
914
+ }
915
+ return h .Do (ctx , req )
916
+ }
917
+
874
918
func formatTime (t time.Time ) string {
875
919
return strconv .FormatFloat (float64 (t .Unix ())+ float64 (t .Nanosecond ())/ 1e9 , 'f' , - 1 , 64 )
876
920
}
0 commit comments