Alerting: unified alerting frontend (#32708)

This commit is contained in:
Domas
2021-04-07 08:42:43 +03:00
committed by GitHub
parent 6082a9360e
commit a56293142a
79 changed files with 3857 additions and 28 deletions
+8 -3
View File
@@ -192,13 +192,18 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
navTree = append(navTree, hs.getProfileNode(c))
}
if setting.AlertingEnabled && (c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR) {
if setting.AlertingEnabled {
alertChildNavs := []*dtos.NavLink{
{Text: "Alert rules", Id: "alert-list", Url: hs.Cfg.AppSubURL + "/alerting/list", Icon: "list-ul"},
{
}
if c.OrgRole == models.ROLE_ADMIN && hs.Cfg.IsNgAlertEnabled() {
alertChildNavs = append(alertChildNavs, &dtos.NavLink{Text: "Routes", Id: "am-routes", Url: hs.Cfg.AppSubURL + "/alerting/routes", Icon: "sitemap"})
}
if c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR {
alertChildNavs = append(alertChildNavs, &dtos.NavLink{
Text: "Notification channels", Id: "channels", Url: hs.Cfg.AppSubURL + "/alerting/notifications",
Icon: "comment-alt-share",
},
})
}
navTree = append(navTree, &dtos.NavLink{
+7 -6
View File
@@ -13,10 +13,10 @@ import (
)
const (
amSilencesPath = "/api/v2/silences"
amSilencePath = "/api/v2/silence/%s"
amAlertGroupsPath = "/api/v2/alerts/groups"
amAlertsPath = "/api/v2/alerts"
amSilencesPath = "/alertmanager/api/v2/silences"
amSilencePath = "/alertmanager/api/v2/silence/%s"
amAlertGroupsPath = "/alertmanager/api/v2/alerts/groups"
amAlertsPath = "/alertmanager/api/v2/alerts"
amConfigPath = "/api/v1/alerts"
)
@@ -44,8 +44,9 @@ func (am *LotexAM) RouteCreateSilence(ctx *models.ReqContext, silenceBody apimod
URL: withPath(*ctx.Req.URL, amSilencesPath),
Body: body,
ContentLength: ln,
Header: map[string][]string{"Content-Type": {"application/json"}},
},
jsonExtractor(&apimodels.GettableSilence{}),
jsonExtractor(nil),
)
}
@@ -83,7 +84,7 @@ func (am *LotexAM) RouteGetAlertingConfig(ctx *models.ReqContext) response.Respo
amConfigPath,
),
},
jsonExtractor(&apimodels.GettableUserConfig{}),
yamlExtractor(&apimodels.GettableUserConfig{}),
)
}
@@ -0,0 +1,167 @@
@alertManagerDatasourceID = 36
###
# create AM configuration
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts
content-type: application/json
{
"template_files": {},
"alertmanager_config": {
"global": {
"resolve_timeout": "4m",
"http_config": {
"BasicAuth": null,
"Authorization": null,
"BearerToken": "",
"BearerTokenFile": "",
"ProxyURL": {},
"TLSConfig": {
"CAFile": "",
"CertFile": "",
"KeyFile": "",
"ServerName": "",
"InsecureSkipVerify": false
},
"FollowRedirects": true
},
"smtp_from": "youraddress@example.org",
"smtp_hello": "localhost",
"smtp_smarthost": "localhost:25",
"smtp_require_tls": true,
"pagerduty_url": "https://events.pagerduty.com/v2/enqueue",
"opsgenie_api_url": "https://api.opsgenie.com/",
"wechat_api_url": "https://qyapi.weixin.qq.com/cgi-bin/",
"victorops_api_url": "https://alert.victorops.com/integrations/generic/20131114/alert/"
},
"route": {
"receiver": "example-email"
},
"templates": [],
"receivers": [
{
"name": "example-email",
"email_configs": [
{
"send_resolved": false,
"to": "youraddress@example.org",
"smarthost": "",
"html": "{{ template \"email.default.html\" . }}",
"tls_config": {
"CAFile": "",
"CertFile": "",
"KeyFile": "",
"ServerName": "",
"InsecureSkipVerify": false
}
}
]
}
]
}
}
###
# get latest AM configuration
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts
content-type: application/json
###
# delete AM configuration
DELETE http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts
###
# create AM alerts
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/alerts
content-type: application/json
[
{
"startsAt": "2021-04-05T14:08:42.087Z",
"endsAt": "2021-04-05T14:08:42.087Z",
"annotations": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"labels": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"generatorURL": "http://localhost"
}
]
###
# get AM alerts
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/alerts
###
# get silences - no silences
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo"
###
# create silence
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences
content-type: application/json
{
"matchers": [
{
"name": "foo",
"value": "bar",
"isRegex": true
}
],
"createdBy": "spapagian",
"comment": "a comment",
"startsAt": "2021-04-05T14:45:09.885Z",
"endsAt": "2021-04-05T16:45:09.885Z"
}
###
# update silence - does not exist
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences
content-type: application/json
{
"id": "something",
"comment": "string",
"createdBy": "string",
"endsAt": "2023-03-31T14:17:04.419Z",
"matchers": [
{
"isRegex": true,
"name": "string",
"value": "string"
}
],
"startsAt": "2021-03-31T13:17:04.419Z"
}
###
# get silences
# @name getSilences
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences
###
@silenceID = {{getSilences.response.body.$.[3].id}}
###
# get silence
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/{{silenceID}}
###
# get silence - unknown
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown
###
# delete silence
DELETE http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/{{silenceID}}
###
# delete silence - unknown
DELETE http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown
+15 -2
View File
@@ -7,6 +7,7 @@ import (
"net/http"
"regexp"
"strconv"
"strings"
"github.com/go-openapi/strfmt"
apimodels "github.com/grafana/alerting-api/pkg/api"
@@ -45,7 +46,7 @@ func backendType(ctx *models.ReqContext, cache datasources.CacheService) (apimod
switch ds.Type {
case "loki", "prometheus":
return apimodels.LoTexRulerBackend, nil
case "grafana-alertmanager-datasource":
case "alertmanager":
return apimodels.AlertmanagerBackend, nil
default:
return 0, fmt.Errorf("unexpected backend type (%v)", ds.Type)
@@ -94,7 +95,19 @@ func (p *AlertingProxy) withReq(
status := resp.Status()
if status >= 400 {
return response.Error(status, string(resp.Body()), nil)
errMessage := string(resp.Body())
// if Content-Type is application/json
// and it is successfully decoded and contains a message
// return this as response error message
if strings.HasPrefix(resp.Header().Get("Content-Type"), "application/json") {
var m map[string]interface{}
if err := json.Unmarshal(resp.Body(), &m); err == nil {
if message, ok := m["message"]; ok {
errMessage = message.(string)
}
}
}
return response.Error(status, errMessage, nil)
}
t, err := extractor(resp.Body())