From dbcd242b09a0e495c5201672610ed4045795fd14 Mon Sep 17 00:00:00 2001 From: Adrian Muraru Date: Wed, 19 Sep 2018 18:43:33 +0300 Subject: [PATCH 01/30] Updated phantomjs render script to take full height screenshots --- tools/phantomjs/render.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/phantomjs/render.js b/tools/phantomjs/render.js index 900660b7ca0..802678fd8da 100644 --- a/tools/phantomjs/render.js +++ b/tools/phantomjs/render.js @@ -56,8 +56,14 @@ if (panelsRendered || totalWaitMs > timeoutMs) { var bb = page.evaluate(function () { - return document.getElementsByClassName("main-view")[0].getBoundingClientRect(); + return document.getElementsByClassName("dashboard-container")[0].getBoundingClientRect(); }); + + // reset viewport to render full page + page.viewportSize = { + width: bb.width, + height: bb.height + }; page.clipRect = { top: bb.top, From ba4d5ddaaafc6c8a72cb6f2f1f09312be10ac9bb Mon Sep 17 00:00:00 2001 From: Adrian Muraru Date: Wed, 19 Sep 2018 19:36:56 +0300 Subject: [PATCH 02/30] Update render.js --- tools/phantomjs/render.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/phantomjs/render.js b/tools/phantomjs/render.js index 802678fd8da..485f6af2d98 100644 --- a/tools/phantomjs/render.js +++ b/tools/phantomjs/render.js @@ -56,7 +56,11 @@ if (panelsRendered || totalWaitMs > timeoutMs) { var bb = page.evaluate(function () { - return document.getElementsByClassName("dashboard-container")[0].getBoundingClientRect(); + var container = document.getElementsByClassName("dashboard-container") + if (container.length == 0) { + container = document.getElementsByClassName("panel-container") + } + return container[0].getBoundingClientRect(); }); // reset viewport to render full page From b7feabdb586b12db16fa4be7b0df675284404fd9 Mon Sep 17 00:00:00 2001 From: Adrian Muraru Date: Sat, 22 Sep 2018 00:49:50 +0300 Subject: [PATCH 03/30] Update render.js --- tools/phantomjs/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/phantomjs/render.js b/tools/phantomjs/render.js index 485f6af2d98..9d6dfcfd121 100644 --- a/tools/phantomjs/render.js +++ b/tools/phantomjs/render.js @@ -50,7 +50,7 @@ function checkIsReady() { var panelsRendered = page.evaluate(function() { - var panelCount = document.querySelectorAll('.panel').length; + var panelCount = document.querySelectorAll('plugin-component').length; return window.panelsRendered >= panelCount; }); From bd1f87ed455c9f53fe194ac6a8d159fcdd469b5d Mon Sep 17 00:00:00 2001 From: Adrian Muraru Date: Sat, 22 Sep 2018 00:54:56 +0300 Subject: [PATCH 04/30] Update render.js --- tools/phantomjs/render.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/phantomjs/render.js b/tools/phantomjs/render.js index 9d6dfcfd121..d0fb0234417 100644 --- a/tools/phantomjs/render.js +++ b/tools/phantomjs/render.js @@ -69,13 +69,6 @@ height: bb.height }; - page.clipRect = { - top: bb.top, - left: bb.left, - width: bb.width, - height: bb.height - }; - page.render(params.png); phantom.exit(); } else { From 6376154b16f0507657d143a4c70cc503d6140a14 Mon Sep 17 00:00:00 2001 From: Dave Waters Date: Mon, 15 Oct 2018 17:16:14 -0400 Subject: [PATCH 05/30] add channel option to disable the resolved alert (OK Message) that is sent when condition returns to normal. --- pkg/api/dtos/alerting.go | 49 ++++++++++--------- pkg/models/alert_notifications.go | 49 ++++++++++--------- pkg/services/alerting/interfaces.go | 1 + pkg/services/alerting/notifiers/base.go | 27 +++++++--- pkg/services/sqlstore/alert_notification.go | 22 +++++---- pkg/services/sqlstore/migrations/alert_mig.go | 3 ++ .../alerting/NotificationsEditCtrl.ts | 1 + .../alerting/partials/notification_edit.html | 7 +++ 8 files changed, 96 insertions(+), 63 deletions(-) diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index 697d0a35a08..2f762910d63 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -49,28 +49,30 @@ func formatShort(interval time.Duration) string { func NewAlertNotification(notification *models.AlertNotification) *AlertNotification { return &AlertNotification{ - Id: notification.Id, - Name: notification.Name, - Type: notification.Type, - IsDefault: notification.IsDefault, - Created: notification.Created, - Updated: notification.Updated, - Frequency: formatShort(notification.Frequency), - SendReminder: notification.SendReminder, - Settings: notification.Settings, + Id: notification.Id, + Name: notification.Name, + Type: notification.Type, + IsDefault: notification.IsDefault, + Created: notification.Created, + Updated: notification.Updated, + Frequency: formatShort(notification.Frequency), + SendReminder: notification.SendReminder, + DisableResolvedMessage: notification.DisableResolvedMessage, + Settings: notification.Settings, } } type AlertNotification struct { - Id int64 `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - IsDefault bool `json:"isDefault"` - SendReminder bool `json:"sendReminder"` - Frequency string `json:"frequency"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - Settings *simplejson.Json `json:"settings"` + Id int64 `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + IsDefault bool `json:"isDefault"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency string `json:"frequency"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + Settings *simplejson.Json `json:"settings"` } type AlertTestCommand struct { @@ -100,11 +102,12 @@ type EvalMatch struct { } type NotificationTestCommand struct { - Name string `json:"name"` - Type string `json:"type"` - SendReminder bool `json:"sendReminder"` - Frequency string `json:"frequency"` - Settings *simplejson.Json `json:"settings"` + Name string `json:"name"` + Type string `json:"type"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency string `json:"frequency"` + Settings *simplejson.Json `json:"settings"` } type PauseAlertCommand struct { diff --git a/pkg/models/alert_notifications.go b/pkg/models/alert_notifications.go index 2128b469fa4..14577fd29e6 100644 --- a/pkg/models/alert_notifications.go +++ b/pkg/models/alert_notifications.go @@ -23,38 +23,41 @@ var ( ) type AlertNotification struct { - Id int64 `json:"id"` - OrgId int64 `json:"-"` - Name string `json:"name"` - Type string `json:"type"` - SendReminder bool `json:"sendReminder"` - Frequency time.Duration `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + Id int64 `json:"id"` + OrgId int64 `json:"-"` + Name string `json:"name"` + Type string `json:"type"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency time.Duration `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` } type CreateAlertNotificationCommand struct { - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - SendReminder bool `json:"sendReminder"` - Frequency string `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency string `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` OrgId int64 `json:"-"` Result *AlertNotification } type UpdateAlertNotificationCommand struct { - Id int64 `json:"id" binding:"Required"` - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - SendReminder bool `json:"sendReminder"` - Frequency string `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings" binding:"Required"` + Id int64 `json:"id" binding:"Required"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency string `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings" binding:"Required"` OrgId int64 `json:"-"` Result *AlertNotification diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index 96294f0624f..7cd0e6ffca8 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -27,6 +27,7 @@ type Notifier interface { GetNotifierId() int64 GetIsDefault() bool GetSendReminder() bool + GetDisableResolvedMessage() bool GetFrequency() time.Duration } diff --git a/pkg/services/alerting/notifiers/base.go b/pkg/services/alerting/notifiers/base.go index fbade2eccac..320148c49c4 100644 --- a/pkg/services/alerting/notifiers/base.go +++ b/pkg/services/alerting/notifiers/base.go @@ -21,6 +21,7 @@ type NotifierBase struct { IsDeault bool UploadImage bool SendReminder bool + DisableResolvedMessage bool Frequency time.Duration log log.Logger @@ -34,14 +35,15 @@ func NewNotifierBase(model *models.AlertNotification) NotifierBase { } return NotifierBase{ - Id: model.Id, - Name: model.Name, - IsDeault: model.IsDefault, - Type: model.Type, - UploadImage: uploadImage, - SendReminder: model.SendReminder, - Frequency: model.Frequency, - log: log.New("alerting.notifier." + model.Name), + Id: model.Id, + Name: model.Name, + IsDeault: model.IsDefault, + Type: model.Type, + UploadImage: uploadImage, + SendReminder: model.SendReminder, + DisableResolvedMessage: model.DisableResolvedMessage, + Frequency: model.Frequency, + log: log.New("alerting.notifier." + model.Name), } } @@ -83,6 +85,11 @@ func (n *NotifierBase) ShouldNotify(ctx context.Context, context *alerting.EvalC } } + // Do not notify when state is OK if DisableResolvedMessage is set to true + if context.Rule.State == models.AlertStateOK && n.DisableResolvedMessage { + return false + } + return true } @@ -106,6 +113,10 @@ func (n *NotifierBase) GetSendReminder() bool { return n.SendReminder } +func (n *NotifierBase) GetDisableResolvedMessage() bool { + return n.DisableResolvedMessage +} + func (n *NotifierBase) GetFrequency() time.Duration { return n.Frequency } diff --git a/pkg/services/sqlstore/alert_notification.go b/pkg/services/sqlstore/alert_notification.go index daaef945b96..e95d4579342 100644 --- a/pkg/services/sqlstore/alert_notification.go +++ b/pkg/services/sqlstore/alert_notification.go @@ -66,6 +66,7 @@ func GetAlertNotificationsToSend(query *m.GetAlertNotificationsToSendQuery) erro alert_notification.updated, alert_notification.settings, alert_notification.is_default, + alert_notification.disable_resolved_message, alert_notification.send_reminder, alert_notification.frequency FROM alert_notification @@ -106,6 +107,7 @@ func getAlertNotificationInternal(query *m.GetAlertNotificationsQuery, sess *DBS alert_notification.updated, alert_notification.settings, alert_notification.is_default, + alert_notification.disable_resolved_message, alert_notification.send_reminder, alert_notification.frequency FROM alert_notification @@ -166,15 +168,16 @@ func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error } alertNotification := &m.AlertNotification{ - OrgId: cmd.OrgId, - Name: cmd.Name, - Type: cmd.Type, - Settings: cmd.Settings, - SendReminder: cmd.SendReminder, - Frequency: frequency, - Created: time.Now(), - Updated: time.Now(), - IsDefault: cmd.IsDefault, + OrgId: cmd.OrgId, + Name: cmd.Name, + Type: cmd.Type, + Settings: cmd.Settings, + SendReminder: cmd.SendReminder, + DisableResolvedMessage: cmd.DisableResolvedMessage, + Frequency: frequency, + Created: time.Now(), + Updated: time.Now(), + IsDefault: cmd.IsDefault, } if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil { @@ -210,6 +213,7 @@ func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error { current.Type = cmd.Type current.IsDefault = cmd.IsDefault current.SendReminder = cmd.SendReminder + current.DisableResolvedMessage = cmd.DisableResolvedMessage if current.SendReminder { if cmd.Frequency == "" { diff --git a/pkg/services/sqlstore/migrations/alert_mig.go b/pkg/services/sqlstore/migrations/alert_mig.go index cadcccf6c95..bc2d9647e03 100644 --- a/pkg/services/sqlstore/migrations/alert_mig.go +++ b/pkg/services/sqlstore/migrations/alert_mig.go @@ -71,6 +71,9 @@ func addAlertMigrations(mg *Migrator) { mg.AddMigration("Add column send_reminder", NewAddColumnMigration(alert_notification, &Column{ Name: "send_reminder", Type: DB_Bool, Nullable: true, Default: "0", })) + mg.AddMigration("Add column disable_resolved_message", NewAddColumnMigration(alert_notification, &Column{ + Name: "disable_resolved_message", Type: DB_Bool, Nullable: false, Default: "1", + })) mg.AddMigration("add index alert_notification org_id & name", NewAddIndexMigration(alert_notification, alert_notification.Indices[0])) diff --git a/public/app/features/alerting/NotificationsEditCtrl.ts b/public/app/features/alerting/NotificationsEditCtrl.ts index 315a9a619a1..92392559480 100644 --- a/public/app/features/alerting/NotificationsEditCtrl.ts +++ b/public/app/features/alerting/NotificationsEditCtrl.ts @@ -12,6 +12,7 @@ export class AlertNotificationEditCtrl { defaults: any = { type: 'email', sendReminder: false, + disableResolvedMessage: false, frequency: '15m', settings: { httpMethod: 'POST', diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index 7b198736b83..1c4003df91e 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -32,6 +32,13 @@ checked="ctrl.model.settings.uploadImage" tooltip="Captures an image and include it in the notification"> + + Date: Tue, 19 Jun 2018 17:06:56 +0900 Subject: [PATCH 06/30] get region list from ec2:DescribeRegions --- pkg/tsdb/cloudwatch/metric_find_query.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index ee9d9583c4e..64e5118e579 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -234,10 +234,22 @@ func parseMultiSelectValue(input string) []string { // Please update the region list in public/app/plugins/datasource/cloudwatch/partials/config.html func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) { regions := []string{ - "ap-northeast-1", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-south-1", "ca-central-1", "cn-north-1", "cn-northwest-1", - "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "sa-east-1", "us-east-1", "us-east-2", "us-gov-west-1", "us-west-1", "us-west-2", "us-isob-east-1", "us-iso-east-1", + "cn-north-1", "cn-northwest-1", "us-gov-west-1", "us-isob-east-1", "us-iso-east-1", } + err := e.ensureClientSession("us-east-1") + if err != nil { + return nil, err + } + r, err := e.ec2Svc.DescribeRegions(&ec2.DescribeRegionsInput{}) + if err != nil { + return nil, err + } + for _, region := range r.Regions { + regions = append(regions, *region.RegionName) + } + sort.Strings(regions) + result := make([]suggestData, 0) for _, region := range regions { result = append(result, suggestData{Text: region, Value: region}) From f6cb01d38a0b406e6c6318f295dc77c61ccefdd5 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Tue, 19 Jun 2018 17:41:26 +0900 Subject: [PATCH 07/30] show all CloudWatch regions --- pkg/tsdb/cloudwatch/metric_find_query.go | 2 +- .../datasource/cloudwatch/config_ctrl.ts | 24 ++++++++++++++++++- .../cloudwatch/partials/config.html | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index 64e5118e579..214b6ccbc67 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -234,7 +234,7 @@ func parseMultiSelectValue(input string) []string { // Please update the region list in public/app/plugins/datasource/cloudwatch/partials/config.html func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) { regions := []string{ - "cn-north-1", "cn-northwest-1", "us-gov-west-1", "us-isob-east-1", "us-iso-east-1", + "cn-north-1", "cn-northwest-1", "us-gov-east-1", "us-gov-west-1", "us-isob-east-1", "us-iso-east-1", } err := e.ensureClientSession("us-east-1") diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts index ff0d39944ca..1ff744854f5 100644 --- a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -1,17 +1,20 @@ export class CloudWatchConfigCtrl { static templateUrl = 'partials/config.html'; current: any; + $http: any; accessKeyExist = false; secretKeyExist = false; /** @ngInject */ - constructor($scope) { + constructor($scope, $http) { this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp'; this.current.jsonData.authType = this.current.jsonData.authType || 'credentials'; this.accessKeyExist = this.current.secureJsonFields.accessKey; this.secretKeyExist = this.current.secureJsonFields.secretKey; + this.$http = $http; + this.getRegions(); } resetAccessKey() { @@ -36,4 +39,23 @@ export class CloudWatchConfigCtrl { { name: 'Monthly', value: 'Monthly', example: '[logstash-]YYYY.MM' }, { name: 'Yearly', value: 'Yearly', example: '[logstash-]YYYY' }, ]; + + regions = []; + + getRegions() { + this.$http.get('https://ip-ranges.amazonaws.com/ip-ranges.json').then(ip_ranges => { + let regions = {}; + ip_ranges.data.prefixes + .map(p => { + return p.region; + }) + .filter(r => { + return r !== 'GLOBAL'; + }) + .forEach(r => { + regions[r] = true; + }); + this.regions = Object.keys(regions).sort(); + }); + } } diff --git a/public/app/plugins/datasource/cloudwatch/partials/config.html b/public/app/plugins/datasource/cloudwatch/partials/config.html index e5ab0910cba..40249d32b7e 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/config.html +++ b/public/app/plugins/datasource/cloudwatch/partials/config.html @@ -39,7 +39,7 @@
- + Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region. From 6564abd5f3984d56ca533672a33ba4c5f4eaf5ea Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Tue, 24 Jul 2018 22:09:33 +0900 Subject: [PATCH 08/30] get regions from after datasource save --- .../datasource/cloudwatch/config_ctrl.ts | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts index 1ff744854f5..7ee6b9dea43 100644 --- a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -1,19 +1,20 @@ +import _ from 'lodash'; export class CloudWatchConfigCtrl { static templateUrl = 'partials/config.html'; current: any; - $http: any; + datasourceSrv: any; accessKeyExist = false; secretKeyExist = false; /** @ngInject */ - constructor($scope, $http) { + constructor($scope, datasourceSrv) { this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp'; this.current.jsonData.authType = this.current.jsonData.authType || 'credentials'; this.accessKeyExist = this.current.secureJsonFields.accessKey; this.secretKeyExist = this.current.secureJsonFields.secretKey; - this.$http = $http; + this.datasourceSrv = datasourceSrv; this.getRegions(); } @@ -40,22 +41,41 @@ export class CloudWatchConfigCtrl { { name: 'Yearly', value: 'Yearly', example: '[logstash-]YYYY' }, ]; - regions = []; + regions = [ + 'ap-northeast-1', + 'ap-northeast-2', + 'ap-northeast-3', + 'ap-south-1', + 'ap-southeast-1', + 'ap-southeast-2', + 'ca-central-1', + 'cn-north-1', + 'cn-northwest-1', + 'eu-central-1', + 'eu-north-1', + 'eu-west-1', + 'eu-west-2', + 'eu-west-3', + 'me-south-1', + 'sa-east-1', + 'us-east-1', + 'us-east-2', + 'us-gov-east-1', + 'us-gov-west-1', + 'us-iso-east-1', + 'us-isob-east-1', + 'us-west-1', + 'us-west-2', + ]; getRegions() { - this.$http.get('https://ip-ranges.amazonaws.com/ip-ranges.json').then(ip_ranges => { - let regions = {}; - ip_ranges.data.prefixes - .map(p => { - return p.region; - }) - .filter(r => { - return r !== 'GLOBAL'; - }) - .forEach(r => { - regions[r] = true; - }); - this.regions = Object.keys(regions).sort(); - }); + this.datasourceSrv + .loadDatasource(this.current.name) + .then(ds => { + return ds.getRegions(); + }) + .then(regions => { + this.regions = _.uniq(this.regions.concat(_.map(regions, 'value'))); + }); } } From 61e27109614533a9c1b2188bc528abac4fe93a49 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Sat, 13 Oct 2018 15:48:13 +0900 Subject: [PATCH 09/30] add error message --- public/app/plugins/datasource/cloudwatch/config_ctrl.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts index 7ee6b9dea43..345315be0d2 100644 --- a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -76,6 +76,8 @@ export class CloudWatchConfigCtrl { }) .then(regions => { this.regions = _.uniq(this.regions.concat(_.map(regions, 'value'))); + }, err => { + console.error('failed to call ec2:DescribeRegions, please check iam setting'); }); } } From a417d6fa2139ec654097a10d0a49f74d5abd8edb Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Sat, 13 Oct 2018 15:54:12 +0900 Subject: [PATCH 10/30] don't merge hard coded region list --- public/app/plugins/datasource/cloudwatch/config_ctrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts index 345315be0d2..02a171f3f15 100644 --- a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -75,7 +75,7 @@ export class CloudWatchConfigCtrl { return ds.getRegions(); }) .then(regions => { - this.regions = _.uniq(this.regions.concat(_.map(regions, 'value'))); + this.regions = _.map(regions, 'value'); }, err => { console.error('failed to call ec2:DescribeRegions, please check iam setting'); }); From fce50cf1b8cc05fdc7fae97572c675e916f54604 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Tue, 16 Oct 2018 12:06:57 +0900 Subject: [PATCH 11/30] return default region list from backend --- pkg/tsdb/cloudwatch/metric_find_query.go | 12 ++++-- .../datasource/cloudwatch/config_ctrl.ts | 40 +++++-------------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index 214b6ccbc67..f2633bd6be6 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -234,6 +234,8 @@ func parseMultiSelectValue(input string) []string { // Please update the region list in public/app/plugins/datasource/cloudwatch/partials/config.html func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) { regions := []string{ + "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-south-1", "ap-southeast-1", "ap-southeast-2", "ca-central-1", + "eu-central-1", "eu-north-1", "eu-west-1", "eu-west-2", "eu-west-3", "me-south-1", "sa-east-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2", "cn-north-1", "cn-northwest-1", "us-gov-east-1", "us-gov-west-1", "us-isob-east-1", "us-iso-east-1", } @@ -243,10 +245,12 @@ func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *s } r, err := e.ec2Svc.DescribeRegions(&ec2.DescribeRegionsInput{}) if err != nil { - return nil, err - } - for _, region := range r.Regions { - regions = append(regions, *region.RegionName) + // ignore error for backward compatibility + plog.Error("Failed to get regions", "error", err) + } else { + for _, region := range r.Regions { + regions = append(regions, *region.RegionName) + } } sort.Strings(regions) diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts index 02a171f3f15..d46f5cf1580 100644 --- a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -41,32 +41,7 @@ export class CloudWatchConfigCtrl { { name: 'Yearly', value: 'Yearly', example: '[logstash-]YYYY' }, ]; - regions = [ - 'ap-northeast-1', - 'ap-northeast-2', - 'ap-northeast-3', - 'ap-south-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'ca-central-1', - 'cn-north-1', - 'cn-northwest-1', - 'eu-central-1', - 'eu-north-1', - 'eu-west-1', - 'eu-west-2', - 'eu-west-3', - 'me-south-1', - 'sa-east-1', - 'us-east-1', - 'us-east-2', - 'us-gov-east-1', - 'us-gov-west-1', - 'us-iso-east-1', - 'us-isob-east-1', - 'us-west-1', - 'us-west-2', - ]; + regions = []; getRegions() { this.datasourceSrv @@ -74,10 +49,13 @@ export class CloudWatchConfigCtrl { .then(ds => { return ds.getRegions(); }) - .then(regions => { - this.regions = _.map(regions, 'value'); - }, err => { - console.error('failed to call ec2:DescribeRegions, please check iam setting'); - }); + .then( + regions => { + this.regions = _.map(regions, 'value'); + }, + err => { + console.error('failed to get latest regions'); + } + ); } } From 18c73631ea2f538348bf2eb643dc15b75bbb3ebf Mon Sep 17 00:00:00 2001 From: Dave Waters Date: Tue, 16 Oct 2018 07:12:32 -0400 Subject: [PATCH 12/30] rename UI Option, align with control, update tests --- .../sqlstore/alert_notification_test.go | 26 ++++++++++--------- .../alerting/partials/notification_edit.html | 4 +-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index ed682bae5c6..d804988a55a 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -227,18 +227,20 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("Can update alert notification", func() { newCmd := &models.UpdateAlertNotificationCommand{ - Name: "NewName", - Type: "webhook", - OrgId: cmd.Result.OrgId, - SendReminder: true, - Frequency: "60s", - Settings: simplejson.New(), - Id: cmd.Result.Id, + Name: "NewName", + Type: "webhook", + OrgId: cmd.Result.OrgId, + SendReminder: true, + DisableResolvedMessage: true, + Frequency: "60s", + Settings: simplejson.New(), + Id: cmd.Result.Id, } err := UpdateAlertNotification(newCmd) So(err, ShouldBeNil) So(newCmd.Result.Name, ShouldEqual, "NewName") So(newCmd.Result.Frequency, ShouldEqual, 60*time.Second) + So(newCmd.Result.DisableResolvedMessage, ShouldBeTrue) }) Convey("Can update alert notification to disable sending of reminders", func() { @@ -257,12 +259,12 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Can search using an array of ids", func() { - cmd1 := models.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - cmd2 := models.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - cmd3 := models.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - cmd4 := models.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd1 := models.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} + cmd2 := models.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} + cmd3 := models.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} + cmd4 := models.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} - otherOrg := models.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + otherOrg := models.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} So(CreateAlertNotificationCommand(&cmd1), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd2), ShouldBeNil) diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index 1c4003df91e..1ecab3c3e3c 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -34,10 +34,10 @@ + tooltip="Disable the resolved message [OK] that is sent when alerting state returns to false"> Date: Tue, 16 Oct 2018 17:33:38 -0400 Subject: [PATCH 13/30] fix gofmt, add test, correct noted concerns with default value --- pkg/api/dtos/alerting.go | 52 +++++++++---------- pkg/models/alert_notifications.go | 50 +++++++++--------- pkg/services/alerting/notifiers/base.go | 33 ++++++------ pkg/services/alerting/notifiers/base_test.go | 5 ++ pkg/services/sqlstore/alert_notification.go | 18 +++---- .../sqlstore/alert_notification_test.go | 22 ++++---- pkg/services/sqlstore/migrations/alert_mig.go | 2 +- 7 files changed, 93 insertions(+), 89 deletions(-) diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index 2f762910d63..33f10c805ab 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -49,30 +49,30 @@ func formatShort(interval time.Duration) string { func NewAlertNotification(notification *models.AlertNotification) *AlertNotification { return &AlertNotification{ - Id: notification.Id, - Name: notification.Name, - Type: notification.Type, - IsDefault: notification.IsDefault, - Created: notification.Created, - Updated: notification.Updated, - Frequency: formatShort(notification.Frequency), - SendReminder: notification.SendReminder, - DisableResolvedMessage: notification.DisableResolvedMessage, - Settings: notification.Settings, + Id: notification.Id, + Name: notification.Name, + Type: notification.Type, + IsDefault: notification.IsDefault, + Created: notification.Created, + Updated: notification.Updated, + Frequency: formatShort(notification.Frequency), + SendReminder: notification.SendReminder, + DisableResolvedMessage: notification.DisableResolvedMessage, + Settings: notification.Settings, } } type AlertNotification struct { - Id int64 `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - IsDefault bool `json:"isDefault"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - Settings *simplejson.Json `json:"settings"` + Id int64 `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + IsDefault bool `json:"isDefault"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency string `json:"frequency"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + Settings *simplejson.Json `json:"settings"` } type AlertTestCommand struct { @@ -102,12 +102,12 @@ type EvalMatch struct { } type NotificationTestCommand struct { - Name string `json:"name"` - Type string `json:"type"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - Settings *simplejson.Json `json:"settings"` + Name string `json:"name"` + Type string `json:"type"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency string `json:"frequency"` + Settings *simplejson.Json `json:"settings"` } type PauseAlertCommand struct { diff --git a/pkg/models/alert_notifications.go b/pkg/models/alert_notifications.go index 14577fd29e6..f9327a4d4bc 100644 --- a/pkg/models/alert_notifications.go +++ b/pkg/models/alert_notifications.go @@ -23,41 +23,41 @@ var ( ) type AlertNotification struct { - Id int64 `json:"id"` - OrgId int64 `json:"-"` - Name string `json:"name"` - Type string `json:"type"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency time.Duration `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + Id int64 `json:"id"` + OrgId int64 `json:"-"` + Name string `json:"name"` + Type string `json:"type"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency time.Duration `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` } type CreateAlertNotificationCommand struct { - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - SendReminder bool `json:"sendReminder"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + SendReminder bool `json:"sendReminder"` DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings"` + Frequency string `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` OrgId int64 `json:"-"` Result *AlertNotification } type UpdateAlertNotificationCommand struct { - Id int64 `json:"id" binding:"Required"` - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings" binding:"Required"` + Id int64 `json:"id" binding:"Required"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + SendReminder bool `json:"sendReminder"` + DisableResolvedMessage bool `json:"disableResolvedMessage"` + Frequency string `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings" binding:"Required"` OrgId int64 `json:"-"` Result *AlertNotification diff --git a/pkg/services/alerting/notifiers/base.go b/pkg/services/alerting/notifiers/base.go index 320148c49c4..1b14059b1e6 100644 --- a/pkg/services/alerting/notifiers/base.go +++ b/pkg/services/alerting/notifiers/base.go @@ -6,7 +6,6 @@ import ( "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/alerting" ) @@ -15,14 +14,14 @@ const ( ) type NotifierBase struct { - Name string - Type string - Id int64 - IsDeault bool - UploadImage bool - SendReminder bool - DisableResolvedMessage bool - Frequency time.Duration + Name string + Type string + Id int64 + IsDeault bool + UploadImage bool + SendReminder bool + DisableResolvedMessage bool + Frequency time.Duration log log.Logger } @@ -35,15 +34,15 @@ func NewNotifierBase(model *models.AlertNotification) NotifierBase { } return NotifierBase{ - Id: model.Id, - Name: model.Name, - IsDeault: model.IsDefault, - Type: model.Type, - UploadImage: uploadImage, - SendReminder: model.SendReminder, + Id: model.Id, + Name: model.Name, + IsDeault: model.IsDefault, + Type: model.Type, + UploadImage: uploadImage, + SendReminder: model.SendReminder, DisableResolvedMessage: model.DisableResolvedMessage, - Frequency: model.Frequency, - log: log.New("alerting.notifier." + model.Name), + Frequency: model.Frequency, + log: log.New("alerting.notifier." + model.Name), } } diff --git a/pkg/services/alerting/notifiers/base_test.go b/pkg/services/alerting/notifiers/base_test.go index 5e46d3ad72e..281d0bfb238 100644 --- a/pkg/services/alerting/notifiers/base_test.go +++ b/pkg/services/alerting/notifiers/base_test.go @@ -179,5 +179,10 @@ func TestBaseNotifier(t *testing.T) { base := NewNotifierBase(model) So(base.UploadImage, ShouldBeTrue) }) + + Convey("default value should be false for backwards compatibility", func() { + base := NewNotifierBase(model) + So(base.DisableResolvedMessage, ShouldBeFalse) + }) }) } diff --git a/pkg/services/sqlstore/alert_notification.go b/pkg/services/sqlstore/alert_notification.go index e95d4579342..d82d5814869 100644 --- a/pkg/services/sqlstore/alert_notification.go +++ b/pkg/services/sqlstore/alert_notification.go @@ -168,16 +168,16 @@ func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error } alertNotification := &m.AlertNotification{ - OrgId: cmd.OrgId, - Name: cmd.Name, - Type: cmd.Type, - Settings: cmd.Settings, - SendReminder: cmd.SendReminder, + OrgId: cmd.OrgId, + Name: cmd.Name, + Type: cmd.Type, + Settings: cmd.Settings, + SendReminder: cmd.SendReminder, DisableResolvedMessage: cmd.DisableResolvedMessage, - Frequency: frequency, - Created: time.Now(), - Updated: time.Now(), - IsDefault: cmd.IsDefault, + Frequency: frequency, + Created: time.Now(), + Updated: time.Now(), + IsDefault: cmd.IsDefault, } if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil { diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index d804988a55a..116cff7021d 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -227,14 +227,14 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("Can update alert notification", func() { newCmd := &models.UpdateAlertNotificationCommand{ - Name: "NewName", - Type: "webhook", - OrgId: cmd.Result.OrgId, - SendReminder: true, + Name: "NewName", + Type: "webhook", + OrgId: cmd.Result.OrgId, + SendReminder: true, DisableResolvedMessage: true, - Frequency: "60s", - Settings: simplejson.New(), - Id: cmd.Result.Id, + Frequency: "60s", + Settings: simplejson.New(), + Id: cmd.Result.Id, } err := UpdateAlertNotification(newCmd) So(err, ShouldBeNil) @@ -259,10 +259,10 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Can search using an array of ids", func() { - cmd1 := models.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} - cmd2 := models.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} - cmd3 := models.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} - cmd4 := models.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} + cmd1 := m.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd2 := m.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd3 := m.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd4 := m.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} otherOrg := models.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} diff --git a/pkg/services/sqlstore/migrations/alert_mig.go b/pkg/services/sqlstore/migrations/alert_mig.go index bc2d9647e03..7add7ee3e86 100644 --- a/pkg/services/sqlstore/migrations/alert_mig.go +++ b/pkg/services/sqlstore/migrations/alert_mig.go @@ -72,7 +72,7 @@ func addAlertMigrations(mg *Migrator) { Name: "send_reminder", Type: DB_Bool, Nullable: true, Default: "0", })) mg.AddMigration("Add column disable_resolved_message", NewAddColumnMigration(alert_notification, &Column{ - Name: "disable_resolved_message", Type: DB_Bool, Nullable: false, Default: "1", + Name: "disable_resolved_message", Type: DB_Bool, Nullable: false, Default: "0", })) mg.AddMigration("add index alert_notification org_id & name", NewAddIndexMigration(alert_notification, alert_notification.Indices[0])) From 0eae7b077d1c63992cb5d8a85cc3fd6f49805e42 Mon Sep 17 00:00:00 2001 From: Dave Waters Date: Tue, 16 Oct 2018 17:38:59 -0400 Subject: [PATCH 14/30] fix gofmt, add test, correct noted concerns with default value --- .../sqlstore/alert_notification_test.go | 179 +++++------------- 1 file changed, 47 insertions(+), 132 deletions(-) diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index 116cff7021d..83fb42db9bb 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" + m "github.com/grafana/grafana/pkg/models" . "github.com/smartystreets/goconvey/convey" ) @@ -14,133 +14,50 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("Testing Alert notification sql access", t, func() { InitTestDB(t) - Convey("Alert notification state", func() { - var alertID int64 = 7 - var orgID int64 = 5 - var notifierID int64 = 10 - oldTimeNow := timeNow - now := time.Date(2018, 9, 30, 0, 0, 0, 0, time.UTC) - timeNow = func() time.Time { return now } + Convey("Alert notification journal", func() { + var alertId int64 = 5 + var orgId int64 = 5 + var notifierId int64 = 5 - Convey("Get no existing state should create a new state", func() { - query := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} - err := GetOrCreateAlertNotificationState(context.Background(), query) - So(err, ShouldBeNil) - So(query.Result, ShouldNotBeNil) - So(query.Result.State, ShouldEqual, "unknown") - So(query.Result.Version, ShouldEqual, 0) - So(query.Result.UpdatedAt, ShouldEqual, now.Unix()) + Convey("Getting last journal should raise error if no one exists", func() { + query := &m.GetLatestNotificationQuery{AlertId: alertId, OrgId: orgId, NotifierId: notifierId} + err := GetLatestNotification(context.Background(), query) + So(err, ShouldEqual, m.ErrJournalingNotFound) - Convey("Get existing state should not create a new state", func() { - query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} - err := GetOrCreateAlertNotificationState(context.Background(), query2) + Convey("shoulbe be able to record two journaling events", func() { + createCmd := &m.RecordNotificationJournalCommand{AlertId: alertId, NotifierId: notifierId, OrgId: orgId, Success: true, SentAt: 1} + + err := RecordNotificationJournal(context.Background(), createCmd) So(err, ShouldBeNil) - So(query2.Result, ShouldNotBeNil) - So(query2.Result.Id, ShouldEqual, query.Result.Id) - So(query2.Result.UpdatedAt, ShouldEqual, now.Unix()) - }) - Convey("Update existing state to pending with correct version should update database", func() { - s := *query.Result + createCmd.SentAt += 1000 //increase epoch - cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.Id, - Version: s.Version, - AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, - } - - err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) + err = RecordNotificationJournal(context.Background(), createCmd) So(err, ShouldBeNil) - So(cmd.ResultVersion, ShouldEqual, 1) - query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} - err = GetOrCreateAlertNotificationState(context.Background(), query2) - So(err, ShouldBeNil) - So(query2.Result.Version, ShouldEqual, 1) - So(query2.Result.State, ShouldEqual, models.AlertNotificationStatePending) - So(query2.Result.UpdatedAt, ShouldEqual, now.Unix()) - - Convey("Update existing state to completed should update database", func() { - s := *query.Result - setStateCmd := models.SetAlertNotificationStateToCompleteCommand{ - Id: s.Id, - Version: cmd.ResultVersion, - } - err := SetAlertNotificationStateToCompleteCommand(context.Background(), &setStateCmd) + Convey("get last journaling event", func() { + err := GetLatestNotification(context.Background(), query) So(err, ShouldBeNil) + So(query.Result.SentAt, ShouldEqual, 1001) - query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} - err = GetOrCreateAlertNotificationState(context.Background(), query3) - So(err, ShouldBeNil) - So(query3.Result.Version, ShouldEqual, 2) - So(query3.Result.State, ShouldEqual, models.AlertNotificationStateCompleted) - So(query3.Result.UpdatedAt, ShouldEqual, now.Unix()) - }) + Convey("be able to clear all journaling for an notifier", func() { + cmd := &m.CleanNotificationJournalCommand{AlertId: alertId, NotifierId: notifierId, OrgId: orgId} + err := CleanNotificationJournal(context.Background(), cmd) + So(err, ShouldBeNil) - Convey("Update existing state to completed should update database. regardless of version", func() { - s := *query.Result - unknownVersion := int64(1000) - cmd := models.SetAlertNotificationStateToCompleteCommand{ - Id: s.Id, - Version: unknownVersion, - } - err := SetAlertNotificationStateToCompleteCommand(context.Background(), &cmd) - So(err, ShouldBeNil) - - query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} - err = GetOrCreateAlertNotificationState(context.Background(), query3) - So(err, ShouldBeNil) - So(query3.Result.Version, ShouldEqual, unknownVersion+1) - So(query3.Result.State, ShouldEqual, models.AlertNotificationStateCompleted) - So(query3.Result.UpdatedAt, ShouldEqual, now.Unix()) + Convey("querying for last junaling should raise error", func() { + query := &m.GetLatestNotificationQuery{AlertId: alertId, OrgId: orgId, NotifierId: notifierId} + err := GetLatestNotification(context.Background(), query) + So(err, ShouldEqual, m.ErrJournalingNotFound) + }) + }) }) }) - - Convey("Update existing state to pending with incorrect version should return version mismatch error", func() { - s := *query.Result - s.Version = 1000 - cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.NotifierId, - Version: s.Version, - AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, - } - err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) - So(err, ShouldEqual, models.ErrAlertNotificationStateVersionConflict) - }) - - Convey("Updating existing state to pending with incorrect version since alert rule state update version is higher", func() { - s := *query.Result - cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.Id, - Version: s.Version, - AlertRuleStateUpdatedVersion: 1000, - } - err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) - So(err, ShouldBeNil) - - So(cmd.ResultVersion, ShouldEqual, 1) - }) - - Convey("different version and same alert state change version should return error", func() { - s := *query.Result - s.Version = 1000 - cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.Id, - Version: s.Version, - AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, - } - err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) - So(err, ShouldNotBeNil) - }) - }) - - Reset(func() { - timeNow = oldTimeNow }) }) Convey("Alert notifications should be empty", func() { - cmd := &models.GetAlertNotificationsQuery{ + cmd := &m.GetAlertNotificationsQuery{ OrgId: 2, Name: "email", } @@ -151,7 +68,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Cannot save alert notifier with send reminder = true", func() { - cmd := &models.CreateAlertNotificationCommand{ + cmd := &m.CreateAlertNotificationCommand{ Name: "ops", Type: "email", OrgId: 1, @@ -161,7 +78,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("and missing frequency", func() { err := CreateAlertNotificationCommand(cmd) - So(err, ShouldEqual, models.ErrNotificationFrequencyNotFound) + So(err, ShouldEqual, m.ErrNotificationFrequencyNotFound) }) Convey("invalid frequency", func() { @@ -173,7 +90,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Cannot update alert notifier with send reminder = false", func() { - cmd := &models.CreateAlertNotificationCommand{ + cmd := &m.CreateAlertNotificationCommand{ Name: "ops update", Type: "email", OrgId: 1, @@ -184,14 +101,14 @@ func TestAlertNotificationSQLAccess(t *testing.T) { err := CreateAlertNotificationCommand(cmd) So(err, ShouldBeNil) - updateCmd := &models.UpdateAlertNotificationCommand{ + updateCmd := &m.UpdateAlertNotificationCommand{ Id: cmd.Result.Id, SendReminder: true, } Convey("and missing frequency", func() { err := UpdateAlertNotification(updateCmd) - So(err, ShouldEqual, models.ErrNotificationFrequencyNotFound) + So(err, ShouldEqual, m.ErrNotificationFrequencyNotFound) }) Convey("invalid frequency", func() { @@ -204,7 +121,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Can save Alert Notification", func() { - cmd := &models.CreateAlertNotificationCommand{ + cmd := &m.CreateAlertNotificationCommand{ Name: "ops", Type: "email", OrgId: 1, @@ -226,25 +143,23 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Can update alert notification", func() { - newCmd := &models.UpdateAlertNotificationCommand{ - Name: "NewName", - Type: "webhook", - OrgId: cmd.Result.OrgId, - SendReminder: true, - DisableResolvedMessage: true, - Frequency: "60s", - Settings: simplejson.New(), - Id: cmd.Result.Id, + newCmd := &m.UpdateAlertNotificationCommand{ + Name: "NewName", + Type: "webhook", + OrgId: cmd.Result.OrgId, + SendReminder: true, + Frequency: "60s", + Settings: simplejson.New(), + Id: cmd.Result.Id, } err := UpdateAlertNotification(newCmd) So(err, ShouldBeNil) So(newCmd.Result.Name, ShouldEqual, "NewName") So(newCmd.Result.Frequency, ShouldEqual, 60*time.Second) - So(newCmd.Result.DisableResolvedMessage, ShouldBeTrue) }) Convey("Can update alert notification to disable sending of reminders", func() { - newCmd := &models.UpdateAlertNotificationCommand{ + newCmd := &m.UpdateAlertNotificationCommand{ Name: "NewName", Type: "webhook", OrgId: cmd.Result.OrgId, @@ -264,7 +179,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { cmd3 := m.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} cmd4 := m.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - otherOrg := models.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, DisableResolvedMessage: false, Frequency: "10s", Settings: simplejson.New()} + otherOrg := m.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} So(CreateAlertNotificationCommand(&cmd1), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd2), ShouldBeNil) @@ -273,7 +188,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { So(CreateAlertNotificationCommand(&otherOrg), ShouldBeNil) Convey("search", func() { - query := &models.GetAlertNotificationsToSendQuery{ + query := &m.GetAlertNotificationsToSendQuery{ Ids: []int64{cmd1.Result.Id, cmd2.Result.Id, 112341231}, OrgId: 1, } @@ -284,7 +199,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("all", func() { - query := &models.GetAllAlertNotificationsQuery{ + query := &m.GetAllAlertNotificationsQuery{ OrgId: 1, } From a3137e731d2520f0ef0aa96545d1ef7c778cc8ce Mon Sep 17 00:00:00 2001 From: Dave Waters Date: Tue, 16 Oct 2018 20:39:07 -0400 Subject: [PATCH 15/30] clean up tests --- .../sqlstore/alert_notification_test.go | 187 +++++++++++++----- 1 file changed, 136 insertions(+), 51 deletions(-) diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index 83fb42db9bb..1b922783bcb 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/grafana/grafana/pkg/components/simplejson" - m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/models" . "github.com/smartystreets/goconvey/convey" ) @@ -14,50 +14,133 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("Testing Alert notification sql access", t, func() { InitTestDB(t) - Convey("Alert notification journal", func() { - var alertId int64 = 5 - var orgId int64 = 5 - var notifierId int64 = 5 + Convey("Alert notification state", func() { + var alertID int64 = 7 + var orgID int64 = 5 + var notifierID int64 = 10 + oldTimeNow := timeNow + now := time.Date(2018, 9, 30, 0, 0, 0, 0, time.UTC) + timeNow = func() time.Time { return now } - Convey("Getting last journal should raise error if no one exists", func() { - query := &m.GetLatestNotificationQuery{AlertId: alertId, OrgId: orgId, NotifierId: notifierId} - err := GetLatestNotification(context.Background(), query) - So(err, ShouldEqual, m.ErrJournalingNotFound) + Convey("Get no existing state should create a new state", func() { + query := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} + err := GetOrCreateAlertNotificationState(context.Background(), query) + So(err, ShouldBeNil) + So(query.Result, ShouldNotBeNil) + So(query.Result.State, ShouldEqual, "unknown") + So(query.Result.Version, ShouldEqual, 0) + So(query.Result.UpdatedAt, ShouldEqual, now.Unix()) - Convey("shoulbe be able to record two journaling events", func() { - createCmd := &m.RecordNotificationJournalCommand{AlertId: alertId, NotifierId: notifierId, OrgId: orgId, Success: true, SentAt: 1} - - err := RecordNotificationJournal(context.Background(), createCmd) + Convey("Get existing state should not create a new state", func() { + query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} + err := GetOrCreateAlertNotificationState(context.Background(), query2) So(err, ShouldBeNil) + So(query2.Result, ShouldNotBeNil) + So(query2.Result.Id, ShouldEqual, query.Result.Id) + So(query2.Result.UpdatedAt, ShouldEqual, now.Unix()) + }) - createCmd.SentAt += 1000 //increase epoch + Convey("Update existing state to pending with correct version should update database", func() { + s := *query.Result - err = RecordNotificationJournal(context.Background(), createCmd) + cmd := models.SetAlertNotificationStateToPendingCommand{ + Id: s.Id, + Version: s.Version, + AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, + } + + err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) So(err, ShouldBeNil) + So(cmd.ResultVersion, ShouldEqual, 1) - Convey("get last journaling event", func() { - err := GetLatestNotification(context.Background(), query) + query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} + err = GetOrCreateAlertNotificationState(context.Background(), query2) + So(err, ShouldBeNil) + So(query2.Result.Version, ShouldEqual, 1) + So(query2.Result.State, ShouldEqual, models.AlertNotificationStatePending) + So(query2.Result.UpdatedAt, ShouldEqual, now.Unix()) + + Convey("Update existing state to completed should update database", func() { + s := *query.Result + setStateCmd := models.SetAlertNotificationStateToCompleteCommand{ + Id: s.Id, + Version: cmd.ResultVersion, + } + err := SetAlertNotificationStateToCompleteCommand(context.Background(), &setStateCmd) So(err, ShouldBeNil) - So(query.Result.SentAt, ShouldEqual, 1001) - Convey("be able to clear all journaling for an notifier", func() { - cmd := &m.CleanNotificationJournalCommand{AlertId: alertId, NotifierId: notifierId, OrgId: orgId} - err := CleanNotificationJournal(context.Background(), cmd) - So(err, ShouldBeNil) + query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} + err = GetOrCreateAlertNotificationState(context.Background(), query3) + So(err, ShouldBeNil) + So(query3.Result.Version, ShouldEqual, 2) + So(query3.Result.State, ShouldEqual, models.AlertNotificationStateCompleted) + So(query3.Result.UpdatedAt, ShouldEqual, now.Unix()) + }) - Convey("querying for last junaling should raise error", func() { - query := &m.GetLatestNotificationQuery{AlertId: alertId, OrgId: orgId, NotifierId: notifierId} - err := GetLatestNotification(context.Background(), query) - So(err, ShouldEqual, m.ErrJournalingNotFound) - }) - }) + Convey("Update existing state to completed should update database. regardless of version", func() { + s := *query.Result + unknownVersion := int64(1000) + cmd := models.SetAlertNotificationStateToCompleteCommand{ + Id: s.Id, + Version: unknownVersion, + } + err := SetAlertNotificationStateToCompleteCommand(context.Background(), &cmd) + So(err, ShouldBeNil) + + query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID} + err = GetOrCreateAlertNotificationState(context.Background(), query3) + So(err, ShouldBeNil) + So(query3.Result.Version, ShouldEqual, unknownVersion+1) + So(query3.Result.State, ShouldEqual, models.AlertNotificationStateCompleted) + So(query3.Result.UpdatedAt, ShouldEqual, now.Unix()) }) }) + + Convey("Update existing state to pending with incorrect version should return version mismatch error", func() { + s := *query.Result + s.Version = 1000 + cmd := models.SetAlertNotificationStateToPendingCommand{ + Id: s.NotifierId, + Version: s.Version, + AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, + } + err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) + So(err, ShouldEqual, models.ErrAlertNotificationStateVersionConflict) + }) + + Convey("Updating existing state to pending with incorrect version since alert rule state update version is higher", func() { + s := *query.Result + cmd := models.SetAlertNotificationStateToPendingCommand{ + Id: s.Id, + Version: s.Version, + AlertRuleStateUpdatedVersion: 1000, + } + err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) + So(err, ShouldBeNil) + + So(cmd.ResultVersion, ShouldEqual, 1) + }) + + Convey("different version and same alert state change version should return error", func() { + s := *query.Result + s.Version = 1000 + cmd := models.SetAlertNotificationStateToPendingCommand{ + Id: s.Id, + Version: s.Version, + AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, + } + err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) + So(err, ShouldNotBeNil) + }) + }) + + Reset(func() { + timeNow = oldTimeNow }) }) Convey("Alert notifications should be empty", func() { - cmd := &m.GetAlertNotificationsQuery{ + cmd := &models.GetAlertNotificationsQuery{ OrgId: 2, Name: "email", } @@ -68,7 +151,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Cannot save alert notifier with send reminder = true", func() { - cmd := &m.CreateAlertNotificationCommand{ + cmd := &models.CreateAlertNotificationCommand{ Name: "ops", Type: "email", OrgId: 1, @@ -78,7 +161,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("and missing frequency", func() { err := CreateAlertNotificationCommand(cmd) - So(err, ShouldEqual, m.ErrNotificationFrequencyNotFound) + So(err, ShouldEqual, models.ErrNotificationFrequencyNotFound) }) Convey("invalid frequency", func() { @@ -90,7 +173,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Cannot update alert notifier with send reminder = false", func() { - cmd := &m.CreateAlertNotificationCommand{ + cmd := &models.CreateAlertNotificationCommand{ Name: "ops update", Type: "email", OrgId: 1, @@ -101,14 +184,14 @@ func TestAlertNotificationSQLAccess(t *testing.T) { err := CreateAlertNotificationCommand(cmd) So(err, ShouldBeNil) - updateCmd := &m.UpdateAlertNotificationCommand{ + updateCmd := &models.UpdateAlertNotificationCommand{ Id: cmd.Result.Id, SendReminder: true, } Convey("and missing frequency", func() { err := UpdateAlertNotification(updateCmd) - So(err, ShouldEqual, m.ErrNotificationFrequencyNotFound) + So(err, ShouldEqual, models.ErrNotificationFrequencyNotFound) }) Convey("invalid frequency", func() { @@ -121,7 +204,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Can save Alert Notification", func() { - cmd := &m.CreateAlertNotificationCommand{ + cmd := &models.CreateAlertNotificationCommand{ Name: "ops", Type: "email", OrgId: 1, @@ -143,23 +226,25 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Can update alert notification", func() { - newCmd := &m.UpdateAlertNotificationCommand{ - Name: "NewName", - Type: "webhook", - OrgId: cmd.Result.OrgId, - SendReminder: true, - Frequency: "60s", - Settings: simplejson.New(), - Id: cmd.Result.Id, + newCmd := &models.UpdateAlertNotificationCommand{ + Name: "NewName", + Type: "webhook", + OrgId: cmd.Result.OrgId, + SendReminder: true, + DisableResolvedMessage: true, + Frequency: "60s", + Settings: simplejson.New(), + Id: cmd.Result.Id, } err := UpdateAlertNotification(newCmd) So(err, ShouldBeNil) So(newCmd.Result.Name, ShouldEqual, "NewName") So(newCmd.Result.Frequency, ShouldEqual, 60*time.Second) + So(newCmd.Result.DisableResolvedMessage, ShouldBeTrue) }) Convey("Can update alert notification to disable sending of reminders", func() { - newCmd := &m.UpdateAlertNotificationCommand{ + newCmd := &models.UpdateAlertNotificationCommand{ Name: "NewName", Type: "webhook", OrgId: cmd.Result.OrgId, @@ -174,12 +259,12 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("Can search using an array of ids", func() { - cmd1 := m.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - cmd2 := m.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - cmd3 := m.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - cmd4 := m.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd1 := models.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd2 := models.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd3 := models.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + cmd4 := models.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} - otherOrg := m.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} + otherOrg := models.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, Frequency: "10s", Settings: simplejson.New()} So(CreateAlertNotificationCommand(&cmd1), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd2), ShouldBeNil) @@ -188,7 +273,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { So(CreateAlertNotificationCommand(&otherOrg), ShouldBeNil) Convey("search", func() { - query := &m.GetAlertNotificationsToSendQuery{ + query := &models.GetAlertNotificationsToSendQuery{ Ids: []int64{cmd1.Result.Id, cmd2.Result.Id, 112341231}, OrgId: 1, } @@ -199,7 +284,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { }) Convey("all", func() { - query := &m.GetAllAlertNotificationsQuery{ + query := &models.GetAllAlertNotificationsQuery{ OrgId: 1, } From c64a5a6e0ed12fd8714d8a4d60c432f2911ad79c Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Wed, 17 Oct 2018 14:02:02 +0900 Subject: [PATCH 16/30] re-add hard coded region list --- .../datasource/cloudwatch/config_ctrl.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts index d46f5cf1580..6fe48cb1715 100644 --- a/public/app/plugins/datasource/cloudwatch/config_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/config_ctrl.ts @@ -41,7 +41,32 @@ export class CloudWatchConfigCtrl { { name: 'Yearly', value: 'Yearly', example: '[logstash-]YYYY' }, ]; - regions = []; + regions = [ + 'ap-northeast-1', + 'ap-northeast-2', + 'ap-northeast-3', + 'ap-south-1', + 'ap-southeast-1', + 'ap-southeast-2', + 'ca-central-1', + 'cn-north-1', + 'cn-northwest-1', + 'eu-central-1', + 'eu-north-1', + 'eu-west-1', + 'eu-west-2', + 'eu-west-3', + 'me-south-1', + 'sa-east-1', + 'us-east-1', + 'us-east-2', + 'us-gov-east-1', + 'us-gov-west-1', + 'us-iso-east-1', + 'us-isob-east-1', + 'us-west-1', + 'us-west-2', + ]; getRegions() { this.datasourceSrv From 5566a61aef2aa3dc750a2408688324d4c6bb9b95 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 17 Oct 2018 09:53:03 +0200 Subject: [PATCH 17/30] alerting: tests default value for disable resolve message --- pkg/services/sqlstore/alert_notification_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index 1b922783bcb..680685a6d22 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -219,6 +219,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { So(cmd.Result.OrgId, ShouldNotEqual, 0) So(cmd.Result.Type, ShouldEqual, "email") So(cmd.Result.Frequency, ShouldEqual, 10*time.Second) + So(cmd.Result.DisableResolvedMessage, ShouldBeFalse) Convey("Cannot save Alert Notification with the same name", func() { err = CreateAlertNotificationCommand(cmd) From 70385119bc29e0cf8fa6b501b18938a4e7b03c79 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 17 Oct 2018 10:41:18 +0200 Subject: [PATCH 18/30] removes d in disableResolvedMessage --- pkg/api/dtos/alerting.go | 52 +++++++++---------- pkg/models/alert_notifications.go | 52 +++++++++---------- pkg/services/alerting/interfaces.go | 2 +- pkg/services/alerting/notifiers/base.go | 42 +++++++-------- pkg/services/alerting/notifiers/base_test.go | 2 +- pkg/services/sqlstore/alert_notification.go | 28 +++++----- .../sqlstore/alert_notification_test.go | 20 +++---- pkg/services/sqlstore/migrations/alert_mig.go | 4 +- .../alerting/NotificationsEditCtrl.ts | 2 +- .../alerting/partials/notification_edit.html | 14 ++--- 10 files changed, 109 insertions(+), 109 deletions(-) diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index 33f10c805ab..c037831f341 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -49,30 +49,30 @@ func formatShort(interval time.Duration) string { func NewAlertNotification(notification *models.AlertNotification) *AlertNotification { return &AlertNotification{ - Id: notification.Id, - Name: notification.Name, - Type: notification.Type, - IsDefault: notification.IsDefault, - Created: notification.Created, - Updated: notification.Updated, - Frequency: formatShort(notification.Frequency), - SendReminder: notification.SendReminder, - DisableResolvedMessage: notification.DisableResolvedMessage, - Settings: notification.Settings, + Id: notification.Id, + Name: notification.Name, + Type: notification.Type, + IsDefault: notification.IsDefault, + Created: notification.Created, + Updated: notification.Updated, + Frequency: formatShort(notification.Frequency), + SendReminder: notification.SendReminder, + DisableResolveMessage: notification.DisableResolveMessage, + Settings: notification.Settings, } } type AlertNotification struct { - Id int64 `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - IsDefault bool `json:"isDefault"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - Settings *simplejson.Json `json:"settings"` + Id int64 `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + IsDefault bool `json:"isDefault"` + SendReminder bool `json:"sendReminder"` + DisableResolveMessage bool `json:"disableResolveMessage"` + Frequency string `json:"frequency"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + Settings *simplejson.Json `json:"settings"` } type AlertTestCommand struct { @@ -102,12 +102,12 @@ type EvalMatch struct { } type NotificationTestCommand struct { - Name string `json:"name"` - Type string `json:"type"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - Settings *simplejson.Json `json:"settings"` + Name string `json:"name"` + Type string `json:"type"` + SendReminder bool `json:"sendReminder"` + DisableResolveMessage bool `json:"disableResolveMessage"` + Frequency string `json:"frequency"` + Settings *simplejson.Json `json:"settings"` } type PauseAlertCommand struct { diff --git a/pkg/models/alert_notifications.go b/pkg/models/alert_notifications.go index f9327a4d4bc..e0fd12937ed 100644 --- a/pkg/models/alert_notifications.go +++ b/pkg/models/alert_notifications.go @@ -23,41 +23,41 @@ var ( ) type AlertNotification struct { - Id int64 `json:"id"` - OrgId int64 `json:"-"` - Name string `json:"name"` - Type string `json:"type"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency time.Duration `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + Id int64 `json:"id"` + OrgId int64 `json:"-"` + Name string `json:"name"` + Type string `json:"type"` + SendReminder bool `json:"sendReminder"` + DisableResolveMessage bool `json:"disableResolveMessage"` + Frequency time.Duration `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` } type CreateAlertNotificationCommand struct { - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + SendReminder bool `json:"sendReminder"` + DisableResolveMessage bool `json:"disableResolveMessage"` + Frequency string `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings"` OrgId int64 `json:"-"` Result *AlertNotification } type UpdateAlertNotificationCommand struct { - Id int64 `json:"id" binding:"Required"` - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - SendReminder bool `json:"sendReminder"` - DisableResolvedMessage bool `json:"disableResolvedMessage"` - Frequency string `json:"frequency"` - IsDefault bool `json:"isDefault"` - Settings *simplejson.Json `json:"settings" binding:"Required"` + Id int64 `json:"id" binding:"Required"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + SendReminder bool `json:"sendReminder"` + DisableResolveMessage bool `json:"disableResolveMessage"` + Frequency string `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings" binding:"Required"` OrgId int64 `json:"-"` Result *AlertNotification diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index 7cd0e6ffca8..040d0991861 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -27,7 +27,7 @@ type Notifier interface { GetNotifierId() int64 GetIsDefault() bool GetSendReminder() bool - GetDisableResolvedMessage() bool + GetDisableResolveMessage() bool GetFrequency() time.Duration } diff --git a/pkg/services/alerting/notifiers/base.go b/pkg/services/alerting/notifiers/base.go index 1b14059b1e6..d141d6cd257 100644 --- a/pkg/services/alerting/notifiers/base.go +++ b/pkg/services/alerting/notifiers/base.go @@ -14,14 +14,14 @@ const ( ) type NotifierBase struct { - Name string - Type string - Id int64 - IsDeault bool - UploadImage bool - SendReminder bool - DisableResolvedMessage bool - Frequency time.Duration + Name string + Type string + Id int64 + IsDeault bool + UploadImage bool + SendReminder bool + DisableResolveMessage bool + Frequency time.Duration log log.Logger } @@ -34,15 +34,15 @@ func NewNotifierBase(model *models.AlertNotification) NotifierBase { } return NotifierBase{ - Id: model.Id, - Name: model.Name, - IsDeault: model.IsDefault, - Type: model.Type, - UploadImage: uploadImage, - SendReminder: model.SendReminder, - DisableResolvedMessage: model.DisableResolvedMessage, - Frequency: model.Frequency, - log: log.New("alerting.notifier." + model.Name), + Id: model.Id, + Name: model.Name, + IsDeault: model.IsDefault, + Type: model.Type, + UploadImage: uploadImage, + SendReminder: model.SendReminder, + DisableResolveMessage: model.DisableResolveMessage, + Frequency: model.Frequency, + log: log.New("alerting.notifier." + model.Name), } } @@ -84,8 +84,8 @@ func (n *NotifierBase) ShouldNotify(ctx context.Context, context *alerting.EvalC } } - // Do not notify when state is OK if DisableResolvedMessage is set to true - if context.Rule.State == models.AlertStateOK && n.DisableResolvedMessage { + // Do not notify when state is OK if DisableResolveMessage is set to true + if context.Rule.State == models.AlertStateOK && n.DisableResolveMessage { return false } @@ -112,8 +112,8 @@ func (n *NotifierBase) GetSendReminder() bool { return n.SendReminder } -func (n *NotifierBase) GetDisableResolvedMessage() bool { - return n.DisableResolvedMessage +func (n *NotifierBase) GetDisableResolveMessage() bool { + return n.DisableResolveMessage } func (n *NotifierBase) GetFrequency() time.Duration { diff --git a/pkg/services/alerting/notifiers/base_test.go b/pkg/services/alerting/notifiers/base_test.go index 281d0bfb238..5062828cb4f 100644 --- a/pkg/services/alerting/notifiers/base_test.go +++ b/pkg/services/alerting/notifiers/base_test.go @@ -182,7 +182,7 @@ func TestBaseNotifier(t *testing.T) { Convey("default value should be false for backwards compatibility", func() { base := NewNotifierBase(model) - So(base.DisableResolvedMessage, ShouldBeFalse) + So(base.DisableResolveMessage, ShouldBeFalse) }) }) } diff --git a/pkg/services/sqlstore/alert_notification.go b/pkg/services/sqlstore/alert_notification.go index d82d5814869..afe6269510f 100644 --- a/pkg/services/sqlstore/alert_notification.go +++ b/pkg/services/sqlstore/alert_notification.go @@ -66,7 +66,7 @@ func GetAlertNotificationsToSend(query *m.GetAlertNotificationsToSendQuery) erro alert_notification.updated, alert_notification.settings, alert_notification.is_default, - alert_notification.disable_resolved_message, + alert_notification.disable_resolve_message, alert_notification.send_reminder, alert_notification.frequency FROM alert_notification @@ -107,7 +107,7 @@ func getAlertNotificationInternal(query *m.GetAlertNotificationsQuery, sess *DBS alert_notification.updated, alert_notification.settings, alert_notification.is_default, - alert_notification.disable_resolved_message, + alert_notification.disable_resolve_message, alert_notification.send_reminder, alert_notification.frequency FROM alert_notification @@ -168,16 +168,16 @@ func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error } alertNotification := &m.AlertNotification{ - OrgId: cmd.OrgId, - Name: cmd.Name, - Type: cmd.Type, - Settings: cmd.Settings, - SendReminder: cmd.SendReminder, - DisableResolvedMessage: cmd.DisableResolvedMessage, - Frequency: frequency, - Created: time.Now(), - Updated: time.Now(), - IsDefault: cmd.IsDefault, + OrgId: cmd.OrgId, + Name: cmd.Name, + Type: cmd.Type, + Settings: cmd.Settings, + SendReminder: cmd.SendReminder, + DisableResolveMessage: cmd.DisableResolveMessage, + Frequency: frequency, + Created: time.Now(), + Updated: time.Now(), + IsDefault: cmd.IsDefault, } if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil { @@ -213,7 +213,7 @@ func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error { current.Type = cmd.Type current.IsDefault = cmd.IsDefault current.SendReminder = cmd.SendReminder - current.DisableResolvedMessage = cmd.DisableResolvedMessage + current.DisableResolveMessage = cmd.DisableResolveMessage if current.SendReminder { if cmd.Frequency == "" { @@ -228,7 +228,7 @@ func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error { current.Frequency = frequency } - sess.UseBool("is_default", "send_reminder") + sess.UseBool("is_default", "send_reminder", "disable_resolve_message") if affected, err := sess.ID(cmd.Id).Update(current); err != nil { return err diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index 680685a6d22..629a6292eb5 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -219,7 +219,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { So(cmd.Result.OrgId, ShouldNotEqual, 0) So(cmd.Result.Type, ShouldEqual, "email") So(cmd.Result.Frequency, ShouldEqual, 10*time.Second) - So(cmd.Result.DisableResolvedMessage, ShouldBeFalse) + So(cmd.Result.DisableResolveMessage, ShouldBeFalse) Convey("Cannot save Alert Notification with the same name", func() { err = CreateAlertNotificationCommand(cmd) @@ -228,20 +228,20 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("Can update alert notification", func() { newCmd := &models.UpdateAlertNotificationCommand{ - Name: "NewName", - Type: "webhook", - OrgId: cmd.Result.OrgId, - SendReminder: true, - DisableResolvedMessage: true, - Frequency: "60s", - Settings: simplejson.New(), - Id: cmd.Result.Id, + Name: "NewName", + Type: "webhook", + OrgId: cmd.Result.OrgId, + SendReminder: true, + DisableResolveMessage: true, + Frequency: "60s", + Settings: simplejson.New(), + Id: cmd.Result.Id, } err := UpdateAlertNotification(newCmd) So(err, ShouldBeNil) So(newCmd.Result.Name, ShouldEqual, "NewName") So(newCmd.Result.Frequency, ShouldEqual, 60*time.Second) - So(newCmd.Result.DisableResolvedMessage, ShouldBeTrue) + So(newCmd.Result.DisableResolveMessage, ShouldBeTrue) }) Convey("Can update alert notification to disable sending of reminders", func() { diff --git a/pkg/services/sqlstore/migrations/alert_mig.go b/pkg/services/sqlstore/migrations/alert_mig.go index 7add7ee3e86..198a47b50ff 100644 --- a/pkg/services/sqlstore/migrations/alert_mig.go +++ b/pkg/services/sqlstore/migrations/alert_mig.go @@ -71,8 +71,8 @@ func addAlertMigrations(mg *Migrator) { mg.AddMigration("Add column send_reminder", NewAddColumnMigration(alert_notification, &Column{ Name: "send_reminder", Type: DB_Bool, Nullable: true, Default: "0", })) - mg.AddMigration("Add column disable_resolved_message", NewAddColumnMigration(alert_notification, &Column{ - Name: "disable_resolved_message", Type: DB_Bool, Nullable: false, Default: "0", + mg.AddMigration("Add column disable_resolve_message", NewAddColumnMigration(alert_notification, &Column{ + Name: "disable_resolve_message", Type: DB_Bool, Nullable: false, Default: "0", })) mg.AddMigration("add index alert_notification org_id & name", NewAddIndexMigration(alert_notification, alert_notification.Indices[0])) diff --git a/public/app/features/alerting/NotificationsEditCtrl.ts b/public/app/features/alerting/NotificationsEditCtrl.ts index 92392559480..2607121bb0e 100644 --- a/public/app/features/alerting/NotificationsEditCtrl.ts +++ b/public/app/features/alerting/NotificationsEditCtrl.ts @@ -12,7 +12,7 @@ export class AlertNotificationEditCtrl { defaults: any = { type: 'email', sendReminder: false, - disableResolvedMessage: false, + disableResolveMessage: false, frequency: '15m', settings: { httpMethod: 'POST', diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index 1ecab3c3e3c..b2cd2f21e4d 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -21,28 +21,28 @@ + label="Disable Resolve Message" + label-class="width-14" + checked="ctrl.model.disableResolveMessage" + tooltip="Disable the resolve message [OK] that is sent when alerting state returns to false"> From 518e485e366231641bcb3290177efbc5b56b1f38 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 17 Oct 2018 10:52:46 +0200 Subject: [PATCH 19/30] docs: update cloudwatch iam policy description --- docs/sources/features/datasources/cloudwatch.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index 7adc6ebe4fb..be36d108475 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -46,7 +46,7 @@ Checkout AWS docs on [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGu ## IAM Policies Grafana needs permissions granted via IAM to be able to read CloudWatch metrics -and EC2 tags/instances. You can attach these permissions to IAM roles and +and EC2 tags/instances/regions. You can attach these permissions to IAM roles and utilize Grafana's built-in support for assuming roles. Here is a minimal policy example: @@ -65,11 +65,12 @@ Here is a minimal policy example: "Resource": "*" }, { - "Sid": "AllowReadingTagsFromEC2", + "Sid": "AllowReadingTagsInstancesRegionsFromEC2", "Effect": "Allow", "Action": [ "ec2:DescribeTags", - "ec2:DescribeInstances" + "ec2:DescribeInstances", + "ec2:DescribeRegions" ], "Resource": "*" } From 52f398e7f794215375c767b87149951297287d23 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 17 Oct 2018 10:58:06 +0200 Subject: [PATCH 20/30] cloudwatch: return a distinct list of regions --- pkg/tsdb/cloudwatch/metric_find_query.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index f2633bd6be6..b74af76f09a 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -249,7 +249,18 @@ func (e *CloudWatchExecutor) handleGetRegions(ctx context.Context, parameters *s plog.Error("Failed to get regions", "error", err) } else { for _, region := range r.Regions { - regions = append(regions, *region.RegionName) + exists := false + + for _, existingRegion := range regions { + if existingRegion == *region.RegionName { + exists = true + break + } + } + + if !exists { + regions = append(regions, *region.RegionName) + } } } sort.Strings(regions) From 2afec0e7fb36a3016b0718c95f1b4ee758b41e53 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 17 Oct 2018 11:12:02 +0200 Subject: [PATCH 21/30] changelog: adds note about closing #12330 and #6696 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1189b5fe112..4c276d01594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * **Postgres/MySQL/MSSQL**: Adds support for configuration of max open/idle connections and connection max lifetime. Also, panels with multiple SQL queries will now be executed concurrently [#11711](https://github.com/grafana/grafana/issues/11711), thx [@connection-reset](https://github.com/connection-reset) * **MSSQL**: Add encrypt setting to allow configuration of how data sent between client and server are encrypted [#13629](https://github.com/grafana/grafana/issues/13629), thx [@ramiro](https://github.com/ramiro) +* **Alerting**: Option to disable OK alert notifications [#12330](https://github.com/grafana/grafana/issues/12330) & [#6696](https://github.com/grafana/grafana/issues/6696), thx [@davewat](https://github.com/davewat) ### Minor From 6a89874290e432258aba384d1531447b31a51d72 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 17 Oct 2018 12:01:27 +0200 Subject: [PATCH 22/30] changelog: add notes about closing #12308 [skip ci] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c276d01594..f7a1ba382fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ ### Minor -* **Datasource Proxy**: Keep trailing slash for datasource proxy requests [#13326](https://github.com/grafana/grafana/pull/13326), thx [@ryantxu](https://github.com/ryantxu) +* **Cloudwatch**: Show all available CloudWatch regions [#12308](https://github.com/grafana/grafana/issues/12308), thx [@mtanda](https://github.com/mtanda) * **Units**: New clock time format, to format ms or second values as for example `01h:59m`, [#13635](https://github.com/grafana/grafana/issues/13635), thx [@franciscocpg](https://github.com/franciscocpg) +* **Datasource Proxy**: Keep trailing slash for datasource proxy requests [#13326](https://github.com/grafana/grafana/pull/13326), thx [@ryantxu](https://github.com/ryantxu) ### Breaking changes From ec0fd96f08f017a2d3ea694bed35b99437233d7d Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 17 Oct 2018 12:30:07 +0200 Subject: [PATCH 23/30] Use closure for calling interpolateVariable --- public/app/plugins/datasource/postgres/datasource.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/postgres/datasource.ts b/public/app/plugins/datasource/postgres/datasource.ts index f1db05cabe8..49f4afb4271 100644 --- a/public/app/plugins/datasource/postgres/datasource.ts +++ b/public/app/plugins/datasource/postgres/datasource.ts @@ -50,7 +50,7 @@ export class PostgresDatasource { intervalMs: options.intervalMs, maxDataPoints: options.maxDataPoints, datasourceId: this.id, - rawSql: queryModel.render(this.interpolateVariable), + rawSql: queryModel.render((value, variable) => this.interpolateVariable(value, variable)), format: target.format, }; }); @@ -82,7 +82,9 @@ export class PostgresDatasource { const query = { refId: options.annotation.name, datasourceId: this.id, - rawSql: this.templateSrv.replace(options.annotation.rawQuery, options.scopedVars, this.interpolateVariable), + rawSql: this.templateSrv.replace(options.annotation.rawQuery, options.scopedVars, (value, variable) => + this.interpolateVariable(value, variable) + ), format: 'table', }; @@ -108,7 +110,7 @@ export class PostgresDatasource { const interpolatedQuery = { refId: refId, datasourceId: this.id, - rawSql: this.templateSrv.replace(query, {}, this.interpolateVariable), + rawSql: this.templateSrv.replace(query, {}, (value, variable) => this.interpolateVariable(value, variable)), format: 'table', }; From 7b656097a72396bb351dec391578860918619e33 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 17 Oct 2018 13:30:07 +0200 Subject: [PATCH 24/30] postgres: use arrow function declaration of interpolateVariable --- public/app/plugins/datasource/postgres/datasource.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/public/app/plugins/datasource/postgres/datasource.ts b/public/app/plugins/datasource/postgres/datasource.ts index 49f4afb4271..13948c5d793 100644 --- a/public/app/plugins/datasource/postgres/datasource.ts +++ b/public/app/plugins/datasource/postgres/datasource.ts @@ -20,7 +20,7 @@ export class PostgresDatasource { this.interval = (instanceSettings.jsonData || {}).timeInterval; } - interpolateVariable(value, variable) { + interpolateVariable = (value, variable) => { if (typeof value === 'string') { if (variable.multi || variable.includeAll) { return this.queryModel.quoteLiteral(value); @@ -37,7 +37,7 @@ export class PostgresDatasource { return this.queryModel.quoteLiteral(v); }); return quotedValues.join(','); - } + }; query(options) { const queries = _.filter(options.targets, target => { @@ -50,7 +50,7 @@ export class PostgresDatasource { intervalMs: options.intervalMs, maxDataPoints: options.maxDataPoints, datasourceId: this.id, - rawSql: queryModel.render((value, variable) => this.interpolateVariable(value, variable)), + rawSql: queryModel.render(this.interpolateVariable), format: target.format, }; }); @@ -82,9 +82,7 @@ export class PostgresDatasource { const query = { refId: options.annotation.name, datasourceId: this.id, - rawSql: this.templateSrv.replace(options.annotation.rawQuery, options.scopedVars, (value, variable) => - this.interpolateVariable(value, variable) - ), + rawSql: this.templateSrv.replace(options.annotation.rawQuery, options.scopedVars, this.interpolateVariable), format: 'table', }; @@ -110,7 +108,7 @@ export class PostgresDatasource { const interpolatedQuery = { refId: refId, datasourceId: this.id, - rawSql: this.templateSrv.replace(query, {}, (value, variable) => this.interpolateVariable(value, variable)), + rawSql: this.templateSrv.replace(query, {}, this.interpolateVariable), format: 'table', }; From b036678c89dde688c53272e701ba25ea85d08209 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 17 Oct 2018 13:46:41 +0200 Subject: [PATCH 25/30] changelog: add notes about closing #13692 [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7a1ba382fc..319e80aff21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ * Postgres/MySQL/MSSQL datasources now per default uses `max open connections` = `unlimited` (earlier 10), `max idle connections` = `2` (earlier 10) and `connection max lifetime` = `4` hours (earlier unlimited) +# 5.3.2 (unreleased) + +* **Postgres**: Fix template variables error [#13692](https://github.com/grafana/grafana/issues/13692), thx [@svenklemm](https://github.com/svenklemm) + # 5.3.1 (2018-10-16) * **Render**: Fix PhantomJS render of graph panel when legend displayed as table to the right [#13616](https://github.com/grafana/grafana/issues/13616) From cd96ef3590089e519f4e57bd6fb04761fd8f1f1d Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 17 Oct 2018 14:34:40 +0200 Subject: [PATCH 26/30] docs: installing custom plugins in docker. [skip ci] --- docs/sources/installation/docker.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/sources/installation/docker.md b/docs/sources/installation/docker.md index ba0d6199ba4..52353ede8c2 100644 --- a/docs/sources/installation/docker.md +++ b/docs/sources/installation/docker.md @@ -87,7 +87,7 @@ docker run \ ## Building a custom Grafana image with pre-installed plugins -In the [grafana-docker](https://github.com/grafana/grafana-docker/) there is a folder called `custom/` which includes a `Dockerfile` that can be used to build a custom Grafana image. It accepts `GRAFANA_VERSION` and `GF_INSTALL_PLUGINS` as build arguments. +In the [grafana-docker](https://github.com/grafana/grafana/tree/master/packaging/docker) there is a folder called `custom/` which includes a `Dockerfile` that can be used to build a custom Grafana image. It accepts `GRAFANA_VERSION` and `GF_INSTALL_PLUGINS` as build arguments. Example of how to build and run: ```bash @@ -103,6 +103,21 @@ docker run \ grafana:latest-with-plugins ``` +## Installing Plugins from other sources + +> Only available in Grafana v5.3.1+ + +It's possible to install plugins from custom url:s by specifying the url like this: `GF_INSTALL_PLUGINS=;` + +```bash +docker run \ + -d \ + -p 3000:3000 \ + --name=grafana \ + -e "GF_INSTALL_PLUGINS=http://plugin-domain.com/my-custom-plugin.zip;custom-plugin" \ + grafana/grafana +``` + ## Configuring AWS Credentials for CloudWatch Support ```bash From 3245227016e8f60a9171f37791a4d6e4edaa8c82 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 17 Oct 2018 15:58:52 +0200 Subject: [PATCH 27/30] permissions: cleanup. --- pkg/api/datasources.go | 18 +----------------- pkg/models/datasource.go | 10 ++-------- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index e9eb78fbe13..e7614614076 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -17,24 +17,8 @@ func GetDataSources(c *m.ReqContext) Response { return Error(500, "Failed to query datasources", err) } - dsFilterQuery := m.DatasourcesPermissionFilterQuery{ - User: c.SignedInUser, - Datasources: query.Result, - } - - var datasources []*m.DataSource - if err := bus.Dispatch(&dsFilterQuery); err != nil { - if err != bus.ErrHandlerNotFound { - return Error(500, "Could not get datasources", err) - } - - datasources = query.Result - } else { - datasources = dsFilterQuery.Result - } - result := make(dtos.DataSourceList, 0) - for _, ds := range datasources { + for _, ds := range query.Result { dsItem := dtos.DataSourceListItemDTO{ OrgId: ds.OrgId, Id: ds.Id, diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index 488fb2fe1fa..b71d17ec0d1 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -195,8 +195,8 @@ type GetDataSourceByNameQuery struct { type DsPermissionType int const ( - DsPermissionQuery DsPermissionType = 1 << iota - DsPermissionNoAccess + DsPermissionNoAccess DsPermissionType = iota + DsPermissionQuery ) func (p DsPermissionType) String() string { @@ -207,12 +207,6 @@ func (p DsPermissionType) String() string { return names[int(p)] } -type HasRequiredDataSourcePermissionQuery struct { - Id int64 - User *SignedInUser - RequiredPermission DsPermissionType -} - type GetDataSourcePermissionsForUserQuery struct { User *SignedInUser Result map[int64]DsPermissionType From a8c5ab76b35b6921fd06aef9238a6f0551351885 Mon Sep 17 00:00:00 2001 From: Michael Huynh Date: Sun, 14 Oct 2018 10:43:48 +0800 Subject: [PATCH 28/30] Fix typeahead behaviour for QueryField These changes were originally intended to address a bug whereby a suggestion for an already selected label value continues to appear. However, they also appear to fix several other problems in the area: - Wrong suggestions when using negated label matching operators - Misaligned label value suggestion replacements Related: #13484 --- public/app/features/explore/PromQueryField.tsx | 12 ++++++------ public/app/features/explore/QueryField.tsx | 8 +++++++- public/app/features/explore/utils/prometheus.ts | 13 +++++-------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/public/app/features/explore/PromQueryField.tsx b/public/app/features/explore/PromQueryField.tsx index 889666c5e35..442e51af987 100644 --- a/public/app/features/explore/PromQueryField.tsx +++ b/public/app/features/explore/PromQueryField.tsx @@ -111,7 +111,7 @@ export function willApplySuggestion( case 'context-label-values': { // Always add quotes and remove existing ones instead - if (!(typeaheadText.startsWith('="') || typeaheadText.startsWith('"'))) { + if (!typeaheadText.match(/^(!?=~?"|")/)) { suggestion = `"${suggestion}`; } if (getNextCharacter() !== '"') { @@ -421,7 +421,7 @@ class PromQueryField extends React.PureComponent -1; const existingKeys = parsedSelector ? parsedSelector.labelKeys : []; - if ((text && text.startsWith('=')) || _.includes(wrapperClasses, 'attr-value')) { + if ((text && text.match(/^!?=~?/)) || _.includes(wrapperClasses, 'attr-value')) { // Label values if (labelKey && this.state.labelValues[selector] && this.state.labelValues[selector][labelKey]) { const labelValues = this.state.labelValues[selector][labelKey]; @@ -571,10 +571,10 @@ class PromQueryField extends React.PureComponentLog labels ) : ( - - - - )} + + + + )}
diff --git a/public/app/features/explore/QueryField.tsx b/public/app/features/explore/QueryField.tsx index c89893b4f28..c3c41b7ab17 100644 --- a/public/app/features/explore/QueryField.tsx +++ b/public/app/features/explore/QueryField.tsx @@ -228,7 +228,13 @@ class QueryField extends React.PureComponent s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim(); // const cleanSelectorRegexp = /\{(\w+="[^"\n]*?")(,\w+="[^"\n]*?")*\}/; const selectorRegexp = /\{[^}]*?\}/; -const labelRegexp = /\b\w+="[^"\n]*?"/g; +const labelRegexp = /\b(\w+)(!?=~?)("[^"\n]*?")/g; export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any[]; selector: string } { if (!query.match(selectorRegexp)) { // Special matcher for metrics @@ -66,11 +66,8 @@ export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any // Extract clean labels to form clean selector, incomplete labels are dropped const selector = query.slice(prefixOpen, suffixClose); const labels = {}; - selector.replace(labelRegexp, match => { - const delimiterIndex = match.indexOf('='); - const key = match.slice(0, delimiterIndex); - const value = match.slice(delimiterIndex + 1, match.length); - labels[key] = value; + selector.replace(labelRegexp, (_, key, operator, value) => { + labels[key] = { value, operator }; return ''; }); @@ -78,12 +75,12 @@ export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any const metricPrefix = query.slice(0, prefixOpen); const metricMatch = metricPrefix.match(/[A-Za-z:][\w:]*$/); if (metricMatch) { - labels['__name__'] = `"${metricMatch[0]}"`; + labels['__name__'] = { value: `"${metricMatch[0]}"`, operator: '=' }; } // Build sorted selector const labelKeys = Object.keys(labels).sort(); - const cleanSelector = labelKeys.map(key => `${key}=${labels[key]}`).join(','); + const cleanSelector = labelKeys.map(key => `${key}${labels[key].operator}${labels[key].value}`).join(','); const selectorString = ['{', cleanSelector, '}'].join(''); From 22e0ff8b9ce404b5af1f99c54a44d3255ce08175 Mon Sep 17 00:00:00 2001 From: Michael Huynh Date: Sun, 14 Oct 2018 10:45:00 +0800 Subject: [PATCH 29/30] Update PromQueryField tests to address fixed bug Related: #13484 --- .../features/explore/PromQueryField.test.tsx | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/public/app/features/explore/PromQueryField.test.tsx b/public/app/features/explore/PromQueryField.test.tsx index c82a1cd448f..802bb695f2c 100644 --- a/public/app/features/explore/PromQueryField.test.tsx +++ b/public/app/features/explore/PromQueryField.test.tsx @@ -96,11 +96,14 @@ describe('PromQueryField typeahead handling', () => { it('returns label suggestions on label context but leaves out labels that already exist', () => { const instance = shallow( - + ).instance() as PromQueryField; - const value = Plain.deserialize('{job="foo",}'); + const value = Plain.deserialize('{job1="foo",job2!="foo",job3=~"foo",}'); const range = value.selection.merge({ - anchorOffset: 11, + anchorOffset: 36, }); const valueWithSelection = value.change().select(range).value; const result = instance.getTypeahead({ @@ -113,6 +116,33 @@ describe('PromQueryField typeahead handling', () => { expect(result.suggestions).toEqual([{ items: [{ label: 'bar' }], label: 'Labels' }]); }); + it('returns label value suggestions inside a label value context after a negated matching operator', () => { + const instance = shallow( + + ).instance() as PromQueryField; + const value = Plain.deserialize('{label!=}'); + const range = value.selection.merge({ anchorOffset: 8 }); + const valueWithSelection = value.change().select(range).value; + const result = instance.getTypeahead({ + text: '!=', + prefix: '', + wrapperClasses: ['context-labels'], + labelKey: 'label', + value: valueWithSelection, + }); + expect(result.context).toBe('context-label-values'); + expect(result.suggestions).toEqual([ + { + items: [{ label: 'a' }, { label: 'b' }, { label: 'c' }], + label: 'Label values for "label"', + }, + ]); + }); + it('returns a refresher on label context and unavailable metric', () => { const instance = shallow( From 675bf00727a47a003a7cdcaa37298507407e29df Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 17 Oct 2018 16:39:03 +0200 Subject: [PATCH 30/30] A list of where to make changes when upgrading Go and Node.js (#13693) --- README.md | 2 +- UPGRADING_DEPENDENCIES.md | 89 ++++++++++++++++++++ docs/sources/project/building_from_source.md | 2 +- 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 UPGRADING_DEPENDENCIES.md diff --git a/README.md b/README.md index 1179385d10c..c07cced6684 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ the latest master builds [here](https://grafana.com/grafana/download) ### Dependencies -- Go 1.11 +- Go (Latest Stable) - NodeJS LTS ### Building the backend diff --git a/UPGRADING_DEPENDENCIES.md b/UPGRADING_DEPENDENCIES.md new file mode 100644 index 00000000000..f3d2adbd71a --- /dev/null +++ b/UPGRADING_DEPENDENCIES.md @@ -0,0 +1,89 @@ +# Guide to Upgrading Dependencies + +Upgrading Go or Node.js requires making changes in many different files. See below for a list and explanation for each. + +## Go + +- CircleCi +- `grafana/build-container` +- Appveyor +- Dockerfile + +## Node.js + +- CircleCI +- `grafana/build-container` +- Appveyor +- Dockerfile + +## Go Dependencies + +Updated using `dep`. + +- `Gopkg.toml` +- `Gopkg.lock` + +## Node.js Dependencies + +Updated using `yarn`. + +- `package.json` + +## Where to make changes + +### CircleCI + +Our builds run on CircleCI through our build script. + +#### Files + +- `.circleci/config.yml`. + +#### Dependencies + +- nodejs +- golang +- grafana/build-container (our custom docker build container) + +### grafana/build-container + +The main build step (in CircleCI) is built using a custom build container that comes pre-baked with some of the neccesary dependencies. + +Link: [grafana-build-container](https://github.com/grafana/grafana-build-container) + +#### Dependencies + +- fpm +- nodejs +- golang +- crosscompiling (several compilers) + +### Appveyor + +Master and release builds trigger test runs on Appveyors build environment so that tests will run on Windows. + +#### Files: + +- `appveyor.yml` + +#### Dependencies + +- nodejs +- golang + +### Dockerfile + +There is a Docker build for Grafana in the root of the project that allows anyone to build Grafana just using Docker. + +#### Files + +- `Dockerfile` + +#### Dependencies + +- nodejs +- golang + +### Local developer environments + +Please send out a notice in the grafana-dev slack channel when updating Go or Node.js to make it easier for everyone to update their local developer environments. \ No newline at end of file diff --git a/docs/sources/project/building_from_source.md b/docs/sources/project/building_from_source.md index ea75b9797e8..eed05f05fa6 100644 --- a/docs/sources/project/building_from_source.md +++ b/docs/sources/project/building_from_source.md @@ -13,7 +13,7 @@ dev environment. Grafana ships with its own required backend server; also comple ## Dependencies -- [Go 1.11](https://golang.org/dl/) +- [Go (Latest Stable)](https://golang.org/dl/) - [Git](https://git-scm.com/downloads) - [NodeJS LTS](https://nodejs.org/download/) - node-gyp is the Node.js native addon build tool and it requires extra dependencies: python 2.7, make and GCC. These are already installed for most Linux distros and MacOS. See the Building On Windows section or the [node-gyp installation instructions](https://github.com/nodejs/node-gyp#installation) for more details.