diff --git a/go.mod b/go.mod index d4b40f4d37b..a1160eac204 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( github.com/google/go-cmp v0.5.5 github.com/google/uuid v1.2.0 github.com/gosimple/slug v1.9.0 - github.com/grafana/alerting-api v0.0.0-20210407150830-64bd267999d1 + github.com/grafana/alerting-api v0.0.0-20210409134845-c36ac1eae41b github.com/grafana/grafana-aws-sdk v0.4.0 github.com/grafana/grafana-live-sdk v0.0.4 github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 @@ -103,4 +103,4 @@ require ( gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b xorm.io/core v0.7.3 xorm.io/xorm v0.8.2 -) +) \ No newline at end of file diff --git a/go.sum b/go.sum index 7598296fcfe..539f59ec842 100644 --- a/go.sum +++ b/go.sum @@ -816,8 +816,18 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs= github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg= +github.com/grafana/alerting-api v0.0.0-20210331135037-3294563b51bb h1:Hj25Whc/TRv0hSLm5VN0FJ5R4yZ6M4ycRcBgu7bsEAc= +github.com/grafana/alerting-api v0.0.0-20210331135037-3294563b51bb/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY= +github.com/grafana/alerting-api v0.0.0-20210405171311-97906879c771 h1:CTmKHUu2n0O9fPTSXb+s5FO8Em9Atw57Z7mvw7lt6IM= +github.com/grafana/alerting-api v0.0.0-20210405171311-97906879c771/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY= github.com/grafana/alerting-api v0.0.0-20210407150830-64bd267999d1 h1:pbG8BsRHezUvUjMxwq+uZsx1ZMEQsfSj26KSd/H3A9g= github.com/grafana/alerting-api v0.0.0-20210407150830-64bd267999d1/go.mod h1:Ce2PwraBlFMa+P0ArBzubfB/BXZV35mfYWQjM8C/BSE= +github.com/grafana/alerting-api v0.0.0-20210408130544-2969b61275a5 h1:r33Ruhf3TNvT4Y+acR8R0zBtNULnThnJRlAje2AFt6c= +github.com/grafana/alerting-api v0.0.0-20210408130544-2969b61275a5/go.mod h1:Ce2PwraBlFMa+P0ArBzubfB/BXZV35mfYWQjM8C/BSE= +github.com/grafana/alerting-api v0.0.0-20210408135630-bc10f737ceff h1:3l/P+TIOXJy1zX/kZB6dFAMPQrpwpiN3cLMVr8B8u4Q= +github.com/grafana/alerting-api v0.0.0-20210408135630-bc10f737ceff/go.mod h1:Ce2PwraBlFMa+P0ArBzubfB/BXZV35mfYWQjM8C/BSE= +github.com/grafana/alerting-api v0.0.0-20210409134845-c36ac1eae41b h1:QG52Et3EVCxPoYZifm91bRPVknccfjQURcpi7zXVut8= +github.com/grafana/alerting-api v0.0.0-20210409134845-c36ac1eae41b/go.mod h1:Ce2PwraBlFMa+P0ArBzubfB/BXZV35mfYWQjM8C/BSE= github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036 h1:GplhUk6Xes5JIhUUrggPcPBhOn+eT8+WsHiebvq7GgA= github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/grafana/grafana v1.9.2-0.20210308201921-4ce0a49eac03/go.mod h1:AHRRvd4utJGY25J5nW8aL7wZzn/LcJ0z2za9oOp14j4= diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index 0ef50a9135e..6f27baf7f64 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -211,6 +211,8 @@ func toGettableExtendedRuleNode(r ngmodels.AlertRule) apimodels.GettableExtended RuleGroup: r.RuleGroup, NoDataState: apimodels.NoDataState(r.NoDataState), ExecErrState: apimodels.ExecutionErrorState(r.ExecErrState), + For: r.For, + Annotations: r.Annotations, }, } } @@ -225,6 +227,8 @@ func toPostableExtendedRuleNode(r ngmodels.AlertRule) apimodels.PostableExtended UID: r.UID, NoDataState: apimodels.NoDataState(r.NoDataState), ExecErrState: apimodels.ExecutionErrorState(r.ExecErrState), + For: r.For, + Annotations: r.Annotations, }, } } diff --git a/pkg/services/ngalert/api/legacy_trans_dev.go b/pkg/services/ngalert/api/legacy_trans_dev.go index 080da283ee4..9e7a72a1b91 100644 --- a/pkg/services/ngalert/api/legacy_trans_dev.go +++ b/pkg/services/ngalert/api/legacy_trans_dev.go @@ -158,23 +158,26 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response { return response.Error(400, "unable to translate nodata/exec error settings", fmt.Errorf("unable to translate nodata/exec error settings for alert id %v: %w", id, err)) } - // TODO: What to do with Rule Tags - // ruleTags := map[string]string{} - // for k, v := range oldAlert.Settings.Get("alertRuleTags").MustMap() { - // sV, ok := v.(string) - // if !ok { - // return response.Error(400, "unable to unmarshal rule tags", - // fmt.Errorf("unexpected type %T for tag %v", v, k)) - // } - // ruleTags[k] = sV - // } - // TODO: Need place to put FOR duration + + ruleTags := map[string]string{} + + for k, v := range oldAlert.Settings.Get("alertRuleTags").MustMap() { + sV, ok := v.(string) + if !ok { + return response.Error(400, "unable to unmarshal rule tags", + fmt.Errorf("unexpected type %T for tag %v", v, k)) + } + ruleTags[k] = sV + } + rule := ngmodels.AlertRule{ Title: oldAlert.Name, Data: sseCond.Data, Condition: sseCond.Condition, NoDataState: *noDataSetting, ExecErrState: *execErrSetting, + For: ngmodels.Duration(oldAlert.For), + Annotations: ruleTags, } rgc := apimodels.PostableRuleGroupConfig{ // TODO? Generate new name on conflict? @@ -204,6 +207,7 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response { } return response.JSON(200, cmd) } + func transAdjustInterval(freq int64) model.Duration { // 10 corresponds to the SchedulerCfg, but TODO not worrying about fetching for now. var baseFreq int64 = 10 @@ -212,6 +216,7 @@ func transAdjustInterval(freq int64) model.Duration { } return model.Duration(time.Duration((freq - (freq % baseFreq))) * time.Second) } + func transGetAlertById(id int64, user models.SignedInUser) (*models.Alert, int, error) { getAlert := &models.GetAlertByIdQuery{ Id: id, @@ -224,6 +229,7 @@ func transGetAlertById(id int64, user models.SignedInUser) (*models.Alert, int, } return getAlert.Result, 0, nil } + func transGetAlertsDashById(dashboardId int64, user models.SignedInUser) (*models.Dashboard, int, error) { getDash := &models.GetDashboardQuery{ Id: dashboardId, @@ -234,6 +240,7 @@ func transGetAlertsDashById(dashboardId int64, user models.SignedInUser) (*model } return getDash.Result, 0, nil } + func transToSSECondition(m *models.Alert, user models.SignedInUser) (*ngmodels.Condition, error) { sb, err := m.Settings.ToDB() if err != nil { @@ -245,6 +252,7 @@ func transToSSECondition(m *models.Alert, user models.SignedInUser) (*ngmodels.C } return evalCond, nil } + func transNoDataExecSettings(m *models.Alert, user models.SignedInUser) (*ngmodels.NoDataState, *ngmodels.ExecutionErrorState, error) { oldNoData := m.Settings.Get("noDataState").MustString() noDataSetting, err := transNoData(oldNoData) @@ -258,6 +266,7 @@ func transNoDataExecSettings(m *models.Alert, user models.SignedInUser) (*ngmode } return &noDataSetting, &execErrSetting, nil } + func transNoData(s string) (ngmodels.NoDataState, error) { switch s { case "ok": @@ -271,6 +280,7 @@ func transNoData(s string) (ngmodels.NoDataState, error) { } return ngmodels.NoData, fmt.Errorf("unrecognized No Data setting %v", s) } + func transExecErr(s string) (ngmodels.ExecutionErrorState, error) { switch s { case "alerting": diff --git a/pkg/services/ngalert/api/test-data/post-rulegroup-42.json b/pkg/services/ngalert/api/test-data/post-rulegroup-42.json index 8d8efb73ff1..737d10dbaea 100644 --- a/pkg/services/ngalert/api/test-data/post-rulegroup-42.json +++ b/pkg/services/ngalert/api/test-data/post-rulegroup-42.json @@ -6,6 +6,10 @@ "grafana_alert": { "title": "prom query with SSE", "condition": "condition", + "for": 5, + "annotations": { + "foo": "bar" + }, "data": [ { "refId": "query", diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index 2098e48dd7b..c9ad6b2aaef 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -52,6 +52,8 @@ type AlertRule struct { RuleGroup string NoDataState NoDataState ExecErrState ExecutionErrorState + For Duration + Annotations map[string]string } // AlertRuleKey is the alert definition identifier @@ -100,6 +102,8 @@ type AlertRuleVersion struct { IntervalSeconds int64 NoDataState NoDataState ExecErrState ExecutionErrorState + For Duration + Annotations map[string]string } // GetAlertRuleByUIDQuery is the query for retrieving/deleting an alert rule by UID and organisation ID. diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index 09a564f297b..9bb80936afd 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -211,6 +211,9 @@ func (st DBstore) UpsertAlertRules(rules []UpsertRule) error { r.New.RuleGroup = r.Existing.RuleGroup r.New.Version = r.Existing.Version + 1 + r.New.For = r.Existing.For + r.New.Annotations = r.Existing.Annotations + if err := st.ValidateAlertRule(r.New, true); err != nil { return err } @@ -241,6 +244,8 @@ func (st DBstore) UpsertAlertRules(rules []UpsertRule) error { IntervalSeconds: r.New.IntervalSeconds, NoDataState: r.New.NoDataState, ExecErrState: r.New.ExecErrState, + For: r.New.For, + Annotations: r.New.Annotations, }) } @@ -422,6 +427,8 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error { IntervalSeconds: int64(time.Duration(cmd.RuleGroupConfig.Interval).Seconds()), NamespaceUID: cmd.NamespaceUID, RuleGroup: ruleGroup, + For: r.GrafanaManagedAlert.For, + Annotations: r.GrafanaManagedAlert.Annotations, NoDataState: ngmodels.NoDataState(r.GrafanaManagedAlert.NoDataState), ExecErrState: ngmodels.ExecutionErrorState(r.GrafanaManagedAlert.ExecErrState), }, diff --git a/pkg/services/ngalert/store/database_mig.go b/pkg/services/ngalert/store/database_mig.go index d1e418b969c..341b9a40211 100644 --- a/pkg/services/ngalert/store/database_mig.go +++ b/pkg/services/ngalert/store/database_mig.go @@ -147,6 +147,12 @@ func AddAlertRuleMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64) mg.AddMigration("alter alert_rule table data column to mediumtext in mysql", migrator.NewRawSQLMigration(""). Mysql("ALTER TABLE alert_rule MODIFY data MEDIUMTEXT;")) + + // add for column + mg.AddMigration("add column for to alert_rule", migrator.NewAddColumnMigration(alertRule, &migrator.Column{Name: "for", Type: migrator.DB_BigInt, Nullable: false, Default: "0"})) + + // add annotations column + mg.AddMigration("add column annotations to alert_rule", migrator.NewAddColumnMigration(alertRule, &migrator.Column{Name: "annotations", Type: migrator.DB_Text, Nullable: true})) } func AddAlertRuleVersionMigrations(mg *migrator.Migrator) { @@ -181,4 +187,10 @@ func AddAlertRuleVersionMigrations(mg *migrator.Migrator) { mg.AddMigration("alter alert_rule_version table data column to mediumtext in mysql", migrator.NewRawSQLMigration(""). Mysql("ALTER TABLE alert_rule_version MODIFY data MEDIUMTEXT;")) + + // add for column + mg.AddMigration("add column for to alert_rule_version", migrator.NewAddColumnMigration(alertRuleVersion, &migrator.Column{Name: "for", Type: migrator.DB_BigInt, Nullable: false, Default: "0"})) + + // add annotations column + mg.AddMigration("add column annotations to alert_rule_version", migrator.NewAddColumnMigration(alertRuleVersion, &migrator.Column{Name: "annotations", Type: migrator.DB_Text, Nullable: true})) }