From 81ba27e6c3dbe1d238ec6e605b727a05649d4efa Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki Date: Tue, 23 Feb 2021 19:13:04 +0200 Subject: [PATCH] Add testing endpoints --- pkg/api/testing.go | 110 +++++++++++++++++++++++ spec.json | 215 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 pkg/api/testing.go diff --git a/pkg/api/testing.go b/pkg/api/testing.go new file mode 100644 index 00000000000..51c95f61f72 --- /dev/null +++ b/pkg/api/testing.go @@ -0,0 +1,110 @@ +package api + +import ( + "time" + + "github.com/grafana/grafana/pkg/services/ngalert/eval" + "github.com/prometheus/alertmanager/config" +) + +// swagger:route Get /api/v1/receiver/test testing RouteTestReceiverConfig +// +// Test receiver +// +// Consumes: +// - application/json +// +// Produces: +// - application/json +// +// Responses: +// 200: Success +// 412: SmtpNotEnabled +// 500: Failure + +// swagger:route Get /api/v1/rule/test testing RouteTestRuleConfig +// +// Test rule +// +// Consumes: +// - application/json +// +// Produces: +// - application/json +// +// Responses: +// 200: TestRuleResponse + +// swagger:parameters RouteTestReceiverConfig +type TestReceiverRequest struct { + // in:body + Body ExtendedReceiver +} + +// swagger:parameters RouteTestRuleConfig +type TestRuleRequest struct { + // in:body + Body TestRulePayload +} + +// swagger:model +type TestRulePayload struct { + Expr LotexQuery `json:"expr,omitempty"` + // GrafanaManagedCondition for grafana alerts + GrafanaManagedCondition EvalAlertConditionCommand `json:"grafana_condition,omitempty"` +} + +// swagger:model +type LotexQuery struct { + // Example: (node_filesystem_avail_bytes{fstype!="",job="integrations/node_exporter"} node_filesystem_size_bytes{fstype!="",job="integrations/node_exporter"} * 100 < 5 and node_filesystem_readonly{fstype!="",job="integrations/node_exporter"} == 0) + Expr string + // DatasourceUID is required if the query will be sent to grafana to be executed + DatasourceUID string `json:"datasourceUid,omitempty"` +} + +// swagger:model +type EvalAlertConditionCommand struct { + Condition string `json:"condition"` + Data []eval.AlertQuery `json:"data"` + Now time.Time `json:"now"` +} + +// swagger:model +type TestRuleResponse struct { + Alerts interface{} `json:"alerts"` + GrafanaAlertInstances AlertInstancesResponse `json:"grafana_alert_instances"` +} + +// swagger:model +type AlertInstancesResponse struct { + // Instances is an array of arrow encoded dataframes + // each frame has a single row, and a column for each instance (alert identified by unique labels) with a boolean value (firing/not firing) + Instances [][]byte `json:"instances"` +} + +// swagger:model +type ExtendedReceiver struct { + EmailConfigs config.EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` + PagerdutyConfigs config.PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` + SlackConfigs config.SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` + WebhookConfigs config.WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` + OpsGenieConfigs config.OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` + WechatConfigs config.WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` + PushoverConfigs config.PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` + VictorOpsConfigs config.VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` + GrafanaReceiver GrafanaReceiver `yaml:"grafana_managed_receiver,omitempty" json:"grafana_managed_receiver,omitempty"` +} + +// swagger:model +type Success ResponseDetails + +// swagger:model +type SmtpNotEnabled ResponseDetails + +// swagger:model +type Failure ResponseDetails + +// swagger:model +type ResponseDetails struct { + Msg string `json:"msg"` +} diff --git a/spec.json b/spec.json index 902ef53a00e..b2c9b2c90ac 100644 --- a/spec.json +++ b/spec.json @@ -325,6 +325,82 @@ } } }, + "/api/v1/receiver/test": { + "get": { + "description": "Test receiver", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "testing" + ], + "operationId": "RouteTestReceiverConfig", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/ExtendedReceiver" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Success" + } + }, + "412": { + "description": "SmtpNotEnabled", + "schema": { + "$ref": "#/definitions/SmtpNotEnabled" + } + }, + "500": { + "description": "Failure", + "schema": { + "$ref": "#/definitions/Failure" + } + } + } + } + }, + "/api/v1/rule/test": { + "get": { + "description": "Test rule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "testing" + ], + "operationId": "RouteTestRuleConfig", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/TestRulePayload" + } + } + ], + "responses": { + "200": { + "description": "TestRuleResponse", + "schema": { + "$ref": "#/definitions/TestRuleResponse" + } + } + } + } + }, "/api/v1/rules": { "get": { "description": "List rule groups", @@ -683,6 +759,24 @@ "AlertGroups": { "$ref": "#/definitions/alertGroups" }, + "AlertInstancesResponse": { + "type": "object", + "properties": { + "instances": { + "description": "Instances is an array of arrow encoded dataframes\neach frame has a single row, and a column for each instance (alert identified by unique labels) with a boolean value (firing/not firing)", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8" + } + }, + "x-go-name": "Instances" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, "AlertNotification": { "type": "object", "properties": { @@ -1203,6 +1297,61 @@ "title": "ErrorType models the different API error types.", "x-go-package": "github.com/prometheus/client_golang/api/prometheus/v1" }, + "EvalAlertConditionCommand": { + "type": "object", + "properties": { + "condition": { + "type": "string", + "x-go-name": "Condition" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/AlertQuery" + }, + "x-go-name": "Data" + }, + "now": { + "type": "string", + "format": "date-time", + "x-go-name": "Now" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, + "ExtendedReceiver": { + "type": "object", + "properties": { + "email_configs": { + "$ref": "#/definitions/EmailConfig" + }, + "grafana_managed_receiver": { + "$ref": "#/definitions/GrafanaReceiver" + }, + "opsgenie_configs": { + "$ref": "#/definitions/OpsGenieConfig" + }, + "pagerduty_configs": { + "$ref": "#/definitions/PagerdutyConfig" + }, + "pushover_configs": { + "$ref": "#/definitions/PushoverConfig" + }, + "slack_configs": { + "$ref": "#/definitions/SlackConfig" + }, + "victorops_configs": { + "$ref": "#/definitions/VictorOpsConfig" + }, + "webhook_configs": { + "$ref": "#/definitions/WebhookConfig" + }, + "wechat_configs": { + "$ref": "#/definitions/WechatConfig" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, "ExtendedRuleNode": { "type": "object", "properties": { @@ -1295,6 +1444,9 @@ }, "x-go-package": "github.com/grafana/alerting-api/pkg/api" }, + "Failure": { + "$ref": "#/definitions/ResponseDetails" + }, "GetableSilence": { "$ref": "#/definitions/silence" }, @@ -1472,6 +1624,21 @@ }, "x-go-package": "github.com/prometheus/common/model" }, + "LotexQuery": { + "type": "object", + "properties": { + "Expr": { + "type": "string", + "example": "(node_filesystem_avail_bytes{fstype!=\"\",job=\"integrations/node_exporter\"} node_filesystem_size_bytes{fstype!=\"\",job=\"integrations/node_exporter\"} * 100 \u003c 5 and node_filesystem_readonly{fstype!=\"\",job=\"integrations/node_exporter\"} == 0)" + }, + "datasourceUid": { + "description": "DatasourceUID is required if the query will be sent to grafana to be executed", + "type": "string", + "x-go-name": "DatasourceUID" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, "MatchRegexps": { "type": "object", "title": "MatchRegexps represents a map of Regexp.", @@ -1868,6 +2035,16 @@ }, "x-go-package": "github.com/grafana/grafana/pkg/services/ngalert/eval" }, + "ResponseDetails": { + "type": "object", + "properties": { + "msg": { + "type": "string", + "x-go-name": "Msg" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, "RoleType": { "type": "string", "x-go-package": "github.com/grafana/grafana/pkg/models" @@ -2328,6 +2505,12 @@ }, "x-go-package": "github.com/prometheus/alertmanager/config" }, + "SmtpNotEnabled": { + "$ref": "#/definitions/ResponseDetails" + }, + "Success": { + "$ref": "#/definitions/ResponseDetails" + }, "TLSConfig": { "type": "object", "title": "TLSConfig configures the options for TLS connections.", @@ -2355,9 +2538,35 @@ }, "x-go-package": "github.com/prometheus/common/config" }, - "URL": { + "TestRulePayload": { "type": "object", - "title": "URL is a custom URL type that allows validation at configuration load time.", + "properties": { + "expr": { + "$ref": "#/definitions/LotexQuery" + }, + "grafana_condition": { + "$ref": "#/definitions/EvalAlertConditionCommand" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, + "TestRuleResponse": { + "type": "object", + "properties": { + "alerts": { + "type": "object", + "x-go-name": "Alerts" + }, + "grafana_alert_instances": { + "$ref": "#/definitions/AlertInstancesResponse" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, + "URL": { + "description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.", + "type": "object", + "title": "A URL represents a parsed URL (technically, a URI reference).", "properties": { "ForceQuery": { "type": "boolean" @@ -2390,7 +2599,7 @@ "$ref": "#/definitions/Userinfo" } }, - "x-go-package": "github.com/prometheus/common/config" + "x-go-package": "net/url" }, "UpdateDashboardAclCommand": { "type": "object",