Alerting: Support simplified routing receivers in Prometheus conversion API (#105135)
Adds ability to set notifications settings using the Prometheus conversion API.
The API now supports a new optional header: X-Grafana-Alerting-Notification-Settings which can be used to specify notification settings.
The value of the header is the AlertRuleNotificationSettings structure in JSON:
mimirtool rules load alerts.yaml --extra-headers 'X-Grafana-Alerting-Notification-Settings: {"receiver": "my-webhook", "group_by": ["cluster", "pod"]}'
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/validation"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/prom"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
@@ -41,6 +43,10 @@ const (
|
||||
// These headers control the paused state of newly created rules. By default, rules are not paused.
|
||||
recordingRulesPausedHeader = "X-Grafana-Alerting-Recording-Rules-Paused"
|
||||
alertRulesPausedHeader = "X-Grafana-Alerting-Alert-Rules-Paused"
|
||||
|
||||
// notificationSettingsHeader is the header that specifies the notification settings to be used for the rules.
|
||||
// The value should be a JSON-encoded AlertRuleNotificationSettings object.
|
||||
notificationSettingsHeader = "X-Grafana-Alerting-Notification-Settings"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -49,7 +55,7 @@ var (
|
||||
errutil.WithPublicMessage(fmt.Sprintf("Missing datasource UID header: %s", datasourceUIDHeader)),
|
||||
).Errorf("missing datasource UID header")
|
||||
|
||||
errInvalidHeaderValueMsg = "Invalid value for header {{.Public.Header}}: must be 'true' or 'false'"
|
||||
errInvalidHeaderValueMsg = "Invalid value for header {{.Public.Header}}: {{.Public.Error}}"
|
||||
errInvalidHeaderValueBase = errutil.ValidationFailed("alerting.invalidHeaderValue").MustTemplate(errInvalidHeaderValueMsg, errutil.WithPublic(errInvalidHeaderValueMsg))
|
||||
|
||||
errRecordingRulesNotEnabled = errutil.ValidationFailed(
|
||||
@@ -63,8 +69,8 @@ var (
|
||||
).Errorf("recording rules target datasources configuration not enabled")
|
||||
)
|
||||
|
||||
func errInvalidHeaderValue(header string) error {
|
||||
return errInvalidHeaderValueBase.Build(errutil.TemplateData{Public: map[string]any{"Header": header}})
|
||||
func errInvalidHeaderValue(header string, err error) error {
|
||||
return errInvalidHeaderValueBase.Build(errutil.TemplateData{Public: map[string]any{"Header": header, "Error": err}})
|
||||
}
|
||||
|
||||
// ConvertPrometheusSrv converts Prometheus rules to Grafana rules
|
||||
@@ -369,6 +375,12 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusPostRuleGroups(c *context
|
||||
// to ensure we can return them in this API in Prometheus format.
|
||||
keepOriginalRuleDefinition := provenance == models.ProvenanceConvertedPrometheus
|
||||
|
||||
notificationSettings, err := parseNotificationSettingsHeader(c)
|
||||
if err != nil {
|
||||
logger.Error("Failed to parse notification settings header", "error", err)
|
||||
return errorToResponse(err)
|
||||
}
|
||||
|
||||
// 2. Convert Prometheus Rules to GMA
|
||||
grafanaGroups := make([]*models.AlertRuleGroup, 0, len(promNamespaces))
|
||||
for ns, rgs := range promNamespaces {
|
||||
@@ -394,7 +406,18 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusPostRuleGroups(c *context
|
||||
}
|
||||
}
|
||||
|
||||
grafanaGroup, err := srv.convertToGrafanaRuleGroup(c, ds, tds, namespace.UID, rg, pauseRecordingRules, pauseAlertRules, keepOriginalRuleDefinition, logger)
|
||||
grafanaGroup, err := srv.convertToGrafanaRuleGroup(
|
||||
c,
|
||||
ds,
|
||||
tds,
|
||||
namespace.UID,
|
||||
rg,
|
||||
pauseRecordingRules,
|
||||
pauseAlertRules,
|
||||
keepOriginalRuleDefinition,
|
||||
notificationSettings,
|
||||
logger,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("Failed to convert Prometheus rules to Grafana rules", "error", err)
|
||||
return errorToResponse(err)
|
||||
@@ -442,6 +465,7 @@ func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup(
|
||||
pauseRecordingRules bool,
|
||||
pauseAlertRules bool,
|
||||
keepOriginalRuleDefinition bool,
|
||||
notificationSettings []models.NotificationSettings,
|
||||
logger log.Logger,
|
||||
) (*models.AlertRuleGroup, error) {
|
||||
logger.Info("Converting Prometheus rules to Grafana rules", "rules", len(promGroup.Rules), "folder_uid", namespaceUID, "datasource_uid", ds.UID, "datasource_type", ds.Type)
|
||||
@@ -479,6 +503,7 @@ func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup(
|
||||
},
|
||||
KeepOriginalRuleDefinition: util.Pointer(keepOriginalRuleDefinition),
|
||||
EvaluationOffset: &srv.cfg.PrometheusConversion.RuleQueryOffset,
|
||||
NotificationSettings: notificationSettings,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -503,7 +528,7 @@ func parseBooleanHeader(header string, headerName string) (bool, error) {
|
||||
}
|
||||
val, err := strconv.ParseBool(header)
|
||||
if err != nil {
|
||||
return false, errInvalidHeaderValue(headerName)
|
||||
return false, errInvalidHeaderValue(headerName, errors.New("must be 'true' or 'false'"))
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
@@ -597,3 +622,23 @@ func getProvenance(ctx *contextmodel.ReqContext) models.Provenance {
|
||||
}
|
||||
return models.ProvenanceConvertedPrometheus
|
||||
}
|
||||
|
||||
func parseNotificationSettingsHeader(ctx *contextmodel.ReqContext) ([]models.NotificationSettings, error) {
|
||||
var notificationSettings []models.NotificationSettings
|
||||
notificationSettingsJSON := ctx.Req.Header.Get(notificationSettingsHeader)
|
||||
|
||||
if notificationSettingsJSON != "" {
|
||||
var settings apimodels.AlertRuleNotificationSettings
|
||||
var err error
|
||||
|
||||
if err := json.Unmarshal([]byte(notificationSettingsJSON), &settings); err != nil {
|
||||
return nil, errInvalidHeaderValue(notificationSettingsHeader, errors.New("invalid JSON"))
|
||||
}
|
||||
notificationSettings, err = validation.ValidateNotificationSettings(&settings)
|
||||
if err != nil {
|
||||
return nil, errInvalidHeaderValue(notificationSettingsHeader, err)
|
||||
}
|
||||
}
|
||||
|
||||
return notificationSettings, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user