Alerting: Consistently return Prometheus-style responses from rules APIs. (#86600)

* Alerting: Consistently return Prometheus-style responses from rules APIs.

This commit is part refactor and part fix. The /rules API occasionally returns
error responses which are inconsistent with other error responses. This fixes
that, and adds a function to map from Prometheus error type and HTTP code.

* Fix integration tests

* Linter happiness

* Make linter more happy

* Fix up one more place returning non-Prometheus responses
This commit is contained in:
Steve Simpson
2024-04-19 21:03:20 +02:00
committed by GitHub
parent 9ed616a7b3
commit 6ea97e41fb
4 changed files with 116 additions and 34 deletions
@@ -3,6 +3,7 @@ package definitions
import (
"container/heap"
"fmt"
"net/http"
"sort"
"strings"
"time"
@@ -98,6 +99,23 @@ type RuleGroup struct {
EvaluationTime float64 `json:"evaluationTime"`
}
// HTTPStatusCode returns the HTTP status code for a given Prometheus style error.
func (d DiscoveryBase) HTTPStatusCode() int {
if d.Status == "success" {
return http.StatusOK
}
// Mapping taken from prometheus/web/api/v1/api.go
// Note this is not exhaustive as our API does not return
// the same spectrum of errors as Prometheus does.
switch d.ErrorType {
case v1.ErrBadData:
return http.StatusBadRequest
default:
return http.StatusInternalServerError
}
}
// RuleGroupsBy is a function that defines the ordering of Rule Groups.
type RuleGroupsBy func(a1, a2 *RuleGroup) bool
@@ -1,12 +1,53 @@
package definitions
import (
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestDiscoveryBaseHTTPStatusCode(t *testing.T) {
tc := []struct {
name string
input DiscoveryBase
expected int
}{{
name: "OK when status is success",
input: DiscoveryBase{
Status: "success",
},
expected: http.StatusOK,
}, {
name: "InternalServerError when status is error but no error type",
input: DiscoveryBase{
Status: "error",
},
expected: http.StatusInternalServerError,
}, {
name: "BadRequest when status is error and type is bad_data",
input: DiscoveryBase{
Status: "error",
ErrorType: "bad_data",
},
expected: http.StatusBadRequest,
}, {
name: "InternalServerError when status is error and type is server_error",
input: DiscoveryBase{
Status: "error",
ErrorType: "server_error",
},
expected: http.StatusInternalServerError,
}}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
assert.EqualValues(t, tt.expected, tt.input.HTTPStatusCode())
})
}
}
func TestSortAlertsByImportance(t *testing.T) {
tm1, tm2 := time.Now(), time.Now().Add(time.Second)
tc := []struct {
@@ -66,7 +107,6 @@ func TestSortAlertsByImportance(t *testing.T) {
}
func TestTopKAlertsByImportance(t *testing.T) {
// tm1, tm2 := time.Now(), time.Now().Add(time.Second)
tc := []struct {
name string
k int