Refuse an /events aggregate that also asks for distinct_resources
distinct_resources sends /events to openvoxdb's legacy compiler, which has no function or group_by, so every backend failed and the client saw a generic 502 instead of the reason. - Refuse an aggregate carrying a truthy distinct_resources with 400, before any fan-out - Read the param the way openvoxdb does, so any capitalisation of "true" counts - Leave non-aggregate distinct_resources queries and every other route alone - Document the refusal
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var allNodes = []string{nodeAlpha, nodeBeta, nodeGamma, nodeShared}
|
||||
@@ -701,6 +702,53 @@ func TestEventsAggregatesAreSummed(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// distinct_resources moves /events onto openvoxdb's legacy compiler, which has
|
||||
// no function or group_by: both backends fail identically, so without a refusal
|
||||
// a client mistake reaches the caller as an outage-shaped 502.
|
||||
func TestEventsAggregateWithDistinctResourcesIsRefused(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
distinct := func(q string) url.Values {
|
||||
v := url.Values{
|
||||
"distinct_resources": {"true"},
|
||||
"distinct_start_time": {fixtureTime(-24 * time.Hour)},
|
||||
"distinct_end_time": {fixtureTime(time.Hour)},
|
||||
}
|
||||
if q != "" {
|
||||
v.Set("query", q)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
params := distinct(`["extract",[["function","count"]]]`)
|
||||
resp := rawGet(t, eventsPath, params)
|
||||
if resp.status != http.StatusBadRequest {
|
||||
t.Fatalf("status %d, want 400: %s", resp.status, resp.body)
|
||||
}
|
||||
if !strings.Contains(string(resp.body), "distinct_resources") {
|
||||
t.Errorf("refusal %q does not name the incompatibility", resp.body)
|
||||
}
|
||||
if got := h.a.queryStatus(ctx, t, eventsPath, params); got == http.StatusOK {
|
||||
t.Errorf("backend %s answered the aggregate with 200, so the refusal is unnecessary", h.a.name)
|
||||
}
|
||||
|
||||
t.Run("a plain distinct_resources query is still served", func(t *testing.T) {
|
||||
params := distinct("")
|
||||
want := len(h.a.query(ctx, t, eventsPath, params)) + len(h.b.query(ctx, t, eventsPath, params))
|
||||
if want == 0 {
|
||||
t.Fatal("the fixture yields no distinct-resources events, so the union proves nothing")
|
||||
}
|
||||
rows := get(t, eventsPath, params).rows(t)
|
||||
if len(rows) != want {
|
||||
t.Fatalf("/events with distinct_resources returned %d records, want %d", len(rows), want)
|
||||
}
|
||||
for _, row := range rows {
|
||||
if _, ok := row["resource_title"].(string); !ok {
|
||||
t.Fatalf("/events record is not an event: %v", row)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// countsByCertname reads a ["function","count"] + group_by "certname" result set.
|
||||
func countsByCertname(t *testing.T, rows []map[string]any) map[string]int {
|
||||
t.Helper()
|
||||
|
||||
Reference in New Issue
Block a user