mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 16:15:42 -06:00
* add a feature toggle * add the fields for attribute, kind and identifier to permission Co-authored-by: Kalle Persson <kalle.persson@grafana.com> * set the new fields when new permissions are stored * add migrations Co-authored-by: Kalle Persson <kalle.persson@grafana.com> * remove comments * Update pkg/services/accesscontrol/migrator/migrator.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * feedback: put column migrations behind the feature toggle, added an index, changed how wildcard scopes are split * PR feedback: add a comment and revert an accidentally changed file * PR feedback: handle the case with : in resource identifier * switch from checking feature toggle through cfg to checking it through featuremgmt * don't put the column migrations behind a feature toggle after all - this breaks permission queries from db --------- Co-authored-by: Kalle Persson <kalle.persson@grafana.com> Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
1196 lines
39 KiB
Go
1196 lines
39 KiB
Go
package alerting
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/prometheus/common/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/expr"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
datasourceService "github.com/grafana/grafana/pkg/services/datasources/service"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/services/org"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/tests/testinfra"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
)
|
|
|
|
func TestIntegrationAlertRulePermissions(t *testing.T) {
|
|
testinfra.SQLiteIntegrationTest(t)
|
|
|
|
// Setup Grafana and its Database
|
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
DisableLegacyAlerting: true,
|
|
EnableUnifiedAlerting: true,
|
|
DisableAnonymous: true,
|
|
AppModeProduction: true,
|
|
})
|
|
|
|
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
|
permissionsStore := resourcepermissions.NewStore(store, featuremgmt.WithFeatures())
|
|
|
|
// Create a user to make authenticated requests
|
|
userID := createUser(t, store, user.CreateUserCommand{
|
|
DefaultOrgRole: string(org.RoleEditor),
|
|
Password: "password",
|
|
Login: "grafana",
|
|
})
|
|
|
|
apiClient := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
|
|
|
// Create the namespace we'll save our alerts to.
|
|
apiClient.CreateFolder(t, "folder1", "folder1")
|
|
// Create the namespace we'll save our alerts to.
|
|
apiClient.CreateFolder(t, "folder2", "folder2")
|
|
|
|
// Create rule under folder1
|
|
createRule(t, apiClient, "folder1")
|
|
|
|
// Create rule under folder2
|
|
createRule(t, apiClient, "folder2")
|
|
|
|
// With the rules created, let's make sure that rule definitions are stored.
|
|
{
|
|
u := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules", grafanaListedAddr)
|
|
// nolint:gosec
|
|
resp, err := http.Get(u)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, resp.StatusCode, 200)
|
|
|
|
body, _ := rulesNamespaceWithoutVariableValues(t, b)
|
|
expectedGetNamespaceResponseBody := `
|
|
{
|
|
"folder1":[
|
|
{
|
|
"name":"arulegroup",
|
|
"interval":"1m",
|
|
"rules":[
|
|
{
|
|
"annotations": {
|
|
"annotation1": "val1"
|
|
},
|
|
"expr":"",
|
|
"for": "2m",
|
|
"labels": {
|
|
"label1": "val1"
|
|
},
|
|
"grafana_alert":{
|
|
"id":1,
|
|
"orgId":1,
|
|
"title":"rule under folder folder1",
|
|
"condition":"A",
|
|
"data":[
|
|
{
|
|
"refId":"A",
|
|
"queryType":"",
|
|
"relativeTimeRange":{
|
|
"from":18000,
|
|
"to":10800
|
|
},
|
|
"datasourceUid":"__expr__",
|
|
"model":{
|
|
"expression":"2 + 3 \u003E 1",
|
|
"intervalMs":1000,
|
|
"maxDataPoints":43200,
|
|
"type":"math"
|
|
}
|
|
}
|
|
],
|
|
"updated":"2021-02-21T01:10:30Z",
|
|
"intervalSeconds":60,
|
|
"is_paused":false,
|
|
"version":1,
|
|
"uid":"uid",
|
|
"namespace_uid":"nsuid",
|
|
"namespace_id":1,
|
|
"rule_group":"arulegroup",
|
|
"no_data_state":"NoData",
|
|
"exec_err_state":"Alerting"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"folder2":[
|
|
{
|
|
"name":"arulegroup",
|
|
"interval":"1m",
|
|
"rules":[
|
|
{
|
|
"annotations": {
|
|
"annotation1": "val1"
|
|
},
|
|
"expr":"",
|
|
"for": "2m",
|
|
"labels": {
|
|
"label1": "val1"
|
|
},
|
|
"grafana_alert":{
|
|
"id":2,
|
|
"orgId":1,
|
|
"title":"rule under folder folder2",
|
|
"condition":"A",
|
|
"data":[
|
|
{
|
|
"refId":"A",
|
|
"queryType":"",
|
|
"relativeTimeRange":{
|
|
"from":18000,
|
|
"to":10800
|
|
},
|
|
"datasourceUid":"__expr__",
|
|
"model":{
|
|
"expression":"2 + 3 \u003E 1",
|
|
"intervalMs":1000,
|
|
"maxDataPoints":43200,
|
|
"type":"math"
|
|
}
|
|
}
|
|
],
|
|
"updated":"2021-02-21T01:10:30Z",
|
|
"intervalSeconds":60,
|
|
"is_paused":false,
|
|
"version":1,
|
|
"uid":"uid",
|
|
"namespace_uid":"nsuid",
|
|
"namespace_id":2,
|
|
"rule_group":"arulegroup",
|
|
"no_data_state":"NoData",
|
|
"exec_err_state":"Alerting"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}`
|
|
assert.JSONEq(t, expectedGetNamespaceResponseBody, body)
|
|
|
|
// remove permissions from folder2
|
|
removeFolderPermission(t, permissionsStore, 1, userID, org.RoleEditor, "folder2")
|
|
apiClient.ReloadCachedPermissions(t)
|
|
|
|
// make sure that folder2 is not included in the response
|
|
// nolint:gosec
|
|
resp, err = http.Get(u)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err = io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, resp.StatusCode, 200)
|
|
|
|
body, _ = rulesNamespaceWithoutVariableValues(t, b)
|
|
expectedGetNamespaceResponseBody = `
|
|
{
|
|
"folder1":[
|
|
{
|
|
"name":"arulegroup",
|
|
"interval":"1m",
|
|
"rules":[
|
|
{
|
|
"annotations": {
|
|
"annotation1": "val1"
|
|
},
|
|
"expr":"",
|
|
"for": "2m",
|
|
"labels": {
|
|
"label1": "val1"
|
|
},
|
|
"grafana_alert":{
|
|
"id":1,
|
|
"orgId":1,
|
|
"title":"rule under folder folder1",
|
|
"condition":"A",
|
|
"data":[
|
|
{
|
|
"refId":"A",
|
|
"queryType":"",
|
|
"relativeTimeRange":{
|
|
"from":18000,
|
|
"to":10800
|
|
},
|
|
"datasourceUid":"__expr__",
|
|
"model":{
|
|
"expression":"2 + 3 \u003E 1",
|
|
"intervalMs":1000,
|
|
"maxDataPoints":43200,
|
|
"type":"math"
|
|
}
|
|
}
|
|
],
|
|
"updated":"2021-02-21T01:10:30Z",
|
|
"intervalSeconds":60,
|
|
"is_paused":false,
|
|
"version":1,
|
|
"uid":"uid",
|
|
"namespace_uid":"nsuid",
|
|
"namespace_id":1,
|
|
"rule_group":"arulegroup",
|
|
"no_data_state":"NoData",
|
|
"exec_err_state":"Alerting"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}`
|
|
assert.JSONEq(t, expectedGetNamespaceResponseBody, body)
|
|
}
|
|
|
|
// Remove permissions from folder1.
|
|
removeFolderPermission(t, permissionsStore, 1, userID, org.RoleEditor, "folder1")
|
|
apiClient.ReloadCachedPermissions(t)
|
|
{
|
|
u := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules", grafanaListedAddr)
|
|
// nolint:gosec
|
|
resp, err := http.Get(u)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, resp.StatusCode, 200)
|
|
require.JSONEq(t, `{}`, string(b))
|
|
}
|
|
}
|
|
|
|
func createRule(t *testing.T, client apiClient, folder string) {
|
|
t.Helper()
|
|
|
|
interval, err := model.ParseDuration("1m")
|
|
require.NoError(t, err)
|
|
doubleInterval := 2 * interval
|
|
rules := apimodels.PostableRuleGroupConfig{
|
|
Name: "arulegroup",
|
|
Interval: interval,
|
|
Rules: []apimodels.PostableExtendedRuleNode{
|
|
{
|
|
ApiRuleNode: &apimodels.ApiRuleNode{
|
|
For: &doubleInterval,
|
|
Labels: map[string]string{"label1": "val1"},
|
|
Annotations: map[string]string{"annotation1": "val1"},
|
|
},
|
|
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
|
Title: fmt.Sprintf("rule under folder %s", folder),
|
|
Condition: "A",
|
|
Data: []apimodels.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
RelativeTimeRange: apimodels.RelativeTimeRange{
|
|
From: apimodels.Duration(time.Duration(5) * time.Hour),
|
|
To: apimodels.Duration(time.Duration(3) * time.Hour),
|
|
},
|
|
DatasourceUID: expr.DatasourceUID,
|
|
Model: json.RawMessage(`{
|
|
"type": "math",
|
|
"expression": "2 + 3 > 1"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
status, body := client.PostRulesGroup(t, folder, &rules)
|
|
assert.Equal(t, http.StatusAccepted, status)
|
|
require.JSONEq(t, `{"message":"rule group updated successfully"}`, body)
|
|
}
|
|
|
|
func TestIntegrationAlertRuleConflictingTitle(t *testing.T) {
|
|
testinfra.SQLiteIntegrationTest(t)
|
|
|
|
// Setup Grafana and its Database
|
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
DisableLegacyAlerting: true,
|
|
EnableUnifiedAlerting: true,
|
|
EnableQuota: true,
|
|
DisableAnonymous: true,
|
|
ViewersCanEdit: true,
|
|
AppModeProduction: true,
|
|
})
|
|
|
|
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
|
|
|
// Create user
|
|
createUser(t, store, user.CreateUserCommand{
|
|
DefaultOrgRole: string(org.RoleAdmin),
|
|
Password: "admin",
|
|
Login: "admin",
|
|
})
|
|
|
|
apiClient := newAlertingApiClient(grafanaListedAddr, "admin", "admin")
|
|
|
|
// Create the namespace we'll save our alerts to.
|
|
apiClient.CreateFolder(t, "folder1", "folder1")
|
|
// Create the namespace we'll save our alerts to.
|
|
apiClient.CreateFolder(t, "folder2", "folder2")
|
|
|
|
rules := newTestingRuleConfig(t)
|
|
|
|
status, body := apiClient.PostRulesGroup(t, "folder1", &rules)
|
|
assert.Equal(t, http.StatusAccepted, status)
|
|
require.JSONEq(t, `{"message":"rule group updated successfully"}`, body)
|
|
|
|
// fetch the created rules, so we can get the uid's and trigger
|
|
// and update by reusing the uid's
|
|
createdRuleGroup := apiClient.GetRulesGroup(t, "folder1", rules.Name).GettableRuleGroupConfig
|
|
require.Len(t, createdRuleGroup.Rules, 2)
|
|
|
|
t.Run("trying to create alert with same title under same folder should fail", func(t *testing.T) {
|
|
rulesWithUID := convertGettableRuleGroupToPostable(createdRuleGroup)
|
|
rulesWithUID.Rules = append(rulesWithUID.Rules, rules.Rules[0]) // Create new copy of first rule.
|
|
|
|
status, body := apiClient.PostRulesGroup(t, "folder1", &rulesWithUID)
|
|
assert.Equal(t, http.StatusInternalServerError, status)
|
|
|
|
var res map[string]interface{}
|
|
require.NoError(t, json.Unmarshal([]byte(body), &res))
|
|
require.Equal(t, "failed to update rule group: failed to add rules: a conflicting alert rule is found: rule title under the same organisation and folder should be unique", res["message"])
|
|
})
|
|
|
|
t.Run("trying to update an alert to the title of an existing alert in the same folder should fail", func(t *testing.T) {
|
|
rulesWithUID := convertGettableRuleGroupToPostable(createdRuleGroup)
|
|
rulesWithUID.Rules[1].GrafanaManagedAlert.Title = "AlwaysFiring"
|
|
|
|
status, body := apiClient.PostRulesGroup(t, "folder1", &rulesWithUID)
|
|
assert.Equal(t, http.StatusInternalServerError, status)
|
|
|
|
var res map[string]interface{}
|
|
require.NoError(t, json.Unmarshal([]byte(body), &res))
|
|
require.Equal(t, "failed to update rule group: failed to update rules: a conflicting alert rule is found: rule title under the same organisation and folder should be unique", res["message"])
|
|
})
|
|
|
|
t.Run("trying to create alert with same title under another folder should succeed", func(t *testing.T) {
|
|
rules := newTestingRuleConfig(t)
|
|
status, body := apiClient.PostRulesGroup(t, "folder2", &rules)
|
|
assert.Equal(t, http.StatusAccepted, status)
|
|
require.JSONEq(t, `{"message":"rule group updated successfully"}`, body)
|
|
})
|
|
|
|
t.Run("trying to swap titles of existing alerts in the same folder should work", func(t *testing.T) {
|
|
rulesWithUID := convertGettableRuleGroupToPostable(createdRuleGroup)
|
|
title0 := rulesWithUID.Rules[0].GrafanaManagedAlert.Title
|
|
title1 := rulesWithUID.Rules[1].GrafanaManagedAlert.Title
|
|
rulesWithUID.Rules[0].GrafanaManagedAlert.Title = title1
|
|
rulesWithUID.Rules[1].GrafanaManagedAlert.Title = title0
|
|
|
|
status, body := apiClient.PostRulesGroup(t, "folder1", &rulesWithUID)
|
|
assert.Equal(t, http.StatusAccepted, status)
|
|
require.JSONEq(t, `{"message":"rule group updated successfully"}`, body)
|
|
})
|
|
|
|
t.Run("trying to update titles of existing alerts in a chain in the same folder should work", func(t *testing.T) {
|
|
rulesWithUID := convertGettableRuleGroupToPostable(createdRuleGroup)
|
|
rulesWithUID.Rules[0].GrafanaManagedAlert.Title = rulesWithUID.Rules[1].GrafanaManagedAlert.Title
|
|
rulesWithUID.Rules[1].GrafanaManagedAlert.Title = "something new"
|
|
|
|
status, body := apiClient.PostRulesGroup(t, "folder1", &rulesWithUID)
|
|
assert.Equal(t, http.StatusAccepted, status)
|
|
require.JSONEq(t, `{"message":"rule group updated successfully"}`, body)
|
|
})
|
|
}
|
|
|
|
func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) {
|
|
testinfra.SQLiteIntegrationTest(t)
|
|
|
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
EnableFeatureToggles: []string{"ngalert"},
|
|
DisableAnonymous: true,
|
|
AppModeProduction: true,
|
|
})
|
|
|
|
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
|
|
|
// Create a user to make authenticated requests
|
|
createUser(t, store, user.CreateUserCommand{
|
|
DefaultOrgRole: string(org.RoleEditor),
|
|
Password: "password",
|
|
Login: "grafana",
|
|
})
|
|
|
|
apiClient := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
|
|
|
dashboardUID := "default"
|
|
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
|
|
apiClient.CreateFolder(t, "default", "default")
|
|
|
|
interval, err := model.ParseDuration("10s")
|
|
require.NoError(t, err)
|
|
|
|
// Now, let's create some rules
|
|
{
|
|
rules := apimodels.PostableRuleGroupConfig{
|
|
Name: "anotherrulegroup",
|
|
Rules: []apimodels.PostableExtendedRuleNode{
|
|
{
|
|
ApiRuleNode: &apimodels.ApiRuleNode{
|
|
For: &interval,
|
|
Labels: map[string]string{},
|
|
Annotations: map[string]string{
|
|
"__dashboardUid__": dashboardUID,
|
|
"__panelId__": "1",
|
|
},
|
|
},
|
|
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
|
Title: "AlwaysFiring",
|
|
Condition: "A",
|
|
Data: []apimodels.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
RelativeTimeRange: apimodels.RelativeTimeRange{
|
|
From: apimodels.Duration(time.Duration(5) * time.Hour),
|
|
To: apimodels.Duration(time.Duration(3) * time.Hour),
|
|
},
|
|
DatasourceUID: expr.DatasourceUID,
|
|
Model: json.RawMessage(`{
|
|
"type": "math",
|
|
"expression": "2 + 3 > 1"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
|
Title: "AlwaysFiringButSilenced",
|
|
Condition: "A",
|
|
Data: []apimodels.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
RelativeTimeRange: apimodels.RelativeTimeRange{
|
|
From: apimodels.Duration(time.Duration(5) * time.Hour),
|
|
To: apimodels.Duration(time.Duration(3) * time.Hour),
|
|
},
|
|
DatasourceUID: expr.DatasourceUID,
|
|
Model: json.RawMessage(`{
|
|
"type": "math",
|
|
"expression": "2 + 3 > 1"
|
|
}`),
|
|
},
|
|
},
|
|
NoDataState: apimodels.NoDataState(ngmodels.Alerting),
|
|
ExecErrState: apimodels.ExecutionErrorState(ngmodels.AlertingErrState),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
status, body := apiClient.PostRulesGroup(t, "default", &rules)
|
|
assert.Equal(t, http.StatusAccepted, status)
|
|
require.JSONEq(t, `{"message":"rule group updated successfully"}`, body)
|
|
}
|
|
|
|
expectedAllJSON := fmt.Sprintf(`
|
|
{
|
|
"default": [{
|
|
"name": "anotherrulegroup",
|
|
"interval": "1m",
|
|
"rules": [{
|
|
"expr": "",
|
|
"for": "10s",
|
|
"annotations": {
|
|
"__dashboardUid__": "%s",
|
|
"__panelId__": "1"
|
|
},
|
|
"grafana_alert": {
|
|
"id": 1,
|
|
"orgId": 1,
|
|
"title": "AlwaysFiring",
|
|
"condition": "A",
|
|
"data": [{
|
|
"refId": "A",
|
|
"queryType": "",
|
|
"relativeTimeRange": {
|
|
"from": 18000,
|
|
"to": 10800
|
|
},
|
|
"datasourceUid": "__expr__",
|
|
"model": {
|
|
"expression": "2 + 3 \u003e 1",
|
|
"intervalMs": 1000,
|
|
"maxDataPoints": 43200,
|
|
"type": "math"
|
|
}
|
|
}],
|
|
"updated": "2021-02-21T01:10:30Z",
|
|
"intervalSeconds": 60,
|
|
"is_paused": false,
|
|
"version": 1,
|
|
"uid": "uid",
|
|
"namespace_uid": "nsuid",
|
|
"namespace_id": 1,
|
|
"rule_group": "anotherrulegroup",
|
|
"no_data_state": "NoData",
|
|
"exec_err_state": "Alerting"
|
|
}
|
|
}, {
|
|
"expr": "",
|
|
"for":"0s",
|
|
"grafana_alert": {
|
|
"id": 2,
|
|
"orgId": 1,
|
|
"title": "AlwaysFiringButSilenced",
|
|
"condition": "A",
|
|
"data": [{
|
|
"refId": "A",
|
|
"queryType": "",
|
|
"relativeTimeRange": {
|
|
"from": 18000,
|
|
"to": 10800
|
|
},
|
|
"datasourceUid": "__expr__",
|
|
"model": {
|
|
"expression": "2 + 3 \u003e 1",
|
|
"intervalMs": 1000,
|
|
"maxDataPoints": 43200,
|
|
"type": "math"
|
|
}
|
|
}],
|
|
"updated": "2021-02-21T01:10:30Z",
|
|
"intervalSeconds": 60,
|
|
"is_paused": false,
|
|
"version": 1,
|
|
"uid": "uid",
|
|
"namespace_uid": "nsuid",
|
|
"namespace_id": 1,
|
|
"rule_group": "anotherrulegroup",
|
|
"no_data_state": "Alerting",
|
|
"exec_err_state": "Alerting"
|
|
}
|
|
}]
|
|
}]
|
|
}`, dashboardUID)
|
|
expectedFilteredByJSON := fmt.Sprintf(`
|
|
{
|
|
"default": [{
|
|
"name": "anotherrulegroup",
|
|
"interval": "1m",
|
|
"rules": [{
|
|
"expr": "",
|
|
"for": "10s",
|
|
"annotations": {
|
|
"__dashboardUid__": "%s",
|
|
"__panelId__": "1"
|
|
},
|
|
"grafana_alert": {
|
|
"id": 1,
|
|
"orgId": 1,
|
|
"title": "AlwaysFiring",
|
|
"condition": "A",
|
|
"data": [{
|
|
"refId": "A",
|
|
"queryType": "",
|
|
"relativeTimeRange": {
|
|
"from": 18000,
|
|
"to": 10800
|
|
},
|
|
"datasourceUid": "__expr__",
|
|
"model": {
|
|
"expression": "2 + 3 \u003e 1",
|
|
"intervalMs": 1000,
|
|
"maxDataPoints": 43200,
|
|
"type": "math"
|
|
}
|
|
}],
|
|
"updated": "2021-02-21T01:10:30Z",
|
|
"intervalSeconds": 60,
|
|
"is_paused": false,
|
|
"version": 1,
|
|
"uid": "uid",
|
|
"namespace_uid": "nsuid",
|
|
"namespace_id": 1,
|
|
"rule_group": "anotherrulegroup",
|
|
"no_data_state": "NoData",
|
|
"exec_err_state": "Alerting"
|
|
}
|
|
}]
|
|
}]
|
|
}`, dashboardUID)
|
|
expectedNoneJSON := `{}`
|
|
|
|
// Now, let's see how this looks like.
|
|
{
|
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules", grafanaListedAddr)
|
|
// nolint:gosec
|
|
resp, err := http.Get(promRulesURL)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
|
|
body, _ := rulesNamespaceWithoutVariableValues(t, b)
|
|
require.JSONEq(t, expectedAllJSON, body)
|
|
}
|
|
|
|
// Now, let's check we get the same rule when filtering by dashboard_uid
|
|
{
|
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules?dashboard_uid=%s", grafanaListedAddr, dashboardUID)
|
|
// nolint:gosec
|
|
resp, err := http.Get(promRulesURL)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
|
|
body, _ := rulesNamespaceWithoutVariableValues(t, b)
|
|
require.JSONEq(t, expectedFilteredByJSON, body)
|
|
}
|
|
|
|
// Now, let's check we get no rules when filtering by an unknown dashboard_uid
|
|
{
|
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules?dashboard_uid=%s", grafanaListedAddr, "abc")
|
|
// nolint:gosec
|
|
resp, err := http.Get(promRulesURL)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
|
|
require.JSONEq(t, expectedNoneJSON, string(b))
|
|
}
|
|
|
|
// Now, let's check we get the same rule when filtering by dashboard_uid and panel_id
|
|
{
|
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules?dashboard_uid=%s&panel_id=1", grafanaListedAddr, dashboardUID)
|
|
// nolint:gosec
|
|
resp, err := http.Get(promRulesURL)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
|
|
body, _ := rulesNamespaceWithoutVariableValues(t, b)
|
|
require.JSONEq(t, expectedFilteredByJSON, body)
|
|
}
|
|
|
|
// Now, let's check we get no rules when filtering by dashboard_uid and unknown panel_id
|
|
{
|
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules?dashboard_uid=%s&panel_id=2", grafanaListedAddr, dashboardUID)
|
|
// nolint:gosec
|
|
resp, err := http.Get(promRulesURL)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 200, resp.StatusCode)
|
|
|
|
require.JSONEq(t, expectedNoneJSON, string(b))
|
|
}
|
|
|
|
// Now, let's check an invalid panel_id returns a 400 Bad Request response
|
|
{
|
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules?dashboard_uid=%s&panel_id=invalid", grafanaListedAddr, dashboardUID)
|
|
// nolint:gosec
|
|
resp, err := http.Get(promRulesURL)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
var res map[string]interface{}
|
|
require.NoError(t, json.Unmarshal(b, &res))
|
|
require.Equal(t, `invalid panel_id: strconv.ParseInt: parsing "invalid": invalid syntax`, res["message"])
|
|
}
|
|
|
|
// Now, let's check a panel_id without dashboard_uid returns a 400 Bad Request response
|
|
{
|
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules?panel_id=1", grafanaListedAddr)
|
|
// nolint:gosec
|
|
resp, err := http.Get(promRulesURL)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := resp.Body.Close()
|
|
require.NoError(t, err)
|
|
})
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
var res map[string]interface{}
|
|
require.NoError(t, json.Unmarshal(b, &res))
|
|
require.Equal(t, "panel_id must be set with dashboard_uid", res["message"])
|
|
}
|
|
}
|
|
|
|
func TestIntegrationRuleGroupSequence(t *testing.T) {
|
|
testinfra.SQLiteIntegrationTest(t)
|
|
|
|
// Setup Grafana and its Database
|
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
DisableLegacyAlerting: true,
|
|
EnableUnifiedAlerting: true,
|
|
DisableAnonymous: true,
|
|
AppModeProduction: true,
|
|
})
|
|
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
|
|
|
// Create a user to make authenticated requests
|
|
createUser(t, store, user.CreateUserCommand{
|
|
DefaultOrgRole: string(org.RoleEditor),
|
|
Password: "password",
|
|
Login: "grafana",
|
|
})
|
|
|
|
client := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
|
folder1Title := "folder1"
|
|
client.CreateFolder(t, util.GenerateShortUID(), folder1Title)
|
|
|
|
group1 := generateAlertRuleGroup(5, alertRuleGen())
|
|
group2 := generateAlertRuleGroup(5, alertRuleGen())
|
|
|
|
status, _ := client.PostRulesGroup(t, folder1Title, &group1)
|
|
require.Equal(t, http.StatusAccepted, status)
|
|
status, _ = client.PostRulesGroup(t, folder1Title, &group2)
|
|
require.Equal(t, http.StatusAccepted, status)
|
|
|
|
t.Run("should persist order of the rules in a group", func(t *testing.T) {
|
|
group1Get := client.GetRulesGroup(t, folder1Title, group1.Name)
|
|
assert.Equal(t, group1.Name, group1Get.Name)
|
|
assert.Equal(t, group1.Interval, group1Get.Interval)
|
|
assert.Len(t, group1Get.Rules, len(group1.Rules))
|
|
for i, getRule := range group1Get.Rules {
|
|
rule := group1.Rules[i]
|
|
assert.Equal(t, getRule.GrafanaManagedAlert.Title, rule.GrafanaManagedAlert.Title)
|
|
assert.NotEmpty(t, getRule.GrafanaManagedAlert.UID)
|
|
}
|
|
|
|
// now shuffle the rules
|
|
postableGroup1 := convertGettableRuleGroupToPostable(group1Get.GettableRuleGroupConfig)
|
|
rand.Shuffle(len(postableGroup1.Rules), func(i, j int) {
|
|
postableGroup1.Rules[i], postableGroup1.Rules[j] = postableGroup1.Rules[j], postableGroup1.Rules[i]
|
|
})
|
|
expectedUids := make([]string, 0, len(postableGroup1.Rules))
|
|
for _, rule := range postableGroup1.Rules {
|
|
expectedUids = append(expectedUids, rule.GrafanaManagedAlert.UID)
|
|
}
|
|
status, _ := client.PostRulesGroup(t, folder1Title, &postableGroup1)
|
|
require.Equal(t, http.StatusAccepted, status)
|
|
|
|
group1Get = client.GetRulesGroup(t, folder1Title, group1.Name)
|
|
|
|
require.Len(t, group1Get.Rules, len(postableGroup1.Rules))
|
|
|
|
actualUids := make([]string, 0, len(group1Get.Rules))
|
|
for _, getRule := range group1Get.Rules {
|
|
actualUids = append(actualUids, getRule.GrafanaManagedAlert.UID)
|
|
}
|
|
assert.Equal(t, expectedUids, actualUids)
|
|
})
|
|
|
|
t.Run("should be able to move a rule from another group in a specific position", func(t *testing.T) {
|
|
group1Get := client.GetRulesGroup(t, folder1Title, group1.Name)
|
|
group2Get := client.GetRulesGroup(t, folder1Title, group2.Name)
|
|
|
|
movedRule := convertGettableRuleToPostable(group2Get.Rules[3])
|
|
// now shuffle the rules
|
|
postableGroup1 := convertGettableRuleGroupToPostable(group1Get.GettableRuleGroupConfig)
|
|
postableGroup1.Rules = append(append(append([]apimodels.PostableExtendedRuleNode{}, postableGroup1.Rules[0:1]...), movedRule), postableGroup1.Rules[2:]...)
|
|
expectedUids := make([]string, 0, len(postableGroup1.Rules))
|
|
for _, rule := range postableGroup1.Rules {
|
|
expectedUids = append(expectedUids, rule.GrafanaManagedAlert.UID)
|
|
}
|
|
status, _ := client.PostRulesGroup(t, folder1Title, &postableGroup1)
|
|
require.Equal(t, http.StatusAccepted, status)
|
|
|
|
group1Get = client.GetRulesGroup(t, folder1Title, group1.Name)
|
|
|
|
require.Len(t, group1Get.Rules, len(postableGroup1.Rules))
|
|
|
|
actualUids := make([]string, 0, len(group1Get.Rules))
|
|
for _, getRule := range group1Get.Rules {
|
|
actualUids = append(actualUids, getRule.GrafanaManagedAlert.UID)
|
|
}
|
|
assert.Equal(t, expectedUids, actualUids)
|
|
|
|
group2Get = client.GetRulesGroup(t, folder1Title, group2.Name)
|
|
assert.Len(t, group2Get.Rules, len(group2.Rules)-1)
|
|
for _, rule := range group2Get.Rules {
|
|
require.NotEqual(t, movedRule.GrafanaManagedAlert.UID, rule.GrafanaManagedAlert.UID)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestIntegrationRuleUpdate(t *testing.T) {
|
|
testinfra.SQLiteIntegrationTest(t)
|
|
|
|
// Setup Grafana and its Database
|
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
DisableLegacyAlerting: true,
|
|
EnableUnifiedAlerting: true,
|
|
DisableAnonymous: true,
|
|
AppModeProduction: true,
|
|
})
|
|
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
|
permissionsStore := resourcepermissions.NewStore(store, featuremgmt.WithFeatures())
|
|
|
|
// Create a user to make authenticated requests
|
|
userID := createUser(t, store, user.CreateUserCommand{
|
|
DefaultOrgRole: string(org.RoleEditor),
|
|
Password: "password",
|
|
Login: "grafana",
|
|
})
|
|
|
|
if setting.IsEnterprise {
|
|
// add blanket access to data sources.
|
|
_, err := permissionsStore.SetUserResourcePermission(context.Background(),
|
|
1,
|
|
accesscontrol.User{ID: userID},
|
|
resourcepermissions.SetResourcePermissionCommand{
|
|
Actions: []string{
|
|
datasources.ActionQuery,
|
|
},
|
|
Resource: datasources.ScopeRoot,
|
|
ResourceID: "*",
|
|
ResourceAttribute: "uid",
|
|
}, nil)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create a user to make authenticated requests
|
|
createUser(t, store, user.CreateUserCommand{
|
|
DefaultOrgRole: string(org.RoleAdmin),
|
|
Password: "admin",
|
|
Login: "admin",
|
|
})
|
|
|
|
adminClient := newAlertingApiClient(grafanaListedAddr, "admin", "admin")
|
|
|
|
client := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
|
folder1Title := "folder1"
|
|
client.CreateFolder(t, util.GenerateShortUID(), folder1Title)
|
|
|
|
t.Run("should be able to reset 'for' to 0", func(t *testing.T) {
|
|
group := generateAlertRuleGroup(1, alertRuleGen())
|
|
expected := model.Duration(10 * time.Second)
|
|
group.Rules[0].ApiRuleNode.For = &expected
|
|
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
getGroup := client.GetRulesGroup(t, folder1Title, group.Name)
|
|
require.Equal(t, expected, *getGroup.Rules[0].ApiRuleNode.For)
|
|
|
|
group = convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
expected = 0
|
|
group.Rules[0].ApiRuleNode.For = &expected
|
|
status, body = client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
|
|
getGroup = client.GetRulesGroup(t, folder1Title, group.Name)
|
|
require.Equal(t, expected, *getGroup.Rules[0].ApiRuleNode.For)
|
|
})
|
|
t.Run("when data source missing", func(t *testing.T) {
|
|
var groupName string
|
|
{
|
|
ds1 := adminClient.CreateTestDatasource(t)
|
|
group := generateAlertRuleGroup(3, alertRuleGen(withDatasourceQuery(ds1.Body.Datasource.UID)))
|
|
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
|
|
getGroup := client.GetRulesGroup(t, folder1Title, group.Name)
|
|
group = convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
|
|
require.Len(t, group.Rules, 3)
|
|
|
|
adminClient.DeleteDatasource(t, ds1.Body.Datasource.UID)
|
|
|
|
// expire datasource caching
|
|
<-time.After(datasourceService.DefaultCacheTTL + 1*time.Second) // TODO delete when TTL could be configured
|
|
|
|
groupName = group.Name
|
|
}
|
|
|
|
t.Run("noop should not fail", func(t *testing.T) {
|
|
getGroup := client.GetRulesGroup(t, folder1Title, groupName)
|
|
group := convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post noop rule group. Response: %s", body)
|
|
})
|
|
t.Run("should not let update rule if it does not fix datasource", func(t *testing.T) {
|
|
getGroup := client.GetRulesGroup(t, folder1Title, groupName)
|
|
group := convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
|
|
group.Rules[0].GrafanaManagedAlert.Title = uuid.NewString()
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
|
|
if status == http.StatusAccepted {
|
|
getGroup = client.GetRulesGroup(t, folder1Title, group.Name)
|
|
assert.NotEqualf(t, group.Rules[0].GrafanaManagedAlert.Title, getGroup.Rules[0].GrafanaManagedAlert.Title, "group was updated")
|
|
}
|
|
require.Equalf(t, http.StatusBadRequest, status, "expected BadRequest. Response: %s", body)
|
|
assert.Contains(t, body, "data source not found")
|
|
})
|
|
t.Run("should let delete broken rule", func(t *testing.T) {
|
|
getGroup := client.GetRulesGroup(t, folder1Title, groupName)
|
|
group := convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
|
|
// remove the last rule.
|
|
group.Rules = group.Rules[0 : len(group.Rules)-1]
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to delete last rule from group. Response: %s", body)
|
|
|
|
getGroup = client.GetRulesGroup(t, folder1Title, group.Name)
|
|
group = convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
require.Len(t, group.Rules, 2)
|
|
})
|
|
t.Run("should let fix single rule", func(t *testing.T) {
|
|
getGroup := client.GetRulesGroup(t, folder1Title, groupName)
|
|
group := convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
|
|
ds2 := adminClient.CreateTestDatasource(t)
|
|
withDatasourceQuery(ds2.Body.Datasource.UID)(&group.Rules[0])
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post noop rule group. Response: %s", body)
|
|
|
|
getGroup = client.GetRulesGroup(t, folder1Title, group.Name)
|
|
group = convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
require.Equal(t, ds2.Body.Datasource.UID, group.Rules[0].GrafanaManagedAlert.Data[0].DatasourceUID)
|
|
})
|
|
t.Run("should let delete group", func(t *testing.T) {
|
|
status, body := client.DeleteRulesGroup(t, folder1Title, groupName)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post noop rule group. Response: %s", body)
|
|
})
|
|
})
|
|
}
|
|
|
|
func newTestingRuleConfig(t *testing.T) apimodels.PostableRuleGroupConfig {
|
|
interval, err := model.ParseDuration("1m")
|
|
require.NoError(t, err)
|
|
|
|
firstRule := apimodels.PostableExtendedRuleNode{
|
|
ApiRuleNode: &apimodels.ApiRuleNode{
|
|
For: &interval,
|
|
Labels: map[string]string{"label1": "val1"},
|
|
Annotations: map[string]string{"annotation1": "val1"},
|
|
},
|
|
// this rule does not explicitly set no data and error states
|
|
// therefore it should get the default values
|
|
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
|
Title: "AlwaysFiring",
|
|
Condition: "A",
|
|
Data: []apimodels.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
RelativeTimeRange: apimodels.RelativeTimeRange{
|
|
From: apimodels.Duration(time.Duration(5) * time.Hour),
|
|
To: apimodels.Duration(time.Duration(3) * time.Hour),
|
|
},
|
|
DatasourceUID: expr.DatasourceUID,
|
|
Model: json.RawMessage(`{
|
|
"type": "math",
|
|
"expression": "2 + 3 > 1"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
secondRule := apimodels.PostableExtendedRuleNode{
|
|
ApiRuleNode: &apimodels.ApiRuleNode{
|
|
For: &interval,
|
|
Labels: map[string]string{"label1": "val1"},
|
|
Annotations: map[string]string{"annotation1": "val1"},
|
|
},
|
|
// this rule does not explicitly set no data and error states
|
|
// therefore it should get the default values
|
|
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
|
Title: "AlwaysFiring2",
|
|
Condition: "A",
|
|
Data: []apimodels.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
RelativeTimeRange: apimodels.RelativeTimeRange{
|
|
From: apimodels.Duration(time.Duration(5) * time.Hour),
|
|
To: apimodels.Duration(time.Duration(3) * time.Hour),
|
|
},
|
|
DatasourceUID: expr.DatasourceUID,
|
|
Model: json.RawMessage(`{
|
|
"type": "math",
|
|
"expression": "2 + 3 > 1"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return apimodels.PostableRuleGroupConfig{
|
|
Name: "arulegroup",
|
|
Rules: []apimodels.PostableExtendedRuleNode{
|
|
firstRule,
|
|
secondRule,
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestIntegrationRulePause(t *testing.T) {
|
|
testinfra.SQLiteIntegrationTest(t)
|
|
|
|
// Setup Grafana and its Database
|
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
|
DisableLegacyAlerting: true,
|
|
EnableUnifiedAlerting: true,
|
|
DisableAnonymous: true,
|
|
AppModeProduction: true,
|
|
})
|
|
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
|
|
|
// Create a user to make authenticated requests
|
|
createUser(t, store, user.CreateUserCommand{
|
|
DefaultOrgRole: string(org.RoleEditor),
|
|
Password: "password",
|
|
Login: "grafana",
|
|
})
|
|
|
|
client := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
|
folder1Title := "folder1"
|
|
client.CreateFolder(t, util.GenerateShortUID(), folder1Title)
|
|
|
|
t.Run("should create a paused rule if isPaused is true", func(t *testing.T) {
|
|
group := generateAlertRuleGroup(1, alertRuleGen())
|
|
expectedIsPaused := true
|
|
group.Rules[0].GrafanaManagedAlert.IsPaused = &expectedIsPaused
|
|
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
getGroup := client.GetRulesGroup(t, folder1Title, group.Name)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to get rule group. Response: %s", body)
|
|
require.Equal(t, expectedIsPaused, getGroup.Rules[0].GrafanaManagedAlert.IsPaused)
|
|
})
|
|
|
|
t.Run("should create a unpaused rule if isPaused is false", func(t *testing.T) {
|
|
group := generateAlertRuleGroup(1, alertRuleGen())
|
|
expectedIsPaused := false
|
|
group.Rules[0].GrafanaManagedAlert.IsPaused = &expectedIsPaused
|
|
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
getGroup := client.GetRulesGroup(t, folder1Title, group.Name)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to get rule group. Response: %s", body)
|
|
require.Equal(t, expectedIsPaused, getGroup.Rules[0].GrafanaManagedAlert.IsPaused)
|
|
})
|
|
|
|
t.Run("should create a unpaused rule if isPaused is not present", func(t *testing.T) {
|
|
group := generateAlertRuleGroup(1, alertRuleGen())
|
|
group.Rules[0].GrafanaManagedAlert.IsPaused = nil
|
|
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
getGroup := client.GetRulesGroup(t, folder1Title, group.Name)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to get rule group. Response: %s", body)
|
|
require.False(t, getGroup.Rules[0].GrafanaManagedAlert.IsPaused)
|
|
})
|
|
|
|
getBooleanPointer := func(b bool) *bool { return &b }
|
|
testCases := []struct {
|
|
description string
|
|
isPausedInDb bool
|
|
isPausedInBody *bool
|
|
expectedIsPausedInDb bool
|
|
}{
|
|
{
|
|
description: "should pause rule if there is a paused rule in DB and isPaused is true",
|
|
isPausedInDb: true,
|
|
isPausedInBody: getBooleanPointer(true),
|
|
expectedIsPausedInDb: true,
|
|
},
|
|
{
|
|
description: "should unpause rule if there is a paused rule in DB and isPaused is false",
|
|
isPausedInDb: true,
|
|
isPausedInBody: getBooleanPointer(false),
|
|
expectedIsPausedInDb: false,
|
|
},
|
|
{
|
|
description: "should keep rule paused if there is a paused rule in DB and isPaused is not present",
|
|
isPausedInDb: true,
|
|
isPausedInBody: nil,
|
|
expectedIsPausedInDb: true,
|
|
},
|
|
{
|
|
description: "should pause rule if there is an unpaused rule in DB and isPaused is true",
|
|
isPausedInDb: false,
|
|
isPausedInBody: getBooleanPointer(true),
|
|
expectedIsPausedInDb: true,
|
|
},
|
|
{
|
|
description: "should unpause rule if there is an unpaused rule in DB and isPaused is false",
|
|
isPausedInDb: false,
|
|
isPausedInBody: getBooleanPointer(false),
|
|
expectedIsPausedInDb: false,
|
|
},
|
|
{
|
|
description: "should keep rule unpaused if there is an unpaused rule in DB and isPaused is not present",
|
|
isPausedInDb: false,
|
|
isPausedInBody: nil,
|
|
expectedIsPausedInDb: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
group := generateAlertRuleGroup(1, alertRuleGen())
|
|
group.Rules[0].GrafanaManagedAlert.IsPaused = &tc.isPausedInDb
|
|
|
|
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
getGroup := client.GetRulesGroup(t, folder1Title, group.Name)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to get rule group. Response: %s", body)
|
|
|
|
group = convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
|
group.Rules[0].GrafanaManagedAlert.IsPaused = tc.isPausedInBody
|
|
status, body = client.PostRulesGroup(t, folder1Title, &group)
|
|
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
|
|
|
getGroup = client.GetRulesGroup(t, folder1Title, group.Name)
|
|
require.Equal(t, tc.expectedIsPausedInDb, getGroup.Rules[0].GrafanaManagedAlert.IsPaused)
|
|
})
|
|
}
|
|
}
|