mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
You can now get alert rules for a dashboard or a panel using /api/v1/rules endpoints. (#39476)
Get alert rules for a dashboard and panel in /api/v1/rules
This commit is contained in:
parent
a3783a4eb7
commit
2a4c1b1aa6
@ -2,8 +2,11 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
@ -51,6 +54,13 @@ func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Re
|
||||
return response.JSON(http.StatusOK, alertResponse)
|
||||
}
|
||||
|
||||
func getPanelIDFromRequest(r *http.Request) (int64, error) {
|
||||
if s := strings.TrimSpace(r.URL.Query().Get("panel_id")); s != "" {
|
||||
return strconv.ParseInt(s, 10, 64)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Response {
|
||||
ruleResponse := apimodels.RuleResponse{
|
||||
DiscoveryBase: apimodels.DiscoveryBase{
|
||||
@ -71,9 +81,20 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
|
||||
namespaceUIDs = append(namespaceUIDs, k)
|
||||
}
|
||||
|
||||
dashboardUID := c.Query("dashboard_uid")
|
||||
panelID, err := getPanelIDFromRequest(c.Req)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "invalid panel_id")
|
||||
}
|
||||
if dashboardUID == "" && panelID != 0 {
|
||||
return ErrResp(http.StatusBadRequest, errors.New("panel_id must be set with dashboard_uid"), "")
|
||||
}
|
||||
|
||||
ruleGroupQuery := ngmodels.ListOrgRuleGroupsQuery{
|
||||
OrgID: c.SignedInUser.OrgId,
|
||||
NamespaceUIDs: namespaceUIDs,
|
||||
DashboardUID: dashboardUID,
|
||||
PanelID: panelID,
|
||||
}
|
||||
if err := srv.store.GetOrgRuleGroups(&ruleGroupQuery); err != nil {
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
@ -83,7 +104,9 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
|
||||
}
|
||||
|
||||
alertRuleQuery := ngmodels.ListAlertRulesQuery{
|
||||
OrgID: c.SignedInUser.OrgId,
|
||||
OrgID: c.SignedInUser.OrgId,
|
||||
DashboardUID: dashboardUID,
|
||||
PanelID: panelID,
|
||||
}
|
||||
if err := srv.store.GetOrgAlertRules(&alertRuleQuery); err != nil {
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
|
@ -158,9 +158,20 @@ func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response
|
||||
namespaceUIDs = append(namespaceUIDs, k)
|
||||
}
|
||||
|
||||
dashboardUID := c.Query("dashboard_uid")
|
||||
panelID, err := getPanelIDFromRequest(c.Req)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "invalid panel_id")
|
||||
}
|
||||
if dashboardUID == "" && panelID != 0 {
|
||||
return ErrResp(http.StatusBadRequest, errors.New("panel_id must be set with dashboard_uid"), "")
|
||||
}
|
||||
|
||||
q := ngmodels.ListAlertRulesQuery{
|
||||
OrgID: c.SignedInUser.OrgId,
|
||||
NamespaceUIDs: namespaceUIDs,
|
||||
DashboardUID: dashboardUID,
|
||||
PanelID: panelID,
|
||||
}
|
||||
|
||||
if err := srv.store.GetOrgAlertRules(&q); err != nil {
|
||||
@ -210,7 +221,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response
|
||||
result[namespace] = append(result[namespace], ruleGroupConfig)
|
||||
}
|
||||
}
|
||||
return response.JSON(http.StatusAccepted, result)
|
||||
return response.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConfig apimodels.PostableRuleGroupConfig) response.Response {
|
||||
|
@ -183,6 +183,14 @@ type GetSilencesParams struct {
|
||||
Filter []string `json:"filter"`
|
||||
}
|
||||
|
||||
// swagger:parameters RouteGetRuleStatuses
|
||||
type GetRuleStatusesParams struct {
|
||||
// in: query
|
||||
DashboardUID string
|
||||
// in: query
|
||||
PanelID int64
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
type GettableStatus struct {
|
||||
// cluster
|
||||
|
@ -86,6 +86,14 @@ type PathRouleGroupConfig struct {
|
||||
Groupname string
|
||||
}
|
||||
|
||||
// swagger:parameters RouteGetRulesConfig
|
||||
type PathGetRulesParams struct {
|
||||
// in: query
|
||||
DashboardUID string
|
||||
// in: query
|
||||
PanelID int64
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
type RuleGroupConfigResponse struct {
|
||||
GettableRuleGroupConfig
|
||||
|
@ -2869,6 +2869,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@ -2920,9 +2921,7 @@
|
||||
"status",
|
||||
"updatedAt"
|
||||
],
|
||||
"type": "object",
|
||||
"x-go-name": "GettableSilence",
|
||||
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models"
|
||||
"type": "object"
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
@ -3800,6 +3799,17 @@
|
||||
"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 \"grafana\" for requests to be handled by grafana\nand the numeric datasource id for requests to be forwarded to a datasource",
|
||||
"in": "path",
|
||||
@ -3832,6 +3842,17 @@
|
||||
"name": "Recipient",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "DashboardUID",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"format": "int64",
|
||||
"in": "query",
|
||||
"name": "PanelID",
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
|
@ -592,6 +592,17 @@
|
||||
],
|
||||
"operationId": "RouteGetRuleStatuses",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "DashboardUID",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"name": "PanelID",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Recipient should be \"grafana\" for requests to be handled by grafana\nand the numeric datasource id for requests to be forwarded to a datasource",
|
||||
@ -627,6 +638,17 @@
|
||||
"name": "Recipient",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "DashboardUID",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"name": "PanelID",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -3877,6 +3899,7 @@
|
||||
"$ref": "#/definitions/gettableAlerts"
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
@ -3929,8 +3952,6 @@
|
||||
"x-go-name": "UpdatedAt"
|
||||
}
|
||||
},
|
||||
"x-go-name": "GettableSilence",
|
||||
"x-go-package": "github.com/prometheus/alertmanager/api/v2/models",
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
},
|
||||
"gettableSilences": {
|
||||
|
@ -58,8 +58,10 @@ type AlertRule struct {
|
||||
Updated time.Time
|
||||
IntervalSeconds int64
|
||||
Version int64
|
||||
UID string `xorm:"uid"`
|
||||
NamespaceUID string `xorm:"namespace_uid"`
|
||||
UID string `xorm:"uid"`
|
||||
NamespaceUID string `xorm:"namespace_uid"`
|
||||
DashboardUID *string `xorm:"dashboard_uid"`
|
||||
PanelID *int64 `xorm:"panel_id"`
|
||||
RuleGroup string
|
||||
NoDataState NoDataState
|
||||
ExecErrState ExecutionErrorState
|
||||
@ -137,6 +139,11 @@ type ListAlertRulesQuery struct {
|
||||
NamespaceUIDs []string
|
||||
ExcludeOrgs []int64
|
||||
|
||||
// DashboardUID and PanelID are optional and allow filtering rules
|
||||
// to return just those for a dashboard and panel.
|
||||
DashboardUID string
|
||||
PanelID int64
|
||||
|
||||
Result []*AlertRule
|
||||
}
|
||||
|
||||
@ -156,6 +163,11 @@ type ListRuleGroupAlertRulesQuery struct {
|
||||
NamespaceUID string
|
||||
RuleGroup string
|
||||
|
||||
// DashboardUID and PanelID are optional and allow filtering rules
|
||||
// to return just those for a dashboard and panel.
|
||||
DashboardUID string
|
||||
PanelID int64
|
||||
|
||||
Result []*AlertRule
|
||||
}
|
||||
|
||||
@ -164,6 +176,11 @@ type ListOrgRuleGroupsQuery struct {
|
||||
OrgID int64
|
||||
NamespaceUIDs []string
|
||||
|
||||
// DashboardUID and PanelID are optional and allow filtering rules
|
||||
// to return just those for a dashboard and panel.
|
||||
DashboardUID string
|
||||
PanelID int64
|
||||
|
||||
Result [][]string
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -332,6 +333,17 @@ func (st DBstore) GetOrgAlertRules(query *ngmodels.ListAlertRulesQuery) error {
|
||||
q = fmt.Sprintf("%s AND namespace_uid IN (%s)", q, strings.Join(placeholders, ","))
|
||||
}
|
||||
|
||||
if query.DashboardUID != "" {
|
||||
params = append(params, query.DashboardUID)
|
||||
q = fmt.Sprintf("%s AND dashboard_uid = ?", q)
|
||||
if query.PanelID != 0 {
|
||||
params = append(params, query.PanelID)
|
||||
q = fmt.Sprintf("%s AND panel_id = ?", q)
|
||||
}
|
||||
}
|
||||
|
||||
q = fmt.Sprintf("%s ORDER BY id ASC", q)
|
||||
|
||||
if err := sess.SQL(q, params...).Find(&alertRules); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -359,10 +371,20 @@ func (st DBstore) GetNamespaceAlertRules(query *ngmodels.ListNamespaceAlertRules
|
||||
// GetRuleGroupAlertRules is a handler for retrieving rule group alert rules of specific organisation.
|
||||
func (st DBstore) GetRuleGroupAlertRules(query *ngmodels.ListRuleGroupAlertRulesQuery) error {
|
||||
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
alertRules := make([]*ngmodels.AlertRule, 0)
|
||||
|
||||
q := "SELECT * FROM alert_rule WHERE org_id = ? and namespace_uid = ? and rule_group = ?"
|
||||
if err := sess.SQL(q, query.OrgID, query.NamespaceUID, query.RuleGroup).Find(&alertRules); err != nil {
|
||||
args := []interface{}{query.OrgID, query.NamespaceUID, query.RuleGroup}
|
||||
|
||||
if query.DashboardUID != "" {
|
||||
q = fmt.Sprintf("%s and dashboard_uid = ?", q)
|
||||
args = append(args, query.DashboardUID)
|
||||
if query.PanelID != 0 {
|
||||
q = fmt.Sprintf("%s and panel_id = ?", q)
|
||||
args = append(args, query.PanelID)
|
||||
}
|
||||
}
|
||||
|
||||
alertRules := make([]*ngmodels.AlertRule, 0)
|
||||
if err := sess.SQL(q, args...).Find(&alertRules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -528,6 +550,18 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error {
|
||||
new.Labels = r.ApiRuleNode.Labels
|
||||
}
|
||||
|
||||
if s := new.Annotations["__dashboardUid__"]; s != "" {
|
||||
new.DashboardUID = &s
|
||||
}
|
||||
|
||||
if s := new.Annotations["__panelId__"]; s != "" {
|
||||
panelID, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the __panelId__ annotation does not contain a valid Panel ID: %w", err)
|
||||
}
|
||||
new.PanelID = &panelID
|
||||
}
|
||||
|
||||
upsertRule := UpsertRule{
|
||||
New: new,
|
||||
}
|
||||
@ -569,7 +603,19 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error {
|
||||
func (st DBstore) GetOrgRuleGroups(query *ngmodels.ListOrgRuleGroupsQuery) error {
|
||||
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||
var ruleGroups [][]string
|
||||
q := "SELECT DISTINCT rule_group, namespace_uid, (select title from dashboard where org_id = alert_rule.org_id and uid = alert_rule.namespace_uid) AS namespace_title FROM alert_rule WHERE org_id = ?"
|
||||
q := `
|
||||
SELECT DISTINCT
|
||||
rule_group,
|
||||
namespace_uid,
|
||||
(
|
||||
SELECT title
|
||||
FROM dashboard
|
||||
WHERE
|
||||
org_id = alert_rule.org_id AND
|
||||
uid = alert_rule.namespace_uid
|
||||
) AS namespace_title
|
||||
FROM alert_rule
|
||||
WHERE org_id = ?`
|
||||
params := []interface{}{query.OrgID}
|
||||
|
||||
if len(query.NamespaceUIDs) > 0 {
|
||||
@ -580,6 +626,16 @@ func (st DBstore) GetOrgRuleGroups(query *ngmodels.ListOrgRuleGroupsQuery) error
|
||||
}
|
||||
q = fmt.Sprintf(" %s AND namespace_uid IN (%s)", q, strings.Join(placeholders, ","))
|
||||
}
|
||||
|
||||
if query.DashboardUID != "" {
|
||||
q = fmt.Sprintf("%s and dashboard_uid = ?", q)
|
||||
params = append(params, query.DashboardUID)
|
||||
if query.PanelID != 0 {
|
||||
q = fmt.Sprintf("%s and panel_id = ?", q)
|
||||
params = append(params, query.PanelID)
|
||||
}
|
||||
}
|
||||
|
||||
q = fmt.Sprintf(" %s ORDER BY namespace_title", q)
|
||||
|
||||
if err := sess.SQL(q, params...).Find(&ruleGroups); err != nil {
|
||||
|
@ -54,6 +54,7 @@ func (*OSSMigrations) AddMigration(mg *Migrator) {
|
||||
ualert.RerunDashAlertMigration(mg)
|
||||
addSecretsMigration(mg)
|
||||
addKVStoreMigrations(mg)
|
||||
ualert.AddDashboardUIDPanelIDMigration(mg)
|
||||
}
|
||||
|
||||
func addMigrationLogMigrations(mg *Migrator) {
|
||||
|
@ -204,6 +204,33 @@ func AddAlertRuleMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64)
|
||||
mg.AddMigration("add index in alert_rule on org_id, namespase_uid and title columns", migrator.NewAddIndexMigration(alertRule, &migrator.Index{
|
||||
Cols: []string{"org_id", "namespace_uid", "title"}, Type: migrator.UniqueIndex,
|
||||
}))
|
||||
|
||||
mg.AddMigration("add dashboard_uid column to alert_rule", migrator.NewAddColumnMigration(
|
||||
migrator.Table{Name: "alert_rule"},
|
||||
&migrator.Column{
|
||||
Name: "dashboard_uid",
|
||||
Type: migrator.DB_NVarchar,
|
||||
Length: 40,
|
||||
Nullable: true,
|
||||
},
|
||||
))
|
||||
|
||||
mg.AddMigration("add panel_id column to alert_rule", migrator.NewAddColumnMigration(
|
||||
migrator.Table{Name: "alert_rule"},
|
||||
&migrator.Column{
|
||||
Name: "panel_id",
|
||||
Type: migrator.DB_BigInt,
|
||||
Nullable: true,
|
||||
},
|
||||
))
|
||||
|
||||
mg.AddMigration("add index in alert_rule on org_id, dashboard_uid and panel_id columns", migrator.NewAddIndexMigration(
|
||||
migrator.Table{Name: "alert_rule"},
|
||||
&migrator.Index{
|
||||
Name: "IDX_alert_rule_org_id_dashboard_uid_panel_id",
|
||||
Cols: []string{"org_id", "dashboard_uid", "panel_id"},
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
func AddAlertRuleVersionMigrations(mg *migrator.Migrator) {
|
||||
|
@ -101,6 +101,73 @@ func RerunDashAlertMigration(mg *migrator.Migrator) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddDashboardUIDPanelIDMigration(mg *migrator.Migrator) {
|
||||
logs, err := mg.GetMigrationLog()
|
||||
if err != nil {
|
||||
mg.Logger.Crit("alert migration failure: could not get migration log", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
migrationID := "update dashboard_uid and panel_id from existing annotations"
|
||||
_, migrationRun := logs[migrationID]
|
||||
ngEnabled := mg.Cfg.UnifiedAlerting.Enabled
|
||||
undoMigrationID := "undo " + migrationID
|
||||
|
||||
if ngEnabled && !migrationRun {
|
||||
// If ngalert is enabled and the migration has not been run then run it.
|
||||
mg.AddMigration(migrationID, &updateDashboardUIDPanelIDMigration{})
|
||||
} else if !ngEnabled && migrationRun {
|
||||
// If ngalert is disabled and the migration has been run then remove it
|
||||
// from the migration log so it will run if ngalert is re-enabled.
|
||||
mg.AddMigration(undoMigrationID, &clearMigrationEntry{
|
||||
migrationID: migrationID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// updateDashboardUIDPanelIDMigration sets the dashboard_uid and panel_id columns
|
||||
// from the __dashboardUid__ and __panelId__ annotations.
|
||||
type updateDashboardUIDPanelIDMigration struct {
|
||||
migrator.MigrationBase
|
||||
}
|
||||
|
||||
func (m *updateDashboardUIDPanelIDMigration) SQL(_ migrator.Dialect) string {
|
||||
return "set dashboard_uid and panel_id migration"
|
||||
}
|
||||
|
||||
func (m *updateDashboardUIDPanelIDMigration) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
|
||||
var results []struct {
|
||||
ID int64 `xorm:"id"`
|
||||
Annotations map[string]string `xorm:"annotations"`
|
||||
}
|
||||
if err := sess.SQL(`SELECT id, annotations FROM alert_rule`).Find(&results); err != nil {
|
||||
return fmt.Errorf("failed to get annotations for all alert rules: %w", err)
|
||||
}
|
||||
for _, next := range results {
|
||||
var (
|
||||
dashboardUID *string
|
||||
panelID *int64
|
||||
)
|
||||
if s, ok := next.Annotations["__dashboardUid__"]; ok {
|
||||
dashboardUID = &s
|
||||
}
|
||||
if s, ok := next.Annotations["__panelId__"]; ok {
|
||||
i, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the __panelId__ annotation does not contain a valid Panel ID: %w", err)
|
||||
}
|
||||
panelID = &i
|
||||
}
|
||||
if _, err := sess.Exec(`UPDATE alert_rule SET dashboard_uid = ?, panel_id = ? WHERE id = ?`,
|
||||
dashboardUID,
|
||||
panelID,
|
||||
next.ID); err != nil {
|
||||
return fmt.Errorf("failed to set dashboard_uid and panel_id for alert rule: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// clearMigrationEntry removes an entry fromt the migration_log table.
|
||||
// This migration is not recorded in the migration_log so that it can re-run several times.
|
||||
type clearMigrationEntry struct {
|
||||
|
@ -722,7 +722,7 @@ func TestDeleteFolderWithRules(t *testing.T) {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 202, resp.StatusCode)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
re := regexp.MustCompile(`"uid":"([\w|-]+)"`)
|
||||
b = re.ReplaceAll(b, []byte(`"uid":""`))
|
||||
@ -833,7 +833,7 @@ func TestDeleteFolderWithRules(t *testing.T) {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 202, resp.StatusCode)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
assert.JSONEq(t, "{}", string(b))
|
||||
}
|
||||
}
|
||||
|
@ -264,6 +264,301 @@ func TestPrometheusRules(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusRulesFilterByDashboard(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
DisableAnonymous: true,
|
||||
})
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
// override bus to get the GetSignedInUserQuery handler
|
||||
store.Bus = bus.GetBus()
|
||||
|
||||
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
|
||||
dashboardUID, err := createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Password: "password",
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
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: []ngmodels.AlertQuery{
|
||||
{
|
||||
RefID: "A",
|
||||
RelativeTimeRange: ngmodels.RelativeTimeRange{
|
||||
From: ngmodels.Duration(time.Duration(5) * time.Hour),
|
||||
To: ngmodels.Duration(time.Duration(3) * time.Hour),
|
||||
},
|
||||
DatasourceUID: "-100",
|
||||
Model: json.RawMessage(`{
|
||||
"type": "math",
|
||||
"expression": "2 + 3 > 1"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
||||
Title: "AlwaysFiringButSilenced",
|
||||
Condition: "A",
|
||||
Data: []ngmodels.AlertQuery{
|
||||
{
|
||||
RefID: "A",
|
||||
RelativeTimeRange: ngmodels.RelativeTimeRange{
|
||||
From: ngmodels.Duration(time.Duration(5) * time.Hour),
|
||||
To: ngmodels.Duration(time.Duration(3) * time.Hour),
|
||||
},
|
||||
DatasourceUID: "-100",
|
||||
Model: json.RawMessage(`{
|
||||
"type": "math",
|
||||
"expression": "2 + 3 > 1"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
NoDataState: apimodels.NoDataState(ngmodels.Alerting),
|
||||
ExecErrState: apimodels.ExecutionErrorState(ngmodels.AlertingErrState),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
enc := json.NewEncoder(&buf)
|
||||
err := enc.Encode(&rules)
|
||||
require.NoError(t, err)
|
||||
|
||||
u := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules/default", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
resp, err := http.Post(u, "application/json", &buf)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, resp.StatusCode, 202)
|
||||
require.JSONEq(t, `{"message":"rule group updated successfully"}`, string(b))
|
||||
}
|
||||
|
||||
expectedAllJSON := fmt.Sprintf(`
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"groups": [{
|
||||
"name": "anotherrulegroup",
|
||||
"file": "default",
|
||||
"rules": [{
|
||||
"state": "inactive",
|
||||
"name": "AlwaysFiring",
|
||||
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
|
||||
"duration": 10,
|
||||
"annotations": {
|
||||
"__dashboardUid__": "%s",
|
||||
"__panelId__": "1"
|
||||
},
|
||||
"labels": null,
|
||||
"health": "ok",
|
||||
"lastError": "",
|
||||
"type": "alerting",
|
||||
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||
"evaluationTime": 0
|
||||
}, {
|
||||
"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
|
||||
}],
|
||||
"interval": 60,
|
||||
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||
"evaluationTime": 0
|
||||
}]
|
||||
}
|
||||
}`, dashboardUID)
|
||||
expectedFilteredByJSON := fmt.Sprintf(`
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"groups": [{
|
||||
"name": "anotherrulegroup",
|
||||
"file": "default",
|
||||
"rules": [{
|
||||
"state": "inactive",
|
||||
"name": "AlwaysFiring",
|
||||
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
|
||||
"duration": 10,
|
||||
"annotations": {
|
||||
"__dashboardUid__": "%s",
|
||||
"__panelId__": "1"
|
||||
},
|
||||
"labels": null,
|
||||
"health": "ok",
|
||||
"lastError": "",
|
||||
"type": "alerting",
|
||||
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||
"evaluationTime": 0
|
||||
}],
|
||||
"interval": 60,
|
||||
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||
"evaluationTime": 0
|
||||
}]
|
||||
}
|
||||
}`, dashboardUID)
|
||||
expectedNoneJSON := `
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"groups": []
|
||||
}
|
||||
}`
|
||||
|
||||
// Now, let's see how this looks like.
|
||||
{
|
||||
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/prometheus/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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
require.JSONEq(t, expectedAllJSON, string(b))
|
||||
}
|
||||
|
||||
// Now, let's check we get the same rule when filtering by dashboard_uid
|
||||
{
|
||||
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/prometheus/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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
require.JSONEq(t, expectedFilteredByJSON, string(b))
|
||||
}
|
||||
|
||||
// Now, let's check we get no rules when filtering by an unknown dashboard_uid
|
||||
{
|
||||
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/prometheus/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 := ioutil.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/prometheus/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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
require.JSONEq(t, expectedFilteredByJSON, string(b))
|
||||
}
|
||||
|
||||
// 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/prometheus/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 := ioutil.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/prometheus/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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"message":"invalid panel_id: strconv.ParseInt: parsing \"invalid\": invalid syntax"}`, string(b))
|
||||
}
|
||||
|
||||
// Now, let's check a panel_id without dashboard_uid returns a 400 Bad Request response
|
||||
{
|
||||
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/prometheus/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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"message":"panel_id must be set with dashboard_uid"}`, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusRulesPermissions(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
|
@ -65,7 +65,7 @@ func TestAlertRulePermissions(t *testing.T) {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, resp.StatusCode, 202)
|
||||
assert.Equal(t, resp.StatusCode, 200)
|
||||
|
||||
body, _ := rulesNamespaceWithoutVariableValues(t, b)
|
||||
expectedGetNamespaceResponseBody := `
|
||||
@ -187,7 +187,7 @@ func TestAlertRulePermissions(t *testing.T) {
|
||||
b, err = ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, resp.StatusCode, 202)
|
||||
assert.Equal(t, resp.StatusCode, 200)
|
||||
|
||||
body, _ = rulesNamespaceWithoutVariableValues(t, b)
|
||||
expectedGetNamespaceResponseBody = `
|
||||
@ -427,3 +427,349 @@ func TestAlertRuleConflictingTitle(t *testing.T) {
|
||||
require.JSONEq(t, `{"message":"rule group updated successfully"}`, string(b))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRulerRulesFilterByDashboard(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
DisableAnonymous: true,
|
||||
})
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
// override bus to get the GetSignedInUserQuery handler
|
||||
store.Bus = bus.GetBus()
|
||||
|
||||
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
|
||||
dashboardUID, err := createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Password: "password",
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
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: []ngmodels.AlertQuery{
|
||||
{
|
||||
RefID: "A",
|
||||
RelativeTimeRange: ngmodels.RelativeTimeRange{
|
||||
From: ngmodels.Duration(time.Duration(5) * time.Hour),
|
||||
To: ngmodels.Duration(time.Duration(3) * time.Hour),
|
||||
},
|
||||
DatasourceUID: "-100",
|
||||
Model: json.RawMessage(`{
|
||||
"type": "math",
|
||||
"expression": "2 + 3 > 1"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
||||
Title: "AlwaysFiringButSilenced",
|
||||
Condition: "A",
|
||||
Data: []ngmodels.AlertQuery{
|
||||
{
|
||||
RefID: "A",
|
||||
RelativeTimeRange: ngmodels.RelativeTimeRange{
|
||||
From: ngmodels.Duration(time.Duration(5) * time.Hour),
|
||||
To: ngmodels.Duration(time.Duration(3) * time.Hour),
|
||||
},
|
||||
DatasourceUID: "-100",
|
||||
Model: json.RawMessage(`{
|
||||
"type": "math",
|
||||
"expression": "2 + 3 > 1"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
NoDataState: apimodels.NoDataState(ngmodels.Alerting),
|
||||
ExecErrState: apimodels.ExecutionErrorState(ngmodels.AlertingErrState),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
enc := json.NewEncoder(&buf)
|
||||
err := enc.Encode(&rules)
|
||||
require.NoError(t, err)
|
||||
|
||||
u := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules/default", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
resp, err := http.Post(u, "application/json", &buf)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, resp.StatusCode, 202)
|
||||
require.JSONEq(t, `{"message":"rule group updated successfully"}`, string(b))
|
||||
}
|
||||
|
||||
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": "-100",
|
||||
"model": {
|
||||
"expression": "2 + 3 \u003e 1",
|
||||
"intervalMs": 1000,
|
||||
"maxDataPoints": 43200,
|
||||
"type": "math"
|
||||
}
|
||||
}],
|
||||
"updated": "2021-02-21T01:10:30Z",
|
||||
"intervalSeconds": 60,
|
||||
"version": 1,
|
||||
"uid": "uid",
|
||||
"namespace_uid": "nsuid",
|
||||
"namespace_id": 1,
|
||||
"rule_group": "anotherrulegroup",
|
||||
"no_data_state": "NoData",
|
||||
"exec_err_state": "Alerting"
|
||||
}
|
||||
}, {
|
||||
"expr": "",
|
||||
"grafana_alert": {
|
||||
"id": 2,
|
||||
"orgId": 1,
|
||||
"title": "AlwaysFiringButSilenced",
|
||||
"condition": "A",
|
||||
"data": [{
|
||||
"refId": "A",
|
||||
"queryType": "",
|
||||
"relativeTimeRange": {
|
||||
"from": 18000,
|
||||
"to": 10800
|
||||
},
|
||||
"datasourceUid": "-100",
|
||||
"model": {
|
||||
"expression": "2 + 3 \u003e 1",
|
||||
"intervalMs": 1000,
|
||||
"maxDataPoints": 43200,
|
||||
"type": "math"
|
||||
}
|
||||
}],
|
||||
"updated": "2021-02-21T01:10:30Z",
|
||||
"intervalSeconds": 60,
|
||||
"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": "-100",
|
||||
"model": {
|
||||
"expression": "2 + 3 \u003e 1",
|
||||
"intervalMs": 1000,
|
||||
"maxDataPoints": 43200,
|
||||
"type": "math"
|
||||
}
|
||||
}],
|
||||
"updated": "2021-02-21T01:10:30Z",
|
||||
"intervalSeconds": 60,
|
||||
"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 := ioutil.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 := ioutil.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 := ioutil.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 := ioutil.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 := ioutil.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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"message":"invalid panel_id: strconv.ParseInt: parsing \"invalid\": invalid syntax"}`, string(b))
|
||||
}
|
||||
|
||||
// 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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"message":"panel_id must be set with dashboard_uid"}`, string(b))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user