Alerting: unified alerting frontend (#32708)
This commit is contained in:
+8
-3
@@ -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{
|
||||
|
||||
@@ -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
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user