Skip to content
This repository was archived by the owner on Jan 15, 2024. It is now read-only.

Commit 2c14d00

Browse files
authored
Merge branch 'master' into alexweav/policies
2 parents b5a29e7 + e527c99 commit 2c14d00

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

alerting_alert_rule.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package gapi
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"time"
8+
)
9+
10+
// AlertRule represents a Grafana Alert Rule.
11+
type AlertRule struct {
12+
Annotations map[string]string `json:"annotations,omitempty"`
13+
Condition string `json:"condition"`
14+
Data []*AlertQuery `json:"data"`
15+
ExecErrState ExecErrState `json:"execErrState"`
16+
FolderUID string `json:"folderUid"`
17+
ID int64 `json:"id,omitempty"`
18+
Labels map[string]string `json:"labels,omitempty"`
19+
NoDataState NoDataState `json:"noDataState"`
20+
OrgID int64 `json:"orgId"`
21+
RuleGroup string `json:"ruleGroup"`
22+
Title string `json:"title"`
23+
UID string `json:"uid,omitempty"`
24+
Updated time.Time `json:"updated"`
25+
ForDuration time.Duration `json:"for"`
26+
Provenance string `json:"provenance"`
27+
}
28+
29+
// AlertQuery represents a single query stage associated with an alert definition.
30+
type AlertQuery struct {
31+
DatasourceUID string `json:"datasourceUid,omitempty"`
32+
Model interface{} `json:"model"`
33+
QueryType string `json:"queryType,omitempty"`
34+
RefID string `json:"refId,omitempty"`
35+
RelativeTimeRange RelativeTimeRange `json:"relativeTimeRange"`
36+
}
37+
38+
type ExecErrState string
39+
type NoDataState string
40+
41+
const (
42+
ErrOK ExecErrState = "OK"
43+
ErrError ExecErrState = "Error"
44+
ErrAlerting ExecErrState = "Alerting"
45+
NoDataOk NoDataState = "OK"
46+
NoData NoDataState = "NoData"
47+
NoDataAlerting NoDataState = "Alerting"
48+
)
49+
50+
// RelativeTimeRange represents the time range for an alert query.
51+
type RelativeTimeRange struct {
52+
From time.Duration `json:"from"`
53+
To time.Duration `json:"to"`
54+
}
55+
56+
// AlertRule fetches a single alert rule, identified by its UID.
57+
func (c *Client) AlertRule(uid string) (AlertRule, error) {
58+
path := fmt.Sprintf("/api/v1/provisioning/alert-rules/%s", uid)
59+
result := AlertRule{}
60+
err := c.request("GET", path, nil, nil, &result)
61+
if err != nil {
62+
return AlertRule{}, err
63+
}
64+
return result, err
65+
}
66+
67+
// NewAlertRule creates a new alert rule and returns its UID.
68+
func (c *Client) NewAlertRule(ar *AlertRule) (string, error) {
69+
req, err := json.Marshal(ar)
70+
if err != nil {
71+
return "", err
72+
}
73+
result := AlertRule{}
74+
err = c.request("POST", "/api/v1/provisioning/alert-rules", nil, bytes.NewBuffer(req), &result)
75+
if err != nil {
76+
return "", err
77+
}
78+
return result.UID, nil
79+
}
80+
81+
// UpdateAlertRule replaces an alert rule, identified by the alert rule's UID.
82+
func (c *Client) UpdateAlertRule(ar *AlertRule) error {
83+
uri := fmt.Sprintf("/api/v1/provisioning/alert-rules/%s", ar.UID)
84+
req, err := json.Marshal(ar)
85+
if err != nil {
86+
return err
87+
}
88+
89+
return c.request("PUT", uri, nil, bytes.NewBuffer(req), nil)
90+
}
91+
92+
// DeleteAlertRule deletes a alert rule, identified by the alert rule's UID.
93+
func (c *Client) DeleteAlertRule(uid string) error {
94+
uri := fmt.Sprintf("/api/v1/provisioning/alert-rules/%s", uid)
95+
return c.request("DELETE", uri, nil, nil, nil)
96+
}

