api marshaling & validation

This commit is contained in:
Owen Diehl
2021-02-24 16:53:23 -05:00
parent ded0d4a969
commit 0ef573754f
2 changed files with 297 additions and 0 deletions
+59
View File
@@ -174,10 +174,68 @@ type AlertingConfigResponse struct {
type ApiAlertingConfig struct {
config.Config
// Override/eliminate Routes tree in AM config - we'll set it via new fields
Route struct{} `yaml:"route,omitempty" json:"route,omitempty"`
AlertManagerRoute *config.Route `yaml:"alertmanager_route,omitempty" json:"alertmanager_route,omitempty"`
GrafanaManagedRoute *config.Route `yaml:"grafana_managed_route,omitempty" json:"grafana_managed_route,omitempty"`
// Override with our superset receiver type
Receivers []*ApiReceiver `yaml:"receivers,omitempty" json:"receivers,omitempty"`
}
func (c *ApiAlertingConfig) UnmarshalJSON(b []byte) error {
type plain ApiAlertingConfig
if err := json.Unmarshal(b, (*plain)(c)); err != nil {
return err
}
return c.validate()
}
// validate ensures that the two routing trees use the correct receiver types.
func (c *ApiAlertingConfig) validate() error {
receivers := make(map[string]ReceiverType, len(c.Receivers))
for _, r := range c.Receivers {
receivers[r.Name] = r.Type()
}
for _, receiver := range AllReceivers(c.GrafanaManagedRoute) {
t, ok := receivers[receiver]
if !ok {
return fmt.Errorf("unexpected receiver (%s) is undefined", receiver)
}
if t != GrafanaReceiverType {
return fmt.Errorf("unexpected receiver (%s): cannot use Alertmanager receiver types in Grafana managed routes", receiver)
}
}
for _, receiver := range AllReceivers(c.AlertManagerRoute) {
t, ok := receivers[receiver]
if !ok {
return fmt.Errorf("unexpected receiver (%s) is undefined", receiver)
}
if t != AlertmanagerReceiverType {
return fmt.Errorf("unexpected receiver (%s): cannot use Grafana receiver types in non-Grafana managed routes", receiver)
}
}
return nil
}
// AllReceivers will recursively walk a routing tree and return a list of all the
// referenced receiver names.
func AllReceivers(route *config.Route) (res []string) {
res = append(res, route.Receiver)
for _, subRoute := range route.Routes {
res = append(res, AllReceivers(subRoute)...)
}
return res
}
type GrafanaReceiver models.CreateAlertNotificationCommand
type ReceiverType int
@@ -227,6 +285,7 @@ func (r *ApiReceiver) UnmarshalJSON(b []byte) error {
}
}
return nil
}
+238
View File
@@ -63,3 +63,241 @@ func Test_ApiReceiver_Marshaling(t *testing.T) {
})
}
}
func Test_AllReceivers(t *testing.T) {
input := &config.Route{
Receiver: "foo",
Routes: []*config.Route{
{
Receiver: "bar",
Routes: []*config.Route{
{
Receiver: "bazz",
},
},
},
{
Receiver: "buzz",
},
},
}
require.Equal(t, []string{"foo", "bar", "bazz", "buzz"}, AllReceivers(input))
}
func Test_ApiAlertingConfig_Marshaling(t *testing.T) {
for _, tc := range []struct {
desc string
input ApiAlertingConfig
err bool
}{
{
desc: "success",
input: ApiAlertingConfig{
Config: config.Config{},
AlertManagerRoute: &config.Route{
Receiver: "am",
Routes: []*config.Route{
{
Receiver: "am",
},
},
},
GrafanaManagedRoute: &config.Route{
Receiver: "graf",
Routes: []*config.Route{
{
Receiver: "graf",
},
},
},
Receivers: []*ApiReceiver{
{
Receiver: config.Receiver{
Name: "am",
EmailConfigs: []*config.EmailConfig{{}},
},
},
{
Receiver: config.Receiver{
Name: "graf",
},
GrafanaReceivers: GrafanaReceivers{
GrafanaManagedReceivers: []*GrafanaReceiver{{}},
},
},
},
},
},
{
desc: "failure undefined am receiver",
input: ApiAlertingConfig{
Config: config.Config{},
AlertManagerRoute: &config.Route{
Receiver: "am",
Routes: []*config.Route{
{
Receiver: "unmentioned",
},
},
},
GrafanaManagedRoute: &config.Route{
Receiver: "graf",
Routes: []*config.Route{
{
Receiver: "graf",
},
},
},
Receivers: []*ApiReceiver{
{
Receiver: config.Receiver{
Name: "am",
EmailConfigs: []*config.EmailConfig{{}},
},
},
{
Receiver: config.Receiver{
Name: "graf",
},
GrafanaReceivers: GrafanaReceivers{
GrafanaManagedReceivers: []*GrafanaReceiver{{}},
},
},
},
},
err: true,
},
{
desc: "failure undefined graf receiver",
input: ApiAlertingConfig{
Config: config.Config{},
AlertManagerRoute: &config.Route{
Receiver: "am",
Routes: []*config.Route{
{
Receiver: "am",
},
},
},
GrafanaManagedRoute: &config.Route{
Receiver: "graf",
Routes: []*config.Route{
{
Receiver: "unmentioned",
},
},
},
Receivers: []*ApiReceiver{
{
Receiver: config.Receiver{
Name: "am",
EmailConfigs: []*config.EmailConfig{{}},
},
},
{
Receiver: config.Receiver{
Name: "graf",
},
GrafanaReceivers: GrafanaReceivers{
GrafanaManagedReceivers: []*GrafanaReceiver{{}},
},
},
},
},
err: true,
},
{
desc: "failure mixed AM in Grafana",
input: ApiAlertingConfig{
Config: config.Config{},
AlertManagerRoute: &config.Route{
Receiver: "am",
Routes: []*config.Route{
{
Receiver: "am",
},
},
},
GrafanaManagedRoute: &config.Route{
Receiver: "graf",
Routes: []*config.Route{
{
Receiver: "am",
},
},
},
Receivers: []*ApiReceiver{
{
Receiver: config.Receiver{
Name: "am",
EmailConfigs: []*config.EmailConfig{{}},
},
},
{
Receiver: config.Receiver{
Name: "graf",
},
GrafanaReceivers: GrafanaReceivers{
GrafanaManagedReceivers: []*GrafanaReceiver{{}},
},
},
},
},
err: true,
},
{
desc: "failure mixed Grafana in AM",
input: ApiAlertingConfig{
Config: config.Config{},
AlertManagerRoute: &config.Route{
Receiver: "am",
Routes: []*config.Route{
{
Receiver: "graf",
},
},
},
GrafanaManagedRoute: &config.Route{
Receiver: "graf",
Routes: []*config.Route{
{
Receiver: "graf",
},
},
},
Receivers: []*ApiReceiver{
{
Receiver: config.Receiver{
Name: "am",
EmailConfigs: []*config.EmailConfig{{}},
},
},
{
Receiver: config.Receiver{
Name: "graf",
},
GrafanaReceivers: GrafanaReceivers{
GrafanaManagedReceivers: []*GrafanaReceiver{{}},
},
},
},
},
err: true,
},
} {
t.Run(tc.desc, func(t *testing.T) {
encoded, err := json.Marshal(tc.input)
require.Nil(t, err)
var out ApiAlertingConfig
err = json.Unmarshal(encoded, &out)
if tc.err {
require.Error(t, err)
} else {
require.Equal(t, tc.input, out)
}
})
}
}