Browse Source

manually rebasing changes from promql-parser branch

forest-feature-rebase-2
forest 1 year ago
parent
commit
1f7ecc4ac4
  1. 2
      go.mod
  2. 4
      go.sum
  3. 145
      services/alertmanager/alertmanager.go

2
go.mod

@ -3,6 +3,7 @@ module github.com/matrix-org/go-neb
go 1.14
require (
giit.cyberia.club/~forest/promql-parser v1.10.11
github.com/PuerkitoBio/goquery v1.5.1 // indirect
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect
@ -36,7 +37,6 @@ require (
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 // indirect
github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v0.8.1-0.20160916180340-5636dc67ae77
github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335 // indirect
github.com/prometheus/common v0.0.0-20161002210234-85637ea67b04 // indirect

4
go.sum

@ -1,4 +1,6 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
giit.cyberia.club/~forest/promql-parser v1.10.11 h1:Eipn8ocEjVuwEYUg/eMxq1YFUlJsitkulIrmls/iD9Q=
giit.cyberia.club/~forest/promql-parser v1.10.11/go.mod h1:Gdg3hYMxFI7gORfHlyV/Wn9PSYz95oShichBO6FgHIs=
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -93,6 +95,8 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.1-0.20160916180340-5636dc67ae77 h1:YXoHPWLq9PIcMoZg7znMmEzqYHBszdXSemwGQRJoiSk=
github.com/prometheus/client_golang v0.8.1-0.20160916180340-5636dc67ae77/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=

145
services/alertmanager/alertmanager.go

@ -5,14 +5,20 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
html "html/template"
"net/http"
"net/url"
"strings"
text "text/template"
"time"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
promlabels "giit.cyberia.club/~forest/promql-parser/pkg/labels"
promqlparser "giit.cyberia.club/~forest/promql-parser/promql/parser"
)
// ServiceType of the Alertmanager service.
@ -94,6 +100,18 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
filters = append(filters, fmt.Sprintf("%s%%3D\"%s\"", label, val))
}
alert.SilenceURL = fmt.Sprintf("%s#silences/new?filter={%s}", notif.ExternalURL, strings.Join(filters, ","))
newGeneratorURL, err := s.fixupPrometheusGeneratorURL(alert.GeneratorURL, alert.Labels)
if err != nil {
log.WithError(err).Error("Alertmanager webhook received an invalid GeneratorURL. I won't try to fix it up.")
} else {
alert.GeneratorURL = newGeneratorURL
}
description, hasDescription := alert.Annotations["description"]
if hasDescription {
alert.Annotations["description"] = s.addTimestampToDescription(description)
}
}
for roomID, templates := range s.Rooms {
@ -140,6 +158,125 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
w.WriteHeader(200)
}
func (s *Service) addTimestampToDescription(description string) string {
return fmt.Sprintf("%s. Happened at %s (UTC). ", description, time.Now().UTC().Format("15:04"))
}
func (s *Service) fixupPrometheusGeneratorURL(urlString string, alertLabels map[string]string) (string, error) {
url, err := url.Parse(urlString)
if err != nil {
return "", err
}
expressionString := url.Query().Get("g0.expr")
if expressionString != "" {
newExpressionString, err := s.fixupPrometheusExpression(expressionString, alertLabels)
if err != nil {
return "", err
}
url.Query().Set("g0.expr", newExpressionString)
}
url.Query().Set("g0.end_input", time.Now().Add(time.Duration(30)*time.Minute).UTC().Format("2006-01-02 15:04"))
url.Query().Set("g0.range_input", "1h")
url.Query().Set("g0.tab", "0")
url.Query().Set("g0.stacked", "0")
return url.String(), nil
}
func (s *Service) fixupPrometheusExpression(expressionString string, alertLabels map[string]string) (string, error) {
expr, err := promqlparser.ParseExpr(expressionString)
if err != nil {
return "", err
}
comparisonOperators := map[promqlparser.ItemType]bool{
promqlparser.GTR: true,
promqlparser.GTE: true,
promqlparser.LTE: true,
promqlparser.LSS: true,
promqlparser.EQL: true,
promqlparser.EQL_REGEX: true,
promqlparser.EQLC: true,
promqlparser.NEQ: true,
promqlparser.NEQ_REGEX: true,
}
// This function returns true if niether this expression nor any of its recursive children are query-ish
// in other words it returns true for ((90+10)*100) but returns false for irate(node_cpu_seconds_total{mode="idle"}[10m])
isLiteral := func(expr promqlparser.Expr) bool {
blah := false
foundDynamicContents := &blah
promqlparser.Inspect(expr, func(node promqlparser.Node, path []promqlparser.Node) error {
switch typedNode := node.(type) {
case *promqlparser.VectorSelector:
*foundDynamicContents = (typedNode != nil)
case *promqlparser.SubqueryExpr:
*foundDynamicContents = (typedNode != nil)
case *promqlparser.MatrixSelector:
*foundDynamicContents = (typedNode != nil)
case *promqlparser.AggregateExpr:
*foundDynamicContents = (typedNode != nil)
case *promqlparser.EvalStmt:
*foundDynamicContents = (typedNode != nil)
default:
}
return nil
})
return !*foundDynamicContents
}
var newRootOfExpr *promqlparser.Expr = &expr
promqlparser.Inspect(expr, func(node promqlparser.Node, path []promqlparser.Node) error {
if node != nil {
switch typedNode := node.(type) {
case *promqlparser.BinaryExpr:
// if the outermost Node is a binary comparison op like x > y, a <= b, etc
if _, isComparison := comparisonOperators[typedNode.Op]; isComparison && len(path) == 0 {
// now we have to figure out which side is dynamic and which side is static (only contains literals)
lhsIsLiteral := isLiteral(typedNode.LHS)
rhsIsLiteral := isLiteral(typedNode.RHS)
// if one side is dynamic, make that side be the new root of the expression (trim the comparison off the outside)
if !lhsIsLiteral && rhsIsLiteral {
newRootOfExpr = &typedNode.LHS
}
if lhsIsLiteral && !rhsIsLiteral {
newRootOfExpr = &typedNode.RHS
}
}
case *promqlparser.VectorSelector:
alreadyQualifiedLabels := map[string]bool{}
for _, v := range typedNode.LabelMatchers {
alreadyQualifiedLabels[v.Name] = true
}
// qualify any vectorSelector in the query with the labels that we know matched the alert condition
// as long as this specific label is not already matched on
for k, v := range alertLabels {
if !alreadyQualifiedLabels[k] {
typedNode.LabelMatchers = append(typedNode.LabelMatchers, &promlabels.Matcher{
Type: promlabels.MatchEqual,
Name: k,
Value: v,
})
}
}
default:
}
}
return nil
})
return (*newRootOfExpr).(promqlparser.Node).String(), nil
}
// Register makes sure the Config information supplied is valid.
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error {
s.WebhookURL = s.webhookEndpointURL

Loading…
Cancel
Save