Compare commits

...

5 Commits

Author SHA1 Message Date
Abhishek Kumar Singh
f83df4d5cc Merge branch 'main' into chore/notification_channel_migrations 2026-04-09 12:08:54 +05:30
Abhishek Kumar Singh
537ec5a3f9 chore: fix lint issue 2026-04-08 21:27:41 +05:30
Abhishek Kumar Singh
2774fa4655 chore: removed usage of types package + fix comma issue in template 2026-04-08 21:03:07 +05:30
Abhishek Kumar Singh
336e79737c chore: updated frontend mocks with new template format 2026-04-08 19:19:49 +05:30
Abhishek Kumar Singh
85e6403738 chore: default notification channel migration to remove ruleId and ruleSource 2026-04-08 17:09:23 +05:30
15 changed files with 1199 additions and 17 deletions

View File

@@ -5,9 +5,12 @@ export const PagerInitialConfig: Partial<PagerChannel> = {
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
{{" "}}(
{{- with .CommonLabels.Remove .GroupLabels.Names }}
{{- range $index, $label := .SortedPairs -}}
{{ if $index }}, {{ end }}
{{- $first := true -}}
{{- range $label := .SortedPairs -}}
{{- if or (eq $label.Name "ruleId") (eq $label.Name "ruleSource") }}{{ continue }}{{ end -}}
{{- if not $first }}, {{ end -}}
{{- $label.Name }}="{{ $label.Value -}}"
{{- $first = false -}}
{{- end }}
{{- end -}}
)
@@ -30,7 +33,7 @@ export const OpsgenieInitialConfig: Partial<OpsgenieChannel> = {
{{ range .Alerts.Firing }}
- Message: {{ .Annotations.description }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }}
{{ end }} Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Source: {{ .GeneratorURL }}
@@ -41,7 +44,7 @@ export const OpsgenieInitialConfig: Partial<OpsgenieChannel> = {
{{ range .Alerts.Resolved }}
- Message: {{ .Annotations.description }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }}
{{ end }} Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Source: {{ .GeneratorURL }}
@@ -400,7 +403,7 @@ export const EmailInitialConfig: Partial<EmailChannel> = {
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />
@@ -427,7 +430,7 @@ export const EmailInitialConfig: Partial<EmailChannel> = {
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />

View File

@@ -73,16 +73,19 @@ function CreateAlertChannels({
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}}
*Details:*
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} • *{{ .Name }}:* {{ .Value }}
{{ end }}
{{ end }}`,
title: `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
{{" "}}(
{{- with .CommonLabels.Remove .GroupLabels.Names }}
{{- range $index, $label := .SortedPairs -}}
{{ if $index }}, {{ end }}
{{- $first := true -}}
{{- range $label := .SortedPairs -}}
{{- if or (eq $label.Name "ruleId") (eq $label.Name "ruleSource") }}{{ continue }}{{ end -}}
{{- if not $first }}, {{ end -}}
{{- $label.Name }}="{{ $label.Value -}}"
{{- $first = false -}}
{{- end }}
{{- end -}}
)

View File

@@ -6,7 +6,7 @@ export const allAlertChannels = [
name: 'Dummy-Channel',
type: 'slack',
data:
'{"name":"Dummy-Channel","slack_configs":[{"api_url":"https://discord.com/api/webhooks/dummy_webhook_id/dummy_webhook_token/slack","channel":"#dummy_channel","send_resolved":true,"text":"{{ range .Alerts -}}\\n *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} dummy_summary\\n\\n *Summary:* {{ .Annotations.summary }}\\n *Description:* {{ .Annotations.description }}\\n\\n *Details:*\\n {{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}\\n {{ end }}\\n {{ end }}","title":"[{{ .Status | toUpper }}{{ if eq .Status \\"firing\\" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\\n {{- if gt (len .CommonLabels) (len .GroupLabels) -}}\\n {{\\" \\"}}(\\n {{- with .CommonLabels.Remove .GroupLabels.Names }}\\n {{- range $index, $label := .SortedPairs -}}\\n {{ if $index }}, {{ end }}\\n {{- $label.Name }}=\\"{{ $label.Value -}}\\"\\n {{- end }}\\n {{- end -}}\\n )\\n {{- end }}"}]}',
'{"name":"Dummy-Channel","slack_configs":[{"api_url":"https://discord.com/api/webhooks/dummy_webhook_id/dummy_webhook_token/slack","channel":"#dummy_channel","send_resolved":true,"text":"{{ range .Alerts -}}\\n *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} dummy_summary\\n\\n *Summary:* {{ .Annotations.summary }}\\n *Description:* {{ .Annotations.description }}\\n\\n *Details:*\\n {{ range .Labels.SortedPairs }}{{- if or (eq .Name \\"ruleId\\") (eq .Name \\"ruleSource\\") }}{{ continue }}{{ end -}} • *{{ .Name }}:* {{ .Value }}\\n {{ end }}\\n {{ end }}","title":"[{{ .Status | toUpper }}{{ if eq .Status \\"firing\\" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\\n {{- if gt (len .CommonLabels) (len .GroupLabels) -}}\\n {{\\" \\"}}(\\n {{- with .CommonLabels.Remove .GroupLabels.Names }}\\n {{- $first := true -}}\\n {{- range $label := .SortedPairs -}}\\n {{- if or (eq $label.Name \\"ruleId\\") (eq $label.Name \\"ruleSource\\") }}{{ continue }}{{ end -}}\\n {{- if not $first }}, {{ end -}}\\n {{- $label.Name }}=\\"{{ $label.Value -}}\\"\\n {{- $first = false -}}\\n {{- end }}\\n {{- end -}}\\n )\\n {{- end }}"}]}',
},
];
@@ -16,20 +16,20 @@ export const editAlertChannelInitialValue = {
channel: '#dummy_channel',
send_resolved: true,
text:
'{{ range .Alerts -}}\n *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} dummy_summary\n\n *Summary:* {{ .Annotations.summary }}\n *Description:* {{ .Annotations.description }}\n\n *Details:*\n {{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}\n {{ end }}\n {{ end }}',
'{{ range .Alerts -}}\n *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} dummy_summary\n\n *Summary:* {{ .Annotations.summary }}\n *Description:* {{ .Annotations.description }}\n\n *Details:*\n {{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} • *{{ .Name }}:* {{ .Value }}\n {{ end }}\n {{ end }}',
title:
'[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\n {{- if gt (len .CommonLabels) (len .GroupLabels) -}}\n {{" "}}(\n {{- with .CommonLabels.Remove .GroupLabels.Names }}\n {{- range $index, $label := .SortedPairs -}}\n {{ if $index }}, {{ end }}\n {{- $label.Name }}="{{ $label.Value -}}"\n {{- end }}\n {{- end -}}\n )\n {{- end }}',
'[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\n {{- if gt (len .CommonLabels) (len .GroupLabels) -}}\n {{" "}}(\n {{- with .CommonLabels.Remove .GroupLabels.Names }}\n {{- $first := true -}}\n {{- range $label := .SortedPairs -}}\n {{- if or (eq $label.Name "ruleId") (eq $label.Name "ruleSource") }}{{ continue }}{{ end -}}\n {{- if not $first }}, {{ end -}}\n {{- $label.Name }}="{{ $label.Value -}}"\n {{- $first = false -}}\n {{- end }}\n {{- end -}}\n )\n {{- end }}',
type: 'slack',
name: 'Dummy-Channel',
};
export const slackTitleDefaultValue = `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} {{- if gt (len .CommonLabels) (len .GroupLabels) -}} {{" "}}( {{- with .CommonLabels.Remove .GroupLabels.Names }} {{- range $index, $label := .SortedPairs -}} {{ if $index }}, {{ end }} {{- $label.Name }}="{{ $label.Value -}}" {{- end }} {{- end -}} ) {{- end }}`;
export const slackTitleDefaultValue = `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} {{- if gt (len .CommonLabels) (len .GroupLabels) -}} {{" "}}( {{- with .CommonLabels.Remove .GroupLabels.Names }} {{- $first := true -}} {{- range $label := .SortedPairs -}} {{- if or (eq $label.Name "ruleId") (eq $label.Name "ruleSource") }}{{ continue }}{{ end -}} {{- if not $first }}, {{ end -}} {{- $label.Name }}="{{ $label.Value -}}" {{- $first = false -}} {{- end }} {{- end -}} ) {{- end }}`;
export const slackDescriptionDefaultValue = `{{ range .Alerts -}} *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} *Summary:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} *RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}} *RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}} *Details:* {{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }} {{ end }} {{ end }}`;
export const slackDescriptionDefaultValue = `{{ range .Alerts -}} *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} *Summary:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} *RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}} *RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}} *Details:* {{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} • *{{ .Name }}:* {{ .Value }} {{ end }} {{ end }}`;
export const editSlackDescriptionDefaultValue = `{{ range .Alerts -}} *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} dummy_summary *Summary:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} *Details:* {{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }} {{ end }} {{ end }}`;
export const editSlackDescriptionDefaultValue = `{{ range .Alerts -}} *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} dummy_summary *Summary:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} *Details:* {{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} • *{{ .Name }}:* {{ .Value }} {{ end }} {{ end }}`;
export const pagerDutyDescriptionDefaultVaule = `{{ if gt (len .Alerts.Firing) 0 -}} Alerts Firing: {{ range .Alerts.Firing }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }} {{ if gt (len .Alerts.Resolved) 0 -}} Alerts Resolved: {{ range .Alerts.Resolved }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }}`;
export const pagerDutyDescriptionDefaultVaule = `{{ if gt (len .Alerts.Firing) 0 -}} Alerts Firing: {{ range .Alerts.Firing }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }} {{ if gt (len .Alerts.Resolved) 0 -}} Alerts Resolved: {{ range .Alerts.Resolved }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }}`;
export const pagerDutyAdditionalDetailsDefaultValue = JSON.stringify({
firing: `{{ .Alerts.Firing | toJson }}`,
@@ -40,7 +40,7 @@ export const pagerDutyAdditionalDetailsDefaultValue = JSON.stringify({
export const opsGenieMessageDefaultValue = `{{ .CommonLabels.alertname }}`;
export const opsGenieDescriptionDefaultValue = `{{ if gt (len .Alerts.Firing) 0 -}} Alerts Firing: {{ range .Alerts.Firing }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }} {{ if gt (len .Alerts.Resolved) 0 -}} Alerts Resolved: {{ range .Alerts.Resolved }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }}`;
export const opsGenieDescriptionDefaultValue = `{{ if gt (len .Alerts.Firing) 0 -}} Alerts Firing: {{ range .Alerts.Firing }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }} {{ if gt (len .Alerts.Resolved) 0 -}} Alerts Resolved: {{ range .Alerts.Resolved }} - Message: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }} {{ end }} Annotations: {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ end }} Source: {{ .GeneratorURL }} {{ end }} {{- end }}`;
export const opsGeniePriorityDefaultValue =
'{{ if eq (index .Alerts 0).Labels.severity "critical" }}P1{{ else if eq (index .Alerts 0).Labels.severity "warning" }}P2{{ else if eq (index .Alerts 0).Labels.severity "info" }}P3{{ else }}P4{{ end }}';

View File

@@ -195,6 +195,7 @@ func NewSQLMigrationProviderFactories(
sqlmigration.NewDeprecateAPIKeyFactory(sqlstore, sqlschema),
sqlmigration.NewServiceAccountAuthzactory(sqlstore),
sqlmigration.NewDropUserDeletedAtFactory(sqlstore, sqlschema),
sqlmigration.NewChannelTemplatesMigratorFactory(sqlstore),
)
}

View File

@@ -0,0 +1,273 @@
package sqlmigration
import (
"context"
"crypto/md5"
_ "embed"
"encoding/json"
"fmt"
"log/slog"
"strings"
"time"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/prometheus/alertmanager/config"
"github.com/uptrace/bun"
"github.com/uptrace/bun/migrate"
)
//go:embed templates/old_slack_text.tmpl
var oldSlackTextTemplate string
//go:embed templates/new_slack_text.tmpl
var newSlackTextTemplate string
//go:embed templates/old_slack_title.tmpl
var oldSlackTitleTemplate string
//go:embed templates/new_slack_title.tmpl
var newSlackTitleTemplate string
//go:embed templates/old_pagerduty_description.tmpl
var oldPagerdutyDescriptionTemplate string
//go:embed templates/new_pagerduty_description.tmpl
var newPagerdutyDescriptionTemplate string
//go:embed templates/old_opsgenie_description.tmpl
var oldOpsgenieDescriptionTemplate string
//go:embed templates/new_opsgenie_description.tmpl
var newOpsgenieDescriptionTemplate string
//go:embed templates/old_email_html.tmpl
var oldEmailHTMLTemplate string
//go:embed templates/new_email_html.tmpl
var newEmailHTMLTemplate string
type notificationChannelRow struct {
bun.BaseModel `bun:"table:notification_channel"`
ID string `bun:"id,pk"`
CreatedAt time.Time `bun:"created_at"`
UpdatedAt time.Time `bun:"updated_at"`
Name string `bun:"name"`
Type string `bun:"type"`
Data string `bun:"data"`
OrgID string `bun:"org_id"`
}
type alertmanagerConfigRow struct {
bun.BaseModel `bun:"table:alertmanager_config"`
ID string `bun:"id,pk"`
CreatedAt time.Time `bun:"created_at"`
UpdatedAt time.Time `bun:"updated_at"`
Config string `bun:"config"`
Hash string `bun:"hash"`
OrgID string `bun:"org_id"`
}
func computeConfigHash(raw string) string {
sum := md5.Sum([]byte(raw))
return fmt.Sprintf("%x", sum)
}
func normalizeTemplate(s string) string {
return strings.TrimSpace(s)
}
type migrateDefaultChannelTemplates struct {
sqlstore sqlstore.SQLStore
logger *slog.Logger
}
func NewChannelTemplatesMigratorFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
return factory.NewProviderFactory(
factory.MustNewName("update_channel_templates"),
func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
return &migrateDefaultChannelTemplates{
sqlstore: sqlstore,
logger: ps.Logger,
}, nil
},
)
}
func (m *migrateDefaultChannelTemplates) Register(migrations *migrate.Migrations) error {
return migrations.Register(m.Up, m.Down)
}
func (m *migrateDefaultChannelTemplates) Down(context.Context, *bun.DB) error { return nil }
// patchReceiver walks the receiver's *Configs slices and performs exact-match
// substitution on template fields. Returns true if any field was modified.
func patchReceiver(receiver *config.Receiver) bool {
changed := false
for _, cfg := range receiver.SlackConfigs {
if cfg == nil {
continue
}
if normalizeTemplate(cfg.Title) == normalizeTemplate(oldSlackTitleTemplate) {
cfg.Title = newSlackTitleTemplate
changed = true
}
if normalizeTemplate(cfg.Text) == normalizeTemplate(oldSlackTextTemplate) {
cfg.Text = newSlackTextTemplate
changed = true
}
}
for _, cfg := range receiver.MSTeamsV2Configs {
if cfg == nil {
continue
}
if normalizeTemplate(cfg.Title) == normalizeTemplate(oldSlackTitleTemplate) {
cfg.Title = newSlackTitleTemplate
changed = true
}
if normalizeTemplate(cfg.Text) == normalizeTemplate(oldSlackTextTemplate) {
cfg.Text = newSlackTextTemplate
changed = true
}
}
for _, cfg := range receiver.PagerdutyConfigs {
if cfg == nil {
continue
}
if normalizeTemplate(cfg.Description) == normalizeTemplate(oldPagerdutyDescriptionTemplate) {
cfg.Description = newPagerdutyDescriptionTemplate
changed = true
}
}
for _, cfg := range receiver.OpsGenieConfigs {
if cfg == nil {
continue
}
if normalizeTemplate(cfg.Description) == normalizeTemplate(oldOpsgenieDescriptionTemplate) {
cfg.Description = newOpsgenieDescriptionTemplate
changed = true
}
}
for _, cfg := range receiver.EmailConfigs {
if cfg == nil {
continue
}
if normalizeTemplate(cfg.HTML) == normalizeTemplate(oldEmailHTMLTemplate) {
cfg.HTML = newEmailHTMLTemplate
changed = true
}
}
return changed
}
func (m *migrateDefaultChannelTemplates) Up(ctx context.Context, db *bun.DB) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
// Rewrite notification_channel rows that match the old default template
var channels []*notificationChannelRow
if err := tx.NewSelect().Model(&channels).Scan(ctx); err != nil {
return err
}
for _, channel := range channels {
var receiver config.Receiver
if err := json.Unmarshal([]byte(channel.Data), &receiver); err != nil {
m.logger.WarnContext(ctx, "skipping notification_channel update: failed to unmarshal data",
slog.String("id", channel.ID), slog.String("name", channel.Name), slog.String("org_id", channel.OrgID), slog.Any("error", err))
continue
}
if !patchReceiver(&receiver) {
m.logger.InfoContext(ctx, "notification_channel template modified, skipping",
slog.String("id", channel.ID), slog.String("name", channel.Name), slog.String("org_id", channel.OrgID))
continue
}
data, err := json.Marshal(receiver)
if err != nil {
return err
}
channel.Data = string(data)
channel.UpdatedAt = time.Now().UTC()
if _, err := tx.NewUpdate().
Model(channel).
Set("data = ?", channel.Data).
Set("updated_at = ?", channel.UpdatedAt).
WherePK().
Exec(ctx); err != nil {
return err
}
m.logger.InfoContext(ctx, "patched notification_channel",
slog.String("id", channel.ID), slog.String("name", channel.Name), slog.String("org_id", channel.OrgID))
}
// Update the embedded receivers in alertmanager_config
var configs []*alertmanagerConfigRow
if err := tx.NewSelect().Model(&configs).Scan(ctx); err != nil {
return err
}
for _, row := range configs {
var alertmanagerConfig config.Config
if err := json.Unmarshal([]byte(row.Config), &alertmanagerConfig); err != nil {
m.logger.WarnContext(ctx, "skipping alertmanager_config: failed to unmarshal config",
slog.String("id", row.ID), slog.String("org_id", row.OrgID), slog.Any("error", err))
continue
}
changed := false
for i := range alertmanagerConfig.Receivers {
if patchReceiver(&alertmanagerConfig.Receivers[i]) {
changed = true
}
}
if !changed {
m.logger.InfoContext(ctx, "alertmanager_config template up-to-date, skipping",
slog.String("id", row.ID), slog.String("org_id", row.OrgID))
continue
}
rawConfig, err := json.Marshal(&alertmanagerConfig)
if err != nil {
return err
}
row.Config = string(rawConfig)
row.Hash = computeConfigHash(row.Config)
row.UpdatedAt = time.Now().UTC()
if _, err := tx.NewUpdate().
Model(row).
Set("config = ?", row.Config).
Set("hash = ?", row.Hash).
Set("updated_at = ?", row.UpdatedAt).
WherePK().
Exec(ctx); err != nil {
return err
}
m.logger.InfoContext(ctx, "patched alertmanager_config",
slog.String("id", row.ID), slog.String("org_id", row.OrgID))
}
return tx.Commit()
}

View File

@@ -0,0 +1,392 @@
<!--
Credits: https://github.com/mailgun/transactional-email-templates
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{{ template "__subject" . }}</title>
<style>
/* -------------------------------------
GLOBAL
A very basic CSS reset
------------------------------------- */
* {
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
box-sizing: border-box;
font-size: 14px;
}
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
width: 100% !important;
height: 100%;
line-height: 1.6em;
/* 1.6em * 14px = 22.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 22px;*/
}
/* Let's make sure all tables have defaults */
table td {
vertical-align: top;
}
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
body {
background-color: #f6f6f6;
}
.body-wrap {
background-color: #f6f6f6;
width: 100%;
}
.container {
display: block !important;
max-width: 600px !important;
margin: 0 auto !important;
/* makes it centered */
clear: both !important;
}
.content {
max-width: 600px;
margin: 0 auto;
display: block;
padding: 20px;
}
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background-color: #fff;
border: 1px solid #e9e9e9;
border-radius: 3px;
}
.content-wrap {
padding: 30px;
}
.content-block {
padding: 0 0 20px;
}
.header {
width: 100%;
margin-bottom: 20px;
}
.footer {
width: 100%;
clear: both;
color: #999;
padding: 20px;
}
.footer p,
.footer a,
.footer td {
color: #999;
font-size: 12px;
}
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3 {
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
color: #000;
margin: 40px 0 0;
line-height: 1.2em;
font-weight: 400;
}
h1 {
font-size: 32px;
font-weight: 500;
/* 1.2em * 32px = 38.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 38px;*/
}
h2 {
font-size: 24px;
/* 1.2em * 24px = 28.8px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 29px;*/
}
h3 {
font-size: 18px;
/* 1.2em * 18px = 21.6px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 22px;*/
}
h4 {
font-size: 14px;
font-weight: 600;
}
p,
ul,
ol {
margin-bottom: 10px;
font-weight: normal;
}
p li,
ul li,
ol li {
margin-left: 5px;
list-style-position: inside;
}
/* -------------------------------------
LINKS & BUTTONS
------------------------------------- */
a {
color: #348eda;
text-decoration: underline;
}
.btn-primary {
text-decoration: none;
color: #FFF;
background-color: #348eda;
border: solid #348eda;
border-width: 10px 20px;
line-height: 2em;
/* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 28px;*/
font-weight: bold;
text-align: center;
cursor: pointer;
display: inline-block;
border-radius: 5px;
text-transform: capitalize;
}
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.aligncenter {
text-align: center;
}
.alignright {
text-align: right;
}
.alignleft {
text-align: left;
}
.clear {
clear: both;
}
/* -------------------------------------
ALERTS
Change the class depending on warning email, good email or bad email
------------------------------------- */
.alert {
font-size: 16px;
color: #fff;
font-weight: 500;
padding: 20px;
text-align: center;
border-radius: 3px 3px 0 0;
}
.alert a {
color: #fff;
text-decoration: none;
font-weight: 500;
font-size: 16px;
}
.alert.alert-warning {
background-color: #E6522C;
}
.alert.alert-bad {
background-color: #D0021B;
}
.alert.alert-good {
background-color: #68B90F;
}
/* -------------------------------------
INVOICE
Styles for the billing table
------------------------------------- */
.invoice {
margin: 40px auto;
text-align: left;
width: 80%;
}
.invoice td {
padding: 5px 0;
}
.invoice .invoice-items {
width: 100%;
}
.invoice .invoice-items td {
border-top: #eee 1px solid;
}
.invoice .invoice-items .total td {
border-top: 2px solid #333;
border-bottom: 2px solid #333;
font-weight: 700;
}
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 640px) {
body {
padding: 0 !important;
}
h1,
h2,
h3,
h4 {
font-weight: 800 !important;
margin: 20px 0 5px !important;
}
h1 {
font-size: 22px !important;
}
h2 {
font-size: 18px !important;
}
h3 {
font-size: 16px !important;
}
.container {
padding: 0 !important;
width: 100% !important;
}
.content {
padding: 0 !important;
}
.content-wrap {
padding: 10px !important;
}
.invoice {
width: 100% !important;
}
}
</style>
</head>
<body itemscope itemtype="http://schema.org/EmailMessage">
<table class="body-wrap">
<tr>
<td></td>
<td class="container" width="600">
<div class="content">
<table class="main" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ if gt (len .Alerts.Firing) 0 }}
<td class="alert alert-warning">
{{ else }}
<td class="alert alert-good">
{{ end }}
{{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }}
{{ end }}
</td>
</tr>
<tr>
<td class="content-wrap">
<table width="100%" cellpadding="0" cellspacing="0">
{{ if gt (len .Alerts.Firing) 0 }}
<tr>
<td class="content-block">
<strong>[{{ .Alerts.Firing | len }}] Firing</strong>
</td>
</tr>
{{ end }}
{{ range .Alerts.Firing }}
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />
</td>
</tr>
{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
{{ if gt (len .Alerts.Firing) 0 }}
<tr>
<td class="content-block">
<br />
<hr />
<br />
</td>
</tr>
{{ end }}
<tr>
<td class="content-block">
<strong>[{{ .Alerts.Resolved | len }}] Resolved</strong>
</td>
</tr>
{{ end }}
{{ range .Alerts.Resolved }}
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />
</td>
</tr>
{{ end }}
</table>
</td>
</tr>
</table>
</div>
</td>
<td></td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,22 @@
{{ if gt (len .Alerts.Firing) 0 -}}
Alerts Firing:
{{ range .Alerts.Firing }}
- Message: {{ .Annotations.description }}
Labels:
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }}
{{ end }} Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Source: {{ .GeneratorURL }}
{{ end }}
{{- end }}
{{ if gt (len .Alerts.Resolved) 0 -}}
Alerts Resolved:
{{ range .Alerts.Resolved }}
- Message: {{ .Annotations.description }}
Labels:
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} - {{ .Name }} = {{ .Value }}
{{ end }} Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Source: {{ .GeneratorURL }}
{{ end }}
{{- end }}

