mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
[Alerting] Route validations (#34393)
* more routing validation * go mod * recursive route validations
This commit is contained in:
parent
958ebce1e1
commit
1d2febfa85
2
go.mod
2
go.mod
@ -14,7 +14,7 @@ replace k8s.io/client-go => k8s.io/client-go v0.18.8
|
|||||||
require (
|
require (
|
||||||
cloud.google.com/go/storage v1.14.0
|
cloud.google.com/go/storage v1.14.0
|
||||||
cuelang.org/go v0.3.2
|
cuelang.org/go v0.3.2
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.16.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.16.0
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.8.0
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.8.0
|
||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/Masterminds/semver v1.5.0
|
github.com/Masterminds/semver v1.5.0
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
||||||
"github.com/prometheus/alertmanager/config"
|
"github.com/prometheus/alertmanager/config"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@ -484,10 +485,26 @@ func (c *Config) UnmarshalJSON(b []byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Route != nil {
|
if c.Route == nil {
|
||||||
if err := c.Route.UnmarshalYAML(noopUnmarshal); err != nil {
|
return fmt.Errorf("no routes provided")
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
// Route is a recursive structure that includes validation in the yaml unmarshaler.
|
||||||
|
// Therefore, we'll redirect json -> yaml to utilize these.
|
||||||
|
b, err := yaml.Marshal(c.Route)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "marshaling route to yaml for validation")
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(b, c.Route)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unmarshaling route for validations")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Route.Receiver) == 0 {
|
||||||
|
return fmt.Errorf("root route must specify a default receiver")
|
||||||
|
}
|
||||||
|
if len(c.Route.Match) > 0 || len(c.Route.MatchRE) > 0 {
|
||||||
|
return fmt.Errorf("root route must not have any matchers")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range c.InhibitRules {
|
for _, r := range c.InhibitRules {
|
||||||
|
@ -243,6 +243,101 @@ func Test_ApiAlertingConfig_Marshaling(t *testing.T) {
|
|||||||
},
|
},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "failure graf no route",
|
||||||
|
input: PostableApiAlertingConfig{
|
||||||
|
Receivers: []*PostableApiReceiver{
|
||||||
|
{
|
||||||
|
Receiver: config.Receiver{
|
||||||
|
Name: "graf",
|
||||||
|
},
|
||||||
|
PostableGrafanaReceivers: PostableGrafanaReceivers{
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "failure graf no default receiver",
|
||||||
|
input: PostableApiAlertingConfig{
|
||||||
|
Config: Config{
|
||||||
|
Route: &config.Route{
|
||||||
|
Routes: []*config.Route{
|
||||||
|
{
|
||||||
|
Receiver: "graf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Receivers: []*PostableApiReceiver{
|
||||||
|
{
|
||||||
|
Receiver: config.Receiver{
|
||||||
|
Name: "graf",
|
||||||
|
},
|
||||||
|
PostableGrafanaReceivers: PostableGrafanaReceivers{
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "failure graf root route with matchers",
|
||||||
|
input: PostableApiAlertingConfig{
|
||||||
|
Config: Config{
|
||||||
|
Route: &config.Route{
|
||||||
|
Receiver: "graf",
|
||||||
|
Routes: []*config.Route{
|
||||||
|
{
|
||||||
|
Receiver: "graf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Match: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Receivers: []*PostableApiReceiver{
|
||||||
|
{
|
||||||
|
Receiver: config.Receiver{
|
||||||
|
Name: "graf",
|
||||||
|
},
|
||||||
|
PostableGrafanaReceivers: PostableGrafanaReceivers{
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "failure graf nested route duplicate group by labels",
|
||||||
|
input: PostableApiAlertingConfig{
|
||||||
|
Config: Config{
|
||||||
|
Route: &config.Route{
|
||||||
|
Receiver: "graf",
|
||||||
|
Routes: []*config.Route{
|
||||||
|
{
|
||||||
|
Receiver: "graf",
|
||||||
|
GroupByStr: []string{"foo", "bar", "foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Receivers: []*PostableApiReceiver{
|
||||||
|
{
|
||||||
|
Receiver: config.Receiver{
|
||||||
|
Name: "graf",
|
||||||
|
},
|
||||||
|
PostableGrafanaReceivers: PostableGrafanaReceivers{
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
encoded, err := json.Marshal(tc.input)
|
encoded, err := json.Marshal(tc.input)
|
||||||
|
Loading…
Reference in New Issue
Block a user