mirror of https://github.com/grafana/grafana
AlertingNG: Split into several packages (#31719)
* AlertingNG: Split into several packages * Move AlertQuery to modelspull/31789/head
parent
124ef813ab
commit
4ce0a49eac
@ -1,22 +0,0 @@ |
||||
package ngalert |
||||
|
||||
import ( |
||||
"fmt" |
||||
"time" |
||||
) |
||||
|
||||
// timeNow makes it possible to test usage of time
|
||||
var timeNow = time.Now |
||||
|
||||
// preSave sets datasource and loads the updated model for each alert query.
|
||||
func (alertDefinition *AlertDefinition) preSave() error { |
||||
for i, q := range alertDefinition.Data { |
||||
err := q.PreSave() |
||||
if err != nil { |
||||
return fmt.Errorf("invalid alert query %s: %w", q.RefID, err) |
||||
} |
||||
alertDefinition.Data[i] = q |
||||
} |
||||
alertDefinition.Updated = timeNow() |
||||
return nil |
||||
} |
@ -0,0 +1,19 @@ |
||||
package api |
||||
|
||||
import ( |
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" |
||||
|
||||
"github.com/grafana/grafana/pkg/api/response" |
||||
"github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
// listAlertInstancesEndpoint handles GET /api/alert-instances.
|
||||
func (api *API) listAlertInstancesEndpoint(c *models.ReqContext) response.Response { |
||||
cmd := ngmodels.ListAlertInstancesQuery{DefinitionOrgID: c.SignedInUser.OrgId} |
||||
|
||||
if err := api.Store.ListAlertInstances(&cmd); err != nil { |
||||
return response.Error(500, "Failed to list alert instances", err) |
||||
} |
||||
|
||||
return response.JSON(200, cmd.Result) |
||||
} |
@ -0,0 +1,23 @@ |
||||
package api |
||||
|
||||
import ( |
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" |
||||
|
||||
"github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
func (api *API) validateOrgAlertDefinition(c *models.ReqContext) { |
||||
uid := c.ParamsEscape(":alertDefinitionUID") |
||||
|
||||
if uid == "" { |
||||
c.JsonApiErr(403, "Permission denied", nil) |
||||
return |
||||
} |
||||
|
||||
query := ngmodels.GetAlertDefinitionByUIDQuery{UID: uid, OrgID: c.SignedInUser.OrgId} |
||||
|
||||
if err := api.Store.GetAlertDefinitionByUID(&query); err != nil { |
||||
c.JsonApiErr(404, "Alert definition not found", nil) |
||||
return |
||||
} |
||||
} |
@ -1,15 +0,0 @@ |
||||
package ngalert |
||||
|
||||
import ( |
||||
"time" |
||||
) |
||||
|
||||
func (sch *schedule) fetchAllDetails(now time.Time) []*AlertDefinition { |
||||
q := listAlertDefinitionsQuery{} |
||||
err := sch.store.getAlertDefinitions(&q) |
||||
if err != nil { |
||||
sch.log.Error("failed to fetch alert definitions", "now", now, "err", err) |
||||
return nil |
||||
} |
||||
return q.Result |
||||
} |
@ -1,17 +0,0 @@ |
||||
package ngalert |
||||
|
||||
import ( |
||||
"github.com/grafana/grafana/pkg/api/response" |
||||
"github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
// listAlertInstancesEndpoint handles GET /api/alert-instances.
|
||||
func (api *apiImpl) listAlertInstancesEndpoint(c *models.ReqContext) response.Response { |
||||
cmd := listAlertInstancesQuery{DefinitionOrgID: c.SignedInUser.OrgId} |
||||
|
||||
if err := api.store.listAlertInstances(&cmd); err != nil { |
||||
return response.Error(500, "Failed to list alert instances", err) |
||||
} |
||||
|
||||
return response.JSON(200, cmd.Result) |
||||
} |
@ -1,163 +0,0 @@ |
||||
// +build integration
|
||||
|
||||
package ngalert |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestAlertInstanceOperations(t *testing.T) { |
||||
_, store := setupTestEnv(t, baseIntervalSeconds) |
||||
|
||||
alertDefinition1 := createTestAlertDefinition(t, store, 60) |
||||
orgID := alertDefinition1.OrgID |
||||
|
||||
alertDefinition2 := createTestAlertDefinition(t, store, 60) |
||||
require.Equal(t, orgID, alertDefinition2.OrgID) |
||||
|
||||
alertDefinition3 := createTestAlertDefinition(t, store, 60) |
||||
require.Equal(t, orgID, alertDefinition3.OrgID) |
||||
|
||||
alertDefinition4 := createTestAlertDefinition(t, store, 60) |
||||
require.Equal(t, orgID, alertDefinition4.OrgID) |
||||
|
||||
t.Run("can save and read new alert instance", func(t *testing.T) { |
||||
saveCmd := &saveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition1.OrgID, |
||||
DefinitionUID: alertDefinition1.UID, |
||||
State: InstanceStateFiring, |
||||
Labels: InstanceLabels{"test": "testValue"}, |
||||
} |
||||
err := store.saveAlertInstance(saveCmd) |
||||
require.NoError(t, err) |
||||
|
||||
getCmd := &getAlertInstanceQuery{ |
||||
DefinitionOrgID: saveCmd.DefinitionOrgID, |
||||
DefinitionUID: saveCmd.DefinitionUID, |
||||
Labels: InstanceLabels{"test": "testValue"}, |
||||
} |
||||
|
||||
err = store.getAlertInstance(getCmd) |
||||
require.NoError(t, err) |
||||
|
||||
require.Equal(t, saveCmd.Labels, getCmd.Result.Labels) |
||||
require.Equal(t, alertDefinition1.OrgID, getCmd.Result.DefinitionOrgID) |
||||
require.Equal(t, alertDefinition1.UID, getCmd.Result.DefinitionUID) |
||||
}) |
||||
|
||||
t.Run("can save and read new alert instance with no labels", func(t *testing.T) { |
||||
saveCmd := &saveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition2.OrgID, |
||||
DefinitionUID: alertDefinition2.UID, |
||||
State: InstanceStateNormal, |
||||
} |
||||
err := store.saveAlertInstance(saveCmd) |
||||
require.NoError(t, err) |
||||
|
||||
getCmd := &getAlertInstanceQuery{ |
||||
DefinitionOrgID: saveCmd.DefinitionOrgID, |
||||
DefinitionUID: saveCmd.DefinitionUID, |
||||
} |
||||
|
||||
err = store.getAlertInstance(getCmd) |
||||
require.NoError(t, err) |
||||
|
||||
require.Equal(t, alertDefinition2.OrgID, getCmd.Result.DefinitionOrgID) |
||||
require.Equal(t, alertDefinition2.UID, getCmd.Result.DefinitionUID) |
||||
require.Equal(t, saveCmd.Labels, getCmd.Result.Labels) |
||||
}) |
||||
|
||||
t.Run("can save two instances with same org_id, uid and different labels", func(t *testing.T) { |
||||
saveCmdOne := &saveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition3.OrgID, |
||||
DefinitionUID: alertDefinition3.UID, |
||||
State: InstanceStateFiring, |
||||
Labels: InstanceLabels{"test": "testValue"}, |
||||
} |
||||
|
||||
err := store.saveAlertInstance(saveCmdOne) |
||||
require.NoError(t, err) |
||||
|
||||
saveCmdTwo := &saveAlertInstanceCommand{ |
||||
DefinitionOrgID: saveCmdOne.DefinitionOrgID, |
||||
DefinitionUID: saveCmdOne.DefinitionUID, |
||||
State: InstanceStateFiring, |
||||
Labels: InstanceLabels{"test": "meow"}, |
||||
} |
||||
err = store.saveAlertInstance(saveCmdTwo) |
||||
require.NoError(t, err) |
||||
|
||||
listCommand := &listAlertInstancesQuery{ |
||||
DefinitionOrgID: saveCmdOne.DefinitionOrgID, |
||||
DefinitionUID: saveCmdOne.DefinitionUID, |
||||
} |
||||
|
||||
err = store.listAlertInstances(listCommand) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listCommand.Result, 2) |
||||
}) |
||||
|
||||
t.Run("can list all added instances in org", func(t *testing.T) { |
||||
listCommand := &listAlertInstancesQuery{ |
||||
DefinitionOrgID: orgID, |
||||
} |
||||
|
||||
err := store.listAlertInstances(listCommand) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listCommand.Result, 4) |
||||
}) |
||||
|
||||
t.Run("can list all added instances in org filtered by current state", func(t *testing.T) { |
||||
listCommand := &listAlertInstancesQuery{ |
||||
DefinitionOrgID: orgID, |
||||
State: InstanceStateNormal, |
||||
} |
||||
|
||||
err := store.listAlertInstances(listCommand) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listCommand.Result, 1) |
||||
}) |
||||
|
||||
t.Run("update instance with same org_id, uid and different labels", func(t *testing.T) { |
||||
saveCmdOne := &saveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition4.OrgID, |
||||
DefinitionUID: alertDefinition4.UID, |
||||
State: InstanceStateFiring, |
||||
Labels: InstanceLabels{"test": "testValue"}, |
||||
} |
||||
|
||||
err := store.saveAlertInstance(saveCmdOne) |
||||
require.NoError(t, err) |
||||
|
||||
saveCmdTwo := &saveAlertInstanceCommand{ |
||||
DefinitionOrgID: saveCmdOne.DefinitionOrgID, |
||||
DefinitionUID: saveCmdOne.DefinitionUID, |
||||
State: InstanceStateNormal, |
||||
Labels: InstanceLabels{"test": "testValue"}, |
||||
} |
||||
err = store.saveAlertInstance(saveCmdTwo) |
||||
require.NoError(t, err) |
||||
|
||||
listCommand := &listAlertInstancesQuery{ |
||||
DefinitionOrgID: alertDefinition4.OrgID, |
||||
DefinitionUID: alertDefinition4.UID, |
||||
} |
||||
|
||||
err = store.listAlertInstances(listCommand) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listCommand.Result, 1) |
||||
|
||||
require.Equal(t, saveCmdTwo.DefinitionOrgID, listCommand.Result[0].DefinitionOrgID) |
||||
require.Equal(t, saveCmdTwo.DefinitionUID, listCommand.Result[0].DefinitionUID) |
||||
require.Equal(t, saveCmdTwo.Labels, listCommand.Result[0].Labels) |
||||
require.Equal(t, saveCmdTwo.State, listCommand.Result[0].CurrentState) |
||||
require.NotEmpty(t, listCommand.Result[0].DefinitionTitle) |
||||
require.Equal(t, alertDefinition4.Title, listCommand.Result[0].DefinitionTitle) |
||||
}) |
||||
} |
@ -1,21 +0,0 @@ |
||||
package ngalert |
||||
|
||||
import ( |
||||
"github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
func (api *apiImpl) validateOrgAlertDefinition(c *models.ReqContext) { |
||||
uid := c.ParamsEscape(":alertDefinitionUID") |
||||
|
||||
if uid == "" { |
||||
c.JsonApiErr(403, "Permission denied", nil) |
||||
return |
||||
} |
||||
|
||||
query := getAlertDefinitionByUIDQuery{UID: uid, OrgID: c.SignedInUser.OrgId} |
||||
|
||||
if err := api.store.getAlertDefinitionByUID(&query); err != nil { |
||||
c.JsonApiErr(404, "Alert definition not found", nil) |
||||
return |
||||
} |
||||
} |
@ -1,115 +0,0 @@ |
||||
package ngalert |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval" |
||||
) |
||||
|
||||
var errAlertDefinitionFailedGenerateUniqueUID = errors.New("failed to generate alert definition UID") |
||||
|
||||
// AlertDefinition is the model for alert definitions in Alerting NG.
|
||||
type AlertDefinition struct { |
||||
ID int64 `xorm:"pk autoincr 'id'" json:"id"` |
||||
OrgID int64 `xorm:"org_id" json:"orgId"` |
||||
Title string `json:"title"` |
||||
Condition string `json:"condition"` |
||||
Data []eval.AlertQuery `json:"data"` |
||||
Updated time.Time `json:"updated"` |
||||
IntervalSeconds int64 `json:"intervalSeconds"` |
||||
Version int64 `json:"version"` |
||||
UID string `xorm:"uid" json:"uid"` |
||||
Paused bool `json:"paused"` |
||||
} |
||||
|
||||
type alertDefinitionKey struct { |
||||
orgID int64 |
||||
definitionUID string |
||||
} |
||||
|
||||
func (k alertDefinitionKey) String() string { |
||||
return fmt.Sprintf("{orgID: %d, definitionUID: %s}", k.orgID, k.definitionUID) |
||||
} |
||||
|
||||
func (alertDefinition *AlertDefinition) getKey() alertDefinitionKey { |
||||
return alertDefinitionKey{orgID: alertDefinition.OrgID, definitionUID: alertDefinition.UID} |
||||
} |
||||
|
||||
// AlertDefinitionVersion is the model for alert definition versions in Alerting NG.
|
||||
type AlertDefinitionVersion struct { |
||||
ID int64 `xorm:"pk autoincr 'id'"` |
||||
AlertDefinitionID int64 `xorm:"alert_definition_id"` |
||||
AlertDefinitionUID string `xorm:"alert_definition_uid"` |
||||
ParentVersion int64 |
||||
RestoredFrom int64 |
||||
Version int64 |
||||
|
||||
Created time.Time |
||||
Title string |
||||
Condition string |
||||
Data []eval.AlertQuery |
||||
IntervalSeconds int64 |
||||
} |
||||
|
||||
var ( |
||||
// errAlertDefinitionNotFound is an error for an unknown alert definition.
|
||||
errAlertDefinitionNotFound = fmt.Errorf("could not find alert definition") |
||||
) |
||||
|
||||
// getAlertDefinitionByUIDQuery is the query for retrieving/deleting an alert definition by UID and organisation ID.
|
||||
type getAlertDefinitionByUIDQuery struct { |
||||
UID string |
||||
OrgID int64 |
||||
|
||||
Result *AlertDefinition |
||||
} |
||||
|
||||
type deleteAlertDefinitionByUIDCommand struct { |
||||
UID string |
||||
OrgID int64 |
||||
} |
||||
|
||||
// saveAlertDefinitionCommand is the query for saving a new alert definition.
|
||||
type saveAlertDefinitionCommand struct { |
||||
Title string `json:"title"` |
||||
OrgID int64 `json:"-"` |
||||
Condition string `json:"condition"` |
||||
Data []eval.AlertQuery `json:"data"` |
||||
IntervalSeconds *int64 `json:"intervalSeconds"` |
||||
|
||||
Result *AlertDefinition |
||||
} |
||||
|
||||
// updateAlertDefinitionCommand is the query for updating an existing alert definition.
|
||||
type updateAlertDefinitionCommand struct { |
||||
Title string `json:"title"` |
||||
OrgID int64 `json:"-"` |
||||
Condition string `json:"condition"` |
||||
Data []eval.AlertQuery `json:"data"` |
||||
IntervalSeconds *int64 `json:"intervalSeconds"` |
||||
UID string `json:"-"` |
||||
|
||||
Result *AlertDefinition |
||||
} |
||||
|
||||
type evalAlertConditionCommand struct { |
||||
Condition string `json:"condition"` |
||||
Data []eval.AlertQuery `json:"data"` |
||||
Now time.Time `json:"now"` |
||||
} |
||||
|
||||
type listAlertDefinitionsQuery struct { |
||||
OrgID int64 `json:"-"` |
||||
|
||||
Result []*AlertDefinition |
||||
} |
||||
|
||||
type updateAlertDefinitionPausedCommand struct { |
||||
OrgID int64 `json:"-"` |
||||
UIDs []string `json:"uids"` |
||||
Paused bool `json:"-"` |
||||
|
||||
ResultCount int64 |
||||
} |
@ -1,4 +1,4 @@ |
||||
package ngalert |
||||
package models |
||||
|
||||
import ( |
||||
// nolint:gosec
|
@ -0,0 +1,147 @@ |
||||
package models |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"time" |
||||
) |
||||
|
||||
var ( |
||||
// ErrAlertDefinitionNotFound is an error for an unknown alert definition.
|
||||
ErrAlertDefinitionNotFound = fmt.Errorf("could not find alert definition") |
||||
// ErrAlertDefinitionFailedGenerateUniqueUID is an error for failure to generate alert definition UID
|
||||
ErrAlertDefinitionFailedGenerateUniqueUID = errors.New("failed to generate alert definition UID") |
||||
) |
||||
|
||||
// AlertDefinition is the model for alert definitions in Alerting NG.
|
||||
type AlertDefinition struct { |
||||
ID int64 `xorm:"pk autoincr 'id'" json:"id"` |
||||
OrgID int64 `xorm:"org_id" json:"orgId"` |
||||
Title string `json:"title"` |
||||
Condition string `json:"condition"` |
||||
Data []AlertQuery `json:"data"` |
||||
Updated time.Time `json:"updated"` |
||||
IntervalSeconds int64 `json:"intervalSeconds"` |
||||
Version int64 `json:"version"` |
||||
UID string `xorm:"uid" json:"uid"` |
||||
Paused bool `json:"paused"` |
||||
} |
||||
|
||||
// AlertDefinitionKey is the alert definition identifier
|
||||
type AlertDefinitionKey struct { |
||||
OrgID int64 |
||||
DefinitionUID string |
||||
} |
||||
|
||||
func (k AlertDefinitionKey) String() string { |
||||
return fmt.Sprintf("{orgID: %d, definitionUID: %s}", k.OrgID, k.DefinitionUID) |
||||
} |
||||
|
||||
// GetKey returns the alert definitions identifier
|
||||
func (alertDefinition *AlertDefinition) GetKey() AlertDefinitionKey { |
||||
return AlertDefinitionKey{OrgID: alertDefinition.OrgID, DefinitionUID: alertDefinition.UID} |
||||
} |
||||
|
||||
// PreSave sets datasource and loads the updated model for each alert query.
|
||||
func (alertDefinition *AlertDefinition) PreSave(timeNow func() time.Time) error { |
||||
for i, q := range alertDefinition.Data { |
||||
err := q.PreSave() |
||||
if err != nil { |
||||
return fmt.Errorf("invalid alert query %s: %w", q.RefID, err) |
||||
} |
||||
alertDefinition.Data[i] = q |
||||
} |
||||
alertDefinition.Updated = timeNow() |
||||
return nil |
||||
} |
||||
|
||||
// AlertDefinitionVersion is the model for alert definition versions in Alerting NG.
|
||||
type AlertDefinitionVersion struct { |
||||
ID int64 `xorm:"pk autoincr 'id'"` |
||||
AlertDefinitionID int64 `xorm:"alert_definition_id"` |
||||
AlertDefinitionUID string `xorm:"alert_definition_uid"` |
||||
ParentVersion int64 |
||||
RestoredFrom int64 |
||||
Version int64 |
||||
|
||||
Created time.Time |
||||
Title string |
||||
Condition string |
||||
Data []AlertQuery |
||||
IntervalSeconds int64 |
||||
} |
||||
|
||||
// GetAlertDefinitionByUIDQuery is the query for retrieving/deleting an alert definition by UID and organisation ID.
|
||||
type GetAlertDefinitionByUIDQuery struct { |
||||
UID string |
||||
OrgID int64 |
||||
|
||||
Result *AlertDefinition |
||||
} |
||||
|
||||
// DeleteAlertDefinitionByUIDCommand is the command for deleting an alert definition
|
||||
type DeleteAlertDefinitionByUIDCommand struct { |
||||
UID string |
||||
OrgID int64 |
||||
} |
||||
|
||||
// SaveAlertDefinitionCommand is the query for saving a new alert definition.
|
||||
type SaveAlertDefinitionCommand struct { |
||||
Title string `json:"title"` |
||||
OrgID int64 `json:"-"` |
||||
Condition string `json:"condition"` |
||||
Data []AlertQuery `json:"data"` |
||||
IntervalSeconds *int64 `json:"intervalSeconds"` |
||||
|
||||
Result *AlertDefinition |
||||
} |
||||
|
||||
// UpdateAlertDefinitionCommand is the query for updating an existing alert definition.
|
||||
type UpdateAlertDefinitionCommand struct { |
||||
Title string `json:"title"` |
||||
OrgID int64 `json:"-"` |
||||
Condition string `json:"condition"` |
||||
Data []AlertQuery `json:"data"` |
||||
IntervalSeconds *int64 `json:"intervalSeconds"` |
||||
UID string `json:"-"` |
||||
|
||||
Result *AlertDefinition |
||||
} |
||||
|
||||
// EvalAlertConditionCommand is the command for evaluating a condition
|
||||
type EvalAlertConditionCommand struct { |
||||
Condition string `json:"condition"` |
||||
Data []AlertQuery `json:"data"` |
||||
Now time.Time `json:"now"` |
||||
} |
||||
|
||||
// ListAlertDefinitionsQuery is the query for listing alert definitions
|
||||
type ListAlertDefinitionsQuery struct { |
||||
OrgID int64 `json:"-"` |
||||
|
||||
Result []*AlertDefinition |
||||
} |
||||
|
||||
// UpdateAlertDefinitionPausedCommand is the command for updating an alert definitions
|
||||
type UpdateAlertDefinitionPausedCommand struct { |
||||
OrgID int64 `json:"-"` |
||||
UIDs []string `json:"uids"` |
||||
Paused bool `json:"-"` |
||||
|
||||
ResultCount int64 |
||||
} |
||||
|
||||
// Condition contains backend expressions and queries and the RefID
|
||||
// of the query or expression that will be evaluated.
|
||||
type Condition struct { |
||||
RefID string `json:"refId"` |
||||
OrgID int64 `json:"-"` |
||||
|
||||
QueriesAndExpressions []AlertQuery `json:"queriesAndExpressions"` |
||||
} |
||||
|
||||
// IsValid checks the condition's validity.
|
||||
func (c Condition) IsValid() bool { |
||||
// TODO search for refIDs in QueriesAndExpressions
|
||||
return len(c.QueriesAndExpressions) != 0 |
||||
} |
@ -0,0 +1,17 @@ |
||||
package schedule |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models" |
||||
) |
||||
|
||||
func (sch *schedule) fetchAllDetails(now time.Time) []*models.AlertDefinition { |
||||
q := models.ListAlertDefinitionsQuery{} |
||||
err := sch.store.GetAlertDefinitions(&q) |
||||
if err != nil { |
||||
sch.log.Error("failed to fetch alert definitions", "now", now, "err", err) |
||||
return nil |
||||
} |
||||
return q.Result |
||||
} |
@ -0,0 +1,165 @@ |
||||
// +build integration
|
||||
|
||||
package tests |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestAlertInstanceOperations(t *testing.T) { |
||||
dbstore := setupTestEnv(t, baseIntervalSeconds) |
||||
|
||||
alertDefinition1 := createTestAlertDefinition(t, dbstore, 60) |
||||
orgID := alertDefinition1.OrgID |
||||
|
||||
alertDefinition2 := createTestAlertDefinition(t, dbstore, 60) |
||||
require.Equal(t, orgID, alertDefinition2.OrgID) |
||||
|
||||
alertDefinition3 := createTestAlertDefinition(t, dbstore, 60) |
||||
require.Equal(t, orgID, alertDefinition3.OrgID) |
||||
|
||||
alertDefinition4 := createTestAlertDefinition(t, dbstore, 60) |
||||
require.Equal(t, orgID, alertDefinition4.OrgID) |
||||
|
||||
t.Run("can save and read new alert instance", func(t *testing.T) { |
||||
saveCmd := &models.SaveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition1.OrgID, |
||||
DefinitionUID: alertDefinition1.UID, |
||||
State: models.InstanceStateFiring, |
||||
Labels: models.InstanceLabels{"test": "testValue"}, |
||||
} |
||||
err := dbstore.SaveAlertInstance(saveCmd) |
||||
require.NoError(t, err) |
||||
|
||||
getCmd := &models.GetAlertInstanceQuery{ |
||||
DefinitionOrgID: saveCmd.DefinitionOrgID, |
||||
DefinitionUID: saveCmd.DefinitionUID, |
||||
Labels: models.InstanceLabels{"test": "testValue"}, |
||||
} |
||||
|
||||
err = dbstore.GetAlertInstance(getCmd) |
||||
require.NoError(t, err) |
||||
|
||||
require.Equal(t, saveCmd.Labels, getCmd.Result.Labels) |
||||
require.Equal(t, alertDefinition1.OrgID, getCmd.Result.DefinitionOrgID) |
||||
require.Equal(t, alertDefinition1.UID, getCmd.Result.DefinitionUID) |
||||
}) |
||||
|
||||
t.Run("can save and read new alert instance with no labels", func(t *testing.T) { |
||||
saveCmd := &models.SaveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition2.OrgID, |
||||
DefinitionUID: alertDefinition2.UID, |
||||
State: models.InstanceStateNormal, |
||||
} |
||||
err := dbstore.SaveAlertInstance(saveCmd) |
||||
require.NoError(t, err) |
||||
|
||||
getCmd := &models.GetAlertInstanceQuery{ |
||||
DefinitionOrgID: saveCmd.DefinitionOrgID, |
||||
DefinitionUID: saveCmd.DefinitionUID, |
||||
} |
||||
|
||||
err = dbstore.GetAlertInstance(getCmd) |
||||
require.NoError(t, err) |
||||
|
||||
require.Equal(t, alertDefinition2.OrgID, getCmd.Result.DefinitionOrgID) |
||||
require.Equal(t, alertDefinition2.UID, getCmd.Result.DefinitionUID) |
||||
require.Equal(t, saveCmd.Labels, getCmd.Result.Labels) |
||||
}) |
||||
|
||||
t.Run("can save two instances with same org_id, uid and different labels", func(t *testing.T) { |
||||
saveCmdOne := &models.SaveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition3.OrgID, |
||||
DefinitionUID: alertDefinition3.UID, |
||||
State: models.InstanceStateFiring, |
||||
Labels: models.InstanceLabels{"test": "testValue"}, |
||||
} |
||||
|
||||
err := dbstore.SaveAlertInstance(saveCmdOne) |
||||
require.NoError(t, err) |
||||
|
||||
saveCmdTwo := &models.SaveAlertInstanceCommand{ |
||||
DefinitionOrgID: saveCmdOne.DefinitionOrgID, |
||||
DefinitionUID: saveCmdOne.DefinitionUID, |
||||
State: models.InstanceStateFiring, |
||||
Labels: models.InstanceLabels{"test": "meow"}, |
||||
} |
||||
err = dbstore.SaveAlertInstance(saveCmdTwo) |
||||
require.NoError(t, err) |
||||
|
||||
listQuery := &models.ListAlertInstancesQuery{ |
||||
DefinitionOrgID: saveCmdOne.DefinitionOrgID, |
||||
DefinitionUID: saveCmdOne.DefinitionUID, |
||||
} |
||||
|
||||
err = dbstore.ListAlertInstances(listQuery) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listQuery.Result, 2) |
||||
}) |
||||
|
||||
t.Run("can list all added instances in org", func(t *testing.T) { |
||||
listQuery := &models.ListAlertInstancesQuery{ |
||||
DefinitionOrgID: orgID, |
||||
} |
||||
|
||||
err := dbstore.ListAlertInstances(listQuery) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listQuery.Result, 4) |
||||
}) |
||||
|
||||
t.Run("can list all added instances in org filtered by current state", func(t *testing.T) { |
||||
listQuery := &models.ListAlertInstancesQuery{ |
||||
DefinitionOrgID: orgID, |
||||
State: models.InstanceStateNormal, |
||||
} |
||||
|
||||
err := dbstore.ListAlertInstances(listQuery) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listQuery.Result, 1) |
||||
}) |
||||
|
||||
t.Run("update instance with same org_id, uid and different labels", func(t *testing.T) { |
||||
saveCmdOne := &models.SaveAlertInstanceCommand{ |
||||
DefinitionOrgID: alertDefinition4.OrgID, |
||||
DefinitionUID: alertDefinition4.UID, |
||||
State: models.InstanceStateFiring, |
||||
Labels: models.InstanceLabels{"test": "testValue"}, |
||||
} |
||||
|
||||
err := dbstore.SaveAlertInstance(saveCmdOne) |
||||
require.NoError(t, err) |
||||
|
||||
saveCmdTwo := &models.SaveAlertInstanceCommand{ |
||||
DefinitionOrgID: saveCmdOne.DefinitionOrgID, |
||||
DefinitionUID: saveCmdOne.DefinitionUID, |
||||
State: models.InstanceStateNormal, |
||||
Labels: models.InstanceLabels{"test": "testValue"}, |
||||
} |
||||
err = dbstore.SaveAlertInstance(saveCmdTwo) |
||||
require.NoError(t, err) |
||||
|
||||
listQuery := &models.ListAlertInstancesQuery{ |
||||
DefinitionOrgID: alertDefinition4.OrgID, |
||||
DefinitionUID: alertDefinition4.UID, |
||||
} |
||||
|
||||
err = dbstore.ListAlertInstances(listQuery) |
||||
require.NoError(t, err) |
||||
|
||||
require.Len(t, listQuery.Result, 1) |
||||
|
||||
require.Equal(t, saveCmdTwo.DefinitionOrgID, listQuery.Result[0].DefinitionOrgID) |
||||
require.Equal(t, saveCmdTwo.DefinitionUID, listQuery.Result[0].DefinitionUID) |
||||
require.Equal(t, saveCmdTwo.Labels, listQuery.Result[0].Labels) |
||||
require.Equal(t, saveCmdTwo.State, listQuery.Result[0].CurrentState) |
||||
require.NotEmpty(t, listQuery.Result[0].DefinitionTitle) |
||||
require.Equal(t, alertDefinition4.Title, listQuery.Result[0].DefinitionTitle) |
||||
}) |
||||
} |
@ -1,79 +0,0 @@ |
||||
package ngalert |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval" |
||||
) |
||||
|
||||
const alertDefinitionMaxTitleLength = 190 |
||||
|
||||
var errEmptyTitleError = errors.New("title is empty") |
||||
|
||||
// validateAlertDefinition validates the alert definition interval and organisation.
|
||||
// If requireData is true checks that it contains at least one alert query
|
||||
func (st storeImpl) validateAlertDefinition(alertDefinition *AlertDefinition, requireData bool) error { |
||||
if !requireData && len(alertDefinition.Data) == 0 { |
||||
return fmt.Errorf("no queries or expressions are found") |
||||
} |
||||
|
||||
if alertDefinition.Title == "" { |
||||
return errEmptyTitleError |
||||
} |
||||
|
||||
if alertDefinition.IntervalSeconds%int64(st.baseInterval.Seconds()) != 0 { |
||||
return fmt.Errorf("invalid interval: %v: interval should be divided exactly by scheduler interval: %v", time.Duration(alertDefinition.IntervalSeconds)*time.Second, st.baseInterval) |
||||
} |
||||
|
||||
// enfore max name length in SQLite
|
||||
if len(alertDefinition.Title) > alertDefinitionMaxTitleLength { |
||||
return fmt.Errorf("name length should not be greater than %d", alertDefinitionMaxTitleLength) |
||||
} |
||||
|
||||
if alertDefinition.OrgID == 0 { |
||||
return fmt.Errorf("no organisation is found") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// validateCondition validates that condition queries refer to existing datasources
|
||||
func (api *apiImpl) validateCondition(c eval.Condition, user *models.SignedInUser, skipCache bool) error { |
||||
var refID string |
||||
|
||||
if len(c.QueriesAndExpressions) == 0 { |
||||
return nil |
||||
} |
||||
|
||||
for _, query := range c.QueriesAndExpressions { |
||||
if c.RefID == query.RefID { |
||||
refID = c.RefID |
||||
} |
||||
|
||||
datasourceUID, err := query.GetDatasource() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
isExpression, err := query.IsExpression() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if isExpression { |
||||
continue |
||||
} |
||||
|
||||
_, err = api.DatasourceCache.GetDatasourceByUID(datasourceUID, user, skipCache) |
||||
if err != nil { |
||||
return fmt.Errorf("failed to get datasource: %s: %w", datasourceUID, err) |
||||
} |
||||
} |
||||
|
||||
if refID == "" { |
||||
return fmt.Errorf("condition %s not found in any query or expression", c.RefID) |
||||
} |
||||
return nil |
||||
} |
Loading…
Reference in new issue