fix(alertmanager): fix templating (#7288)

### Summary

- Fix templating of alertmanagerURL to include links to our alerts page
- Return an empty array instead of null in contextlinks
This commit is contained in:
Vibhu Pandey 2025-03-12 23:15:58 +05:30 committed by GitHub
parent 5dba1f3dbb
commit 06be0f4330
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 171 additions and 2 deletions

View File

@ -218,7 +218,7 @@ func (server *Server) SetConfig(ctx context.Context, alertmanagerConfig *alertma
config := alertmanagerConfig.AlertmanagerConfig()
var err error
server.tmpl, err = template.FromGlobs(config.Templates)
server.tmpl, err = alertmanagertypes.FromGlobs(config.Templates)
if err != nil {
return err
}

View File

@ -148,7 +148,7 @@ func PrepareLinksToLogs(start, end time.Time, filterItems []v3.FilterItem) strin
// i.e Severity text = WARN
// If the Severity text is not part of the group by clause, then we add it as it is
func PrepareFilters(labels map[string]string, whereClauseItems []v3.FilterItem, groupByItems []v3.AttributeKey, keys map[string]v3.AttributeKey) []v3.FilterItem {
var filterItems []v3.FilterItem
filterItems := make([]v3.FilterItem, 0)
added := make(map[string]struct{})

View File

@ -0,0 +1,28 @@
package alertmanagertypes
import (
"bytes"
"fmt"
alertmanagertemplate "github.com/prometheus/alertmanager/template"
)
// FromGlobs overrides the default alertmanager template to add a ruleIdPath template.
// This is used to generate a link to the rule in the alertmanager.
//
// It explicitly checks for a ruleId that is a number and then generates a path to the rule.
func FromGlobs(paths []string) (*alertmanagertemplate.Template, error) {
t, err := alertmanagertemplate.FromGlobs(paths)
if err != nil {
return nil, err
}
if err := t.Parse(bytes.NewReader([]byte(`
{{ define "__ruleIdPath" }}{{ range .CommonLabels.SortedPairs }}{{ if eq .Name "ruleId" }}{{ if match "^[0-9]+$" .Value }}/edit?ruleId={{ .Value | urlquery }}{{ end }}{{ end }}{{ end }}{{ end }}
{{ define "__alertmanagerURL" }}{{ .ExternalURL }}/alerts{{ template "__ruleIdPath" . }}{{ end }}
`))); err != nil {
return nil, fmt.Errorf("error parsing alertmanager templates: %w", err)
}
return t, nil
}

View File

@ -0,0 +1,141 @@
package alertmanagertypes
import (
"net/url"
"testing"
"time"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFromGlobs(t *testing.T) {
template, err := FromGlobs([]string{})
require.NoError(t, err)
template.ExternalURL = &url.URL{Scheme: "http", Host: "localhost:8080", Path: ""}
testCases := []struct {
name string
alerts []*types.Alert
expected string
}{
{
name: "SingleAlertWithValidRuleId",
alerts: []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{
"ruleId": "439453587",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
},
expected: "http://localhost:8080/alerts/edit?ruleId=439453587",
},
{
name: "SingleAlertWithInvalidRuleId",
alerts: []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{
"ruleId": "43textabc",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
},
expected: "http://localhost:8080/alerts",
},
{
name: "MultipleAlertsWithMismatchingRuleId",
alerts: []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{
"ruleId": "1",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
{
Alert: model.Alert{
Labels: model.LabelSet{
"ruleId": "2",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
},
expected: "http://localhost:8080/alerts",
},
{
name: "MultipleAlertsWithMatchingRuleId",
alerts: []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{
"ruleId": "1",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
{
Alert: model.Alert{
Labels: model.LabelSet{
"ruleId": "1",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
},
expected: "http://localhost:8080/alerts/edit?ruleId=1",
},
{
name: "MultipleAlertsWithNoRuleId",
alerts: []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{
"label1": "1",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
{
Alert: model.Alert{
Labels: model.LabelSet{
"label2": "2",
},
},
UpdatedAt: time.Now(),
Timeout: false,
},
},
expected: "http://localhost:8080/alerts",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
data := template.Data("__receiver", model.LabelSet{}, tc.alerts...)
url, err := template.ExecuteTextString(`{{ template "__alertmanagerURL" . }}`, data)
require.NoError(t, err)
assert.Equal(t, tc.expected, url)
url, err = template.ExecuteHTMLString(`{{ template "__alertmanagerURL" . }}`, data)
require.NoError(t, err)
assert.Equal(t, tc.expected, url)
})
}
}