Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alerting: Enable interpolation for notification policies in file provisioning #58956

Merged
merged 3 commits into from Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 35 additions & 7 deletions pkg/services/provisioning/alerting/notification_policy_types.go
@@ -1,26 +1,54 @@
package alerting

import (
"encoding/json"

"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/provisioning/values"
)

type NotificiationPolicyV1 struct {
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
Policy definitions.Route `json:",inline" yaml:",inline"`
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
// We use JSONValue here, as we want to have interpolation the values.
Policy values.JSONValue `json:"-" yaml:"-"`
}

func (v1 *NotificiationPolicyV1) UnmarshalYAML(unmarshal func(interface{}) error) error {
err := v1.Policy.UnmarshalYAML(unmarshal)
if err != nil {
return err
}
// As we also want to unmarshal the orgId and any other field that might be
// added in the future we create an alias type that prevents recursion
// and just uses the default marshler.
type plain NotificiationPolicyV1
return unmarshal((*plain)(v1))
}

func (v1 *NotificiationPolicyV1) mapToModel() NotificiationPolicy {
func (v1 *NotificiationPolicyV1) mapToModel() (NotificiationPolicy, error) {
orgID := v1.OrgID.Value()
if orgID < 1 {
orgID = 1
}
// we don't need any further validation here as it's done by
// the notification policy service
var route definitions.Route
// We need the string json representation, so we marshal the policy back
// as a string and interpolate it at the same time.
data, err := json.Marshal(v1.Policy.Value())
if err != nil {
return NotificiationPolicy{}, err
}
// Now we can take the interpolated string json represtenation of the policy
// and unmarshal it in the concrete type.
err = json.Unmarshal(data, &route)
if err != nil {
return NotificiationPolicy{}, err
}
// We don't need any further validation here as it's done by
// the notification policy service.
return NotificiationPolicy{
OrgID: orgID,
Policy: v1.Policy,
}
Policy: route,
}, nil
}

type NotificiationPolicy struct {
Expand Down
@@ -0,0 +1,37 @@
package alerting

import (
"os"
"testing"

"gopkg.in/yaml.v2"

"github.com/stretchr/testify/require"
)

func TestNotificationPolicy(t *testing.T) {
const (
envKey = "NOTIFIER_EMAIL_REMINDER_FREQUENCY"
envValue = "4h"
)
err := os.Setenv(envKey, envValue)
require.NoError(t, err)
defer func() {
_ = os.Unsetenv(envKey)
}()
data := `orgId: 123
receiver: test
continue: true
repeat_interval: ${NOTIFIER_EMAIL_REMINDER_FREQUENCY}
`
var model NotificiationPolicyV1

err = yaml.Unmarshal([]byte(data), &model)
require.NoError(t, err)
np, err := model.mapToModel()
require.NoError(t, err)
require.Equal(t, int64(123), np.OrgID)
require.Equal(t, "test", np.Policy.Receiver)
require.True(t, np.Policy.Continue)
require.Equal(t, envValue, np.Policy.RepeatInterval.String())
}
13 changes: 10 additions & 3 deletions pkg/services/provisioning/alerting/types.go
Expand Up @@ -51,7 +51,9 @@ func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
if err := fileV1.mapContactPoint(&alertingFile); err != nil {
return AlertingFile{}, fmt.Errorf("failure parsing contact points: %w", err)
}
fileV1.mapPolicies(&alertingFile)
if err := fileV1.mapPolicies(&alertingFile); err != nil {
return AlertingFile{}, fmt.Errorf("failure parsing policies: %w", err)
}
if err := fileV1.mapMuteTimes(&alertingFile); err != nil {
return AlertingFile{}, fmt.Errorf("failure parsing mute times: %w", err)
}
Expand Down Expand Up @@ -89,13 +91,18 @@ func (fileV1 *AlertingFileV1) mapMuteTimes(alertingFile *AlertingFile) error {
return nil
}

func (fileV1 *AlertingFileV1) mapPolicies(alertingFile *AlertingFile) {
func (fileV1 *AlertingFileV1) mapPolicies(alertingFile *AlertingFile) error {
for _, npV1 := range fileV1.Policies {
alertingFile.Policies = append(alertingFile.Policies, npV1.mapToModel())
np, err := npV1.mapToModel()
if err != nil {
return err
}
alertingFile.Policies = append(alertingFile.Policies, np)
}
for _, orgIDV1 := range fileV1.ResetPolicies {
alertingFile.ResetPolicies = append(alertingFile.ResetPolicies, OrgID(orgIDV1.Value()))
}
return nil
}

func (fileV1 *AlertingFileV1) mapContactPoint(alertingFile *AlertingFile) error {
Expand Down