fix(observability-proxy): name the logs time out of range exactly

When both bounds were out of range the answer named one of them at
random, as the check walked a map, and it printed the instant without
its fraction, so a time one nanosecond past the last one vlselect can
hold was shown as a time inside the range. The end is now checked
first and the instant is printed in full; tests pin the inclusive
bound.

Assisted-by: LLM
Signed-off-by: ZverGuy <maximbel2003@gmail.com>
This commit is contained in:
ZverGuy committed 2026-11-10 00:45:07 +07:00
1 parent f8ff307f71
commit f48a94de6d
2 files changed
+30 -5

No files matched your search

+6 -5
View File
@@ -160,10 +160,11 @@ func window(in url.Values, now time.Time, lim LogsLimits) (time.Time, time.Time,
if end.Sub(start) > lim.MaxRange {
start = end.Add(-lim.MaxRange)
}
for k, t := range map[string]time.Time{"start": start, "end": end} {
if herr := sendableTime(k, t); herr != nil {
return time.Time{}, time.Time{}, herr
}
if herr := sendableTime("end", end); herr != nil {
return time.Time{}, time.Time{}, herr
}
if herr := sendableTime("start", start); herr != nil {
return time.Time{}, time.Time{}, herr
}
return start.UTC(), end.UTC(), nil
}
@@ -337,7 +338,7 @@ func scaleDuration(f float64, unit time.Duration) (time.Duration, bool) {
// actually sent are checked; an older start is clamped first.
func sendableTime(key string, t time.Time) *HTTPError {
if t.Before(time.Unix(0, math.MinInt64)) || t.After(time.Unix(0, math.MaxInt64)) {
return errorf(http.StatusBadRequest, "invalid %s: %s is outside 1677..2262", key, t.Format(time.RFC3339))
return errorf(http.StatusBadRequest, "invalid %s: %s is outside 1677..2262", key, t.UTC().Format(time.RFC3339Nano))
}
return nil
}
@@ -17,6 +17,7 @@ limitations under the License.
package observabilityproxy
import (
"math"
"net/http"
"net/url"
"sort"
@@ -408,3 +409,26 @@ func TestBuildLogsParamsOldTimesClamped(t *testing.T) {
t.Errorf("start_offset = %s, want the %s cap", got, lim.MaxTailResume)
}
}
// TestBuildLogsParamsSendableBoundary pins the inclusive int64-nanosecond
// bound and that the refusal names the instant exactly.
func TestBuildLogsParamsSendableBoundary(t *testing.T) {
last := time.Unix(0, math.MaxInt64).UTC()
in := url.Values{"start": {last.Add(-time.Hour).Format(time.RFC3339Nano)}, "end": {last.Format(time.RFC3339Nano)}}
if _, herr := BuildLogsParams("query", in, "tenant-a", testNow, testLimits()); herr != nil {
t.Fatalf("the last int64 nanosecond refused: %v", herr)
}
past := last.Add(time.Nanosecond).Format(time.RFC3339Nano)
in = url.Values{"start": {last.Add(-time.Hour).Format(time.RFC3339Nano)}, "end": {past}}
_, herr := BuildLogsParams("query", in, "tenant-a", testNow, testLimits())
if herr == nil || !strings.Contains(herr.Message, "invalid end") || !strings.Contains(herr.Message, past) {
t.Fatalf("one nanosecond past answered %v, want end refused naming %s", herr, past)
}
// Both out of range: the answer names end, deterministically.
for range 20 {
_, herr := BuildLogsParams("query", url.Values{"start": {"0001-01-01T00:00:00Z"}, "end": {"0001-01-02T00:00:00Z"}}, "tenant-a", testNow, testLimits())
if herr == nil || !strings.Contains(herr.Message, "invalid end") {
t.Fatalf("answer = %v, want end named", herr)
}
}
}