View File

@@ -0,0 +1,14 @@
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
{{" "}}(
{{- with .CommonLabels.Remove .GroupLabels.Names }}
{{- $first := true -}}
{{- range $label := .SortedPairs -}}
{{- if or (eq $label.Name "ruleId") (eq $label.Name "ruleSource") }}{{ continue }}{{ end -}}
{{- if not $first }}, {{ end -}}
{{- $label.Name }}="{{ $label.Value -}}"
{{- $first = false -}}
{{- end }}
{{- end -}}
)
{{- end }}

View File

@@ -0,0 +1,12 @@
{{ range .Alerts -}}
*Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
*Summary:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}}
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}}
*Details:*
{{ range .Labels.SortedPairs }}{{- if or (eq .Name "ruleId") (eq .Name "ruleSource") }}{{ continue }}{{ end -}} • *{{ .Name }}:* {{ .Value }}
{{ end }}
{{ end }}

View File

@@ -0,0 +1,14 @@
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
{{" "}}(
{{- with .CommonLabels.Remove .GroupLabels.Names }}
{{- $first := true -}}
{{- range $label := .SortedPairs -}}
{{- if or (eq $label.Name "ruleId") (eq $label.Name "ruleSource") }}{{ continue }}{{ end -}}
{{- if not $first }}, {{ end -}}
{{- $label.Name }}="{{ $label.Value -}}"
{{- $first = false -}}
{{- end }}
{{- end -}}
)
{{- end }}

