diff --git a/app/vmalert/datasource/vm_prom_api.go b/app/vmalert/datasource/vm_prom_api.go index fc2ef88a9..894775e89 100644 --- a/app/vmalert/datasource/vm_prom_api.go +++ b/app/vmalert/datasource/vm_prom_api.go @@ -168,7 +168,7 @@ func (s *VMStorage) setPrometheusInstantReqParams(r *http.Request, query string, // see https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1232 timestamp = timestamp.Truncate(s.evaluationInterval) } - q.Set("time", fmt.Sprintf("%d", timestamp.Unix())) + q.Set("time", timestamp.Format(time.RFC3339)) if !*disableStepParam && s.evaluationInterval > 0 { // set step as evaluationInterval by default // always convert to seconds to keep compatibility with older // Prometheus versions. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1943 @@ -191,8 +191,8 @@ func (s *VMStorage) setPrometheusRangeReqParams(r *http.Request, query string, s r.URL.Path += "/api/v1/query_range" } q := r.URL.Query() - q.Add("start", fmt.Sprintf("%d", start.Unix())) - q.Add("end", fmt.Sprintf("%d", end.Unix())) + q.Add("start", start.Format(time.RFC3339)) + q.Add("end", end.Format(time.RFC3339)) if s.evaluationInterval > 0 { // set step as evaluationInterval by default // always convert to seconds to keep compatibility with older // Prometheus versions. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1943 diff --git a/app/vmalert/datasource/vm_test.go b/app/vmalert/datasource/vm_test.go index 3df8805c2..5011d300a 100644 --- a/app/vmalert/datasource/vm_test.go +++ b/app/vmalert/datasource/vm_test.go @@ -8,7 +8,6 @@ import ( "net/url" "reflect" "sort" - "strconv" "strings" "testing" "time" @@ -50,8 +49,8 @@ func TestVMInstantQuery(t *testing.T) { 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) + if _, err := time.Parse(time.RFC3339, timeParam); err != nil { + t.Errorf("failed to parse 'time' query param %q: %s", timeParam, err) } switch c { case 0: @@ -193,7 +192,6 @@ func TestVMInstantQuery(t *testing.T) { }, } metricsEqual(t, res.Data, exp) - } func TestVMInstantQueryWithRetry(t *testing.T) { @@ -309,14 +307,14 @@ func TestVMRangeQuery(t *testing.T) { if startTS == "" { t.Errorf("expected 'start' in query param, got nil instead") } - if _, err := strconv.ParseInt(startTS, 10, 64); err != nil { + if _, err := time.Parse(time.RFC3339, startTS); err != nil { t.Errorf("failed to parse 'start' query param: %s", err) } endTS := r.URL.Query().Get("end") if endTS == "" { t.Errorf("expected 'end' in query param, got nil instead") } - if _, err := strconv.ParseInt(endTS, 10, 64); err != nil { + if _, err := time.Parse(time.RFC3339, endTS); err != nil { t.Errorf("failed to parse 'end' query param: %s", err) } step := r.URL.Query().Get("step") @@ -455,8 +453,8 @@ func TestRequestParams(t *testing.T) { false, &VMStorage{}, func(t *testing.T, r *http.Request) { - exp := fmt.Sprintf("query=%s&time=%d", query, timestamp.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + exp := url.Values{"query": {query}, "time": {timestamp.Format(time.RFC3339)}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -464,8 +462,9 @@ func TestRequestParams(t *testing.T) { true, &VMStorage{}, func(t *testing.T, r *http.Request) { - exp := fmt.Sprintf("end=%d&query=%s&start=%d", timestamp.Unix(), query, timestamp.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + ts := timestamp.Format(time.RFC3339) + exp := url.Values{"query": {query}, "start": {ts}, "end": {ts}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -495,8 +494,8 @@ func TestRequestParams(t *testing.T) { 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) + exp := url.Values{"query": {query}, "time": {timestamp.Add(-time.Minute).Format(time.RFC3339)}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -508,8 +507,8 @@ func TestRequestParams(t *testing.T) { func(t *testing.T, r *http.Request) { evalInterval := 15 * time.Second tt := timestamp.Truncate(evalInterval) - exp := fmt.Sprintf("query=%s&step=%v&time=%d", query, evalInterval, tt.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + exp := url.Values{"query": {query}, "step": {evalInterval.String()}, "time": {tt.Format(time.RFC3339)}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -523,8 +522,8 @@ func TestRequestParams(t *testing.T) { evalInterval := 15 * time.Second tt := timestamp.Add(-time.Minute) tt = tt.Truncate(evalInterval) - exp := fmt.Sprintf("query=%s&step=%v&time=%d", query, evalInterval, tt.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + exp := url.Values{"query": {query}, "step": {evalInterval.String()}, "time": {tt.Format(time.RFC3339)}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -534,8 +533,12 @@ func TestRequestParams(t *testing.T) { queryStep: time.Minute, }, func(t *testing.T, r *http.Request) { - exp := fmt.Sprintf("query=%s&step=%ds&time=%d", query, int(time.Minute.Seconds()), timestamp.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + exp := url.Values{ + "query": {query}, + "step": {fmt.Sprintf("%ds", int(time.Minute.Seconds()))}, + "time": {timestamp.Format(time.RFC3339)}, + } + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -547,8 +550,8 @@ func TestRequestParams(t *testing.T) { func(t *testing.T, r *http.Request) { evalInterval := 3 * time.Hour tt := timestamp.Truncate(evalInterval) - exp := fmt.Sprintf("query=%s&step=%ds&time=%d", query, int(evalInterval.Seconds()), tt.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + exp := url.Values{"query": {query}, "step": {fmt.Sprintf("%ds", int(evalInterval.Seconds()))}, "time": {tt.Format(time.RFC3339)}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -558,8 +561,8 @@ func TestRequestParams(t *testing.T) { extraParams: url.Values{"round_digits": {"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) + exp := url.Values{"query": {query}, "round_digits": {"10"}, "time": {timestamp.Format(time.RFC3339)}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -572,9 +575,14 @@ func TestRequestParams(t *testing.T) { }, }, func(t *testing.T, r *http.Request) { - exp := fmt.Sprintf("end=%d&max_lookback=1h&nocache=1&query=%s&start=%d", - timestamp.Unix(), query, timestamp.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + exp := url.Values{ + "query": {query}, + "end": {timestamp.Format(time.RFC3339)}, + "start": {timestamp.Format(time.RFC3339)}, + "nocache": {"1"}, + "max_lookback": {"1h"}, + } + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -584,8 +592,8 @@ func TestRequestParams(t *testing.T) { QueryParams: url.Values{"round_digits": {"2"}}, }), func(t *testing.T, r *http.Request) { - exp := fmt.Sprintf("query=%s&round_digits=2&time=%d", query, timestamp.Unix()) - checkEqualString(t, exp, r.URL.RawQuery) + exp := url.Values{"query": {query}, "round_digits": {"2"}, "time": {timestamp.Format(time.RFC3339)}} + checkEqualString(t, exp.Encode(), r.URL.RawQuery) }, }, { @@ -627,7 +635,7 @@ func TestRequestParams(t *testing.T) { } func TestHeaders(t *testing.T) { - var testCases = []struct { + testCases := []struct { name string vmFn func() *VMStorage checkFn func(t *testing.T, r *http.Request) @@ -692,7 +700,8 @@ func TestHeaders(t *testing.T) { authCfg: cfg, extraHeaders: []keyValue{ {key: "Authorization", value: "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="}, - }} + }, + } }, checkFn: func(t *testing.T, r *http.Request) { u, p, _ := r.BasicAuth() diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 22c7b9bce..2dcdef40a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -49,6 +49,7 @@ The following tip changes can be tested by building VictoriaMetrics components f * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix application routing issues and problems with manual URL changes. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4408). * BUGFIX: add validation for invalid [partial RFC3339 timestamp formats](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#timestamp-formats) in query and export APIs. * BUGFIX: [vmctl](https://docs.victoriametrics.com/vmctl.html): interrupt explore procedure in influx mode if vmctl found no numeric fields. +* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert.html): use RFC3339 time format in query args instead of unix timestamp for all issued queries to Prometheus-like datasources. ## [v1.91.3](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.91.3)