diff --git a/go.mod b/go.mod index d8502021986..6a83c09ce1b 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/prometheus/alertmanager v0.21.1-0.20210211203738-a7ca7b1d2951 github.com/prometheus/client_golang v1.9.0 github.com/prometheus/common v0.15.0 + github.com/prometheus/prometheus v1.8.2-0.20201014093524-73e2ce1bd643 golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect diff --git a/pkg/api/testing.go b/pkg/api/testing.go new file mode 100644 index 00000000000..21fe0cb4e0b --- /dev/null +++ b/pkg/api/testing.go @@ -0,0 +1,111 @@ +package api + +import ( + "time" + + "github.com/grafana/grafana/pkg/services/ngalert/eval" + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/prometheus/promql" +) + +// 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 promql.Vector `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..78f7280390d 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" }, @@ -1459,6 +1611,17 @@ "type": "object", "x-go-package": "github.com/grafana/grafana/pkg/components/simplejson" }, + "Label": { + "type": "object", + "title": "Label is a key/value pair of strings.", + "properties": { + "Name": { + "type": "string", + "x-go-name": "Value" + } + }, + "x-go-package": "github.com/prometheus/prometheus/pkg/labels" + }, "LabelName": { "description": "A LabelName is a key for a LabelSet or Metric. It has a value associated\ntherewith.", "type": "string", @@ -1472,6 +1635,29 @@ }, "x-go-package": "github.com/prometheus/common/model" }, + "Labels": { + "description": "Labels is a sorted set of labels. Order has to be guaranteed upon\ninstantiation.", + "type": "array", + "items": { + "$ref": "#/definitions/Label" + }, + "x-go-package": "github.com/prometheus/prometheus/pkg/labels" + }, + "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.", @@ -1728,6 +1914,21 @@ }, "x-go-package": "github.com/grafana/alerting-api/pkg/api" }, + "Point": { + "type": "object", + "title": "Point represents a single data point for a given timestamp.", + "properties": { + "T": { + "type": "integer", + "format": "int64" + }, + "V": { + "type": "number", + "format": "double" + } + }, + "x-go-package": "github.com/prometheus/prometheus/promql" + }, "PushoverConfig": { "type": "object", "properties": { @@ -1868,6 +2069,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" @@ -2084,6 +2295,24 @@ "title": "RuleType models the type of a rule.", "x-go-package": "github.com/prometheus/client_golang/api/prometheus/v1" }, + "Sample": { + "type": "object", + "title": "Sample is a single sample belonging to a metric.", + "properties": { + "Metric": { + "$ref": "#/definitions/Labels" + }, + "T": { + "type": "integer", + "format": "int64" + }, + "V": { + "type": "number", + "format": "double" + } + }, + "x-go-package": "github.com/prometheus/prometheus/promql" + }, "Secret": { "type": "string", "title": "Secret special type for storing secrets.", @@ -2328,6 +2557,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,6 +2590,30 @@ }, "x-go-package": "github.com/prometheus/common/config" }, + "TestRulePayload": { + "type": "object", + "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": { + "$ref": "#/definitions/Vector" + }, + "grafana_alert_instances": { + "$ref": "#/definitions/AlertInstancesResponse" + } + }, + "x-go-package": "github.com/grafana/alerting-api/pkg/api" + }, "URL": { "type": "object", "title": "URL is a custom URL type that allows validation at configuration load time.", @@ -2406,7 +2665,9 @@ "x-go-package": "github.com/grafana/grafana/pkg/api/dtos" }, "UpsertAlertDefinitionCommand": { + "description": "https://github.com/grafana/grafana/blob/debb82e12417e82a0e2bd09e1a450065f884c1bc/pkg/services/ngalert/models.go#L85", "type": "object", + "title": "UpsertAlertDefinitionCommand is copy of the unexported struct:", "properties": { "condition": { "description": "Condition is the refID of the query or expression to be evaluated", @@ -2464,6 +2725,14 @@ }, "x-go-package": "github.com/grafana/alerting-api/pkg/api" }, + "Vector": { + "description": "Vector is basically only an alias for model.Samples, but the\ncontract is that in a Vector, all Samples have the same timestamp.", + "type": "array", + "items": { + "$ref": "#/definitions/Sample" + }, + "x-go-package": "github.com/prometheus/prometheus/promql" + }, "VictorOpsConfig": { "type": "object", "title": "VictorOpsConfig configures notifications via VictorOps.",