mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Allow more characters in label names so notifications are sent (#38629)
Remove validation for labels to be accepted in the Alertmanager, This helps with datasources that produce non-compatible labels. Adds an "object_matchers" to alert manager routers so we can support labels names with extended characters beyond prometheus/openmetrics. It only does this for the internal Grafana managed Alert Manager. This requires a change to alert manager, so for now we use grafana/alertmanager which is a slight fork, with the intention of going back to upstream. The frontend handles the migration of "matchers" -> "object_matchers" when the route is edited and saved. Once this is done, downgrades will not work old versions will not recognize the "object_matchers". Co-authored-by: Kyle Brandt <kyle@grafana.com> Co-authored-by: Nathan Rodman <nathanrodman@gmail.com>
This commit is contained in:
@@ -10,9 +10,11 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
gokit_log "github.com/go-kit/kit/log"
|
||||
amv2 "github.com/prometheus/alertmanager/api/v2/models"
|
||||
@@ -39,6 +41,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
pb "github.com/prometheus/alertmanager/silence/silencepb"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,6 +60,24 @@ const (
|
||||
memoryAlertsGCInterval = 30 * time.Minute
|
||||
)
|
||||
|
||||
func init() {
|
||||
silence.ValidateMatcher = func(m *pb.Matcher) error {
|
||||
switch m.Type {
|
||||
case pb.Matcher_EQUAL, pb.Matcher_NOT_EQUAL:
|
||||
if !model.LabelValue(m.Pattern).IsValid() {
|
||||
return fmt.Errorf("invalid label value %q", m.Pattern)
|
||||
}
|
||||
case pb.Matcher_REGEXP, pb.Matcher_NOT_REGEXP:
|
||||
if _, err := regexp.Compile(m.Pattern); err != nil {
|
||||
return fmt.Errorf("invalid regular expression %q: %s", m.Pattern, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown matcher type %q", m.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type ClusterPeer interface {
|
||||
AddState(string, cluster.State, prometheus.Registerer) cluster.ClusterChannel
|
||||
Position() int
|
||||
@@ -392,7 +413,7 @@ func (am *Alertmanager) applyConfig(cfg *apimodels.PostableUserConfig, rawConfig
|
||||
routingStage[name] = notify.MultiStage{meshStage, silencingStage, inhibitionStage, stage}
|
||||
}
|
||||
|
||||
am.route = dispatch.NewRoute(cfg.AlertmanagerConfig.Route, nil)
|
||||
am.route = dispatch.NewRoute(cfg.AlertmanagerConfig.Route.AsAMRoute(), nil)
|
||||
am.dispatcher = dispatch.NewDispatcher(am.alerts, am.route, routingStage, am.marker, am.timeoutFunc, &nilLimits{}, am.gokitLogger, am.dispatcherMetrics)
|
||||
|
||||
am.wg.Add(1)
|
||||
@@ -638,22 +659,14 @@ func validateLabelSet(ls model.LabelSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isValidLabelName is ln.IsValid() while additionally allowing spaces.
|
||||
// The regex for Prometheus data model is ^[a-zA-Z_][a-zA-Z0-9_]*$
|
||||
// while we will follow ^[a-zA-Z_][a-zA-Z0-9_ ]*$
|
||||
// isValidLabelName is ln.IsValid() without restrictions other than it can not be empty.
|
||||
// The regex for Prometheus data model is ^[a-zA-Z_][a-zA-Z0-9_]*$.
|
||||
func isValidLabelName(ln model.LabelName) bool {
|
||||
if len(ln) == 0 {
|
||||
return false
|
||||
}
|
||||
for i, b := range ln {
|
||||
if !((b >= 'a' && b <= 'z') ||
|
||||
(b >= 'A' && b <= 'Z') ||
|
||||
b == '_' ||
|
||||
(i > 0 && (b == ' ' || (b >= '0' && b <= '9')))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
return utf8.ValidString(string(ln))
|
||||
}
|
||||
|
||||
// AlertValidationError is the error capturing the validation errors
|
||||
|
||||
@@ -208,48 +208,57 @@ func TestPutAlert(t *testing.T) {
|
||||
}
|
||||
},
|
||||
}, {
|
||||
title: "Invalid labels",
|
||||
title: "Special characters in labels",
|
||||
postableAlerts: apimodels.PostableAlerts{
|
||||
PostableAlerts: []models.PostableAlert{
|
||||
{
|
||||
Alert: models.Alert{
|
||||
Labels: models.LabelSet{"alertname$": "Alert1"},
|
||||
Labels: models.LabelSet{"alertname$": "Alert1", "az3-- __...++!!!£@@312312": "1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expError: &AlertValidationError{
|
||||
Alerts: []models.PostableAlert{
|
||||
expAlerts: func(now time.Time) []*types.Alert {
|
||||
return []*types.Alert{
|
||||
{
|
||||
Alert: models.Alert{
|
||||
Labels: models.LabelSet{"alertname$": "Alert1"},
|
||||
Alert: model.Alert{
|
||||
Labels: model.LabelSet{"alertname$": "Alert1", "az3-- __...++!!!£@@312312": "1"},
|
||||
Annotations: model.LabelSet{},
|
||||
StartsAt: now,
|
||||
EndsAt: now.Add(defaultResolveTimeout),
|
||||
GeneratorURL: "",
|
||||
},
|
||||
UpdatedAt: now,
|
||||
Timeout: true,
|
||||
},
|
||||
},
|
||||
Errors: []error{errors.New("invalid label set: invalid name \"alertname$\"")},
|
||||
}
|
||||
},
|
||||
}, {
|
||||
title: "Invalid annotation",
|
||||
title: "Special characters in annotations",
|
||||
postableAlerts: apimodels.PostableAlerts{
|
||||
PostableAlerts: []models.PostableAlert{
|
||||
{
|
||||
Annotations: models.LabelSet{"msg$": "Alert4 annotation"},
|
||||
Annotations: models.LabelSet{"az3-- __...++!!!£@@312312": "Alert4 annotation"},
|
||||
Alert: models.Alert{
|
||||
Labels: models.LabelSet{"alertname": "Alert1"},
|
||||
Labels: models.LabelSet{"alertname": "Alert4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expError: &AlertValidationError{
|
||||
Alerts: []models.PostableAlert{
|
||||
expAlerts: func(now time.Time) []*types.Alert {
|
||||
return []*types.Alert{
|
||||
{
|
||||
Annotations: models.LabelSet{"msg$": "Alert4 annotation"},
|
||||
Alert: models.Alert{
|
||||
Labels: models.LabelSet{"alertname": "Alert1"},
|
||||
Alert: model.Alert{
|
||||
Labels: model.LabelSet{"alertname": "Alert4"},
|
||||
Annotations: model.LabelSet{"az3-- __...++!!!£@@312312": "Alert4 annotation"},
|
||||
StartsAt: now,
|
||||
EndsAt: now.Add(defaultResolveTimeout),
|
||||
GeneratorURL: "",
|
||||
},
|
||||
UpdatedAt: now,
|
||||
Timeout: true,
|
||||
},
|
||||
},
|
||||
Errors: []error{errors.New("invalid annotations: invalid name \"msg$\"")},
|
||||
}
|
||||
},
|
||||
}, {
|
||||
title: "No labels after removing empty",
|
||||
|
||||
Reference in New Issue
Block a user