[Alerting]: Add alerting endpoint for Query Evaluation (#33174)
* [Alerting]: Add alerting endpoint for Query Evaluation * Fix passing down now parameter * Add validations and test * Fix eval queries and expressions test * Add eval tests
This commit is contained in:
@@ -797,6 +797,344 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
require.JSONEq(t, `{"message":"rule group deleted"}`, string(b))
|
||||
})
|
||||
}
|
||||
|
||||
// test eval conditions
|
||||
testCases := []struct {
|
||||
desc string
|
||||
payload string
|
||||
expectedStatusCode int
|
||||
expectedResponse string
|
||||
}{
|
||||
{
|
||||
desc: "alerting condition",
|
||||
payload: `
|
||||
{
|
||||
"grafana_condition": {
|
||||
"condition": "A",
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"model": {
|
||||
"datasourceUid": "-100",
|
||||
"type":"math",
|
||||
"expression":"1 < 2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedResponse: `{
|
||||
"instances": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "evaluation results",
|
||||
"fields": [
|
||||
{
|
||||
"name": "State",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"Alerting"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: "normal condition",
|
||||
payload: `
|
||||
{
|
||||
"grafana_condition": {
|
||||
"condition": "A",
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"model": {
|
||||
"datasourceUid": "-100",
|
||||
"type":"math",
|
||||
"expression":"1 > 2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedResponse: `{
|
||||
"instances": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "evaluation results",
|
||||
"fields": [
|
||||
{
|
||||
"name": "State",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"Normal"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: "condition not found in any query or expression",
|
||||
payload: `
|
||||
{
|
||||
"grafana_condition": {
|
||||
"condition": "B",
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"model": {
|
||||
"datasourceUid": "-100",
|
||||
"type":"math",
|
||||
"expression":"1 > 2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
expectedResponse: `{"error":"condition B not found in any query or expression","message":"invalid condition"}`,
|
||||
},
|
||||
{
|
||||
desc: "unknown query datasource",
|
||||
payload: `
|
||||
{
|
||||
"grafana_condition": {
|
||||
"condition": "A",
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"model": {
|
||||
"datasourceUid": "unknown"
|
||||
}
|
||||
}
|
||||
],
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
expectedResponse: `{"error":"failed to get datasource: unknown: data source not found","message":"invalid condition"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
u := fmt.Sprintf("http://%s/api/v1/rule/test/grafana", grafanaListedAddr)
|
||||
r := strings.NewReader(tc.payload)
|
||||
// nolint:gosec
|
||||
resp, err := http.Post(u, "application/json", r)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
require.JSONEq(t, tc.expectedResponse, string(b))
|
||||
})
|
||||
}
|
||||
|
||||
// test eval queries and expressions
|
||||
testCases = []struct {
|
||||
desc string
|
||||
payload string
|
||||
expectedStatusCode int
|
||||
expectedResponse string
|
||||
}{
|
||||
{
|
||||
desc: "alerting condition",
|
||||
payload: `
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"model": {
|
||||
"datasourceUid": "-100",
|
||||
"type":"math",
|
||||
"expression":"1 < 2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedResponse: `{
|
||||
"results": {
|
||||
"A": {
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"refId": "A",
|
||||
"fields": [
|
||||
{
|
||||
"name": "A",
|
||||
"type": "number",
|
||||
"typeInfo": {
|
||||
"frame": "float64",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
1
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: "normal condition",
|
||||
payload: `
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"model": {
|
||||
"datasourceUid": "-100",
|
||||
"type":"math",
|
||||
"expression":"1 > 2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedResponse: `{
|
||||
"results": {
|
||||
"A": {
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"refId": "A",
|
||||
"fields": [
|
||||
{
|
||||
"name": "A",
|
||||
"type": "number",
|
||||
"typeInfo": {
|
||||
"frame": "float64",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: "unknown query datasource",
|
||||
payload: `
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"model": {
|
||||
"datasourceUid": "unknown"
|
||||
}
|
||||
}
|
||||
],
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
expectedResponse: `{"error":"failed to get datasource: unknown: data source not found","message":"invalid queries or expressions"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
u := fmt.Sprintf("http://%s/api/v1/eval", grafanaListedAddr)
|
||||
r := strings.NewReader(tc.payload)
|
||||
// nolint:gosec
|
||||
resp, err := http.Post(u, "application/json", r)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
require.JSONEq(t, tc.expectedResponse, string(b))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// createFolder creates a folder for storing our alerts under. Grafana uses folders as a replacement for alert namespaces to match its permission model.
|
||||
|
||||
Reference in New Issue
Block a user