ability to split routes between AM & Grafana
This commit is contained in:
@@ -183,8 +183,8 @@ type GrafanaReceiver models.CreateAlertNotificationCommand
|
||||
type ReceiverType int
|
||||
|
||||
const (
|
||||
GrafanaManagedReceiver ReceiverType = iota
|
||||
AMReceiver
|
||||
GrafanaReceiverType ReceiverType = iota
|
||||
AlertmanagerReceiverType
|
||||
)
|
||||
|
||||
type ApiReceiver struct {
|
||||
@@ -227,11 +227,17 @@ func (r *ApiReceiver) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *ApiReceiver) Type() ReceiverType {
|
||||
if len(r.GrafanaReceivers.GrafanaManagedReceivers) > 0 {
|
||||
return GrafanaReceiverType
|
||||
}
|
||||
return AlertmanagerReceiverType
|
||||
}
|
||||
|
||||
type GrafanaReceivers struct {
|
||||
GrafanaManagedReceivers []*GrafanaReceiver `yaml:"grafana_managed_receiver_configs,omitempty" json:"grafana_managed_receiver_configs,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/alerting-api/pkg/api"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
)
|
||||
@@ -20,10 +22,38 @@ type GrafanaAlertingConfig struct {
|
||||
// }
|
||||
|
||||
// Routing trees must either contain only Grafana Managed receiver types or AlertManager receiver types
|
||||
// func SplitRoutes(routes []*config.Route, receivers map[string]*api.ApiReceiver) (gRoutes, amRoutes []*config.Route, err error) {
|
||||
// for _, route := range routes {
|
||||
// }
|
||||
// }
|
||||
func SplitRoutes(routes []*config.Route, receivers map[string]api.ReceiverType) (gRoutes, amRoutes []*config.Route, err error) {
|
||||
for _, route := range routes {
|
||||
var grafanaCt, amCt int
|
||||
for _, receiver := range allReceivers(route) {
|
||||
t, ok := receivers[receiver]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("unexpected receiver (%s) is undefined", receiver)
|
||||
}
|
||||
|
||||
if t == api.GrafanaReceiverType {
|
||||
grafanaCt++
|
||||
} else {
|
||||
amCt++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO(owen-d): We may want to eventually support this, but short-circuiting for now
|
||||
// to reduce routing tree complexity.
|
||||
if grafanaCt > 0 && amCt > 0 {
|
||||
return nil, nil, fmt.Errorf("cannot mix Grafana Managed receivers with Alertmanager receivers in the same (non-root) routing tree")
|
||||
}
|
||||
|
||||
if grafanaCt > 0 {
|
||||
gRoutes = append(gRoutes, route)
|
||||
} else {
|
||||
amRoutes = append(amRoutes, route)
|
||||
}
|
||||
}
|
||||
|
||||
return gRoutes, amRoutes, nil
|
||||
}
|
||||
|
||||
func allReceivers(route *config.Route) (res []string) {
|
||||
res = append(res, route.Receiver)
|
||||
|
||||
@@ -3,6 +3,7 @@ package validation
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/alerting-api/pkg/api"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -27,3 +28,129 @@ func Test_allReceivers(t *testing.T) {
|
||||
|
||||
require.Equal(t, []string{"foo", "bar", "bazz", "buzz"}, allReceivers(input))
|
||||
}
|
||||
|
||||
func Test_Splitroutes(t *testing.T) {
|
||||
receivers := map[string]api.ReceiverType{
|
||||
"am": api.AlertmanagerReceiverType,
|
||||
"graf": api.GrafanaReceiverType,
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
desc string
|
||||
input []*config.Route
|
||||
receivers map[string]api.ReceiverType
|
||||
err bool
|
||||
gRoutes, amRoutes []*config.Route
|
||||
}{
|
||||
{
|
||||
desc: "splits simple",
|
||||
input: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
},
|
||||
{
|
||||
Receiver: "graf",
|
||||
},
|
||||
},
|
||||
err: false,
|
||||
gRoutes: []*config.Route{
|
||||
{
|
||||
Receiver: "graf",
|
||||
},
|
||||
},
|
||||
amRoutes: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "splits nested",
|
||||
input: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
Routes: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Receiver: "graf",
|
||||
Routes: []*config.Route{
|
||||
{
|
||||
Receiver: "graf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: false,
|
||||
gRoutes: []*config.Route{
|
||||
{
|
||||
Receiver: "graf",
|
||||
Routes: []*config.Route{
|
||||
{
|
||||
Receiver: "graf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
amRoutes: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
Routes: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "errors undefined receiver",
|
||||
input: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
Routes: []*config.Route{
|
||||
{
|
||||
Receiver: "unmentioned",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "errors mixed",
|
||||
input: []*config.Route{
|
||||
{
|
||||
Receiver: "am",
|
||||
Routes: []*config.Route{
|
||||
{
|
||||
Receiver: "graf",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Receiver: "graf",
|
||||
Routes: []*config.Route{
|
||||
{
|
||||
Receiver: "graf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
gRoutes, amRoutes, err := SplitRoutes(tc.input, receivers)
|
||||
if tc.err {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.Equal(t, tc.gRoutes, gRoutes)
|
||||
require.Equal(t, tc.amRoutes, amRoutes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user