View File

@@ -0,0 +1,392 @@
<!--
Credits: https://github.com/mailgun/transactional-email-templates
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{{ template "__subject" . }}</title>
<style>
/* -------------------------------------
GLOBAL
A very basic CSS reset
------------------------------------- */
* {
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
box-sizing: border-box;
font-size: 14px;
}
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
width: 100% !important;
height: 100%;
line-height: 1.6em;
/* 1.6em * 14px = 22.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 22px;*/
}
/* Let's make sure all tables have defaults */
table td {
vertical-align: top;
}
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
body {
background-color: #f6f6f6;
}
.body-wrap {
background-color: #f6f6f6;
width: 100%;
}
.container {
display: block !important;
max-width: 600px !important;
margin: 0 auto !important;
/* makes it centered */
clear: both !important;
}
.content {
max-width: 600px;
margin: 0 auto;
display: block;
padding: 20px;
}
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background-color: #fff;
border: 1px solid #e9e9e9;
border-radius: 3px;
}
.content-wrap {
padding: 30px;
}
.content-block {
padding: 0 0 20px;
}
.header {
width: 100%;
margin-bottom: 20px;
}
.footer {
width: 100%;
clear: both;
color: #999;
padding: 20px;
}
.footer p,
.footer a,
.footer td {
color: #999;
font-size: 12px;
}
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3 {
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
color: #000;
margin: 40px 0 0;
line-height: 1.2em;
font-weight: 400;
}
h1 {
font-size: 32px;
font-weight: 500;
/* 1.2em * 32px = 38.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 38px;*/
}
h2 {
font-size: 24px;
/* 1.2em * 24px = 28.8px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 29px;*/
}
h3 {
font-size: 18px;
/* 1.2em * 18px = 21.6px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 22px;*/
}
h4 {
font-size: 14px;
font-weight: 600;
}
p,
ul,
ol {
margin-bottom: 10px;
font-weight: normal;
}
p li,
ul li,
ol li {
margin-left: 5px;
list-style-position: inside;
}
/* -------------------------------------
LINKS & BUTTONS
------------------------------------- */
a {
color: #348eda;
text-decoration: underline;
}
.btn-primary {
text-decoration: none;
color: #FFF;
background-color: #348eda;
border: solid #348eda;
border-width: 10px 20px;
line-height: 2em;
/* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 28px;*/
font-weight: bold;
text-align: center;
cursor: pointer;
display: inline-block;
border-radius: 5px;
text-transform: capitalize;
}
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.aligncenter {
text-align: center;
}
.alignright {
text-align: right;
}
.alignleft {
text-align: left;
}
.clear {
clear: both;
}
/* -------------------------------------
ALERTS
Change the class depending on warning email, good email or bad email
------------------------------------- */
.alert {
font-size: 16px;
color: #fff;
font-weight: 500;
padding: 20px;
text-align: center;
border-radius: 3px 3px 0 0;
}
.alert a {
color: #fff;
text-decoration: none;
font-weight: 500;
font-size: 16px;
}
.alert.alert-warning {
background-color: #E6522C;
}
.alert.alert-bad {
background-color: #D0021B;
}
.alert.alert-good {
background-color: #68B90F;
}
/* -------------------------------------
INVOICE
Styles for the billing table
------------------------------------- */
.invoice {
margin: 40px auto;
text-align: left;
width: 80%;
}
.invoice td {
padding: 5px 0;
}
.invoice .invoice-items {
width: 100%;
}
.invoice .invoice-items td {
border-top: #eee 1px solid;
}
.invoice .invoice-items .total td {
border-top: 2px solid #333;
border-bottom: 2px solid #333;
font-weight: 700;
}
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 640px) {
body {
padding: 0 !important;
}
h1,
h2,
h3,
h4 {
font-weight: 800 !important;
margin: 20px 0 5px !important;
}
h1 {
font-size: 22px !important;
}
h2 {
font-size: 18px !important;
}
h3 {
font-size: 16px !important;
}
.container {
padding: 0 !important;
width: 100% !important;
}
.content {
padding: 0 !important;
}
.content-wrap {
padding: 10px !important;
}
.invoice {
width: 100% !important;
}
}
</style>
</head>
<body itemscope itemtype="http://schema.org/EmailMessage">
<table class="body-wrap">
<tr>
<td></td>
<td class="container" width="600">
<div class="content">
<table class="main" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ if gt (len .Alerts.Firing) 0 }}
<td class="alert alert-warning">
{{ else }}
<td class="alert alert-good">
{{ end }}
{{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }}
{{ end }}
</td>
</tr>
<tr>
<td class="content-wrap">
<table width="100%" cellpadding="0" cellspacing="0">
{{ if gt (len .Alerts.Firing) 0 }}
<tr>
<td class="content-block">
<strong>[{{ .Alerts.Firing | len }}] Firing</strong>
</td>
</tr>
{{ end }}
{{ range .Alerts.Firing }}
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />
</td>
</tr>
{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
{{ if gt (len .Alerts.Firing) 0 }}
<tr>
<td class="content-block">
<br />
<hr />
<br />
</td>
</tr>
{{ end }}
<tr>
<td class="content-block">
<strong>[{{ .Alerts.Resolved | len }}] Resolved</strong>
</td>
</tr>
{{ end }}
{{ range .Alerts.Resolved }}
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />
</td>
</tr>
{{ end }}
</table>
</td>
</tr>
</table>
</div>
</td>
<td></td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,22 @@
{{ if gt (len .Alerts.Firing) 0 -}}
Alerts Firing:
{{ range .Alerts.Firing }}
- Message: {{ .Annotations.description }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Source: {{ .GeneratorURL }}
{{ end }}
{{- end }}
{{ if gt (len .Alerts.Resolved) 0 -}}
Alerts Resolved:
{{ range .Alerts.Resolved }}
- Message: {{ .Annotations.description }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} Source: {{ .GeneratorURL }}
{{ end }}
{{- end }}

View File

@@ -0,0 +1,11 @@
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
{{" "}}(
{{- with .CommonLabels.Remove .GroupLabels.Names }}
{{- range $index, $label := .SortedPairs -}}
{{ if $index }}, {{ end }}
{{- $label.Name }}="{{ $label.Value -}}"
{{- end }}
{{- end -}}
)
{{- end }}

View File

@@ -0,0 +1,12 @@
{{ range .Alerts -}}
*Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
*Summary:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}}
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}}
*Details:*
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}
{{ end }}
{{ end }}

View File

@@ -0,0 +1,11 @@
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
{{" "}}(
{{- with .CommonLabels.Remove .GroupLabels.Names }}
{{- range $index, $label := .SortedPairs -}}
{{ if $index }}, {{ end }}
{{- $label.Name }}="{{ $label.Value -}}"
{{- end }}
{{- end -}}
)
{{- end }}