Contact point testing (#37308)
This commit adds contact point testing to ngalerts via a new API endpoint. This endpoint accepts JSON containing a list of receiver configurations which are validated and then tested with a notification for a test alert. The endpoint returns JSON for each receiver with a status and error message. It accepts a configurable timeout via the Request-Timeout header (in seconds) up to a maximum of 30 seconds.
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/pkg/errors"
|
||||
@@ -73,6 +74,17 @@ import (
|
||||
// 200: alertGroups
|
||||
// 400: ValidationError
|
||||
|
||||
// swagger:route POST /api/alertmanager/{Recipient}/config/api/v1/receivers/test alertmanager RoutePostTestReceivers
|
||||
//
|
||||
// Test Grafana managed receivers without saving them.
|
||||
//
|
||||
// Responses:
|
||||
//
|
||||
// 200: Ack
|
||||
// 207: MultiStatus
|
||||
// 400: ValidationError
|
||||
// 408: Failure
|
||||
|
||||
// swagger:route GET /api/alertmanager/{Recipient}/api/v2/silences alertmanager RouteGetSilences
|
||||
//
|
||||
// get silences
|
||||
@@ -105,6 +117,40 @@ import (
|
||||
// 200: Ack
|
||||
// 400: ValidationError
|
||||
|
||||
// swagger:model
|
||||
type TestReceiversConfig struct {
|
||||
Receivers []*PostableApiReceiver `yaml:"receivers,omitempty" json:"receivers,omitempty"`
|
||||
}
|
||||
|
||||
// swagger:parameters RoutePostTestReceivers
|
||||
type TestReceiversConfigParams struct {
|
||||
Receivers []*PostableApiReceiver `yaml:"receivers,omitempty" json:"receivers,omitempty"`
|
||||
}
|
||||
|
||||
func (c *TestReceiversConfigParams) ProcessConfig() error {
|
||||
return processReceiverConfigs(c.Receivers)
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
type TestReceiversResult struct {
|
||||
Receivers []TestReceiverResult `json:"receivers"`
|
||||
NotifedAt time.Time `json:"notified_at"`
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
type TestReceiverResult struct {
|
||||
Name string `json:"name"`
|
||||
Configs []TestReceiverConfigResult `json:"grafana_managed_receiver_configs"`
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
type TestReceiverConfigResult struct {
|
||||
Name string `json:"name"`
|
||||
UID string `json:"uid"`
|
||||
Status string `json:"status"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// swagger:parameters RouteCreateSilence
|
||||
type CreateSilenceParams struct {
|
||||
// in:body
|
||||
@@ -345,39 +391,7 @@ func (c *PostableUserConfig) GetGrafanaReceiverMap() map[string]*PostableGrafana
|
||||
|
||||
// ProcessConfig parses grafana receivers, encrypts secrets and assigns UUIDs (if they are missing)
|
||||
func (c *PostableUserConfig) ProcessConfig() error {
|
||||
seenUIDs := make(map[string]struct{})
|
||||
// encrypt secure settings for storing them in DB
|
||||
for _, r := range c.AlertmanagerConfig.Receivers {
|
||||
switch r.Type() {
|
||||
case GrafanaReceiverType:
|
||||
for _, gr := range r.PostableGrafanaReceivers.GrafanaManagedReceivers {
|
||||
for k, v := range gr.SecureSettings {
|
||||
encryptedData, err := util.Encrypt([]byte(v), setting.SecretKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt secure settings: %w", err)
|
||||
}
|
||||
gr.SecureSettings[k] = base64.StdEncoding.EncodeToString(encryptedData)
|
||||
}
|
||||
if gr.UID == "" {
|
||||
retries := 5
|
||||
for i := 0; i < retries; i++ {
|
||||
gen := util.GenerateShortUID()
|
||||
_, ok := seenUIDs[gen]
|
||||
if !ok {
|
||||
gr.UID = gen
|
||||
break
|
||||
}
|
||||
}
|
||||
if gr.UID == "" {
|
||||
return fmt.Errorf("all %d attempts to generate UID for receiver have failed; please retry", retries)
|
||||
}
|
||||
}
|
||||
seenUIDs[gr.UID] = struct{}{}
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return processReceiverConfigs(c.AlertmanagerConfig.Receivers)
|
||||
}
|
||||
|
||||
// MarshalYAML implements yaml.Marshaller.
|
||||
@@ -911,3 +925,39 @@ type GettableGrafanaReceivers struct {
|
||||
type PostableGrafanaReceivers struct {
|
||||
GrafanaManagedReceivers []*PostableGrafanaReceiver `yaml:"grafana_managed_receiver_configs,omitempty" json:"grafana_managed_receiver_configs,omitempty"`
|
||||
}
|
||||
|
||||
func processReceiverConfigs(c []*PostableApiReceiver) error {
|
||||
seenUIDs := make(map[string]struct{})
|
||||
// encrypt secure settings for storing them in DB
|
||||
for _, r := range c {
|
||||
switch r.Type() {
|
||||
case GrafanaReceiverType:
|
||||
for _, gr := range r.PostableGrafanaReceivers.GrafanaManagedReceivers {
|
||||
for k, v := range gr.SecureSettings {
|
||||
encryptedData, err := util.Encrypt([]byte(v), setting.SecretKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt secure settings: %w", err)
|
||||
}
|
||||
gr.SecureSettings[k] = base64.StdEncoding.EncodeToString(encryptedData)
|
||||
}
|
||||
if gr.UID == "" {
|
||||
retries := 5
|
||||
for i := 0; i < retries; i++ {
|
||||
gen := util.GenerateShortUID()
|
||||
_, ok := seenUIDs[gen]
|
||||
if !ok {
|
||||
gr.UID = gen
|
||||
break
|
||||
}
|
||||
}
|
||||
if gr.UID == "" {
|
||||
return fmt.Errorf("all %d attempts to generate UID for receiver have failed; please retry", retries)
|
||||
}
|
||||
}
|
||||
seenUIDs[gr.UID] = struct{}{}
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -485,6 +485,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/alertmanager/{Recipient}/config/api/v1/receivers/test": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"alertmanager"
|
||||
],
|
||||
"summary": "Test Grafana managed receivers without saving them.",
|
||||
"operationId": "RoutePostTestReceivers",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PostableApiReceiver"
|
||||
},
|
||||
"x-go-name": "Receivers",
|
||||
"name": "receivers",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ack",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Ack"
|
||||
}
|
||||
},
|
||||
"207": {
|
||||
"$ref": "#/responses/MultiStatus"
|
||||
},
|
||||
"400": {
|
||||
"description": "ValidationError",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ValidationError"
|
||||
}
|
||||
},
|
||||
"408": {
|
||||
"description": "Failure",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/prometheus/{Recipient}/api/v1/alerts": {
|
||||
"get": {
|
||||
"description": "gets the current alerts",
|
||||
@@ -1707,6 +1750,7 @@
|
||||
"enum": [
|
||||
"Alerting"
|
||||
],
|
||||
"x-go-enum-desc": "Alerting AlertingErrState",
|
||||
"x-go-name": "ExecErrState"
|
||||
},
|
||||
"id": {
|
||||
@@ -1735,6 +1779,7 @@
|
||||
"NoData",
|
||||
"OK"
|
||||
],
|
||||
"x-go-enum-desc": "Alerting Alerting\nNoData NoData\nOK OK",
|
||||
"x-go-name": "NoDataState"
|
||||
},
|
||||
"orgId": {
|
||||
@@ -2547,6 +2592,7 @@
|
||||
"enum": [
|
||||
"Alerting"
|
||||
],
|
||||
"x-go-enum-desc": "Alerting AlertingErrState",
|
||||
"x-go-name": "ExecErrState"
|
||||
},
|
||||
"no_data_state": {
|
||||
@@ -2556,6 +2602,7 @@
|
||||
"NoData",
|
||||
"OK"
|
||||
],
|
||||
"x-go-enum-desc": "Alerting Alerting\nNoData NoData\nOK OK",
|
||||
"x-go-name": "NoDataState"
|
||||
},
|
||||
"title": {
|
||||
@@ -3229,6 +3276,76 @@
|
||||
},
|
||||
"x-go-package": "github.com/prometheus/common/config"
|
||||
},
|
||||
"TestReceiverConfigResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"x-go-name": "Error"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"x-go-name": "Name"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"x-go-name": "Status"
|
||||
},
|
||||
"uid": {
|
||||
"type": "string",
|
||||
"x-go-name": "UID"
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
},
|
||||
"TestReceiverResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grafana_managed_receiver_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TestReceiverConfigResult"
|
||||
},
|
||||
"x-go-name": "Configs"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"x-go-name": "Name"
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
},
|
||||
"TestReceiversConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"receivers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PostableApiReceiver"
|
||||
},
|
||||
"x-go-name": "Receivers"
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
},
|
||||
"TestReceiversResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"notified_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"x-go-name": "NotifedAt"
|
||||
},
|
||||
"receivers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TestReceiverResult"
|
||||
},
|
||||
"x-go-name": "Receivers"
|
||||
}
|
||||
},
|
||||
"x-go-package": "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
},
|
||||
"TestRulePayload": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3483,11 +3600,12 @@
|
||||
"$ref": "#/definitions/alertGroup"
|
||||
},
|
||||
"alertGroups": {
|
||||
"description": "AlertGroups alert groups",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/alertGroup"
|
||||
},
|
||||
"x-go-name": "AlertGroups",
|
||||
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models",
|
||||
"$ref": "#/definitions/alertGroups"
|
||||
},
|
||||
"alertStatus": {
|
||||
@@ -3672,16 +3790,14 @@
|
||||
"$ref": "#/definitions/gettableAlert"
|
||||
},
|
||||
"gettableAlerts": {
|
||||
"description": "GettableAlerts gettable alerts",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableAlert"
|
||||
},
|
||||
"x-go-name": "GettableAlerts",
|
||||
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models",
|
||||
"$ref": "#/definitions/gettableAlerts"
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
@@ -3734,6 +3850,8 @@
|
||||
"x-go-name": "UpdatedAt"
|
||||
}
|
||||
},
|
||||
"x-go-name": "GettableSilence",
|
||||
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models",
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
},
|
||||
"gettableSilences": {
|
||||
@@ -3872,6 +3990,7 @@
|
||||
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models"
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
@@ -3912,8 +4031,6 @@
|
||||
"x-go-name": "StartsAt"
|
||||
}
|
||||
},
|
||||
"x-go-name": "PostableSilence",
|
||||
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models",
|
||||
"$ref": "#/definitions/postableSilence"
|
||||
},
|
||||
"receiver": {
|
||||
|
||||
Reference in New Issue
Block a user