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

Commit c7d9555

Browse files
authored
Merge pull request #95 from grafana/alexweav/policies
Alerting: Add support for Unified Alerting notification policies
2 parents e9dd216 + dbae38f commit c7d9555

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

alerting_notification_policy.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package gapi
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
)
8+
9+
// Represents a notification routing tree in Grafana Alerting.
10+
type NotificationPolicy struct {
11+
Receiver string `json:"receiver,omitempty"`
12+
GroupBy []string `json:"group_by,omitempty"`
13+
Routes []SpecificPolicy `json:"routes,omitempty"`
14+
GroupWait string `json:"group_wait,omitempty"`
15+
GroupInterval string `json:"group_interval,omitempty"`
16+
RepeatInterval string `json:"repeat_interval,omitempty"`
17+
Provenance string `json:"provenance,omitempty"`
18+
}
19+
20+
// Represents a non-root node in a notification routing tree.
21+
type SpecificPolicy struct {
22+
Receiver string `json:"receiver,omitempty"`
23+
GroupBy []string `json:"group_by,omitempty"`
24+
ObjectMatchers Matchers `json:"object_matchers,omitempty"`
25+
MuteTimeIntervals []string `json:"mute_time_intervals,omitempty"`
26+
Continue bool `json:"continue"`
27+
Routes []SpecificPolicy `json:"routes,omitempty"`
28+
GroupWait string `json:"group_wait,omitempty"`
29+
GroupInterval string `json:"group_interval,omitempty"`
30+
RepeatInterval string `json:"repeat_interval,omitempty"`
31+
}
32+
33+
type Matchers []Matcher
34+
35+
type Matcher struct {
36+
Type MatchType
37+
Name string
38+
Value string
39+
}
40+
41+
type MatchType int
42+
43+
const (
44+
MatchEqual MatchType = iota
45+
MatchNotEqual
46+
MatchRegexp
47+
MatchNotRegexp
48+
)
49+
50+
func (m MatchType) String() string {
51+
typeToStr := map[MatchType]string{
52+
MatchEqual: "=",
53+
MatchNotEqual: "!=",
54+
MatchRegexp: "=~",
55+
MatchNotRegexp: "!~",
56+
}
57+
if str, ok := typeToStr[m]; ok {
58+
return str
59+
}
60+
panic("unknown match type")
61+
}
62+
63+
// UnmarshalJSON implements the json.Unmarshaler interface for Matchers.
64+
func (m *Matchers) UnmarshalJSON(data []byte) error {
65+
var rawMatchers [][3]string
66+
if err := json.Unmarshal(data, &rawMatchers); err != nil {
67+
return err
68+
}
69+
for _, rawMatcher := range rawMatchers {
70+
var matchType MatchType
71+
switch rawMatcher[1] {
72+
case "=":
73+
matchType = MatchEqual
74+
case "!=":
75+
matchType = MatchNotEqual
76+
case "=~":
77+
matchType = MatchRegexp
78+
case "!~":
79+
matchType = MatchNotRegexp
80+
default:
81+
return fmt.Errorf("unsupported match type %q in matcher", rawMatcher[1])
82+
}
83+
84+
matcher := Matcher{
85+
Type: matchType,
86+
Name: rawMatcher[0],
87+
Value: rawMatcher[2],
88+
}
89+
*m = append(*m, matcher)
90+
}
91+
return nil
92+
}
93+
94+
// MarshalJSON implements the json.Marshaler interface for Matchers.
95+
func (m Matchers) MarshalJSON() ([]byte, error) {
96+
if len(m) == 0 {
97+
return nil, nil
98+
}
99+
result := make([][3]string, len(m))
100+
for i, matcher := range m {
101+
result[i] = [3]string{matcher.Name, matcher.Type.String(), matcher.Value}
102+
}
103+
return json.Marshal(result)
104+
}
105+
106+
// NotificationPolicy fetches the notification policy tree.
107+
func (c *Client) NotificationPolicy() (NotificationPolicy, error) {
108+
np := NotificationPolicy{}
109+
err := c.request("GET", "/api/v1/provisioning/policies", nil, nil, &np)
110+
return np, err
111+
}
112+
113+
// SetNotificationPolicy sets the notification policy tree.
114+
func (c *Client) SetNotificationPolicy(np *NotificationPolicy) error {
115+
req, err := json.Marshal(np)
116+
if err != nil {
117+
return err
118+
}
119+
return c.request("PUT", "/api/v1/provisioning/policies", nil, bytes.NewBuffer(req), nil)
120+
}

alerting_notification_policy_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package gapi
2+
3+
import (
4+
"testing"
5+
6+
"github.com/gobs/pretty"
7+
)
8+
9+
func TestNotificationPolicies(t *testing.T) {
10+
t.Run("get policy tree succeeds", func(t *testing.T) {
11+
server, client := gapiTestTools(t, 200, notificationPolicyJSON)
12+
defer server.Close()
13+
14+
np, err := client.NotificationPolicy()
15+
16+
if err != nil {
17+
t.Error(err)
18+
}
19+
t.Log(pretty.PrettyFormat(np))
20+
if np.Receiver != "grafana-default-email" {
21+
t.Errorf("wrong receiver, got %#v", np.Receiver)
22+
}
23+
if len(np.Routes) != 1 {
24+
t.Errorf("wrong number of routes returned, got %#v", np)
25+
}
26+
})
27+
28+
t.Run("set policy tree succeeds", func(t *testing.T) {
29+
server, client := gapiTestTools(t, 202, `{"message":"created"}`)
30+
defer server.Close()
31+
np := createNotificationPolicy()
32+
33+
err := client.SetNotificationPolicy(&np)
34+
35+
if err != nil {
36+
t.Error(err)
37+
}
38+
})
39+
}
40+
41+
func createNotificationPolicy() NotificationPolicy {
42+
return NotificationPolicy{
43+
Receiver: "grafana-default-email",
44+
GroupBy: []string{"asdfasdf", "alertname"},
45+
Routes: []SpecificPolicy{
46+
{
47+
Receiver: "grafana-default-email",
48+
ObjectMatchers: Matchers{
49+
{
50+
Type: MatchNotEqual,
51+
Name: "abc",
52+
Value: "def",
53+
},
54+
},
55+
Continue: true,
56+
},
57+
{
58+
Receiver: "grafana-default-email",
59+
ObjectMatchers: Matchers{
60+
{
61+
Type: MatchRegexp,
62+
Name: "jkl",
63+
Value: "something.*",
64+
},
65+
},
66+
Continue: false,
67+
},
68+
},
69+
GroupWait: "10s",
70+
GroupInterval: "5m",
71+
RepeatInterval: "4h",
72+
}
73+
}
74+
75+
const notificationPolicyJSON = `
76+
{
77+
"receiver": "grafana-default-email",
78+
"group_by": [
79+
"..."
80+
],
81+
"routes": [
82+
{
83+
"receiver": "grafana-default-email",
84+
"object_matchers": [
85+
[
86+
"a",
87+
"=",
88+
"b"
89+
],
90+
[
91+
"asdf",
92+
"!=",
93+
"jk"
94+
]
95+
]
96+
}
97+
],
98+
"group_wait": "5s",
99+
"group_interval": "1m",
100+
"repeat_interval": "1h"
101+
}`

0 commit comments

Comments
 (0)