[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:
Sofia Papagiannaki
2021-04-21 22:44:50 +03:00
committed by GitHub
parent ed3f5e6ca3
commit b2288f7ef9
10 changed files with 4315 additions and 3765 deletions
@@ -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.