Alerting: update email template (#34205)

This commit is contained in:
Domas 2021-05-19 19:58:31 +03:00 committed by GitHub
parent 5dca9fd4d8
commit 54c33c6cdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 528 additions and 287 deletions

View File

@ -1,150 +1,218 @@
[[Subject .Subject "[[.Title]]"]]
[[ define "alert" ]]
[[ if gt (len .Annotations.SortedPairs) 0 ]]
<tr>
<td colspan="2" class="annotations">
[[ range .Annotations.SortedPairs ]]
<p>[[ .Name ]]: [[ .Value ]]</p>
[[ end ]]
</td>
</tr>
[[ end ]]
<tr>
<td valign="top" class="labels-heading">Labels:</td>
<td>
<ul class="labels-list">
[[ range .Labels.SortedPairs ]]<li>[[ .Name ]]: [[ .Value ]]</li>[[ end ]]
</ul>
</td>
</tr>
<tr>
<td colspan="2">
[[ if .SilenceURL ]]
<a
href="[[ .SilenceURL ]]"
class="button"
>
<img
alt=""
height="14"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAF1SURBVHgBrZLNTsJAEMf/2wYTPfEI9Q1qvBgjsEl5gB498gj1SDHpEKUckaMn4eiNFyAp4cJN30CPHrn5BV1noTWlFD3oJNuZ3Z39zUcH+A9pUffJpzAoOO/xuvvprZHooYCgLISoa7FyFSCbdOXuApj6M43GUVXWhYZUpKP3k1NZ14+kAOYChleVjnssK/ezKHrLAkQuZWIVKCgKyW+n55cUciZClzLoUPOiqISVlPB+s6ZulnNN/ihG3GfT84jKOwGvMO0krVEewoBoHeTA2glIhRuno7WzkH0Yc633sCz/CoiVshdYRGxGKYSo9ahgHnEPoqzvdxP1b/sEHtgsF2e12ditDD6gPE59XgIO8ytfTlbM1DiTzjk7WQvEYgllZ5fSXeE7vnfTOdkC1GqOBSFO2Mk22JG1TB4l+1VzX/IQgQJpBaHHsB6bA47+rNOHUI1O4A/zw2YWAaaT8UyPNkdcZ6JUs0P+7eouGXuO3WC7j7/KF29iokiLUdaFAAAAAElFTkSuQmCC"
class="button-img"
width="14"
/>
Silence
</a>
[[ end ]]
[[ if .Annotations.runbook_url ]]
<a
href="[[ .Annotations.runbook_url ]]"
class="button"
>
<img
class="button-img"
alt=""
height="14"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADaSURBVHgBrVNLDoIwFJwq0bjTG+ARvIGeQI/ADdSVARY8oki8jZxEbyDewLUfahttAoEWDM7m9bVvJtN0CrQEyzdENLyjt2RgtoFzjcgl1XTyJw/0T4K8ghmBH8Zz1VhqsaHtVBT7CYwP5KY6tk+xw3k2Kgl0YdmyKrIYvEjB73EqbI+rBC1owMDXGdjws0aqm9MK7Mg7ogE6aInWAqUreLQPanJgdtAgB2YHIgeTuhxUOlBPNcBvKPwFT4RHbnDgLOpNw3EEaxEFblJwIPECZhw8MZAlQkX+C95zCzqWDYrK2AAAAABJRU5ErkJggg=="
width="14"
/>
View Runbook
</a>
[[ end ]]
[[ if .DashboardURL]]
<a
href="[[ .DashboardURL ]]"
class="button"
>
<img
alt=""
height="14"
class="button-img"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAE9SURBVHgBrVLNWcMwDH220/bjhEdIR2AC6ASMQEeAU6G9+FLycQojdAUmgE7ACGSEcEybRkj5cUKbnFp9X2JZ8nuypQecaUp+Kxf98BIeJ3cwNxqZDRB89mCTtXuZBvVGwBsCtiUrkYVS8RgHmzeVFB4Lwm9d9Y6XB/EbAhCK7atbbcRfuCjkRNwtt6fs4825RPyli4SkJNA40/wNFCm7cC6sdnnYSdVmQs6Xnla4JjoikDcHmMQYrCSNrI434C7BrA/EXf6Slad0kieY1BPsUKQahcWA5cj7otYTjKG/+/oplQU8oAMx1aKInkbAVL6BJ818ns82wVYHilLnlon4rIOTGeTIWHleB6mq4xfUAfTts1tXPh1YyubfwZGa3HO+K+WWgMeacHBuoOc1m7cr6HSPcvbvpnNhweAS9gdSqGSnoVA2cQAAAABJRU5ErkJggg=="
width="14"
/>
Go to Dashboard
</a>
[[ end ]]
[[ if .PanelURL]]
<a
href="[[ .PanelURL ]]"
class="button"
>
<img
alt=""
class="button-img"
height="14"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAE9SURBVHgBrVLNWcMwDH220/bjhEdIR2AC6ASMQEeAU6G9+FLycQojdAUmgE7ACGSEcEybRkj5cUKbnFp9X2JZ8nuypQecaUp+Kxf98BIeJ3cwNxqZDRB89mCTtXuZBvVGwBsCtiUrkYVS8RgHmzeVFB4Lwm9d9Y6XB/EbAhCK7atbbcRfuCjkRNwtt6fs4825RPyli4SkJNA40/wNFCm7cC6sdnnYSdVmQs6Xnla4JjoikDcHmMQYrCSNrI434C7BrA/EXf6Slad0kieY1BPsUKQahcWA5cj7otYTjKG/+/oplQU8oAMx1aKInkbAVL6BJ818ns82wVYHilLnlon4rIOTGeTIWHleB6mq4xfUAfTts1tXPh1YyubfwZGa3HO+K+WWgMeacHBuoOc1m7cr6HSPcvbvpnNhweAS9gdSqGSnoVA2cQAAAABJRU5ErkJggg=="
width="14"
/>
Go to Panel
</a>
[[ end ]]
[[ if gt (len .GeneratorURL) 0 ]]<a href="[[ .GeneratorURL ]]" class="button">Source</a>[[ end ]]
</td>
</tr>
<tr>
<td colspan="2">
<div style="height: 24px"></div>
<div style="background: #c7d0d9; height: 1px"></div>
<div style="height: 24px"></div>
</td>
</tr>
[[ end ]]
[[ if gt (len .Message) 0 ]]
[[ .Message ]]
[[ else ]]
<style>
.alert-warning {
background-color: #E6522C;
.button {
background: #f1f5f9;
border: 1px solid #c7d0d9;
border-radius: 2px;
color: #464c54;
display: inline-block;
font-size: 12px;
font-weight: bold;
margin: 0 10px 0 0;
padding: 5px 9px;
}
.alert-good {
background-color: #68B90F;
.annotations {
font-size: 14px;
padding: 24px 0 12px 0;
}
.labels-heading {
font-size: 14px;
font-weight: bold;
vertical-align: top;
}
.labels-list {
font-size: 14px;
vertical-align: top;
}
.section-heading {
color: #2c3235;
font-size: 22px;
font-weight: bold;
padding: 0 0 32px 0;
}
.alert-label {
font-size: 16px;
font-weight: bold;
padding: 0 0 0 12px;
text-decoration: underline;
}
.status-tag {
color: #ffffff;
padding: 4px 8px;
text-align: center;
width: 68px
}
.status-firing {
background: #e02f44;
}
.status-resolved {
background: #464c54;
}
.button-img {
height: 14px;
margin: 0 5px 0 0;
vertical-align: sub;
width: 14px;
}
</style>
<table class="row">
<tr>
<td class="wrapper last">
<table class="twelve columns">
<td class="twelve">
<table>
[[ if gt (len .Alerts.Firing) 0 ]]
<tr>
<td class="center">
[[ if gt (len .Alerts.Firing) 0 ]]
<h3 style="color: #E6522C; font-weight: bold; font-style: italic;">[[.Title]]</h3>
[[ else ]]
<h3 style="font-weight: bold; font-style: italic;">[[.Title]]</h3>
[[ end ]]
<td colspan="2" class="section-heading">
Firing: [[ .Alerts.Firing | len ]] alert[[ if gt (len .Alerts.Firing) 1 ]]s[[ end ]][[ if gt (len .GroupLabels.SortedPairs) 1 ]] for
[[ range .GroupLabels.SortedPairs ]]
[[ .Name ]]=[[ .Value ]]
[[ end ]][[ end ]]
</td>
</tr>
</table>
</td>
</tr>
</table>
<table class="row">
<tr>
[[ if gt (len .Alerts.Firing) 0 ]]
<td class="alert-warning twelve column last">
[[ .Alerts | len ]] alert[[ if gt (len .Alerts) 1 ]]s[[ end ]] for
[[ range .GroupLabels.SortedPairs ]]
[[ .Name ]]=[[ .Value ]]
[[ end ]]
</td>
[[ else ]]
<td class="alert-good twelve column last">
[[ .Alerts | len ]] alert[[ if gt (len .Alerts) 1 ]]s[[ end ]] for
[[ range .GroupLabels.SortedPairs ]]
[[ .Name ]]=[[ .Value ]]
[[ end ]]
</td>
[[ end ]]
</td>
</tr>
<tr>
<td class="last">
<table class="twelve columns">
[[ if gt (len .Alerts.Firing) 0 ]]
<tr>
<td class="twelve last">
<h5 style="font-weight: bold;">([[ .Alerts.Firing | len ]]) Firing</h5>
</td>
</tr>
[[ end ]]
[[ range .Alerts.Firing ]]
<tr>
<td class="four">
<h5 style="font-weight: bold">Labels</h5>
</td>
<td class="eight last">
[[ if gt (len .Annotations) 0 ]]<h5 style="font-weight: bold">Annotations</h5>[[ end ]]
</td>
</tr>
<tr>
<td class="four">
[[ range .Labels.SortedPairs ]][[ .Name ]] = [[ .Value ]]<br />[[ end ]]
<a href="[[ .GeneratorURL ]]">Source</a><br />
</td>
<td class="eight last">
[[ range .Annotations.SortedPairs ]][[ .Name ]] = [[ .Value ]]<br />[[ end ]]
</td>
</tr>
<tr>
<td
class="status-tag status-firing"
width="68"
>
Firing
</td>
<td class="alert-label">
[[ .Labels.alertname ]]
</td>
</tr>
[[ template "alert" . ]]
[[ end ]]
[[ if gt (len .Alerts.Resolved) 0 ]]
[[ if gt (len .Alerts.Firing) 0 ]]
[[ end ]]
[[ if gt (len .Alerts.Resolved) 0 ]]
<tr>
<td class="twelve">
<br />
<hr />
<br />
<td colspan="2" class="section-heading">
Resolved: [[ .Alerts.Resolved | len ]] alert[[ if gt (len .Alerts.Resolved) 1 ]]s[[ end ]][[ if gt (len .GroupLabels.SortedPairs) 1 ]] for
[[ range .GroupLabels.SortedPairs ]]
[[ .Name ]]=[[ .Value ]]
[[ end ]][[ end ]]
</td>
</tr>
[[ end ]]
<tr>
<td class="twelve last">
<h5 style="font-weight: bold;">([[ .Alerts.Resolved | len ]]) Resolved</h5>
</td>
</tr>
[[ end ]]
[[ range .Alerts.Resolved ]]
<tr>
<td class="six">
<h5 style="font-weight: bold">Labels</h5>
</td>
<td class="six last">
[[ if gt (len .Annotations) 0 ]]<h5 style="font-weight: bold">Annotations</h5>[[ end ]]
</td>
</tr>
<tr>
<td class="six">
[[ range .Labels.SortedPairs ]][[ .Name ]] = [[ .Value ]]<br />[[ end ]]
<a href="[[ .GeneratorURL ]]">Source</a><br />
</td>
<td class="six last">
[[ range .Annotations.SortedPairs ]][[ .Name ]] = [[ .Value ]]<br />[[ end ]]
</td>
</tr>
<tr>
<td
class="status-tag status-resolved"
width="68"
>
Resolved
</td>
<td class="alert-label">
[[ .Labels.alertname ]]
</td>
</tr>
[[ template "alert" . ]]
[[ end ]]
[[ end ]]
<tr>
<td colspan="2">
<a href="[[ .AlertPageUrl ]]" class="button">Go to alerts page</a>
</td>
</tr>
</table>
</table>
</td>
</tr>
</table>
<table class="row">
<tr>
<td class="wrapper last">
<table class="twelve columns">
<tr>
<td class="center six">
<table class="better-button" align="center" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" class="better-button" bgcolor="#ff8f2b">
<a href="[[.RuleUrl]]" target="_blank">View your Alert rule</a>
</td>
</tr>
</table>
</td>
<td class="center six">
<table class="better-button" align="center" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" class="better-button-alt" bgcolor="#efefef">
<a href="[[.AlertPageUrl]]" target="_blank"> Go to the Alerts page</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
[[ end ]]

View File

@ -3,6 +3,7 @@ package channels
import (
"context"
"fmt"
"net/url"
"path"
gokit_log "github.com/go-kit/kit/log"
@ -66,12 +67,25 @@ func NewEmailNotifier(model *NotificationChannelConfig, t *template.Template) (*
// Notify sends the alert notification.
func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
// We only need ExternalURL from this template object. This hack should go away with https://github.com/prometheus/alertmanager/pull/2508.
data := notify.GetTemplateData(ctx, &template.Template{ExternalURL: en.tmpl.ExternalURL}, as, gokit_log.NewLogfmtLogger(logging.NewWrapper(en.log)))
data, err := ExtendData(notify.GetTemplateData(ctx, &template.Template{ExternalURL: en.tmpl.ExternalURL}, as, gokit_log.NewLogfmtLogger(logging.NewWrapper(en.log))))
if err != nil {
return false, err
}
var tmplErr error
tmpl := notify.TmplText(en.tmpl, data, &tmplErr)
tmpl := TmplText(en.tmpl, data, &tmplErr)
title := tmpl(`{{ template "default.title" . }}`)
u, err := url.Parse(en.tmpl.ExternalURL.String())
if err != nil {
return false, fmt.Errorf("failed to parse external URL: %w", err)
}
basePath := u.Path
u.Path = path.Join(basePath, "/alerting/list")
ruleURL := u.String()
u.RawQuery = "alertState=firing&view=state"
alertPageURL := u.String()
cmd := &models.SendEmailCommandSync{
SendEmailCommand: models.SendEmailCommand{
Subject: title,
@ -84,8 +98,8 @@ func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
"CommonLabels": data.CommonLabels,
"CommonAnnotations": data.CommonAnnotations,
"ExternalURL": data.ExternalURL,
"RuleUrl": path.Join(en.tmpl.ExternalURL.String(), "/alerting/list"),
"AlertPageUrl": path.Join(en.tmpl.ExternalURL.String(), "/alerting/list?alertState=firing&view=state"),
"RuleUrl": ruleURL,
"AlertPageUrl": alertPageURL,
},
To: en.Addresses,
SingleEmail: en.SingleEmail,

View File

@ -18,7 +18,7 @@ import (
func TestEmailNotifier(t *testing.T) {
tmpl := templateForTests(t)
externalURL, err := url.Parse("http://localhost")
externalURL, err := url.Parse("http://localhost/base")
require.NoError(t, err)
tmpl.ExternalURL = externalURL
@ -67,7 +67,7 @@ func TestEmailNotifier(t *testing.T) {
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "AlwaysFiring", "severity": "warning"},
Annotations: model.LabelSet{"runbook_url": "http://fix.me"},
Annotations: model.LabelSet{"runbook_url": "http://fix.me", "__dashboardUid__": "abc", "__panelId__": "5"},
},
},
}
@ -85,20 +85,23 @@ func TestEmailNotifier(t *testing.T) {
"Title": "[FIRING:1] (AlwaysFiring warning)",
"Message": "[FIRING:1] (AlwaysFiring warning)",
"Status": "firing",
"Alerts": template.Alerts{
template.Alert{
Status: "firing",
Labels: template.KV{"alertname": "AlwaysFiring", "severity": "warning"},
Annotations: template.KV{"runbook_url": "http://fix.me"},
Fingerprint: "15a37193dce72bab",
"Alerts": ExtendedAlerts{
ExtendedAlert{
Status: "firing",
Labels: template.KV{"alertname": "AlwaysFiring", "severity": "warning"},
Annotations: template.KV{"runbook_url": "http://fix.me"},
Fingerprint: "15a37193dce72bab",
SilenceURL: "http://localhost/base/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DAlwaysFiring%2Cseverity%3Dwarning",
DashboardURL: "http://localhost/base/d/abc",
PanelURL: "http://localhost/base/d/abc?viewPanel=5",
},
},
"GroupLabels": template.KV{},
"CommonLabels": template.KV{"alertname": "AlwaysFiring", "severity": "warning"},
"CommonAnnotations": template.KV{"runbook_url": "http://fix.me"},
"ExternalURL": "http://localhost",
"RuleUrl": "http:/localhost/alerting/list",
"AlertPageUrl": "http:/localhost/alerting/list?alertState=firing&view=state",
"ExternalURL": "http://localhost/base",
"RuleUrl": "http://localhost/base/alerting/list",
"AlertPageUrl": "http://localhost/base/alerting/list?alertState=firing&view=state",
},
}, expected)
})

View File

@ -0,0 +1,153 @@
package channels
import (
"fmt"
"net/url"
"path"
"sort"
"strings"
"time"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/common/model"
)
type ExtendedAlert struct {
Status string `json:"status"`
Labels template.KV `json:"labels"`
Annotations template.KV `json:"annotations"`
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
GeneratorURL string `json:"generatorURL"`
Fingerprint string `json:"fingerprint"`
SilenceURL string `json:"silenceURL"`
DashboardURL string `json:"dashboardURL"`
PanelURL string `json:"panelURL"`
}
type ExtendedAlerts []ExtendedAlert
type ExtendedData struct {
Receiver string `json:"receiver"`
Status string `json:"status"`
Alerts ExtendedAlerts `json:"alerts"`
GroupLabels template.KV `json:"groupLabels"`
CommonLabels template.KV `json:"commonLabels"`
CommonAnnotations template.KV `json:"commonAnnotations"`
ExternalURL string `json:"externalURL"`
}
func removePrivateItems(kv template.KV) template.KV {
for key := range kv {
if strings.HasPrefix(key, "__") && strings.HasSuffix(key, "__") {
kv = kv.Remove([]string{key})
}
}
return kv
}
func extendAlert(alert template.Alert, externalURL string) (*ExtendedAlert, error) {
extended := ExtendedAlert{
Status: alert.Status,
Labels: alert.Labels,
Annotations: alert.Annotations,
StartsAt: alert.StartsAt,
EndsAt: alert.EndsAt,
GeneratorURL: alert.GeneratorURL,
Fingerprint: alert.Fingerprint,
}
// fill in some grafana-specific urls
if len(externalURL) > 0 {
u, err := url.Parse(externalURL)
if err != nil {
return nil, fmt.Errorf("failed to parse external URL: %w", err)
}
externalPath := u.Path
dashboardUid := alert.Annotations["__dashboardUid__"]
if len(dashboardUid) > 0 {
u.Path = path.Join(externalPath, "/d/", dashboardUid)
extended.DashboardURL = u.String()
panelId := alert.Annotations["__panelId__"]
if len(panelId) > 0 {
u.RawQuery = "viewPanel=" + panelId
extended.PanelURL = u.String()
}
}
matchers := make([]string, 0)
for key, value := range alert.Labels {
if !(strings.HasPrefix(key, "__") && strings.HasSuffix(key, "__")) {
matchers = append(matchers, key+"="+value)
}
}
sort.Strings(matchers)
u.Path = path.Join(externalPath, "/alerting/silence/new")
u.RawQuery = "alertmanager=grafana&matchers=" + url.QueryEscape(strings.Join(matchers, ","))
extended.SilenceURL = u.String()
}
// remove "private" annotations & labels so they don't show up in the template
extended.Annotations = removePrivateItems(extended.Annotations)
extended.Labels = removePrivateItems(extended.Labels)
return &extended, nil
}
func ExtendData(data *template.Data) (*ExtendedData, error) {
alerts := []ExtendedAlert{}
for _, alert := range data.Alerts {
extendedAlert, err := extendAlert(alert, data.ExternalURL)
if err != nil {
return nil, err
}
alerts = append(alerts, *extendedAlert)
}
extended := &ExtendedData{
Receiver: data.Receiver,
Status: data.Status,
Alerts: alerts,
GroupLabels: data.GroupLabels,
CommonLabels: removePrivateItems(data.CommonLabels),
CommonAnnotations: removePrivateItems(data.CommonAnnotations),
ExternalURL: data.ExternalURL,
}
return extended, nil
}
func TmplText(tmpl *template.Template, data *ExtendedData, err *error) func(string) string {
return func(name string) (s string) {
if *err != nil {
return
}
s, *err = tmpl.ExecuteTextString(name, data)
return s
}
}
// Firing returns the subset of alerts that are firing.
func (as ExtendedAlerts) Firing() []ExtendedAlert {
res := []ExtendedAlert{}
for _, a := range as {
if a.Status == string(model.AlertFiring) {
res = append(res, a)
}
}
return res
}
// Resolved returns the subset of alerts that are resolved.
func (as ExtendedAlerts) Resolved() []ExtendedAlert {
res := []ExtendedAlert{}
for _, a := range as {
if a.Status == string(model.AlertResolved) {
res = append(res, a)
}
}
return res
}

View File

@ -1,5 +1,5 @@
import { Silence, SilenceCreatePayload } from 'app/plugins/datasource/alertmanager/types';
import React, { FC, useState } from 'react';
import React, { FC, useMemo, useState } from 'react';
import { Button, Field, FieldSet, Input, LinkButton, TextArea, useStyles } from '@grafana/ui';
import {
DefaultTimeZone,
@ -9,6 +9,7 @@ import {
addDurationToDate,
dateTime,
isValidDate,
UrlQueryMap,
} from '@grafana/data';
import { useDebounce } from 'react-use';
import { config } from '@grafana/runtime';
@ -23,13 +24,34 @@ import { css, cx } from '@emotion/css';
import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector';
import { makeAMLink } from '../../utils/misc';
import { useCleanup } from 'app/core/hooks/useCleanup';
import { useQueryParams } from 'app/core/hooks/useQueryParams';
import { parseQueryParamMatchers } from '../../utils/matchers';
interface Props {
silence?: Silence;
alertManagerSourceName: string;
}
const getDefaultFormValues = (silence?: Silence): SilenceFormFields => {
const defaultsFromQuery = (queryParams: UrlQueryMap): Partial<SilenceFormFields> => {
const defaults: Partial<SilenceFormFields> = {};
const { matchers, comment } = queryParams;
if (typeof matchers === 'string') {
const formMatchers = parseQueryParamMatchers(matchers);
if (formMatchers.length) {
defaults.matchers = formMatchers;
}
}
if (typeof comment === 'string') {
defaults.comment = comment;
}
return defaults;
};
const getDefaultFormValues = (queryParams: UrlQueryMap, silence?: Silence): SilenceFormFields => {
const now = new Date();
if (silence) {
const isExpired = Date.parse(silence.endsAt) < Date.now();
@ -66,12 +88,15 @@ const getDefaultFormValues = (silence?: Silence): SilenceFormFields => {
matcherName: '',
matcherValue: '',
timeZone: DefaultTimeZone,
...defaultsFromQuery(queryParams),
};
}
};
export const SilencesEditor: FC<Props> = ({ silence, alertManagerSourceName }) => {
const formAPI = useForm({ defaultValues: getDefaultFormValues(silence) });
const [queryParams] = useQueryParams();
const defaultValues = useMemo(() => getDefaultFormValues(queryParams, silence), [silence, queryParams]);
const formAPI = useForm({ defaultValues });
const dispatch = useDispatch();
const styles = useStyles(getStyles);

View File

@ -0,0 +1,10 @@
import { Matcher } from 'app/plugins/datasource/alertmanager/types';
import { parseMatcher } from './alertmanager';
// parses comma separated matchers like "foo=bar,baz=~bad*" into SilenceMatcher[]
export function parseQueryParamMatchers(paramValue: string): Matcher[] {
return paramValue
.split(',')
.filter((x) => !!x.trim())
.map((x) => parseMatcher(x.trim()));
}

View File

@ -92,21 +92,12 @@ text-decoration: underline;
table[class="body"] .columns {
table-layout: fixed !important; float: none !important; width: 100% !important; padding-right: 0px !important; padding-left: 0px !important; display: block !important;
}
table[class="body"] .column {
table-layout: fixed !important; float: none !important; width: 100% !important; padding-right: 0px !important; padding-left: 0px !important; display: block !important;
}
table[class="body"] table.columns td {
width: 100% !important;
}
table[class="body"] .columns td.four {
width: 33.333333% !important;
}
table[class="body"] .columns td.six {
width: 50% !important;
}
table[class="body"] .columns td.eight {
width: 66.666666% !important;
}
table[class="body"] .columns td.twelve {
width: 100% !important;
}
@ -215,150 +206,127 @@ text-decoration: underline;
<td class="mini-centered-text" style="color: #343b41; mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 25px 35px; font: 400 16px/27px 'Helvetica Neue', Helvetica, Arial, sans-serif;" align="center" valign="top">
{{Subject .Subject "{{.Title}}"}}
{{ define "alert" }}
{{ if gt (len .Annotations.SortedPairs) 0 }}
</td></tr><tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" class="annotations" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 24px 0 12px;" align="left" valign="top">
{{ range .Annotations.SortedPairs }}
<p style="color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0 0 10px; padding: 0;" align="left">{{ .Name }}: {{ .Value }}</p>
{{ end }}
</td>
</tr>
{{ end }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td valign="top" class="labels-heading" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: bold; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left">Labels:</td>
<td style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left" valign="top">
<ul class="labels-list" style="font-size: 14px; vertical-align: top;">
{{ range .Labels.SortedPairs }}<li>{{ .Name }}: {{ .Value }}</li>{{ end }}
</ul>
</td>
</tr>
<tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left" valign="top">
{{ if .SilenceURL }}
<a href="{{ .SilenceURL }}" class="button" style="color: #464c54; text-decoration: none; background-color: #f1f5f9; border-radius: 2px; display: inline-block; font-size: 12px; font-weight: bold; margin: 0 10px 0 0; padding: 5px 9px; border: 1px solid #c7d0d9;">
<img alt="" height="14" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAF1SURBVHgBrZLNTsJAEMf/2wYTPfEI9Q1qvBgjsEl5gB498gj1SDHpEKUckaMn4eiNFyAp4cJN30CPHrn5BV1noTWlFD3oJNuZ3Z39zUcH+A9pUffJpzAoOO/xuvvprZHooYCgLISoa7FyFSCbdOXuApj6M43GUVXWhYZUpKP3k1NZ14+kAOYChleVjnssK/ezKHrLAkQuZWIVKCgKyW+n55cUciZClzLoUPOiqISVlPB+s6ZulnNN/ihG3GfT84jKOwGvMO0krVEewoBoHeTA2glIhRuno7WzkH0Yc633sCz/CoiVshdYRGxGKYSo9ahgHnEPoqzvdxP1b/sEHtgsF2e12ditDD6gPE59XgIO8ytfTlbM1DiTzjk7WQvEYgllZ5fSXeE7vnfTOdkC1GqOBSFO2Mk22JG1TB4l+1VzX/IQgQJpBaHHsB6bA47+rNOHUI1O4A/zw2YWAaaT8UyPNkdcZ6JUs0P+7eouGXuO3WC7j7/KF29iokiLUdaFAAAAAElFTkSuQmCC" class="button-img" width="14" style="outline: none !important; text-decoration: none !important; -ms-interpolation-mode: bicubic; width: 14px; clear: both; display: block; height: 14px; vertical-align: sub; margin: 0 5px 0 0; border: 0 none;" align="left" />
Silence
</a>
{{ end }}
{{ if .Annotations.runbook_url }}
<a href="{{ .Annotations.runbook_url }}" class="button" style="color: #464c54; text-decoration: none; background-color: #f1f5f9; border-radius: 2px; display: inline-block; font-size: 12px; font-weight: bold; margin: 0 10px 0 0; padding: 5px 9px; border: 1px solid #c7d0d9;">
<img class="button-img" alt="" height="14" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADaSURBVHgBrVNLDoIwFJwq0bjTG+ARvIGeQI/ADdSVARY8oki8jZxEbyDewLUfahttAoEWDM7m9bVvJtN0CrQEyzdENLyjt2RgtoFzjcgl1XTyJw/0T4K8ghmBH8Zz1VhqsaHtVBT7CYwP5KY6tk+xw3k2Kgl0YdmyKrIYvEjB73EqbI+rBC1owMDXGdjws0aqm9MK7Mg7ogE6aInWAqUreLQPanJgdtAgB2YHIgeTuhxUOlBPNcBvKPwFT4RHbnDgLOpNw3EEaxEFblJwIPECZhw8MZAlQkX+C95zCzqWDYrK2AAAAABJRU5ErkJggg==" width="14" style="outline: none !important; text-decoration: none !important; -ms-interpolation-mode: bicubic; width: 14px; clear: both; display: block; height: 14px; vertical-align: sub; margin: 0 5px 0 0; border: 0 none;" align="left" />
View Runbook
</a>
{{ end }}
{{ if .DashboardURL}}
<a href="{{ .DashboardURL }}" class="button" style="color: #464c54; text-decoration: none; background-color: #f1f5f9; border-radius: 2px; display: inline-block; font-size: 12px; font-weight: bold; margin: 0 10px 0 0; padding: 5px 9px; border: 1px solid #c7d0d9;">
<img alt="" height="14" class="button-img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAE9SURBVHgBrVLNWcMwDH220/bjhEdIR2AC6ASMQEeAU6G9+FLycQojdAUmgE7ACGSEcEybRkj5cUKbnFp9X2JZ8nuypQecaUp+Kxf98BIeJ3cwNxqZDRB89mCTtXuZBvVGwBsCtiUrkYVS8RgHmzeVFB4Lwm9d9Y6XB/EbAhCK7atbbcRfuCjkRNwtt6fs4825RPyli4SkJNA40/wNFCm7cC6sdnnYSdVmQs6Xnla4JjoikDcHmMQYrCSNrI434C7BrA/EXf6Slad0kieY1BPsUKQahcWA5cj7otYTjKG/+/oplQU8oAMx1aKInkbAVL6BJ818ns82wVYHilLnlon4rIOTGeTIWHleB6mq4xfUAfTts1tXPh1YyubfwZGa3HO+K+WWgMeacHBuoOc1m7cr6HSPcvbvpnNhweAS9gdSqGSnoVA2cQAAAABJRU5ErkJggg==" width="14" style="outline: none !important; text-decoration: none !important; -ms-interpolation-mode: bicubic; width: 14px; clear: both; display: block; height: 14px; vertical-align: sub; margin: 0 5px 0 0; border: 0 none;" align="left" />
Go to Dashboard
</a>
{{ end }}
{{ if .PanelURL}}
<a href="{{ .PanelURL }}" class="button" style="color: #464c54; text-decoration: none; background-color: #f1f5f9; border-radius: 2px; display: inline-block; font-size: 12px; font-weight: bold; margin: 0 10px 0 0; padding: 5px 9px; border: 1px solid #c7d0d9;">
<img alt="" class="button-img" height="14" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAE9SURBVHgBrVLNWcMwDH220/bjhEdIR2AC6ASMQEeAU6G9+FLycQojdAUmgE7ACGSEcEybRkj5cUKbnFp9X2JZ8nuypQecaUp+Kxf98BIeJ3cwNxqZDRB89mCTtXuZBvVGwBsCtiUrkYVS8RgHmzeVFB4Lwm9d9Y6XB/EbAhCK7atbbcRfuCjkRNwtt6fs4825RPyli4SkJNA40/wNFCm7cC6sdnnYSdVmQs6Xnla4JjoikDcHmMQYrCSNrI434C7BrA/EXf6Slad0kieY1BPsUKQahcWA5cj7otYTjKG/+/oplQU8oAMx1aKInkbAVL6BJ818ns82wVYHilLnlon4rIOTGeTIWHleB6mq4xfUAfTts1tXPh1YyubfwZGa3HO+K+WWgMeacHBuoOc1m7cr6HSPcvbvpnNhweAS9gdSqGSnoVA2cQAAAABJRU5ErkJggg==" width="14" style="outline: none !important; text-decoration: none !important; -ms-interpolation-mode: bicubic; width: 14px; clear: both; display: block; height: 14px; vertical-align: sub; margin: 0 5px 0 0; border: 0 none;" align="left" />
Go to Panel
</a>
{{ end }}
{{ if gt (len .GeneratorURL) 0 }}<a href="{{ .GeneratorURL }}" class="button" style="color: #464c54; text-decoration: none; background-color: #f1f5f9; border-radius: 2px; display: inline-block; font-size: 12px; font-weight: bold; margin: 0 10px 0 0; padding: 5px 9px; border: 1px solid #c7d0d9;">Source</a>{{ end }}
</td>
</tr>
<tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left" valign="top">
<div style="height: 24px;"></div>
<div style="background-color: #c7d0d9; height: 1px;"></div>
<div style="height: 24px;"></div>
</td>
</tr>
{{ end }}
{{ if gt (len .Message) 0 }}
{{ .Message }}
{{ else }}
<table class="row" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 100%; position: relative; display: block; padding: 0px;">
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="wrapper last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; position: relative; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 10px 0px 0px;" align="left" valign="top">
<table class="twelve columns" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 580px; margin: 0 auto; padding: 0;">
<td class="twelve" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left" valign="top">
<table style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; padding: 0;">
{{ if gt (len .Alerts.Firing) 0 }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="center" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="center" valign="top">
{{ if gt (len .Alerts.Firing) 0 }}
<h3 style="color: #E6522C; font-weight: bold; font-style: italic; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 22px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 10px 0; padding: 0;" align="left">{{.Title}}</h3>
{{ else }}
<h3 style="font-weight: bold; font-style: italic; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 22px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 10px 0; padding: 0;" align="left">{{.Title}}</h3>
{{ end }}
<td colspan="2" class="section-heading" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #2c3235; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: bold; line-height: 19px; font-size: 22px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0 0 32px;" align="left" valign="top">
Firing: {{ .Alerts.Firing | len }} alert{{ if gt (len .Alerts.Firing) 1 }}s{{ end }}{{ if gt (len .GroupLabels.SortedPairs) 1 }} for
{{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }}
{{ end }}{{ end }}
</td>
</tr>
</table>
</td>
</tr>
</table>
<table class="row" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 100%; position: relative; display: block; padding: 0px;">
<tr style="vertical-align: top; padding: 0;" align="left">
{{ if gt (len .Alerts.Firing) 0 }}
<td class="alert-warning twelve column last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0 0px 0 0;" align="left" bgcolor="#E6522C" valign="top">
{{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for
{{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }}
{{ end }}
</td>
{{ else }}
<td class="alert-good twelve column last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0 0px 0 0;" align="left" bgcolor="#68B90F" valign="top">
{{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for
{{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }}
{{ end }}
</td>
{{ end }}
</tr>
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0 0px 0 0;" align="left" valign="top">
<table class="twelve columns" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 580px; margin: 0 auto; padding: 0;">
{{ if gt (len .Alerts.Firing) 0 }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="twelve last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 100%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
<h5 style="font-weight: bold; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 18px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left">({{ .Alerts.Firing | len }}) Firing</h5>
</td>
</tr>
{{ end }}
{{ range .Alerts.Firing }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="four" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 33.333333%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
<h5 style="font-weight: bold; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 18px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left">Labels</h5>
</td>
<td class="eight last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 66.666666%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
{{ if gt (len .Annotations) 0 }}<h5 style="font-weight: bold; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 18px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left">Annotations</h5>{{ end }}
</td>
</tr>
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="four" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 33.333333%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}" style="color: #E67612; text-decoration: none;">Source</a><br />
</td>
<td class="eight last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 66.666666%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
</td>
</tr>
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="status-tag status-firing" width="68" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #ffffff; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 68px; margin: 0; padding: 4px 8px;" align="center" bgcolor="#e02f44" valign="top">
Firing
</td>
<td class="alert-label" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: bold; line-height: 19px; font-size: 16px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; text-decoration: underline; margin: 0; padding: 0 0 0 12px;" align="left" valign="top">
{{ .Labels.alertname }}
</td>
</tr>
{{ template "alert" . }}
{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
{{ if gt (len .Alerts.Firing) 0 }}
{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="twelve" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 100%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
<br />
<hr style="color: #d9d9d9; background-color: #d9d9d9; height: 1px; border: none;" />
<br />
<td colspan="2" class="section-heading" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #2c3235; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: bold; line-height: 19px; font-size: 22px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0 0 32px;" align="left" valign="top">
Resolved: {{ .Alerts.Resolved | len }} alert{{ if gt (len .Alerts.Resolved) 1 }}s{{ end }}{{ if gt (len .GroupLabels.SortedPairs) 1 }} for
{{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }}
{{ end }}{{ end }}
</td>
</tr>
{{ end }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="twelve last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 100%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
<h5 style="font-weight: bold; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 18px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left">({{ .Alerts.Resolved | len }}) Resolved</h5>
</td>
</tr>
{{ end }}
{{ range .Alerts.Resolved }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="six" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 50%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
<h5 style="font-weight: bold; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 18px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left">Labels</h5>
</td>
<td class="six last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 50%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
{{ if gt (len .Annotations) 0 }}<h5 style="font-weight: bold; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.3; word-break: normal; font-size: 18px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left">Annotations</h5>{{ end }}
</td>
</tr>
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="six" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 50%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}" style="color: #E67612; text-decoration: none;">Source</a><br />
</td>
<td class="six last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 50%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="left" valign="top">
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
</td>
</tr>
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="status-tag status-resolved" width="68" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #ffffff; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 68px; margin: 0; padding: 4px 8px;" align="center" bgcolor="#464c54" valign="top">
Resolved
</td>
<td class="alert-label" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: bold; line-height: 19px; font-size: 16px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; text-decoration: underline; margin: 0; padding: 0 0 0 12px;" align="left" valign="top">
{{ .Labels.alertname }}
</td>
</tr>
{{ template "alert" . }}
{{ end }}
{{ end }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0;" align="left" valign="top">
<a href="{{ .AlertPageUrl }}" class="button" style="color: #464c54; text-decoration: none; background-color: #f1f5f9; border-radius: 2px; display: inline-block; font-size: 12px; font-weight: bold; margin: 0 10px 0 0; padding: 5px 9px; border: 1px solid #c7d0d9;">Go to alerts page</a>
</td>
</tr>
</table>
</td>
</tr>
</td></tr></table>
</table>
{{ end }}
<table class="row" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 100%; position: relative; display: block; padding: 0px;">
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="wrapper last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; position: relative; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 10px 0px 0px;" align="left" valign="top">
<table class="twelve columns" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 580px; margin: 0 auto; padding: 0;">
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="center six" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 50%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="center" valign="top">
<table class="better-button" align="center" border="0" cellspacing="0" cellpadding="0" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; margin-top: 10px; margin-bottom: 20px; padding: 0;">
<tr style="vertical-align: top; padding: 0;" align="left">
<td align="center" class="better-button" bgcolor="#ff8f2b" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; margin: 0; padding: 0px;" valign="top">
<a href="{{.RuleUrl}}" target="_blank" style="color: #FFF; text-decoration: none; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; display: inline-block; padding: 12px 25px; border: 1px solid #ff8f2b;">View your Alert rule</a>
</td>
</tr>
</table>
</td>
<td class="center six" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 50%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" align="center" valign="top">
<table class="better-button" align="center" border="0" cellspacing="0" cellpadding="0" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; margin-top: 10px; margin-bottom: 20px; padding: 0;">
<tr style="vertical-align: top; padding: 0;" align="left">
<td align="center" class="better-button-alt" bgcolor="#efefef" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; margin: 0; padding: 0px;" valign="top">
<a href="{{.AlertPageUrl}}" target="_blank" style="color: #ff8f2b; text-decoration: none; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; display: inline-block; background-color: #EFEFEF; padding: 12px 25px; border: 1px solid #ff8f2b;"> Go to the Alerts page</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</center></td>
</tr>
</table>
@ -367,7 +335,7 @@ text-decoration: underline;
<td class="wrapper last" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; position: relative; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 10px 20px 0px 0px;" align="left" valign="top">
<table class="twelve columns center" style="border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: center; width: 580px; margin: 0 auto; padding: 0;">
<tr style="vertical-align: top; padding: 0;" align="left">
<td class="twelve" align="center" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; width: 100%; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" valign="top">
<td class="twelve" align="center" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 0px 0px 10px;" valign="top">
<center style="width: 100%; min-width: 580px;">
<p style="font-size: 12px; color: #999999; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0 0 10px; padding: 0;" align="center">
Sent by <a href="{{.AppUrl}}" style="color: #E67612; text-decoration: none;">Grafana v{{.BuildVersion}}</a>
@ -381,9 +349,9 @@ text-decoration: underline;
</td>
</tr>
</table>
</center>
</td>
</tr>
</table>
</body>
</html>