2020-03-13 11:19:31 +01:00
|
|
|
package datasource
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-04-30 08:46:03 +02:00
|
|
|
"fmt"
|
2020-03-13 11:19:31 +01:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2021-04-30 08:46:03 +02:00
|
|
|
"reflect"
|
2020-09-21 14:53:49 +02:00
|
|
|
"strconv"
|
2020-03-13 11:19:31 +01:00
|
|
|
"testing"
|
2020-09-21 14:53:49 +02:00
|
|
|
"time"
|
2020-03-13 11:19:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ctx = context.Background()
|
|
|
|
basicAuthName = "foo"
|
|
|
|
basicAuthPass = "bar"
|
|
|
|
query = "vm_rows"
|
2021-02-01 14:02:44 +01:00
|
|
|
queryRender = "constantLine(10)"
|
2020-03-13 11:19:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestVMSelectQuery(t *testing.T) {
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("/", func(_ http.ResponseWriter, _ *http.Request) {
|
|
|
|
t.Errorf("should not be called")
|
|
|
|
})
|
|
|
|
c := -1
|
2021-02-01 14:02:44 +01:00
|
|
|
mux.HandleFunc("/render", func(w http.ResponseWriter, request *http.Request) {
|
|
|
|
c++
|
|
|
|
switch c {
|
|
|
|
case 7:
|
|
|
|
w.Write([]byte(`[{"target":"constantLine(10)","tags":{"name":"constantLine(10)"},"datapoints":[[10,1611758343],[10,1611758373],[10,1611758403]]}]`))
|
|
|
|
}
|
|
|
|
})
|
2020-03-13 11:19:31 +01:00
|
|
|
mux.HandleFunc("/api/v1/query", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
c++
|
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
t.Errorf("expected POST method got %s", r.Method)
|
|
|
|
}
|
|
|
|
if name, pass, _ := r.BasicAuth(); name != basicAuthName || pass != basicAuthPass {
|
|
|
|
t.Errorf("expected %s:%s as basic auth got %s:%s", basicAuthName, basicAuthPass, name, pass)
|
|
|
|
}
|
|
|
|
if r.URL.Query().Get("query") != query {
|
2020-07-02 17:05:36 +02:00
|
|
|
t.Errorf("expected %s in query param, got %s", query, r.URL.Query().Get("query"))
|
2020-03-13 11:19:31 +01:00
|
|
|
}
|
2020-09-21 14:53:49 +02:00
|
|
|
timeParam := r.URL.Query().Get("time")
|
|
|
|
if timeParam == "" {
|
|
|
|
t.Errorf("expected 'time' in query param, got nil instead")
|
|
|
|
}
|
|
|
|
if _, err := strconv.ParseInt(timeParam, 10, 64); err != nil {
|
|
|
|
t.Errorf("failed to parse 'time' query param: %s", err)
|
|
|
|
}
|
2020-03-13 11:19:31 +01:00
|
|
|
switch c {
|
|
|
|
case 0:
|
|
|
|
conn, _, _ := w.(http.Hijacker).Hijack()
|
|
|
|
_ = conn.Close()
|
|
|
|
case 1:
|
|
|
|
w.WriteHeader(500)
|
|
|
|
case 2:
|
|
|
|
w.Write([]byte("[]"))
|
|
|
|
case 3:
|
|
|
|
w.Write([]byte(`{"status":"error", "errorType":"type:", "error":"some error msg"}`))
|
|
|
|
case 4:
|
|
|
|
w.Write([]byte(`{"status":"unknown"}`))
|
|
|
|
case 5:
|
|
|
|
w.Write([]byte(`{"status":"success","data":{"resultType":"matrix"}}`))
|
|
|
|
case 6:
|
|
|
|
w.Write([]byte(`{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"vm_rows"},"value":[1583786142,"13763"]}]}}`))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
srv := httptest.NewServer(mux)
|
|
|
|
defer srv.Close()
|
2021-04-30 08:46:03 +02:00
|
|
|
|
|
|
|
s := NewVMStorage(srv.URL, basicAuthName, basicAuthPass, time.Minute, 0, false, srv.Client())
|
|
|
|
|
|
|
|
p := NewPrometheusType()
|
|
|
|
pq := s.BuildWithParams(QuerierParams{DataSourceType: &p, EvaluationInterval: 15 * time.Second})
|
|
|
|
|
|
|
|
if _, err := pq.Query(ctx, query); err == nil {
|
2020-03-13 11:19:31 +01:00
|
|
|
t.Fatalf("expected connection error got nil")
|
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
if _, err := pq.Query(ctx, query); err == nil {
|
2020-03-13 11:19:31 +01:00
|
|
|
t.Fatalf("expected invalid response status error got nil")
|
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
if _, err := pq.Query(ctx, query); err == nil {
|
2020-03-13 11:19:31 +01:00
|
|
|
t.Fatalf("expected response body error got nil")
|
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
if _, err := pq.Query(ctx, query); err == nil {
|
2020-03-13 11:19:31 +01:00
|
|
|
t.Fatalf("expected error status got nil")
|
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
if _, err := pq.Query(ctx, query); err == nil {
|
2020-10-07 16:59:50 +02:00
|
|
|
t.Fatalf("expected unknown status got nil")
|
2020-03-13 11:19:31 +01:00
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
if _, err := pq.Query(ctx, query); err == nil {
|
2020-03-13 11:19:31 +01:00
|
|
|
t.Fatalf("expected non-vector resultType error got nil")
|
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
m, err := pq.Query(ctx, query)
|
2020-03-13 11:19:31 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unexpected %s", err)
|
|
|
|
}
|
|
|
|
if len(m) != 1 {
|
2020-07-02 17:05:36 +02:00
|
|
|
t.Fatalf("expected 1 metric got %d in %+v", len(m), m)
|
2020-03-13 11:19:31 +01:00
|
|
|
}
|
|
|
|
expected := Metric{
|
|
|
|
Labels: []Label{{Value: "vm_rows", Name: "__name__"}},
|
|
|
|
Timestamp: 1583786142,
|
|
|
|
Value: 13763,
|
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
if !reflect.DeepEqual(m[0], expected) {
|
2020-03-13 11:19:31 +01:00
|
|
|
t.Fatalf("unexpected metric %+v want %+v", m[0], expected)
|
|
|
|
}
|
2021-04-28 22:41:15 +02:00
|
|
|
|
2021-04-30 08:46:03 +02:00
|
|
|
g := NewGraphiteType()
|
|
|
|
gq := s.BuildWithParams(QuerierParams{DataSourceType: &g})
|
|
|
|
|
|
|
|
m, err = gq.Query(ctx, queryRender)
|
2021-02-01 14:02:44 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unexpected %s", err)
|
|
|
|
}
|
|
|
|
if len(m) != 1 {
|
|
|
|
t.Fatalf("expected 1 metric got %d in %+v", len(m), m)
|
|
|
|
}
|
|
|
|
expected = Metric{
|
|
|
|
Labels: []Label{{Value: "constantLine(10)", Name: "name"}},
|
|
|
|
Timestamp: 1611758403,
|
|
|
|
Value: 10,
|
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
if !reflect.DeepEqual(m[0], expected) {
|
2021-02-01 14:02:44 +01:00
|
|
|
t.Fatalf("unexpected metric %+v want %+v", m[0], expected)
|
|
|
|
}
|
2020-03-13 11:19:31 +01:00
|
|
|
}
|
2021-04-30 08:46:03 +02:00
|
|
|
|
|
|
|
func TestPrepareReq(t *testing.T) {
|
|
|
|
query := "up"
|
|
|
|
timestamp := time.Date(2001, 2, 3, 4, 5, 6, 0, time.UTC)
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
vm *VMStorage
|
|
|
|
checkFn func(t *testing.T, r *http.Request)
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"prometheus path",
|
|
|
|
&VMStorage{
|
|
|
|
dataSourceType: NewPrometheusType(),
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
checkEqualString(t, queryPath, r.URL.Path)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"prometheus prefix",
|
|
|
|
&VMStorage{
|
|
|
|
dataSourceType: NewPrometheusType(),
|
|
|
|
appendTypePrefix: true,
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
checkEqualString(t, prometheusPrefix+queryPath, r.URL.Path)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"graphite path",
|
|
|
|
&VMStorage{
|
|
|
|
dataSourceType: NewGraphiteType(),
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
checkEqualString(t, graphitePath, r.URL.Path)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"graphite prefix",
|
|
|
|
&VMStorage{
|
|
|
|
dataSourceType: NewGraphiteType(),
|
|
|
|
appendTypePrefix: true,
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
checkEqualString(t, graphitePrefix+graphitePath, r.URL.Path)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"default params",
|
|
|
|
&VMStorage{},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
exp := fmt.Sprintf("query=%s&time=%d", query, timestamp.Unix())
|
|
|
|
checkEqualString(t, exp, r.URL.RawQuery)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"basic auth",
|
|
|
|
&VMStorage{
|
|
|
|
basicAuthUser: "foo",
|
|
|
|
basicAuthPass: "bar",
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
u, p, _ := r.BasicAuth()
|
|
|
|
checkEqualString(t, "foo", u)
|
|
|
|
checkEqualString(t, "bar", p)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"lookback",
|
|
|
|
&VMStorage{
|
|
|
|
lookBack: time.Minute,
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
exp := fmt.Sprintf("query=%s&time=%d", query, timestamp.Add(-time.Minute).Unix())
|
|
|
|
checkEqualString(t, exp, r.URL.RawQuery)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"evaluation interval",
|
|
|
|
&VMStorage{
|
|
|
|
evaluationInterval: 15 * time.Second,
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
2021-04-30 09:01:05 +02:00
|
|
|
evalInterval := 15 * time.Second
|
|
|
|
tt := timestamp.Truncate(evalInterval)
|
|
|
|
exp := fmt.Sprintf("query=%s&step=%v&time=%d", query, evalInterval, tt.Unix())
|
2021-04-30 08:46:03 +02:00
|
|
|
checkEqualString(t, exp, r.URL.RawQuery)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"lookback + evaluation interval",
|
|
|
|
&VMStorage{
|
|
|
|
lookBack: time.Minute,
|
|
|
|
evaluationInterval: 15 * time.Second,
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
2021-04-30 09:01:05 +02:00
|
|
|
evalInterval := 15 * time.Second
|
2021-04-30 08:46:03 +02:00
|
|
|
tt := timestamp.Add(-time.Minute)
|
2021-04-30 09:01:05 +02:00
|
|
|
tt = tt.Truncate(evalInterval)
|
|
|
|
exp := fmt.Sprintf("query=%s&step=%v&time=%d", query, evalInterval, tt.Unix())
|
2021-04-30 08:46:03 +02:00
|
|
|
checkEqualString(t, exp, r.URL.RawQuery)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2021-04-30 09:01:05 +02:00
|
|
|
"step override",
|
2021-04-30 08:46:03 +02:00
|
|
|
&VMStorage{
|
|
|
|
queryStep: time.Minute,
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
exp := fmt.Sprintf("query=%s&step=%v&time=%d", query, time.Minute, timestamp.Unix())
|
|
|
|
checkEqualString(t, exp, r.URL.RawQuery)
|
|
|
|
},
|
|
|
|
},
|
2021-05-10 10:11:45 +02:00
|
|
|
{
|
|
|
|
"round digits",
|
|
|
|
&VMStorage{
|
|
|
|
roundDigits: "10",
|
|
|
|
},
|
|
|
|
func(t *testing.T, r *http.Request) {
|
|
|
|
exp := fmt.Sprintf("query=%s&round_digits=10&time=%d", query, timestamp.Unix())
|
|
|
|
checkEqualString(t, exp, r.URL.RawQuery)
|
|
|
|
},
|
|
|
|
},
|
2021-04-30 08:46:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
req, err := tc.vm.prepareReq(query, timestamp)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
}
|
|
|
|
tc.checkFn(t, req)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkEqualString(t *testing.T, exp, got string) {
|
|
|
|
t.Helper()
|
|
|
|
if got != exp {
|
|
|
|
t.Errorf("expected to get %q; got %q", exp, got)
|
|
|
|
}
|
|
|
|
}
|