|
|
|
|
@ -31,7 +31,9 @@ import ( |
|
|
|
|
|
|
|
|
|
"github.com/gogo/protobuf/proto" |
|
|
|
|
"github.com/golang/snappy" |
|
|
|
|
config_util "github.com/prometheus/common/config" |
|
|
|
|
"github.com/prometheus/common/model" |
|
|
|
|
"github.com/prometheus/common/promlog" |
|
|
|
|
"github.com/prometheus/common/route" |
|
|
|
|
|
|
|
|
|
"github.com/prometheus/prometheus/config" |
|
|
|
|
@ -128,30 +130,125 @@ func TestEndpoints(t *testing.T) { |
|
|
|
|
|
|
|
|
|
now := time.Now() |
|
|
|
|
|
|
|
|
|
var tr testTargetRetriever |
|
|
|
|
t.Run("local", func(t *testing.T) { |
|
|
|
|
api := &API{ |
|
|
|
|
Queryable: suite.Storage(), |
|
|
|
|
QueryEngine: suite.QueryEngine(), |
|
|
|
|
targetRetriever: testTargetRetriever{}, |
|
|
|
|
alertmanagerRetriever: testAlertmanagerRetriever{}, |
|
|
|
|
now: func() time.Time { return now }, |
|
|
|
|
config: func() config.Config { return samplePrometheusCfg }, |
|
|
|
|
flagsMap: sampleFlagMap, |
|
|
|
|
ready: func(f http.HandlerFunc) http.HandlerFunc { return f }, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var ar testAlertmanagerRetriever |
|
|
|
|
testEndpoints(t, api, true) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
api := &API{ |
|
|
|
|
Queryable: suite.Storage(), |
|
|
|
|
QueryEngine: suite.QueryEngine(), |
|
|
|
|
targetRetriever: tr, |
|
|
|
|
alertmanagerRetriever: ar, |
|
|
|
|
now: func() time.Time { return now }, |
|
|
|
|
config: func() config.Config { return samplePrometheusCfg }, |
|
|
|
|
flagsMap: sampleFlagMap, |
|
|
|
|
ready: func(f http.HandlerFunc) http.HandlerFunc { return f }, |
|
|
|
|
} |
|
|
|
|
// Run all the API tests against a API that is wired to forward queries via
|
|
|
|
|
// the remote read client to a test server, which in turn sends them to the
|
|
|
|
|
// data from the test suite.
|
|
|
|
|
t.Run("remote", func(t *testing.T) { |
|
|
|
|
server := setupRemote(suite.Storage()) |
|
|
|
|
defer server.Close() |
|
|
|
|
|
|
|
|
|
u, err := url.Parse(server.URL) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
al := promlog.AllowedLevel{} |
|
|
|
|
al.Set("debug") |
|
|
|
|
remote := remote.NewStorage(promlog.New(al), func() (int64, error) { |
|
|
|
|
return 0, nil |
|
|
|
|
}, 1*time.Second) |
|
|
|
|
|
|
|
|
|
err = remote.ApplyConfig(&config.Config{ |
|
|
|
|
RemoteReadConfigs: []*config.RemoteReadConfig{ |
|
|
|
|
{ |
|
|
|
|
URL: &config_util.URL{URL: u}, |
|
|
|
|
RemoteTimeout: model.Duration(1 * time.Second), |
|
|
|
|
ReadRecent: true, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
api := &API{ |
|
|
|
|
Queryable: remote, |
|
|
|
|
QueryEngine: suite.QueryEngine(), |
|
|
|
|
targetRetriever: testTargetRetriever{}, |
|
|
|
|
alertmanagerRetriever: testAlertmanagerRetriever{}, |
|
|
|
|
now: func() time.Time { return now }, |
|
|
|
|
config: func() config.Config { return samplePrometheusCfg }, |
|
|
|
|
flagsMap: sampleFlagMap, |
|
|
|
|
ready: func(f http.HandlerFunc) http.HandlerFunc { return f }, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
testEndpoints(t, api, false) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func setupRemote(s storage.Storage) *httptest.Server { |
|
|
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
req, err := remote.DecodeReadRequest(r) |
|
|
|
|
if err != nil { |
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
resp := prompb.ReadResponse{ |
|
|
|
|
Results: make([]*prompb.QueryResult, len(req.Queries)), |
|
|
|
|
} |
|
|
|
|
for i, query := range req.Queries { |
|
|
|
|
from, through, matchers, err := remote.FromQuery(query) |
|
|
|
|
if err != nil { |
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
querier, err := s.Querier(r.Context(), from, through) |
|
|
|
|
if err != nil { |
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
defer querier.Close() |
|
|
|
|
|
|
|
|
|
set, err := querier.Select(nil, matchers...) |
|
|
|
|
if err != nil { |
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
resp.Results[i], err = remote.ToQueryResult(set) |
|
|
|
|
if err != nil { |
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := remote.EncodeReadResponse(&resp, w); err != nil { |
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
return httptest.NewServer(handler) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func testEndpoints(t *testing.T, api *API, testLabelAPI bool) { |
|
|
|
|
|
|
|
|
|
start := time.Unix(0, 0) |
|
|
|
|
|
|
|
|
|
var tests = []struct { |
|
|
|
|
type test struct { |
|
|
|
|
endpoint apiFunc |
|
|
|
|
params map[string]string |
|
|
|
|
query url.Values |
|
|
|
|
response interface{} |
|
|
|
|
errType errorType |
|
|
|
|
}{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var tests = []test{ |
|
|
|
|
{ |
|
|
|
|
endpoint: api.query, |
|
|
|
|
query: url.Values{ |
|
|
|
|
@ -203,7 +300,7 @@ func TestEndpoints(t *testing.T) { |
|
|
|
|
ResultType: promql.ValueTypeScalar, |
|
|
|
|
Result: promql.Scalar{ |
|
|
|
|
V: 0.333, |
|
|
|
|
T: timestamp.FromTime(now), |
|
|
|
|
T: timestamp.FromTime(api.now()), |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
@ -309,34 +406,6 @@ func TestEndpoints(t *testing.T) { |
|
|
|
|
}, |
|
|
|
|
errType: errorBadData, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
endpoint: api.labelValues, |
|
|
|
|
params: map[string]string{ |
|
|
|
|
"name": "__name__", |
|
|
|
|
}, |
|
|
|
|
response: []string{ |
|
|
|
|
"test_metric1", |
|
|
|
|
"test_metric2", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
endpoint: api.labelValues, |
|
|
|
|
params: map[string]string{ |
|
|
|
|
"name": "foo", |
|
|
|
|
}, |
|
|
|
|
response: []string{ |
|
|
|
|
"bar", |
|
|
|
|
"boo", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
// Bad name parameter.
|
|
|
|
|
{ |
|
|
|
|
endpoint: api.labelValues, |
|
|
|
|
params: map[string]string{ |
|
|
|
|
"name": "not!!!allowed", |
|
|
|
|
}, |
|
|
|
|
errType: errorBadData, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
endpoint: api.series, |
|
|
|
|
query: url.Values{ |
|
|
|
|
@ -500,6 +569,39 @@ func TestEndpoints(t *testing.T) { |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if testLabelAPI { |
|
|
|
|
tests = append(tests, []test{ |
|
|
|
|
{ |
|
|
|
|
endpoint: api.labelValues, |
|
|
|
|
params: map[string]string{ |
|
|
|
|
"name": "__name__", |
|
|
|
|
}, |
|
|
|
|
response: []string{ |
|
|
|
|
"test_metric1", |
|
|
|
|
"test_metric2", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
endpoint: api.labelValues, |
|
|
|
|
params: map[string]string{ |
|
|
|
|
"name": "foo", |
|
|
|
|
}, |
|
|
|
|
response: []string{ |
|
|
|
|
"bar", |
|
|
|
|
"boo", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
// Bad name parameter.
|
|
|
|
|
{ |
|
|
|
|
endpoint: api.labelValues, |
|
|
|
|
params: map[string]string{ |
|
|
|
|
"name": "not!!!allowed", |
|
|
|
|
}, |
|
|
|
|
errType: errorBadData, |
|
|
|
|
}, |
|
|
|
|
}...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
methods := func(f apiFunc) []string { |
|
|
|
|
fp := reflect.ValueOf(f).Pointer() |
|
|
|
|
if fp == reflect.ValueOf(api.query).Pointer() || fp == reflect.ValueOf(api.queryRange).Pointer() { |
|
|
|
|
@ -517,14 +619,14 @@ func TestEndpoints(t *testing.T) { |
|
|
|
|
return http.NewRequest(m, fmt.Sprintf("http://example.com?%s", q.Encode()), nil) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, test := range tests { |
|
|
|
|
for i, test := range tests { |
|
|
|
|
for _, method := range methods(test.endpoint) { |
|
|
|
|
// Build a context with the correct request params.
|
|
|
|
|
ctx := context.Background() |
|
|
|
|
for p, v := range test.params { |
|
|
|
|
ctx = route.WithParam(ctx, p, v) |
|
|
|
|
} |
|
|
|
|
t.Logf("run %s\t%q", method, test.query.Encode()) |
|
|
|
|
t.Logf("run %d\t%s\t%q", i, method, test.query.Encode()) |
|
|
|
|
|
|
|
|
|
req, err := request(method, test.query) |
|
|
|
|
if err != nil { |
|
|
|
|
|