mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Remove dependency on alerting package in definitions (#65390)
* move export rules to definitions package * move provisioning contact point methods to provisioning package * move AlertRuleGroupWithFolderTitle to ngalert models and adapter functions to api's compat * move rule_types files back to where they were before.
This commit is contained in:
parent
d23ebf63d2
commit
9eaffdf5a8
@ -14,7 +14,6 @@ import (
|
||||
alerting_models "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/alerting/file"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@ -64,8 +63,8 @@ type AlertRuleService interface {
|
||||
GetRuleGroup(ctx context.Context, orgID int64, folder, group string) (alerting_models.AlertRuleGroup, error)
|
||||
ReplaceRuleGroup(ctx context.Context, orgID int64, group alerting_models.AlertRuleGroup, userID int64, provenance alerting_models.Provenance) error
|
||||
GetAlertRuleWithFolderTitle(ctx context.Context, orgID int64, ruleUID string) (provisioning.AlertRuleWithFolderTitle, error)
|
||||
GetAlertRuleGroupWithFolderTitle(ctx context.Context, orgID int64, folder, group string) (file.AlertRuleGroupWithFolderTitle, error)
|
||||
GetAlertGroupsWithFolderTitle(ctx context.Context, orgID int64) ([]file.AlertRuleGroupWithFolderTitle, error)
|
||||
GetAlertRuleGroupWithFolderTitle(ctx context.Context, orgID int64, folder, group string) (alerting_models.AlertRuleGroupWithFolderTitle, error)
|
||||
GetAlertGroupsWithFolderTitle(ctx context.Context, orgID int64) ([]alerting_models.AlertRuleGroupWithFolderTitle, error)
|
||||
}
|
||||
|
||||
func (srv *ProvisioningSrv) RouteGetPolicyTree(c *contextmodel.ReqContext) response.Response {
|
||||
@ -347,7 +346,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRulesExport(c *contextmodel.ReqContext)
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get alert rules")
|
||||
}
|
||||
|
||||
e, err := file.NewAlertingFileExport(groupsWithTitle)
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle(groupsWithTitle)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to create alerting file export")
|
||||
}
|
||||
@ -365,7 +364,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleGroupExport(c *contextmodel.ReqCont
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get alert rule group")
|
||||
}
|
||||
|
||||
e, err := file.NewAlertingFileExport([]file.AlertRuleGroupWithFolderTitle{g})
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle([]alerting_models.AlertRuleGroupWithFolderTitle{g})
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to create alerting file export")
|
||||
}
|
||||
@ -383,7 +382,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleExport(c *contextmodel.ReqContext,
|
||||
return ErrResp(http.StatusInternalServerError, err, "")
|
||||
}
|
||||
|
||||
e, err := file.NewAlertingFileExport([]file.AlertRuleGroupWithFolderTitle{{
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle([]alerting_models.AlertRuleGroupWithFolderTitle{{
|
||||
AlertRuleGroup: &alerting_models.AlertRuleGroup{
|
||||
Title: rule.AlertRule.RuleGroup,
|
||||
FolderUID: rule.AlertRule.NamespaceUID,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
@ -125,3 +126,92 @@ func ApiAlertRuleGroupFromAlertRuleGroup(d models.AlertRuleGroup) definitions.Al
|
||||
Rules: rules,
|
||||
}
|
||||
}
|
||||
|
||||
// AlertingFileExportFromAlertRuleGroupWithFolderTitle creates an definitions.AlertingFileExport DTO from []models.AlertRuleGroupWithFolderTitle.
|
||||
func AlertingFileExportFromAlertRuleGroupWithFolderTitle(groups []models.AlertRuleGroupWithFolderTitle) (definitions.AlertingFileExport, error) {
|
||||
f := definitions.AlertingFileExport{APIVersion: 1}
|
||||
for _, group := range groups {
|
||||
export, err := AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle(group)
|
||||
if err != nil {
|
||||
return definitions.AlertingFileExport{}, err
|
||||
}
|
||||
f.Groups = append(f.Groups, export)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle creates a definitions.AlertRuleGroupExport DTO from models.AlertRuleGroup.
|
||||
func AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle(d models.AlertRuleGroupWithFolderTitle) (definitions.AlertRuleGroupExport, error) {
|
||||
rules := make([]definitions.AlertRuleExport, 0, len(d.Rules))
|
||||
for i := range d.Rules {
|
||||
alert, err := AlertRuleExportFromAlertRule(d.Rules[i])
|
||||
if err != nil {
|
||||
return definitions.AlertRuleGroupExport{}, err
|
||||
}
|
||||
rules = append(rules, alert)
|
||||
}
|
||||
return definitions.AlertRuleGroupExport{
|
||||
OrgID: d.OrgID,
|
||||
Name: d.Title,
|
||||
Folder: d.FolderTitle,
|
||||
Interval: model.Duration(time.Duration(d.Interval) * time.Second),
|
||||
Rules: rules,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AlertRuleExportFromAlertRule creates a definitions.AlertRuleExport DTO from models.AlertRule.
|
||||
func AlertRuleExportFromAlertRule(rule models.AlertRule) (definitions.AlertRuleExport, error) {
|
||||
data := make([]definitions.AlertQueryExport, 0, len(rule.Data))
|
||||
for i := range rule.Data {
|
||||
query, err := AlertQueryExportFromAlertQuery(rule.Data[i])
|
||||
if err != nil {
|
||||
return definitions.AlertRuleExport{}, err
|
||||
}
|
||||
data = append(data, query)
|
||||
}
|
||||
|
||||
var dashboardUID string
|
||||
if rule.DashboardUID != nil {
|
||||
dashboardUID = *rule.DashboardUID
|
||||
}
|
||||
|
||||
var panelID int64
|
||||
if rule.PanelID != nil {
|
||||
panelID = *rule.PanelID
|
||||
}
|
||||
|
||||
return definitions.AlertRuleExport{
|
||||
UID: rule.UID,
|
||||
Title: rule.Title,
|
||||
For: model.Duration(rule.For),
|
||||
Condition: rule.Condition,
|
||||
Data: data,
|
||||
DashboardUID: dashboardUID,
|
||||
PanelID: panelID,
|
||||
NoDataState: definitions.NoDataState(rule.NoDataState),
|
||||
ExecErrState: definitions.ExecutionErrorState(rule.ExecErrState),
|
||||
Annotations: rule.Annotations,
|
||||
Labels: rule.Labels,
|
||||
IsPaused: rule.IsPaused,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AlertQueryExportFromAlertQuery creates a definitions.AlertQueryExport DTO from models.AlertQuery.
|
||||
func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQueryExport, error) {
|
||||
// We unmarshal the json.RawMessage model into a map in order to facilitate yaml marshalling.
|
||||
var mdl map[string]interface{}
|
||||
err := json.Unmarshal(query.Model, &mdl)
|
||||
if err != nil {
|
||||
return definitions.AlertQueryExport{}, err
|
||||
}
|
||||
return definitions.AlertQueryExport{
|
||||
RefID: query.RefID,
|
||||
QueryType: query.QueryType,
|
||||
RelativeTimeRange: definitions.RelativeTimeRange{
|
||||
From: definitions.Duration(query.RelativeTimeRange.From),
|
||||
To: definitions.Duration(query.RelativeTimeRange.To),
|
||||
},
|
||||
DatasourceUID: query.DatasourceUID,
|
||||
Model: mdl,
|
||||
}, nil
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/alerting/file"
|
||||
)
|
||||
|
||||
// swagger:route GET /api/v1/provisioning/alert-rules provisioning stable RouteGetAlertRules
|
||||
@ -217,4 +215,41 @@ type AlertRuleGroup struct {
|
||||
|
||||
// AlertingFileExport is the full provisioned file export.
|
||||
// swagger:model
|
||||
type AlertingFileExport = file.AlertingFileExport
|
||||
type AlertingFileExport struct {
|
||||
APIVersion int64 `json:"apiVersion" yaml:"apiVersion"`
|
||||
Groups []AlertRuleGroupExport `json:"groups" yaml:"groups"`
|
||||
}
|
||||
|
||||
// AlertRuleGroupExport is the provisioned file export of AlertRuleGroupV1.
|
||||
type AlertRuleGroupExport struct {
|
||||
OrgID int64 `json:"orgId" yaml:"orgId"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Folder string `json:"folder" yaml:"folder"`
|
||||
Interval model.Duration `json:"interval" yaml:"interval"`
|
||||
Rules []AlertRuleExport `json:"rules" yaml:"rules"`
|
||||
}
|
||||
|
||||
// AlertRuleExport is the provisioned file export of models.AlertRule.
|
||||
type AlertRuleExport struct {
|
||||
UID string `json:"uid" yaml:"uid"`
|
||||
Title string `json:"title" yaml:"title"`
|
||||
Condition string `json:"condition" yaml:"condition"`
|
||||
Data []AlertQueryExport `json:"data" yaml:"data"`
|
||||
DashboardUID string `json:"dasboardUid,omitempty" yaml:"dashboardUid,omitempty"`
|
||||
PanelID int64 `json:"panelId,omitempty" yaml:"panelId,omitempty"`
|
||||
NoDataState NoDataState `json:"noDataState" yaml:"noDataState"`
|
||||
ExecErrState ExecutionErrorState `json:"execErrState" yaml:"execErrState"`
|
||||
For model.Duration `json:"for" yaml:"for"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
IsPaused bool `json:"isPaused" yaml:"isPaused"`
|
||||
}
|
||||
|
||||
// AlertQueryExport is the provisioned export of models.AlertQuery.
|
||||
type AlertQueryExport struct {
|
||||
RefID string `json:"refId" yaml:"refId"`
|
||||
QueryType string `json:"queryType,omitempty" yaml:"queryType,omitempty"`
|
||||
RelativeTimeRange RelativeTimeRange `json:"relativeTimeRange,omitempty" yaml:"relativeTimeRange,omitempty"`
|
||||
DatasourceUID string `json:"datasourceUid" yaml:"datasourceUid"`
|
||||
Model map[string]interface{} `json:"model" yaml:"model"`
|
||||
}
|
||||
|
@ -1,15 +1,7 @@
|
||||
package definitions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/alerting/logging"
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/receivers"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// swagger:route GET /api/v1/provisioning/contact-points provisioning stable RouteGetContactpoints
|
||||
@ -101,63 +93,6 @@ type EmbeddedContactPoint struct {
|
||||
|
||||
const RedactedValue = "[REDACTED]"
|
||||
|
||||
func (e *EmbeddedContactPoint) Valid(decryptFunc receivers.GetDecryptedValueFn) error {
|
||||
if e.Type == "" {
|
||||
return fmt.Errorf("type should not be an empty string")
|
||||
}
|
||||
if e.Settings == nil {
|
||||
return fmt.Errorf("settings should not be empty")
|
||||
}
|
||||
factory, exists := alertingNotify.Factory(e.Type)
|
||||
if !exists {
|
||||
return fmt.Errorf("unknown type '%s'", e.Type)
|
||||
}
|
||||
jsonBytes, err := e.Settings.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, _ := receivers.NewFactoryConfig(&receivers.NotificationChannelConfig{
|
||||
Settings: jsonBytes,
|
||||
Type: e.Type,
|
||||
}, nil, decryptFunc, nil, nil, func(ctx ...interface{}) logging.Logger {
|
||||
return &logging.FakeLogger{}
|
||||
}, setting.BuildVersion)
|
||||
if _, err := factory(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmbeddedContactPoint) SecretKeys() ([]string, error) {
|
||||
notifiers := channels_config.GetAvailableNotifiers()
|
||||
for _, n := range notifiers {
|
||||
if n.Type == e.Type {
|
||||
secureFields := []string{}
|
||||
for _, field := range n.Options {
|
||||
if field.Secure {
|
||||
secureFields = append(secureFields, field.PropertyName)
|
||||
}
|
||||
}
|
||||
return secureFields, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no secrets configured for type '%s'", e.Type)
|
||||
}
|
||||
|
||||
func (e *EmbeddedContactPoint) ExtractSecrets() (map[string]string, error) {
|
||||
secrets := map[string]string{}
|
||||
secretKeys, err := e.SecretKeys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, secretKey := range secretKeys {
|
||||
secretValue := e.Settings.Get(secretKey).MustString()
|
||||
e.Settings.Del(secretKey)
|
||||
secrets[secretKey] = secretValue
|
||||
}
|
||||
return secrets, nil
|
||||
}
|
||||
|
||||
func (e *EmbeddedContactPoint) ResourceID() string {
|
||||
return e.UID
|
||||
}
|
||||
|
@ -136,6 +136,13 @@ type AlertRuleGroup struct {
|
||||
Rules []AlertRule
|
||||
}
|
||||
|
||||
// AlertRuleGroupWithFolderTitle extends AlertRuleGroup with orgID and folder title
|
||||
type AlertRuleGroupWithFolderTitle struct {
|
||||
*AlertRuleGroup
|
||||
OrgID int64
|
||||
FolderTitle string
|
||||
}
|
||||
|
||||
// AlertRule is the model for alert rules in unified alerting.
|
||||
type AlertRule struct {
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/alerting/file"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
@ -407,7 +406,7 @@ func (service *AlertRuleService) deleteRules(ctx context.Context, orgID int64, t
|
||||
}
|
||||
|
||||
// GetAlertRuleGroupWithFolderTitle returns the alert rule group with folder title.
|
||||
func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Context, orgID int64, namespaceUID, group string) (file.AlertRuleGroupWithFolderTitle, error) {
|
||||
func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Context, orgID int64, namespaceUID, group string) (models.AlertRuleGroupWithFolderTitle, error) {
|
||||
q := models.ListAlertRulesQuery{
|
||||
OrgID: orgID,
|
||||
NamespaceUIDs: []string{namespaceUID},
|
||||
@ -415,10 +414,10 @@ func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Co
|
||||
}
|
||||
ruleList, err := service.ruleStore.ListAlertRules(ctx, &q)
|
||||
if err != nil {
|
||||
return file.AlertRuleGroupWithFolderTitle{}, err
|
||||
return models.AlertRuleGroupWithFolderTitle{}, err
|
||||
}
|
||||
if len(ruleList) == 0 {
|
||||
return file.AlertRuleGroupWithFolderTitle{}, store.ErrAlertRuleGroupNotFound
|
||||
return models.AlertRuleGroupWithFolderTitle{}, store.ErrAlertRuleGroupNotFound
|
||||
}
|
||||
|
||||
dq := dashboards.GetDashboardQuery{
|
||||
@ -427,10 +426,10 @@ func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Co
|
||||
}
|
||||
dash, err := service.dashboardService.GetDashboard(ctx, &dq)
|
||||
if err != nil {
|
||||
return file.AlertRuleGroupWithFolderTitle{}, err
|
||||
return models.AlertRuleGroupWithFolderTitle{}, err
|
||||
}
|
||||
|
||||
res := file.AlertRuleGroupWithFolderTitle{
|
||||
res := models.AlertRuleGroupWithFolderTitle{
|
||||
AlertRuleGroup: &models.AlertRuleGroup{
|
||||
Title: ruleList[0].RuleGroup,
|
||||
FolderUID: ruleList[0].NamespaceUID,
|
||||
@ -449,7 +448,7 @@ func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Co
|
||||
}
|
||||
|
||||
// GetAlertGroupsWithFolderTitle returns all groups with folder title that have at least one alert.
|
||||
func (service *AlertRuleService) GetAlertGroupsWithFolderTitle(ctx context.Context, orgID int64) ([]file.AlertRuleGroupWithFolderTitle, error) {
|
||||
func (service *AlertRuleService) GetAlertGroupsWithFolderTitle(ctx context.Context, orgID int64) ([]models.AlertRuleGroupWithFolderTitle, error) {
|
||||
q := models.ListAlertRulesQuery{
|
||||
OrgID: orgID,
|
||||
}
|
||||
@ -487,13 +486,13 @@ func (service *AlertRuleService) GetAlertGroupsWithFolderTitle(ctx context.Conte
|
||||
folderUidToTitle[dash.UID] = dash.Title
|
||||
}
|
||||
|
||||
result := make([]file.AlertRuleGroupWithFolderTitle, 0)
|
||||
result := make([]models.AlertRuleGroupWithFolderTitle, 0)
|
||||
for groupKey, rules := range groups {
|
||||
title, ok := folderUidToTitle[groupKey.NamespaceUID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot find title for folder with uid '%s'", groupKey.NamespaceUID)
|
||||
}
|
||||
result = append(result, file.AlertRuleGroupWithFolderTitle{
|
||||
result = append(result, models.AlertRuleGroupWithFolderTitle{
|
||||
AlertRuleGroup: &models.AlertRuleGroup{
|
||||
Title: rules[0].RuleGroup,
|
||||
FolderUID: rules[0].NamespaceUID,
|
||||
|
@ -7,13 +7,18 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/grafana/alerting/logging"
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/receivers"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@ -131,7 +136,7 @@ func (ecp *ContactPointService) getContactPointDecrypted(ctx context.Context, or
|
||||
|
||||
func (ecp *ContactPointService) CreateContactPoint(ctx context.Context, orgID int64,
|
||||
contactPoint apimodels.EmbeddedContactPoint, provenance models.Provenance) (apimodels.EmbeddedContactPoint, error) {
|
||||
if err := contactPoint.Valid(ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
if err := ValidateContactPoint(contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
return apimodels.EmbeddedContactPoint{}, fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
@ -140,7 +145,7 @@ func (ecp *ContactPointService) CreateContactPoint(ctx context.Context, orgID in
|
||||
return apimodels.EmbeddedContactPoint{}, err
|
||||
}
|
||||
|
||||
extractedSecrets, err := contactPoint.ExtractSecrets()
|
||||
extractedSecrets, err := RemoveSecretsForContactPoint(&contactPoint)
|
||||
if err != nil {
|
||||
return apimodels.EmbeddedContactPoint{}, err
|
||||
}
|
||||
@ -240,7 +245,7 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secretKeys, err := contactPoint.SecretKeys()
|
||||
secretKeys, err := GetSecretKeysForContactPointType(contactPoint.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
@ -252,7 +257,7 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
|
||||
}
|
||||
|
||||
// validate merged values
|
||||
if err := contactPoint.Valid(ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
if err := ValidateContactPoint(contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
@ -265,7 +270,7 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
|
||||
return fmt.Errorf("cannot changed provenance from '%s' to '%s'", storedProvenance, provenance)
|
||||
}
|
||||
// transform to internal model
|
||||
extractedSecrets, err := contactPoint.ExtractSecrets()
|
||||
extractedSecrets, err := RemoveSecretsForContactPoint(&contactPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -494,3 +499,62 @@ func replaceReferences(oldName, newName string, routes ...*apimodels.Route) {
|
||||
replaceReferences(oldName, newName, route.Routes...)
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateContactPoint(e apimodels.EmbeddedContactPoint, decryptFunc receivers.GetDecryptedValueFn) error {
|
||||
if e.Type == "" {
|
||||
return fmt.Errorf("type should not be an empty string")
|
||||
}
|
||||
if e.Settings == nil {
|
||||
return fmt.Errorf("settings should not be empty")
|
||||
}
|
||||
factory, exists := alertingNotify.Factory(e.Type)
|
||||
if !exists {
|
||||
return fmt.Errorf("unknown type '%s'", e.Type)
|
||||
}
|
||||
jsonBytes, err := e.Settings.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, _ := receivers.NewFactoryConfig(&receivers.NotificationChannelConfig{
|
||||
Settings: jsonBytes,
|
||||
Type: e.Type,
|
||||
}, nil, decryptFunc, nil, nil, func(ctx ...interface{}) logging.Logger {
|
||||
return &logging.FakeLogger{}
|
||||
}, setting.BuildVersion)
|
||||
if _, err := factory(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSecretKeysForContactPointType returns settings keys of contact point of the given type that are expected to be secrets. Returns error is contact point type is not known.
|
||||
func GetSecretKeysForContactPointType(contactPointType string) ([]string, error) {
|
||||
notifiers := channels_config.GetAvailableNotifiers()
|
||||
for _, n := range notifiers {
|
||||
if n.Type == contactPointType {
|
||||
var secureFields []string
|
||||
for _, field := range n.Options {
|
||||
if field.Secure {
|
||||
secureFields = append(secureFields, field.PropertyName)
|
||||
}
|
||||
}
|
||||
return secureFields, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no secrets configured for type '%s'", contactPointType)
|
||||
}
|
||||
|
||||
// RemoveSecretsForContactPoint removes all secrets from the contact point's settings and returns them as a map. Returns error if contact point type is not known.
|
||||
func RemoveSecretsForContactPoint(e *apimodels.EmbeddedContactPoint) (map[string]string, error) {
|
||||
s := map[string]string{}
|
||||
secretKeys, err := GetSecretKeysForContactPointType(e.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, secretKey := range secretKeys {
|
||||
secretValue := e.Settings.Get(secretKey).MustString()
|
||||
e.Settings.Del(secretKey)
|
||||
s[secretKey] = secretValue
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/values"
|
||||
)
|
||||
|
||||
@ -94,7 +95,7 @@ func (config *ReceiverV1) mapToModel(name string) (definitions.EmbeddedContactPo
|
||||
}
|
||||
// As the values are not encrypted when coming from disk files,
|
||||
// we can simply return the fallback for validation.
|
||||
err := cp.Valid(func(_ context.Context, _ map[string][]byte, _, fallback string) string {
|
||||
err := provisioning.ValidateContactPoint(cp, func(_ context.Context, _ map[string][]byte, _, fallback string) string {
|
||||
return fallback
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package file
|
||||
package alerting
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -31,11 +31,11 @@ type AlertRuleGroupV1 struct {
|
||||
Rules []AlertRuleV1 `json:"rules" yaml:"rules"`
|
||||
}
|
||||
|
||||
func (ruleGroupV1 *AlertRuleGroupV1) MapToModel() (AlertRuleGroupWithFolderTitle, error) {
|
||||
ruleGroup := AlertRuleGroupWithFolderTitle{AlertRuleGroup: &models.AlertRuleGroup{}}
|
||||
func (ruleGroupV1 *AlertRuleGroupV1) MapToModel() (models.AlertRuleGroupWithFolderTitle, error) {
|
||||
ruleGroup := models.AlertRuleGroupWithFolderTitle{AlertRuleGroup: &models.AlertRuleGroup{}}
|
||||
ruleGroup.Title = ruleGroupV1.Name.Value()
|
||||
if strings.TrimSpace(ruleGroup.Title) == "" {
|
||||
return AlertRuleGroupWithFolderTitle{}, errors.New("rule group has no name set")
|
||||
return models.AlertRuleGroupWithFolderTitle{}, errors.New("rule group has no name set")
|
||||
}
|
||||
ruleGroup.OrgID = ruleGroupV1.OrgID.Value()
|
||||
if ruleGroup.OrgID < 1 {
|
||||
@ -43,29 +43,23 @@ func (ruleGroupV1 *AlertRuleGroupV1) MapToModel() (AlertRuleGroupWithFolderTitle
|
||||
}
|
||||
interval, err := model.ParseDuration(ruleGroupV1.Interval.Value())
|
||||
if err != nil {
|
||||
return AlertRuleGroupWithFolderTitle{}, err
|
||||
return models.AlertRuleGroupWithFolderTitle{}, err
|
||||
}
|
||||
ruleGroup.Interval = int64(time.Duration(interval).Seconds())
|
||||
ruleGroup.FolderTitle = ruleGroupV1.Folder.Value()
|
||||
if strings.TrimSpace(ruleGroup.FolderTitle) == "" {
|
||||
return AlertRuleGroupWithFolderTitle{}, errors.New("rule group has no folder set")
|
||||
return models.AlertRuleGroupWithFolderTitle{}, errors.New("rule group has no folder set")
|
||||
}
|
||||
for _, ruleV1 := range ruleGroupV1.Rules {
|
||||
rule, err := ruleV1.mapToModel(ruleGroup.OrgID)
|
||||
if err != nil {
|
||||
return AlertRuleGroupWithFolderTitle{}, err
|
||||
return models.AlertRuleGroupWithFolderTitle{}, err
|
||||
}
|
||||
ruleGroup.Rules = append(ruleGroup.Rules, rule)
|
||||
}
|
||||
return ruleGroup, nil
|
||||
}
|
||||
|
||||
type AlertRuleGroupWithFolderTitle struct {
|
||||
*models.AlertRuleGroup
|
||||
OrgID int64
|
||||
FolderTitle string
|
||||
}
|
||||
|
||||
type AlertRuleV1 struct {
|
||||
UID values.StringValue `json:"uid" yaml:"uid"`
|
||||
Title values.StringValue `json:"title" yaml:"title"`
|
||||
@ -175,132 +169,3 @@ func (queryV1 *QueryV1) mapToModel() (models.AlertQuery, error) {
|
||||
Model: rawMessage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Response structs
|
||||
|
||||
// AlertingFileExport is the full provisioned file export.
|
||||
// swagger:model
|
||||
type AlertingFileExport struct {
|
||||
APIVersion int64 `json:"apiVersion" yaml:"apiVersion"`
|
||||
Groups []AlertRuleGroupExport `json:"groups" yaml:"groups"`
|
||||
}
|
||||
|
||||
// AlertRuleGroupExport is the provisioned file export of AlertRuleGroupV1.
|
||||
type AlertRuleGroupExport struct {
|
||||
OrgID int64 `json:"orgId" yaml:"orgId"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Folder string `json:"folder" yaml:"folder"`
|
||||
Interval model.Duration `json:"interval" yaml:"interval"`
|
||||
Rules []AlertRuleExport `json:"rules" yaml:"rules"`
|
||||
}
|
||||
|
||||
// AlertRuleExport is the provisioned file export of models.AlertRule.
|
||||
type AlertRuleExport struct {
|
||||
UID string `json:"uid" yaml:"uid"`
|
||||
Title string `json:"title" yaml:"title"`
|
||||
Condition string `json:"condition" yaml:"condition"`
|
||||
Data []AlertQueryExport `json:"data" yaml:"data"`
|
||||
DashboardUID string `json:"dasboardUid,omitempty" yaml:"dashboardUid,omitempty"`
|
||||
PanelID int64 `json:"panelId,omitempty" yaml:"panelId,omitempty"`
|
||||
NoDataState models.NoDataState `json:"noDataState" yaml:"noDataState"`
|
||||
ExecErrState models.ExecutionErrorState `json:"execErrState" yaml:"execErrState"`
|
||||
For model.Duration `json:"for" yaml:"for"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
IsPaused bool `json:"isPaused" yaml:"isPaused"`
|
||||
}
|
||||
|
||||
// AlertQueryExport is the provisioned export of models.AlertQuery.
|
||||
type AlertQueryExport struct {
|
||||
RefID string `json:"refId" yaml:"refId"`
|
||||
QueryType string `json:"queryType,omitempty" yaml:"queryType,omitempty"`
|
||||
RelativeTimeRange models.RelativeTimeRange `json:"relativeTimeRange,omitempty" yaml:"relativeTimeRange,omitempty"`
|
||||
DatasourceUID string `json:"datasourceUid" yaml:"datasourceUid"`
|
||||
Model map[string]interface{} `json:"model" yaml:"model"`
|
||||
}
|
||||
|
||||
// NewAlertingFileExport creates an AlertingFileExport DTO from []AlertRuleGroupWithFolderTitle.
|
||||
func NewAlertingFileExport(groups []AlertRuleGroupWithFolderTitle) (AlertingFileExport, error) {
|
||||
f := AlertingFileExport{APIVersion: 1}
|
||||
for _, group := range groups {
|
||||
export, err := newAlertRuleGroupExport(group)
|
||||
if err != nil {
|
||||
return AlertingFileExport{}, err
|
||||
}
|
||||
f.Groups = append(f.Groups, export)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// newAlertRuleGroupExport creates a AlertRuleGroupExport DTO from models.AlertRuleGroup.
|
||||
func newAlertRuleGroupExport(d AlertRuleGroupWithFolderTitle) (AlertRuleGroupExport, error) {
|
||||
rules := make([]AlertRuleExport, 0, len(d.Rules))
|
||||
for i := range d.Rules {
|
||||
alert, err := newAlertRuleExport(d.Rules[i])
|
||||
if err != nil {
|
||||
return AlertRuleGroupExport{}, err
|
||||
}
|
||||
rules = append(rules, alert)
|
||||
}
|
||||
return AlertRuleGroupExport{
|
||||
OrgID: d.OrgID,
|
||||
Name: d.Title,
|
||||
Folder: d.FolderTitle,
|
||||
Interval: model.Duration(time.Duration(d.Interval) * time.Second),
|
||||
Rules: rules,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newAlertRuleExport creates a AlertRuleExport DTO from models.AlertRule.
|
||||
func newAlertRuleExport(rule models.AlertRule) (AlertRuleExport, error) {
|
||||
data := make([]AlertQueryExport, 0, len(rule.Data))
|
||||
for i := range rule.Data {
|
||||
query, err := newAlertQueryExport(rule.Data[i])
|
||||
if err != nil {
|
||||
return AlertRuleExport{}, err
|
||||
}
|
||||
data = append(data, query)
|
||||
}
|
||||
|
||||
var dashboardUID string
|
||||
if rule.DashboardUID != nil {
|
||||
dashboardUID = *rule.DashboardUID
|
||||
}
|
||||
|
||||
var panelID int64
|
||||
if rule.PanelID != nil {
|
||||
panelID = *rule.PanelID
|
||||
}
|
||||
|
||||
return AlertRuleExport{
|
||||
UID: rule.UID,
|
||||
Title: rule.Title,
|
||||
For: model.Duration(rule.For),
|
||||
Condition: rule.Condition,
|
||||
Data: data,
|
||||
DashboardUID: dashboardUID,
|
||||
PanelID: panelID,
|
||||
NoDataState: rule.NoDataState,
|
||||
ExecErrState: rule.ExecErrState,
|
||||
Annotations: rule.Annotations,
|
||||
Labels: rule.Labels,
|
||||
IsPaused: rule.IsPaused,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newAlertQueryExport creates a AlertQueryExport DTO from models.AlertQuery.
|
||||
func newAlertQueryExport(query models.AlertQuery) (AlertQueryExport, error) {
|
||||
// We unmarshal the json.RawMessage model into a map in order to facilitate yaml marshalling.
|
||||
var mdl map[string]interface{}
|
||||
err := json.Unmarshal(query.Model, &mdl)
|
||||
if err != nil {
|
||||
return AlertQueryExport{}, err
|
||||
}
|
||||
return AlertQueryExport{
|
||||
RefID: query.RefID,
|
||||
QueryType: query.QueryType,
|
||||
RelativeTimeRange: query.RelativeTimeRange,
|
||||
DatasourceUID: query.DatasourceUID,
|
||||
Model: mdl,
|
||||
}, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package file
|
||||
package alerting
|
||||
|
||||
import (
|
||||
"testing"
|
@ -3,7 +3,7 @@ package alerting
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/alerting/file"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/values"
|
||||
)
|
||||
|
||||
@ -16,8 +16,8 @@ type OrgID int64
|
||||
type AlertingFile struct {
|
||||
configVersion
|
||||
Filename string
|
||||
Groups []file.AlertRuleGroupWithFolderTitle
|
||||
DeleteRules []file.RuleDelete
|
||||
Groups []models.AlertRuleGroupWithFolderTitle
|
||||
DeleteRules []RuleDelete
|
||||
ContactPoints []ContactPoint
|
||||
DeleteContactPoints []DeleteContactPoint
|
||||
Policies []NotificiationPolicy
|
||||
@ -31,8 +31,8 @@ type AlertingFile struct {
|
||||
type AlertingFileV1 struct {
|
||||
configVersion
|
||||
Filename string
|
||||
Groups []file.AlertRuleGroupV1 `json:"groups" yaml:"groups"`
|
||||
DeleteRules []file.RuleDeleteV1 `json:"deleteRules" yaml:"deleteRules"`
|
||||
Groups []AlertRuleGroupV1 `json:"groups" yaml:"groups"`
|
||||
DeleteRules []RuleDeleteV1 `json:"deleteRules" yaml:"deleteRules"`
|
||||
ContactPoints []ContactPointV1 `json:"contactPoints" yaml:"contactPoints"`
|
||||
DeleteContactPoints []DeleteContactPointV1 `json:"deleteContactPoints" yaml:"deleteContactPoints"`
|
||||
Policies []NotificiationPolicyV1 `json:"policies" yaml:"policies"`
|
||||
@ -133,7 +133,7 @@ func (fileV1 *AlertingFileV1) mapRules(alertingFile *AlertingFile) error {
|
||||
if orgID < 1 {
|
||||
orgID = 1
|
||||
}
|
||||
ruleDelete := file.RuleDelete{
|
||||
ruleDelete := RuleDelete{
|
||||
UID: ruleDeleteV1.UID.Value(),
|
||||
OrgID: orgID,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user