diff --git a/go.mod b/go.mod index b23d2a4aded..fea23303854 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/google/go-github/v57 v57.0.0 github.com/google/uuid v1.6.0 github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc - github.com/grafana/alerting v0.0.0-20241211182001-0f317eb6b2f7 + github.com/grafana/alerting v0.0.0-20250113170557-b4ab2ba363a8 github.com/grafana/regexp v0.0.0-20240607082908-2cb410fa05da github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/vault/api v1.15.0 diff --git a/go.sum b/go.sum index 6ef8a585ef3..6d5a66aa24a 100644 --- a/go.sum +++ b/go.sum @@ -1269,8 +1269,8 @@ github.com/gosimple/slug v1.1.1 h1:fRu/digW+NMwBIP+RmviTK97Ho/bEj/C9swrCspN3D4= github.com/gosimple/slug v1.1.1/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0= github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc h1:PXZQA2WCxe85Tnn+WEvr8fDpfwibmEPgfgFEaC87G24= github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc/go.mod h1:AHHlOEv1+GGQ3ktHMlhuTUwo3zljV3QJbC0+8o2kn+4= -github.com/grafana/alerting v0.0.0-20241211182001-0f317eb6b2f7 h1:VGLUQ2mwzlF1NGwTxpSfv1RnuOsDlNh/NT5KRvhZ0sQ= -github.com/grafana/alerting v0.0.0-20241211182001-0f317eb6b2f7/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= +github.com/grafana/alerting v0.0.0-20250113170557-b4ab2ba363a8 h1:mdI6P22PgFD7bQ0Yf4h8cfHSldak4nxogvlsTHZyZmc= +github.com/grafana/alerting v0.0.0-20250113170557-b4ab2ba363a8/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= github.com/grafana/dskit v0.0.0-20250107142522-441a90acd4e5 h1:zBXjoJkFV/xPku4vIYvujRyd+iJunlDPPkKQ/FiDf0k= github.com/grafana/dskit v0.0.0-20250107142522-441a90acd4e5/go.mod h1:SPLNCARd4xdjCkue0O6hvuoveuS1dGJjDnfxYe405YQ= github.com/grafana/e2e v0.1.2-0.20240118170847-db90b84177fc h1:BW+LjKJDz0So5LI8UZfW5neWeKpSkWqhmGjQFzcFfLM= diff --git a/pkg/alertmanager/alertmanager.go b/pkg/alertmanager/alertmanager.go index 71659a72472..fd50f5b909b 100644 --- a/pkg/alertmanager/alertmanager.go +++ b/pkg/alertmanager/alertmanager.go @@ -668,7 +668,7 @@ func (am *Alertmanager) buildGrafanaReceiverIntegrations(rcv *alertingNotify.API func buildGrafanaReceiverIntegrations(emailCfg alertingReceivers.EmailSenderConfig, rcv *alertingNotify.APIReceiver, tmpl *template.Template, logger log.Logger) ([]*nfstatus.Integration, error) { // The decrypt functions and the context are used to decrypt the configuration. // We don't need to decrypt anything, so we can pass a no-op decrypt func and a context.Background(). - rCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), rcv, alertingNotify.NoopDecrypt) + rCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), rcv, alertingNotify.NoopDecode, alertingNotify.NoopDecrypt) if err != nil { return nil, err } diff --git a/vendor/github.com/grafana/alerting/notify/receivers.go b/vendor/github.com/grafana/alerting/notify/receivers.go index 09ac3715298..de300802504 100644 --- a/vendor/github.com/grafana/alerting/notify/receivers.go +++ b/vendor/github.com/grafana/alerting/notify/receivers.go @@ -213,6 +213,38 @@ type NotifierConfig[T interface{}] struct { Settings T } +// DecodeSecretsFn is a function used to decode a map of secrets before creating a receiver. +type DecodeSecretsFn func(secrets map[string]string) (map[string][]byte, error) + +// DecodeSecretsFromBase64 is a DecodeSecretsFn that base64-decodes a map of secrets. +func DecodeSecretsFromBase64(secrets map[string]string) (map[string][]byte, error) { + secureSettings := make(map[string][]byte, len(secrets)) + if secrets == nil { + return secureSettings, nil + } + for k, v := range secrets { + d, err := base64.StdEncoding.DecodeString(v) + if err != nil { + return nil, fmt.Errorf("failed to decode secure settings key %s: %w", k, err) + } + secureSettings[k] = d + } + return secureSettings, nil +} + +// NoopDecode is a DecodeSecretsFn that converts a map[string]string into a map[string][]byte without decoding it. +func NoopDecode(secrets map[string]string) (map[string][]byte, error) { + secureSettings := make(map[string][]byte, len(secrets)) + if secrets == nil { + return secureSettings, nil + } + + for k, v := range secrets { + secureSettings[k] = []byte(v) + } + return secureSettings, nil +} + // GetDecryptedValueFn is a function that returns the decrypted value of // the given key. If the key is not present, then it returns the fallback value. type GetDecryptedValueFn func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string @@ -226,12 +258,12 @@ func NoopDecrypt(_ context.Context, sjd map[string][]byte, key string, fallback } // BuildReceiverConfiguration parses, decrypts and validates the APIReceiver. -func BuildReceiverConfiguration(ctx context.Context, api *APIReceiver, decrypt GetDecryptedValueFn) (GrafanaReceiverConfig, error) { +func BuildReceiverConfiguration(ctx context.Context, api *APIReceiver, decode DecodeSecretsFn, decrypt GetDecryptedValueFn) (GrafanaReceiverConfig, error) { result := GrafanaReceiverConfig{ Name: api.Name, } for _, receiver := range api.Integrations { - err := parseNotifier(ctx, &result, receiver, decrypt) + err := parseNotifier(ctx, &result, receiver, decode, decrypt) if err != nil { return GrafanaReceiverConfig{}, IntegrationValidationError{ Integration: receiver, @@ -243,14 +275,10 @@ func BuildReceiverConfiguration(ctx context.Context, api *APIReceiver, decrypt G } // parseNotifier parses receivers and populates the corresponding field in GrafanaReceiverConfig. Returns an error if the configuration cannot be parsed. -func parseNotifier(ctx context.Context, result *GrafanaReceiverConfig, receiver *GrafanaIntegrationConfig, decrypt GetDecryptedValueFn) error { - secureSettings, err := decodeSecretsFromBase64(receiver.SecureSettings) +func parseNotifier(ctx context.Context, result *GrafanaReceiverConfig, receiver *GrafanaIntegrationConfig, decode DecodeSecretsFn, decrypt GetDecryptedValueFn) error { + secureSettings, err := decode(receiver.SecureSettings) if err != nil { - // An error means that the secure settings are not base-64 encoded. - secureSettings = make(map[string][]byte, len(receiver.SecureSettings)) - for k, v := range receiver.SecureSettings { - secureSettings[k] = []byte(v) - } + return err } decryptFn := func(key string, fallback string) string { @@ -396,21 +424,6 @@ func parseNotifier(ctx context.Context, result *GrafanaReceiverConfig, receiver return nil } -func decodeSecretsFromBase64(secrets map[string]string) (map[string][]byte, error) { - secureSettings := make(map[string][]byte, len(secrets)) - if secrets == nil { - return secureSettings, nil - } - for k, v := range secrets { - d, err := base64.StdEncoding.DecodeString(v) - if err != nil { - return nil, fmt.Errorf("failed to decode secure settings key %s: %w", k, err) - } - secureSettings[k] = d - } - return secureSettings, nil -} - // GetActiveReceiversMap returns all receivers that are in use by a route. func GetActiveReceiversMap(r *dispatch.Route) map[string]struct{} { receiversMap := make(map[string]struct{}) diff --git a/vendor/github.com/grafana/alerting/templates/template_data.go b/vendor/github.com/grafana/alerting/templates/template_data.go index 0e0a6bf4015..f9b452624ec 100644 --- a/vendor/github.com/grafana/alerting/templates/template_data.go +++ b/vendor/github.com/grafana/alerting/templates/template_data.go @@ -3,10 +3,14 @@ package templates import ( "context" "encoding/json" + "fmt" + tmplhtml "html/template" "net/url" "path" + "slices" "sort" "strings" + tmpltext "text/template" "time" "github.com/go-kit/log" @@ -65,6 +69,52 @@ type ExtendedData struct { ExternalURL string `json:"externalURL"` } +var DefaultTemplateName = "__default__" + +// DefaultTemplate returns a new Template with all default templates parsed. +func DefaultTemplate(options ...template.Option) (TemplateDefinition, error) { + // We cannot simply append the text of each default file together as there can be (and are) duplicate template + // names. Duplicate templates should override when parsed from separate files but will fail to parse if both are in + // the same file. + // So, instead we allow tmpltext to combine the templates and then convert it to a string afterwards. + // The underlying template is not accessible, so we capture it via template.Option. + var newTextTmpl *tmpltext.Template + var captureTemplate template.Option = func(text *tmpltext.Template, _ *tmplhtml.Template) { + newTextTmpl = text + } + + // Call FromContent without any user-provided templates to get the combined default template. + _, err := FromContent(nil, append(options, captureTemplate)...) + if err != nil { + return TemplateDefinition{}, err + } + + var combinedTemplate strings.Builder + tmpls := newTextTmpl.Templates() + // Sort for a consistent order. + slices.SortFunc(tmpls, func(a, b *tmpltext.Template) int { + return strings.Compare(a.Name(), b.Name()) + }) + + // Recreate the "define" blocks for all templates. Would be nice to have a more direct way to do this. + for _, tmpl := range tmpls { + if tmpl.Name() != "" { + def := tmpl.Tree.Root.String() + if tmpl.Name() == "__text_values_list" { + // Temporary fix for https://github.com/golang/go/commit/6fea4094242fe4e7be8bd7ec0b55df9f6df3f025. + // TODO: Can remove with GO v1.24. + def = strings.Replace(def, "$first := false", "$first = false", 1) + } + + combinedTemplate.WriteString(fmt.Sprintf("{{ define \"%s\" }}%s{{ end }}\n\n", tmpl.Name(), def)) + } + } + return TemplateDefinition{ + Name: DefaultTemplateName, + Template: combinedTemplate.String(), + }, nil +} + // FromContent calls Parse on all provided template content and returns the resulting Template. Content equivalent to templates.FromGlobs. func FromContent(tmpls []string, options ...template.Option) (*Template, error) { t, err := newTemplate(options...) diff --git a/vendor/modules.txt b/vendor/modules.txt index 529b5c99de1..8ee49f26403 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -609,7 +609,7 @@ github.com/gosimple/slug # github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc ## explicit; go 1.13 github.com/grafana-tools/sdk -# github.com/grafana/alerting v0.0.0-20241211182001-0f317eb6b2f7 +# github.com/grafana/alerting v0.0.0-20250113170557-b4ab2ba363a8 ## explicit; go 1.22 github.com/grafana/alerting/cluster github.com/grafana/alerting/definition