alerting_alert_rule_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package gapi
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/gobs/pretty"
8+
)
9+
10+
func TestAlertRules(t *testing.T) {
11+
t.Run("get alert rule succeeds", func(t *testing.T) {
12+
server, client := gapiTestTools(t, 200, getAlertRuleJSON)
13+
defer server.Close()
14+
15+
alertRule, err := client.AlertRule("123abcd")
16+
17+
if err != nil {
18+
t.Error(err)
19+
}
20+
if alertRule.UID != "123abcd" {
21+
t.Errorf("incorrect UID - expected %s got %#v", "123abcd", alertRule.UID)
22+
}
23+
})
24+
25+
t.Run("get non-existent alert rule fails", func(t *testing.T) {
26+
server, client := gapiTestTools(t, 404, "")
27+
defer server.Close()
28+
29+
alertRule, err := client.AlertRule("does not exist")
30+
31+
if err == nil {
32+
t.Errorf("expected error but got nil")
33+
t.Log(pretty.PrettyFormat(alertRule))
34+
}
35+
})
36+
37+
t.Run("create alert rule succeeds", func(t *testing.T) {
38+
server, client := gapiTestTools(t, 201, writeAlertRuleJSON)
39+
defer server.Close()
40+
alertRule := createAlertRule()
41+
uid, err := client.NewAlertRule(&alertRule)
42+
43+
if err != nil {
44+
t.Error(err)
45+
}
46+
if uid != "123abcd" {
47+
t.Errorf("unexpected UID returned, got %s", uid)
48+
}
49+
})
50+
51+
t.Run("update alert rule succeeds", func(t *testing.T) {
52+
server, client := gapiTestTools(t, 200, writeAlertRuleJSON)
53+
defer server.Close()
54+
alertRule := createAlertRule()
55+
alertRule.UID = "foobar"
56+
57+
err := client.UpdateAlertRule(&alertRule)
58+
59+
if err != nil {
60+
t.Error(err)
61+
}
62+
})
63+
64+
t.Run("delete alert rule succeeds", func(t *testing.T) {
65+
server, client := gapiTestTools(t, 204, "")
66+
defer server.Close()
67+
68+
err := client.DeleteAlertRule("123abcd")
69+
70+
if err != nil {
71+
t.Error(err)
72+
}
73+
})
74+
}
75+
76+
func createAlertRule() AlertRule {
77+
return AlertRule{
78+
Condition: "A",
79+
Data: createAlertQueries(),
80+
ExecErrState: ErrOK,
81+
FolderUID: "project_test",
82+
NoDataState: NoDataOk,
83+
OrgID: 1,
84+
RuleGroup: "eval_group_1",
85+
Title: "Always in alarm",
86+
ForDuration: 0,
87+
}
88+
}
89+
90+
func createAlertQueries() []*AlertQuery {
91+
alertQueries := make([]*AlertQuery, 1)
92+
alertQueries[0] = &AlertQuery{
93+
DatasourceUID: "-100",
94+
Model: json.RawMessage(`{"datasourceUid":"-100","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1 == 1","hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"},"queryType":"","refId":"A","relativeTimeRange":{"from":0,"to":0}}`),
95+
QueryType: "",
96+
RefID: "A",
97+
RelativeTimeRange: RelativeTimeRange{From: 0, To: 0},
98+
}
99+
return alertQueries
100+
}
101+
102+
const writeAlertRuleJSON = `
103+
{
104+
"conditions": "A",
105+
"data": [{"datasourceUid":"-100","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1 == 1","hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"},"queryType":"","refId":"A","relativeTimeRange":{"from":0,"to":0}}],
106+
"uid": "123abcd",
107+
"execErrState": "OK",
108+
"folderUID": "project_test",
109+
"noDataState": "OK",
110+
"orgId": 1,
111+
"ruleGroup": "eval_group_1",
112+
"title": "Always in alarm",
113+
"for": 0
114+
}
115+
`
116+
117+
const getAlertRuleJSON = `
118+
{
119+
"conditions": "A",
120+
"data": [{"datasourceUid":"-100","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1 == 1","hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"},"queryType":"","refId":"A","relativeTimeRange":{"from":0,"to":0}}],
121+
"execErrState": "OK",
122+
"folderUID": "project_test",
123+
"noDataState": "OK",
124+
"orgId": 1,
125+
"uid": "123abcd",
126+
"ruleGroup": "eval_group_1",
127+
"title": "Always in alarm",
128+
"for": 0
129+
}
130+
`

0 commit comments

Comments
 (0)