Alerting: Remove internal labels from prometheus compatible API responses (#46548)

* Alerting: Remove internal labels from prometheus compatible API responses

* Appease the linter

* Fix integration tests

* Fix API documentation & linter

* move removal of internal labels to the models
This commit is contained in:
gotjosh 2022-03-16 16:04:19 +00:00 committed by GitHub
parent d5883c1b27
commit a338c78ca8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 290 additions and 5735 deletions

View File

@ -9,17 +9,16 @@ import (
"strings"
"time"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/state"
"github.com/grafana/grafana/pkg/services/ngalert/store"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
)
type PrometheusSrv struct {
@ -28,6 +27,8 @@ type PrometheusSrv struct {
store store.RuleStore
}
const queryIncludeInternalLabels = "includeInternalLabels"
func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Response {
alertResponse := apimodels.AlertResponse{
DiscoveryBase: apimodels.DiscoveryBase{
@ -37,6 +38,12 @@ func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Re
Alerts: []*apimodels.Alert{},
},
}
var labelOptions []ngmodels.LabelOption
if !c.QueryBoolWithDefault(queryIncludeInternalLabels, false) {
labelOptions = append(labelOptions, ngmodels.WithoutInternalLabels())
}
for _, alertState := range srv.manager.GetAll(c.OrgId) {
startsAt := alertState.StartsAt
valString := ""
@ -45,7 +52,7 @@ func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Re
}
alertResponse.Data.Alerts = append(alertResponse.Data.Alerts, &apimodels.Alert{
Labels: map[string]string(alertState.Labels),
Labels: alertState.GetLabels(labelOptions...),
Annotations: alertState.Annotations,
State: alertState.State.String(),
ActiveAt: &startsAt,
@ -73,6 +80,11 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
},
}
var labelOptions []ngmodels.LabelOption
if !c.QueryBoolWithDefault(queryIncludeInternalLabels, false) {
labelOptions = append(labelOptions, ngmodels.WithoutInternalLabels())
}
namespaceMap, err := srv.store.GetNamespaces(c.Req.Context(), c.OrgId, c.SignedInUser)
if err != nil {
return ErrResp(http.StatusInternalServerError, err, "failed to get namespaces visible to the user")
@ -157,7 +169,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
newRule := apimodels.Rule{
Name: rule.Title,
Labels: rule.Labels,
Labels: rule.GetLabels(labelOptions...),
Health: "ok",
Type: apiv1.RuleTypeAlerting,
LastEvaluation: time.Time{},
@ -169,8 +181,9 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
if alertState.State == eval.Alerting {
valString = alertState.LastEvaluationString
}
alert := &apimodels.Alert{
Labels: map[string]string(alertState.Labels),
Labels: alertState.GetLabels(labelOptions...),
Annotations: alertState.Annotations,
State: alertState.State.String(),
ActiveAt: &activeAt,

View File

@ -21,19 +21,14 @@ import (
)
func TestRouteGetAlertStatuses(t *testing.T) {
fakeStore := store.NewFakeRuleStore(t)
fakeAlertInstanceManager := NewFakeAlertInstanceManager(t)
orgID := int64(1)
api := PrometheusSrv{
log: log.NewNopLogger(),
manager: fakeAlertInstanceManager,
store: fakeStore,
}
c := &models.ReqContext{SignedInUser: &models.SignedInUser{OrgId: orgID}}
t.Run("with no alerts", func(t *testing.T) {
_, _, api := setupAPI(t)
req, err := http.NewRequest("GET", "/api/v1/alerts", nil)
require.NoError(t, err)
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID}}
r := api.RouteGetAlertStatuses(c)
require.Equal(t, http.StatusOK, r.Status())
require.JSONEq(t, `
@ -47,7 +42,54 @@ func TestRouteGetAlertStatuses(t *testing.T) {
})
t.Run("with two alerts", func(t *testing.T) {
fakeAlertInstanceManager.GenerateAlertInstances(1, util.GenerateShortUID(), 2)
_, fakeAIM, api := setupAPI(t)
fakeAIM.GenerateAlertInstances(1, util.GenerateShortUID(), 2)
req, err := http.NewRequest("GET", "/api/v1/alerts", nil)
require.NoError(t, err)
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID}}
r := api.RouteGetAlertStatuses(c)
require.Equal(t, http.StatusOK, r.Status())
require.JSONEq(t, `
{
"status": "success",
"data": {
"alerts": [{
"labels": {
"alertname": "test_title_0",
"instance_label": "test",
"label": "test"
},
"annotations": {
"annotation": "test"
},
"state": "Normal",
"activeAt": "0001-01-01T00:00:00Z",
"value": ""
}, {
"labels": {
"alertname": "test_title_1",
"instance_label": "test",
"label": "test"
},
"annotations": {
"annotation": "test"
},
"state": "Normal",
"activeAt": "0001-01-01T00:00:00Z",
"value": ""
}]
}
}`, string(r.Body()))
})
t.Run("with the inclusion of internal labels", func(t *testing.T) {
_, fakeAIM, api := setupAPI(t)
fakeAIM.GenerateAlertInstances(orgID, util.GenerateShortUID(), 2)
req, err := http.NewRequest("GET", "/api/v1/alerts?includeInternalLabels=true", nil)
require.NoError(t, err)
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID}}
r := api.RouteGetAlertStatuses(c)
require.Equal(t, http.StatusOK, r.Status())
require.JSONEq(t, `
@ -138,9 +180,63 @@ func TestRouteGetRuleStatuses(t *testing.T) {
"activeAt": "0001-01-01T00:00:00Z",
"value": ""
}],
"labels": null,
"labels": {
"__a_private_label_on_the_rule__": "a_value"
},
"health": "ok",
"type": "alerting",
"lastEvaluation": "2022-03-10T14:01:00Z",
"duration": 180,
"evaluationTime": 60
}],
"interval": 60,
"lastEvaluation": "2022-03-10T14:01:00Z",
"evaluationTime": 0
}]
}
}
`, string(r.Body()))
})
t.Run("with the inclusion of internal Labels", func(t *testing.T) {
fakeStore, fakeAIM, api := setupAPI(t)
generateRuleAndInstanceWithQuery(t, orgID, fakeAIM, fakeStore, withClassicConditionSingleQuery())
req, err := http.NewRequest("GET", "/api/v1/rules?includeInternalLabels=true", nil)
require.NoError(t, err)
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID}}
r := api.RouteGetRuleStatuses(c)
require.Equal(t, http.StatusOK, r.Status())
require.JSONEq(t, `
{
"status": "success",
"data": {
"groups": [{
"name": "rule-group",
"file": "namespaceUID",
"rules": [{
"state": "inactive",
"name": "AlwaysFiring",
"query": "vector(1)",
"alerts": [{
"labels": {
"job": "prometheus",
"__alert_rule_namespace_uid__": "test_namespace_uid",
"__alert_rule_uid__": "test_alert_rule_uid_0"
},
"annotations": {
"severity": "critical"
},
"state": "Normal",
"activeAt": "0001-01-01T00:00:00Z",
"value": ""
}],
"labels": {
"__a_private_label_on_the_rule__": "a_value",
"__alert_rule_uid__": "RuleUID"
},
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "2022-03-10T14:01:00Z",
"duration": 180,
@ -183,9 +279,10 @@ func TestRouteGetRuleStatuses(t *testing.T) {
"activeAt": "0001-01-01T00:00:00Z",
"value": ""
}],
"labels": null,
"labels": {
"__a_private_label_on_the_rule__": "a_value"
},
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "2022-03-10T14:01:00Z",
"duration": 180,
@ -219,7 +316,11 @@ func generateRuleAndInstanceWithQuery(t *testing.T, orgID int64, fakeAIM *fakeAl
rules := ngmodels.GenerateAlertRules(1, ngmodels.AlertRuleGen(withOrgID(orgID), asFixture(), query))
fakeAIM.GenerateAlertInstances(orgID, rules[0].UID, 1, func(s *state.State) *state.State {
s.Labels = data.Labels{"job": "prometheus"}
s.Labels = data.Labels{
"job": "prometheus",
ngmodels.NamespaceUIDLabel: "test_namespace_uid",
ngmodels.RuleUIDLabel: "test_alert_rule_uid_0",
}
s.Annotations = data.Labels{"severity": "critical"}
return s
})
@ -237,7 +338,10 @@ func asFixture() func(r *ngmodels.AlertRule) {
r.NamespaceUID = "namespaceUID"
r.RuleGroup = "rule-group"
r.UID = "RuleUID"
r.Labels = nil
r.Labels = map[string]string{
"__a_private_label_on_the_rule__": "a_value",
ngmodels.RuleUIDLabel: "RuleUID",
}
r.Annotations = nil
r.IntervalSeconds = 60
r.For = 180 * time.Second

View File

@ -297,14 +297,6 @@ type GetSilencesParams struct {
Filter []string `json:"filter"`
}
// swagger:parameters RouteGetRuleStatuses RouteGetGrafanaRuleStatuses
type GetRuleStatusesParams struct {
// in: query
DashboardUID string
// in: query
PanelID int64
}
// swagger:model
type GettableStatus struct {
// cluster

View File

@ -115,10 +115,10 @@ type Rule struct {
Name string `json:"name"`
// required: true
Query string `json:"query"`
Labels overrideLabels `json:"labels"`
Labels overrideLabels `json:"labels,omitempty"`
// required: true
Health string `json:"health"`
LastError string `json:"lastError"`
LastError string `json:"lastError,omitempty"`
// required: true
Type v1.RuleType `json:"type"`
LastEvaluation time.Time `json:"lastEvaluation"`
@ -142,3 +142,31 @@ type Alert struct {
// override the labels type with a map for generation.
// The custom marshaling for labels.Labels ends up doing this anyways.
type overrideLabels map[string]string
// swagger:parameters RouteGetGrafanaAlertStatuses
type GetGrafanaAlertStatusesParams struct {
// Include Grafana specific labels as part of the response.
// in: query
// required: false
// default: false
IncludeInternalLabels bool `json:"includeInternalLabels"`
}
// swagger:parameters RouteGetGrafanaRuleStatuses
type GetGrafanaRuleStatusesParams struct {
// Include Grafana specific labels as part of the response.
// in: query
// required: false
// default: false
IncludeInternalLabels bool `json:"includeInternalLabels"`
// Filter the list of rules to those that belong to the specified dashboard UID.
// in: query
// required: false
DashboardUID string
// Filter the list of rules to those that belong to the specified panel ID. Dashboard UID must be specified.
// in: query
// required: false
PanelID int64
}

View File

@ -2803,6 +2803,7 @@
"x-go-package": "github.com/prometheus/alertmanager/timeinterval"
},
"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.",
"properties": {
"ForceQuery": {
"type": "boolean"
@ -2835,9 +2836,9 @@
"$ref": "#/definitions/Userinfo"
}
},
"title": "URL is a custom URL type that allows validation at configuration load time.",
"title": "A URL represents a parsed URL (technically, a URI reference).",
"type": "object",
"x-go-package": "github.com/prometheus/common/config"
"x-go-package": "net/url"
},
"Userinfo": {
"description": "The Userinfo type is an immutable encapsulation of username and\npassword details for a URL. An existing Userinfo value is guaranteed\nto have a username set (potentially empty, as allowed by RFC 2396),\nand optionally a password.",
@ -3034,6 +3035,7 @@
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models"
},
"alertGroup": {
"description": "AlertGroup alert group",
"properties": {
"alerts": {
"description": "alerts",
@ -3055,9 +3057,7 @@
"labels",
"receiver"
],
"type": "object",
"x-go-name": "AlertGroup",
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models"
"type": "object"
},
"alertGroups": {
"items": {
@ -4615,6 +4615,16 @@
"get": {
"description": "gets the current alerts",
"operationId": "RouteGetGrafanaAlertStatuses",
"parameters": [
{
"default": false,
"description": "Include Grafana specific labels as part of the response.",
"in": "query",
"name": "includeInternalLabels",
"type": "boolean",
"x-go-name": "IncludeInternalLabels"
}
],
"responses": {
"200": {
"description": "AlertResponse",
@ -4634,11 +4644,21 @@
"operationId": "RouteGetGrafanaRuleStatuses",
"parameters": [
{
"default": false,
"description": "Include Grafana specific labels as part of the response.",
"in": "query",
"name": "includeInternalLabels",
"type": "boolean",
"x-go-name": "IncludeInternalLabels"
},
{
"description": "Filter the list of rules to those that belong to the specified dashboard UID.",
"in": "query",
"name": "DashboardUID",
"type": "string"
},
{
"description": "Filter the list of rules to those that belong to the specified panel ID. Dashboard UID must be specified.",
"format": "int64",
"in": "query",
"name": "PanelID",
@ -4690,17 +4710,6 @@
"description": "gets the evaluation statuses of all rules",
"operationId": "RouteGetRuleStatuses",
"parameters": [
{
"in": "query",
"name": "DashboardUID",
"type": "string"
},
{
"format": "int64",
"in": "query",
"name": "PanelID",
"type": "integer"
},
{
"description": "Recipient should be the numeric datasource id",
"format": "int64",

View File

@ -1022,6 +1022,16 @@
"prometheus"
],
"operationId": "RouteGetGrafanaAlertStatuses",
"parameters": [
{
"type": "boolean",
"default": false,
"x-go-name": "IncludeInternalLabels",
"description": "Include Grafana specific labels as part of the response.",
"name": "includeInternalLabels",
"in": "query"
}
],
"responses": {
"200": {
"description": "AlertResponse",
@ -1040,14 +1050,24 @@
],
"operationId": "RouteGetGrafanaRuleStatuses",
"parameters": [
{
"type": "boolean",
"default": false,
"x-go-name": "IncludeInternalLabels",
"description": "Include Grafana specific labels as part of the response.",
"name": "includeInternalLabels",
"in": "query"
},
{
"type": "string",
"description": "Filter the list of rules to those that belong to the specified dashboard UID.",
"name": "DashboardUID",
"in": "query"
},
{
"type": "integer",
"format": "int64",
"description": "Filter the list of rules to those that belong to the specified panel ID. Dashboard UID must be specified.",
"name": "PanelID",
"in": "query"
}
@ -1097,17 +1117,6 @@
],
"operationId": "RouteGetRuleStatuses",
"parameters": [
{
"type": "string",
"name": "DashboardUID",
"in": "query"
},
{
"type": "integer",
"format": "int64",
"name": "PanelID",
"in": "query"
},
{
"type": "integer",
"format": "int64",
@ -4555,8 +4564,9 @@
"x-go-package": "github.com/prometheus/alertmanager/timeinterval"
},
"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": "URL is a custom URL type that allows validation at configuration load time.",
"title": "A URL represents a parsed URL (technically, a URI reference).",
"properties": {
"ForceQuery": {
"type": "boolean"
@ -4589,7 +4599,7 @@
"$ref": "#/definitions/Userinfo"
}
},
"x-go-package": "github.com/prometheus/common/config"
"x-go-package": "net/url"
},
"Userinfo": {
"description": "The Userinfo type is an immutable encapsulation of username and\npassword details for a URL. An existing Userinfo value is guaranteed\nto have a username set (potentially empty, as allowed by RFC 2396),\nand optionally a password.",
@ -4786,6 +4796,7 @@
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models"
},
"alertGroup": {
"description": "AlertGroup alert group",
"type": "object",
"required": [
"alerts",
@ -4808,8 +4819,6 @@
"$ref": "#/definitions/receiver"
}
},
"x-go-name": "AlertGroup",
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models",
"$ref": "#/definitions/alertGroup"
},
"alertGroups": {

View File

@ -18,12 +18,9 @@ var (
// ErrAlertRuleFailedGenerateUniqueUID is an error for failure to generate alert rule UID
ErrAlertRuleFailedGenerateUniqueUID = errors.New("failed to generate alert rule UID")
// ErrCannotEditNamespace is an error returned if the user does not have permissions to edit the namespace
ErrCannotEditNamespace = errors.New("user does not have permissions to edit the namespace")
// ErrRuleGroupNamespaceNotFound
ErrRuleGroupNamespaceNotFound = errors.New("rule group not found under this namespace")
// ErrAlertRuleFailedValidation
ErrAlertRuleFailedValidation = errors.New("invalid alert rule")
// ErrAlertRuleUniqueConstraintViolation
ErrCannotEditNamespace = errors.New("user does not have permissions to edit the namespace")
ErrRuleGroupNamespaceNotFound = errors.New("rule group not found under this namespace")
ErrAlertRuleFailedValidation = errors.New("invalid alert rule")
ErrAlertRuleUniqueConstraintViolation = errors.New("a conflicting alert rule is found: rule title under the same organisation and folder should be unique")
)
@ -77,6 +74,12 @@ const (
OkErrState ExecutionErrorState = "OK"
)
// InternalLabelNameSet are labels that grafana automatically include as part of the labelset.
var InternalLabelNameSet = map[string]struct{}{
RuleUIDLabel: {},
NamespaceUIDLabel: {},
}
const (
RuleUIDLabel = "__alert_rule_uid__"
NamespaceUIDLabel = "__alert_rule_namespace_uid__"
@ -110,6 +113,29 @@ type AlertRule struct {
Labels map[string]string
}
type LabelOption func(map[string]string)
func WithoutInternalLabels() LabelOption {
return func(labels map[string]string) {
for k := range labels {
if _, ok := InternalLabelNameSet[k]; ok {
delete(labels, k)
}
}
}
}
// GetLabels returns the labels specified as part of the alert rule.
func (alertRule *AlertRule) GetLabels(opts ...LabelOption) map[string]string {
labels := alertRule.Labels
for _, opt := range opts {
opt(labels)
}
return labels
}
// Diff calculates diff between two alert rules. Returns nil if two rules are equal. Otherwise, returns cmputil.DiffReport
func (alertRule *AlertRule) Diff(rule *AlertRule, ignore ...string) cmputil.DiffReport {
var reporter cmputil.DiffReporter

View File

@ -47,7 +47,7 @@ func NewEvaluationValues(m map[string]eval.NumberValueCapture) map[string]*float
return result
}
func (a *State) resultNormal(alertRule *ngModels.AlertRule, result eval.Result) {
func (a *State) resultNormal(_ *ngModels.AlertRule, result eval.Result) {
a.Error = result.Error // should be nil since state is not error
if a.State != eval.Normal {
@ -177,3 +177,13 @@ func (a *State) setEndsAt(alertRule *ngModels.AlertRule, result eval.Result) {
a.EndsAt = result.EvaluatedAt.Add(ends * 3)
}
func (a *State) GetLabels(opts ...ngModels.LabelOption) map[string]string {
labels := a.Labels.Copy()
for _, opt := range opts {
opt(labels)
}
return labels
}

View File

@ -245,7 +245,6 @@ func TestPrometheusRules(t *testing.T) {
"label1": "val1"
},
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -253,9 +252,7 @@ func TestPrometheusRules(t *testing.T) {
"state": "inactive",
"name": "AlwaysFiringButSilenced",
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
"labels": null,
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -300,7 +297,6 @@ func TestPrometheusRules(t *testing.T) {
"label1": "val1"
},
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -308,9 +304,7 @@ func TestPrometheusRules(t *testing.T) {
"state": "inactive",
"name": "AlwaysFiringButSilenced",
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
"labels": null,
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -446,9 +440,7 @@ func TestPrometheusRulesFilterByDashboard(t *testing.T) {
"__dashboardUid__": "%s",
"__panelId__": "1"
},
"labels": null,
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -456,9 +448,7 @@ func TestPrometheusRulesFilterByDashboard(t *testing.T) {
"state": "inactive",
"name": "AlwaysFiringButSilenced",
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
"labels": null,
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -485,9 +475,7 @@ func TestPrometheusRulesFilterByDashboard(t *testing.T) {
"__dashboardUid__": "%s",
"__panelId__": "1"
},
"labels": null,
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -694,7 +682,6 @@ func TestPrometheusRulesPermissions(t *testing.T) {
"label1": "val1"
},
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -718,7 +705,6 @@ func TestPrometheusRulesPermissions(t *testing.T) {
"label1": "val1"
},
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0
@ -767,7 +753,6 @@ func TestPrometheusRulesPermissions(t *testing.T) {
"label1": "val1"
},
"health": "ok",
"lastError": "",
"type": "alerting",
"lastEvaluation": "0001-01-01T00:00:00Z",
"evaluationTime": 0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff