3806b6eec4
CloudMigrations: Omit autoincremented id for alert snapshots (#106598)
If you tried to migrate alerts from two differente sources/on-prem instances,
the autoincremented numeric id would be the same, and since we were creating
the resource in cloud with that same id (the API accepts it), it would
return an error "conflicting alert rule found" because that id is the primary key on the table.
We simply omit the numeric id now and let the API for the cloud instance generate it.
(cherry picked from commit a1f2693fd8)
Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com>
238 lines
8.8 KiB
Go
238 lines
8.8 KiB
Go
package cloudmigrationimpl
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/prometheus/alertmanager/config"
|
|
"github.com/prometheus/common/model"
|
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
ngalertapi "github.com/grafana/grafana/pkg/services/ngalert/api"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
type muteTimeInterval struct {
|
|
UID string `json:"uid"`
|
|
|
|
// There is a lot of custom (de)serialization logic from Alertmanager,
|
|
// and this is the same type used by the underlying API, hence we can use the type as-is.
|
|
config.MuteTimeInterval `json:",inline"`
|
|
}
|
|
|
|
func (s *Service) getAlertMuteTimings(ctx context.Context, signedInUser *user.SignedInUser) ([]muteTimeInterval, error) {
|
|
muteTimings, err := s.ngAlert.Api.MuteTimings.GetMuteTimings(ctx, signedInUser.OrgID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching ngalert mute timings: %w", err)
|
|
}
|
|
|
|
muteTimeIntervals := make([]muteTimeInterval, 0, len(muteTimings))
|
|
|
|
for _, muteTiming := range muteTimings {
|
|
muteTimeIntervals = append(muteTimeIntervals, muteTimeInterval{
|
|
UID: muteTiming.UID,
|
|
MuteTimeInterval: config.MuteTimeInterval{
|
|
Name: muteTiming.Name,
|
|
TimeIntervals: muteTiming.TimeIntervals,
|
|
},
|
|
})
|
|
}
|
|
|
|
return muteTimeIntervals, nil
|
|
}
|
|
|
|
type notificationTemplate struct {
|
|
UID string `json:"uid"`
|
|
Name string `json:"name"`
|
|
Template string `json:"template"`
|
|
}
|
|
|
|
func (s *Service) getNotificationTemplates(ctx context.Context, signedInUser *user.SignedInUser) ([]notificationTemplate, error) {
|
|
templates, err := s.ngAlert.Api.Templates.GetTemplates(ctx, signedInUser.OrgID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching ngalert notification templates: %w", err)
|
|
}
|
|
|
|
notificationTemplates := make([]notificationTemplate, 0, len(templates))
|
|
|
|
for _, template := range templates {
|
|
notificationTemplates = append(notificationTemplates, notificationTemplate{
|
|
UID: template.UID,
|
|
Name: template.Name,
|
|
Template: template.Template,
|
|
})
|
|
}
|
|
|
|
return notificationTemplates, nil
|
|
}
|
|
|
|
type contactPoint struct {
|
|
Settings *simplejson.Json `json:"settings"`
|
|
UID string `json:"uid"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
DisableResolveMessage bool `json:"disableResolveMessage"`
|
|
}
|
|
|
|
func (s *Service) getContactPoints(ctx context.Context, signedInUser *user.SignedInUser) ([]contactPoint, error) {
|
|
query := provisioning.ContactPointQuery{
|
|
OrgID: signedInUser.GetOrgID(),
|
|
Decrypt: true, // needed to recreate the settings in the target instance.
|
|
}
|
|
|
|
embeddedContactPoints, err := s.ngAlert.Api.ContactPointService.GetContactPoints(ctx, query, signedInUser)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching ngalert contact points: %w", err)
|
|
}
|
|
|
|
contactPoints := make([]contactPoint, 0, len(embeddedContactPoints))
|
|
|
|
for _, embeddedContactPoint := range embeddedContactPoints {
|
|
contactPoints = append(contactPoints, contactPoint{
|
|
UID: embeddedContactPoint.UID,
|
|
Name: embeddedContactPoint.Name,
|
|
Type: embeddedContactPoint.Type,
|
|
Settings: embeddedContactPoint.Settings,
|
|
DisableResolveMessage: embeddedContactPoint.DisableResolveMessage,
|
|
})
|
|
}
|
|
|
|
return contactPoints, nil
|
|
}
|
|
|
|
type notificationPolicy struct {
|
|
Name string
|
|
Routes definitions.Route
|
|
}
|
|
|
|
func (s *Service) getNotificationPolicies(ctx context.Context, signedInUser *user.SignedInUser) (notificationPolicy, error) {
|
|
policyTree, _, err := s.ngAlert.Api.Policies.GetPolicyTree(ctx, signedInUser.GetOrgID())
|
|
if err != nil {
|
|
return notificationPolicy{}, fmt.Errorf("fetching ngalert notification policy tree: %w", err)
|
|
}
|
|
|
|
return notificationPolicy{
|
|
Name: "Notification Policy Tree",
|
|
Routes: policyTree,
|
|
}, nil
|
|
}
|
|
|
|
type alertRule struct {
|
|
Updated time.Time `json:"updated,omitempty"`
|
|
Annotations map[string]string `json:"annotations,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
Record *definitions.Record `json:"record"`
|
|
NotificationSettings *definitions.AlertRuleNotificationSettings `json:"notification_settings"`
|
|
FolderUID string `json:"folderUID"`
|
|
RuleGroup string `json:"ruleGroup"`
|
|
NoDataState string `json:"noDataState"`
|
|
Condition string `json:"condition"`
|
|
UID string `json:"uid"`
|
|
Title string `json:"title"`
|
|
ExecErrState string `json:"execErrState"`
|
|
Data []definitions.AlertQuery `json:"data"`
|
|
For model.Duration `json:"for"`
|
|
OrgID int64 `json:"orgID"`
|
|
IsPaused bool `json:"isPaused"`
|
|
}
|
|
|
|
func (s *Service) getAlertRules(ctx context.Context, signedInUser *user.SignedInUser) ([]alertRule, error) {
|
|
alertRules, _, err := s.ngAlert.Api.AlertRules.GetAlertRules(ctx, signedInUser)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching alert rules: %w", err)
|
|
}
|
|
|
|
settingAlertRulesPaused := s.cfg.CloudMigration.AlertRulesState == setting.GMSAlertRulesPaused
|
|
|
|
provisionedAlertRules := make([]alertRule, 0, len(alertRules))
|
|
|
|
for _, rule := range alertRules {
|
|
isPaused := rule.IsPaused
|
|
if settingAlertRulesPaused {
|
|
isPaused = true
|
|
}
|
|
|
|
provisionedAlertRules = append(provisionedAlertRules, alertRule{
|
|
UID: rule.UID,
|
|
OrgID: rule.OrgID,
|
|
FolderUID: rule.NamespaceUID,
|
|
RuleGroup: rule.RuleGroup,
|
|
Title: rule.Title,
|
|
For: model.Duration(rule.For),
|
|
Condition: rule.Condition,
|
|
Data: ngalertapi.ApiAlertQueriesFromAlertQueries(rule.Data),
|
|
Updated: rule.Updated,
|
|
NoDataState: rule.NoDataState.String(),
|
|
ExecErrState: rule.ExecErrState.String(),
|
|
Annotations: rule.Annotations,
|
|
Labels: rule.Labels,
|
|
IsPaused: isPaused,
|
|
NotificationSettings: ngalertapi.AlertRuleNotificationSettingsFromNotificationSettings(rule.NotificationSettings),
|
|
Record: ngalertapi.ApiRecordFromModelRecord(rule.Record),
|
|
})
|
|
}
|
|
|
|
return provisionedAlertRules, nil
|
|
}
|
|
|
|
type alertRuleGroup struct {
|
|
Title string `json:"title"`
|
|
FolderUID string `json:"folderUid"`
|
|
Interval int64 `json:"interval"`
|
|
Rules []alertRule `json:"rules"`
|
|
}
|
|
|
|
func (s *Service) getAlertRuleGroups(ctx context.Context, signedInUser *user.SignedInUser) ([]alertRuleGroup, error) {
|
|
alertRuleGroupsWithFolder, err := s.ngAlert.Api.AlertRules.GetAlertGroupsWithFolderFullpath(ctx, signedInUser, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching alert rule groups with folders: %w", err)
|
|
}
|
|
|
|
settingAlertRulesPaused := s.cfg.CloudMigration.AlertRulesState == setting.GMSAlertRulesPaused
|
|
|
|
alertRuleGroups := make([]alertRuleGroup, 0, len(alertRuleGroupsWithFolder))
|
|
|
|
for _, ruleGroup := range alertRuleGroupsWithFolder {
|
|
provisionedAlertRules := make([]alertRule, 0, len(ruleGroup.Rules))
|
|
|
|
for _, rule := range ruleGroup.Rules {
|
|
isPaused := rule.IsPaused
|
|
if settingAlertRulesPaused {
|
|
isPaused = true
|
|
}
|
|
|
|
provisionedAlertRules = append(provisionedAlertRules, alertRule{
|
|
UID: rule.UID,
|
|
OrgID: rule.OrgID,
|
|
FolderUID: rule.NamespaceUID,
|
|
RuleGroup: rule.RuleGroup,
|
|
Title: rule.Title,
|
|
For: model.Duration(rule.For),
|
|
Condition: rule.Condition,
|
|
Data: ngalertapi.ApiAlertQueriesFromAlertQueries(rule.Data),
|
|
Updated: rule.Updated,
|
|
NoDataState: rule.NoDataState.String(),
|
|
ExecErrState: rule.ExecErrState.String(),
|
|
Annotations: rule.Annotations,
|
|
Labels: rule.Labels,
|
|
IsPaused: isPaused,
|
|
NotificationSettings: ngalertapi.AlertRuleNotificationSettingsFromNotificationSettings(rule.NotificationSettings),
|
|
Record: ngalertapi.ApiRecordFromModelRecord(rule.Record),
|
|
})
|
|
}
|
|
|
|
alertRuleGroups = append(alertRuleGroups, alertRuleGroup{
|
|
Title: ruleGroup.Title,
|
|
FolderUID: ruleGroup.FolderUID,
|
|
Interval: ruleGroup.Interval,
|
|
Rules: provisionedAlertRules,
|
|
})
|
|
}
|
|
|
|
return alertRuleGroups, nil
|
|
}
|