From 50daf7463d09cac933e21e925beaeec7f2a17aae Mon Sep 17 00:00:00 2001 From: Adilet Maratov Date: Thu, 7 Dec 2017 16:13:49 +0600 Subject: [PATCH 1/7] Solves problem with Github authentication restriction by organization membership when the organization's access policy is set to "Access restricted". "Access restricted" policy should not stop user to authenticate. How it is solved: * Take organizations_url field data from user basic data response * Make another request to get all organization the user is a member of (public membership) * Authenticate user if appropriate organization found in that list --- pkg/social/github_oauth.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pkg/social/github_oauth.go b/pkg/social/github_oauth.go index c2a109a43e8..7e348e2363a 100644 --- a/pkg/social/github_oauth.go +++ b/pkg/social/github_oauth.go @@ -58,12 +58,12 @@ func (s *SocialGithub) IsTeamMember(client *http.Client) bool { return false } -func (s *SocialGithub) IsOrganizationMember(client *http.Client) bool { +func (s *SocialGithub) IsOrganizationMember(client *http.Client, organizationsUrl string) bool { if len(s.allowedOrganizations) == 0 { return true } - organizations, err := s.FetchOrganizations(client) + organizations, err := s.FetchOrganizations(client, organizationsUrl) if err != nil { return false } @@ -167,12 +167,12 @@ func (s *SocialGithub) HasMoreRecords(headers http.Header) (string, bool) { } -func (s *SocialGithub) FetchOrganizations(client *http.Client) ([]string, error) { +func (s *SocialGithub) FetchOrganizations(client *http.Client, organizationsUrl string) ([]string, error) { type Record struct { Login string `json:"login"` } - response, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/orgs")) + response, err := HttpGet(client, organizationsUrl) if err != nil { return nil, fmt.Errorf("Error getting organizations: %s", err) } @@ -193,10 +193,12 @@ func (s *SocialGithub) FetchOrganizations(client *http.Client) ([]string, error) } func (s *SocialGithub) UserInfo(client *http.Client) (*BasicUserInfo, error) { + var data struct { - Id int `json:"id"` - Login string `json:"login"` - Email string `json:"email"` + Id int `json:"id"` + Login string `json:"login"` + Email string `json:"email"` + OrganizationsUrl string `json:"organizations_url"` } response, err := HttpGet(client, s.apiUrl) @@ -219,7 +221,7 @@ func (s *SocialGithub) UserInfo(client *http.Client) (*BasicUserInfo, error) { return nil, ErrMissingTeamMembership } - if !s.IsOrganizationMember(client) { + if !s.IsOrganizationMember(client, data.OrganizationsUrl) { return nil, ErrMissingOrganizationMembership } From 0c5ef1453df49ad332a0bbe4995de95328f28be3 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 8 Dec 2017 11:33:15 +0100 Subject: [PATCH 2/7] fixes broken test --- pkg/services/provisioning/dashboards/config_reader_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/provisioning/dashboards/config_reader_test.go b/pkg/services/provisioning/dashboards/config_reader_test.go index 657c0045e3a..56c5a5fcf3d 100644 --- a/pkg/services/provisioning/dashboards/config_reader_test.go +++ b/pkg/services/provisioning/dashboards/config_reader_test.go @@ -8,7 +8,7 @@ import ( var ( simpleDashboardConfig string = "./test-configs/dashboards-from-disk" - brokenConfigs string = "./test-configs/borken-configs" + brokenConfigs string = "./test-configs/broken-configs" ) func TestDashboardsAsConfig(t *testing.T) { From ce809de1ed98841b4414afeab27e2c8e736a9faa Mon Sep 17 00:00:00 2001 From: Sven Klemm <31455525+svenklemm@users.noreply.github.com> Date: Fri, 8 Dec 2017 15:14:10 +0100 Subject: [PATCH 3/7] postgres: change $__timeGroup macro to include "AS time" column alias (#10119) * change $__timeGroup macro to include column alias * update docs and help text for $__timeGroup macro --- docs/sources/features/datasources/postgres.md | 6 +++--- pkg/tsdb/postgres/macros.go | 2 +- pkg/tsdb/postgres/macros_test.go | 2 +- .../plugins/datasource/postgres/partials/query.editor.html | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index e9d65b8f327..7d52df2fd3e 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -48,7 +48,7 @@ Macro example | Description *$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *extract(epoch from dateColumn) BETWEEN 1494410783 AND 1494497183* *$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *to_timestamp(1494410783)* *$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *to_timestamp(1494497183)* -*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *(extract(epoch from "dateColumn")/300)::bigint*300* +*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *(extract(epoch from dateColumn)/300)::bigint*300 AS time* *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* @@ -94,7 +94,7 @@ Example with `metric` column ```sql SELECT - $__timeGroup(time_date_time,'5m') as time, + $__timeGroup(time_date_time,'5m'), min(value_double), 'min' as metric FROM test_data @@ -107,7 +107,7 @@ Example with multiple columns: ```sql SELECT - $__timeGroup(time_date_time,'5m') as time, + $__timeGroup(time_date_time,'5m'), min(value_double) as min_value, max(value_double) as max_value FROM test_data diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index 288787589ce..086eb96655f 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -89,7 +89,7 @@ func (m *PostgresMacroEngine) evaluateMacro(name string, args []string) (string, if err != nil { return "", fmt.Errorf("error parsing interval %v", args[1]) } - return fmt.Sprintf("(extract(epoch from \"%s\")/%v)::bigint*%v", args[0], interval.Seconds(), interval.Seconds()), nil + return fmt.Sprintf("(extract(epoch from %s)/%v)::bigint*%v AS time", args[0], interval.Seconds(), interval.Seconds()), nil case "__unixEpochFilter": if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) diff --git a/pkg/tsdb/postgres/macros_test.go b/pkg/tsdb/postgres/macros_test.go index ff268805259..ebc5191d46e 100644 --- a/pkg/tsdb/postgres/macros_test.go +++ b/pkg/tsdb/postgres/macros_test.go @@ -45,7 +45,7 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(timeRange, "GROUP BY $__timeGroup(time_column,'5m')") So(err, ShouldBeNil) - So(sql, ShouldEqual, "GROUP BY (extract(epoch from \"time_column\")/300)::bigint*300") + So(sql, ShouldEqual, "GROUP BY (extract(epoch from time_column)/300)::bigint*300 AS time") }) Convey("interpolate __timeTo function", func() { diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index 574fca33901..163970a9ad5 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -50,11 +50,11 @@ Macros: - $__timeEpoch -> extract(epoch from column) as "time" - $__timeFilter(column) -> extract(epoch from column) BETWEEN 1492750877 AND 1492750877 - $__unixEpochFilter(column) -> column > 1492750877 AND column < 1492750877 -- $__timeGroup(column,'5m') -> (extract(epoch from "dateColumn")/300)::bigint*300 +- $__timeGroup(column,'5m') -> (extract(epoch from column)/300)::bigint*300 AS time Example of group by and order by with $__timeGroup: SELECT - $__timeGroup(date_time_col, '1h') AS time, + $__timeGroup(date_time_col, '1h'), sum(value) as value FROM yourtable GROUP BY time From 0506cfdc1d767496df9aeec8c10400be381bd5e0 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 8 Dec 2017 16:09:32 +0100 Subject: [PATCH 4/7] changelog: adds ntoe about closing #10111 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b85f8cd65a6..b8b1dc8bdce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,9 +33,12 @@ From `/etc/grafana/datasources` to `/etc/grafana/provisioning/datasources` when * **Dashboard**: Make it possible to start dashboards from search and dashboard list panel [#1871](https://github.com/grafana/grafana/issues/1871) * **Annotations**: Posting annotations now return the id of the annotation [#9798](https://github.com/grafana/grafana/issues/9798) * **Systemd**: Use systemd notification ready flag [#10024](https://github.com/grafana/grafana/issues/10024), thx [@jgrassler](https://github.com/jgrassler) +* **Github**: Use organizations_url provided from github to verify user belongs in org. [#10111](https://github.com/grafana/grafana/issues/10111), thx [@adiletmaratov](https://github.com/adiletmaratov) + ## Tech * **RabbitMq**: Remove support for publishing events to RabbitMQ [#9645](https://github.com/grafana/grafana/issues/9645) + ## Fixes * **Sensu**: Send alert message to sensu output [#9551](https://github.com/grafana/grafana/issues/9551), thx [@cjchand](https://github.com/cjchand) * **Singlestat**: suppress error when result contains no datapoints [#9636](https://github.com/grafana/grafana/issues/9636), thx [@utkarshcmu](https://github.com/utkarshcmu) From f7ed24475cf32a77388620544062d380c74d206d Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 8 Dec 2017 14:11:48 +0100 Subject: [PATCH 5/7] wait for all sub routines to finish simple solution for waiting for all go sub routines to finish before closing Grafana. We would use errGroup here as well but I dont like spreading context's all over the place. closes #10131 --- pkg/api/http_server.go | 2 +- pkg/cmd/grafana-server/main.go | 33 ++++++++++++++++++------- pkg/cmd/grafana-server/server.go | 42 ++++++++++++-------------------- pkg/models/server.go | 6 ----- 4 files changed, 41 insertions(+), 42 deletions(-) delete mode 100644 pkg/models/server.go diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 89456d20d8c..0366b9aedad 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -95,7 +95,7 @@ func (hs *HttpServer) Start(ctx context.Context) error { func (hs *HttpServer) Shutdown(ctx context.Context) error { err := hs.httpSrv.Shutdown(ctx) - hs.log.Info("stopped http server") + hs.log.Info("Stopped HTTP server") return err } diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index 183e4b047cd..edf827ac4ad 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -14,8 +14,8 @@ import ( "net/http" _ "net/http/pprof" + "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/metrics" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" _ "github.com/grafana/grafana/pkg/services/alerting/conditions" @@ -40,9 +40,6 @@ var homePath = flag.String("homepath", "", "path to grafana install/home path, d var pidFile = flag.String("pidfile", "", "path to pid file") var exitChan = make(chan int) -func init() { -} - func main() { v := flag.Bool("v", false, "prints current version and exits") profile := flag.Bool("profile", false, "Turn on pprof profiling") @@ -82,12 +79,28 @@ func main() { setting.BuildStamp = buildstampInt64 metrics.M_Grafana_Version.WithLabelValues(version).Set(1) - + shutdownCompleted := make(chan int) server := NewGrafanaServer() - server.Start() + + go listenToSystemSignals(server, shutdownCompleted) + + go func() { + code := 0 + if err := server.Start(); err != nil { + log.Error2("Startup failed", "error", err) + code = 1 + } + + exitChan <- code + }() + + code := <-shutdownCompleted + log.Info2("Grafana shutdown completed.", "code", code) + log.Close() + os.Exit(code) } -func listenToSystemSignals(server models.GrafanaServer) { +func listenToSystemSignals(server *GrafanaServerImpl, shutdownCompleted chan int) { signalChan := make(chan os.Signal, 1) ignoreChan := make(chan os.Signal, 1) code := 0 @@ -97,10 +110,12 @@ func listenToSystemSignals(server models.GrafanaServer) { select { case sig := <-signalChan: - // Stops trace if profiling has been enabled - trace.Stop() + trace.Stop() // Stops trace if profiling has been enabled server.Shutdown(0, fmt.Sprintf("system signal: %s", sig)) + shutdownCompleted <- 0 case code = <-exitChan: + trace.Stop() // Stops trace if profiling has been enabled server.Shutdown(code, "startup error") + shutdownCompleted <- code } } diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go index 820295a9b88..b84c3d4e3d6 100644 --- a/pkg/cmd/grafana-server/server.go +++ b/pkg/cmd/grafana-server/server.go @@ -11,7 +11,6 @@ import ( "strconv" "time" - "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/services/provisioning" "golang.org/x/sync/errgroup" @@ -20,7 +19,6 @@ import ( "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/metrics" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/cleanup" @@ -33,7 +31,7 @@ import ( "github.com/grafana/grafana/pkg/tracing" ) -func NewGrafanaServer() models.GrafanaServer { +func NewGrafanaServer() *GrafanaServerImpl { rootCtx, shutdownFn := context.WithCancel(context.Background()) childRoutines, childCtx := errgroup.WithContext(rootCtx) @@ -54,9 +52,7 @@ type GrafanaServerImpl struct { httpServer *api.HttpServer } -func (g *GrafanaServerImpl) Start() { - go listenToSystemSignals(g) - +func (g *GrafanaServerImpl) Start() error { g.initLogging() g.writePIDFile() @@ -69,16 +65,12 @@ func (g *GrafanaServerImpl) Start() { plugins.Init() if err := provisioning.Init(g.context, setting.HomePath, setting.Cfg); err != nil { - logger.Error("Failed to provision Grafana from config", "error", err) - g.Shutdown(1, "Startup failed") - return + return fmt.Errorf("Failed to provision Grafana from config. error: %v", err) } closer, err := tracing.Init(setting.Cfg) if err != nil { - g.log.Error("Tracing settings is not valid", "error", err) - g.Shutdown(1, "Startup failed") - return + return fmt.Errorf("Tracing settings is not valid. error: %v", err) } defer closer.Close() @@ -93,13 +85,12 @@ func (g *GrafanaServerImpl) Start() { g.childRoutines.Go(func() error { return cleanUpService.Run(g.context) }) if err = notifications.Init(); err != nil { - g.log.Error("Notification service failed to initialize", "error", err) - g.Shutdown(1, "Startup failed") - return + return fmt.Errorf("Notification service failed to initialize. error: %v", err) } - SendSystemdNotification("READY=1") - g.startHttpServer() + sendSystemdNotification("READY=1") + + return g.startHttpServer() } func initSql() { @@ -123,16 +114,16 @@ func (g *GrafanaServerImpl) initLogging() { setting.LogConfigurationInfo() } -func (g *GrafanaServerImpl) startHttpServer() { +func (g *GrafanaServerImpl) startHttpServer() error { g.httpServer = api.NewHttpServer() err := g.httpServer.Start(g.context) if err != nil { - g.log.Error("Fail to start server", "error", err) - g.Shutdown(1, "Startup failed") - return + return fmt.Errorf("Fail to start server. error: %v", err) } + + return nil } func (g *GrafanaServerImpl) Shutdown(code int, reason string) { @@ -145,10 +136,9 @@ func (g *GrafanaServerImpl) Shutdown(code int, reason string) { g.shutdownFn() err = g.childRoutines.Wait() - - g.log.Info("Shutdown completed", "reason", err) - log.Close() - os.Exit(code) + if err != nil && err != context.Canceled { + g.log.Error("Server shutdown completed with an error", "error", err) + } } func (g *GrafanaServerImpl) writePIDFile() { @@ -173,7 +163,7 @@ func (g *GrafanaServerImpl) writePIDFile() { g.log.Info("Writing PID file", "path", *pidFile, "pid", pid) } -func SendSystemdNotification(state string) error { +func sendSystemdNotification(state string) error { notifySocket := os.Getenv("NOTIFY_SOCKET") if notifySocket == "" { diff --git a/pkg/models/server.go b/pkg/models/server.go deleted file mode 100644 index 4d683835256..00000000000 --- a/pkg/models/server.go +++ /dev/null @@ -1,6 +0,0 @@ -package models - -type GrafanaServer interface { - Start() - Shutdown(code int, reason string) -} From 8e7166b5c4a77c52d68dc0c01adc67eeb8b2a54c Mon Sep 17 00:00:00 2001 From: Mikael Olenfalk Date: Mon, 11 Dec 2017 09:37:27 +0100 Subject: [PATCH 6/7] Explicitly specify default region in CloudWatch datasource (#9440) The datasource uses the default region in the query if the region is "" in the settings. However setting the region to an empty string is almost impossible and rendered incorrectly. This commit introduces a special value "default" for region which is shown in the drop down and is translated to the default region of the data source when performing queries. --- .../features/datasources/cloudwatch.md | 5 +- pkg/tsdb/cloudwatch/credentials.go | 5 ++ .../datasource/cloudwatch/datasource.js | 21 ++++-- .../cloudwatch/query_parameter_ctrl.ts | 8 ++- .../cloudwatch/specs/datasource_specs.ts | 69 +++++++++++++++++++ 5 files changed, 98 insertions(+), 10 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index bdf661dc4fc..648957ed96e 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -78,11 +78,14 @@ CloudWatch Datasource Plugin provides the following queries you can specify in t edit view. They allow you to fill a variable's options list with things like `region`, `namespaces`, `metric names` and `dimension keys/values`. +In place of `region` you can specify `default` to use the default region configured in the datasource for the query, +e.g. `metrics(AWS/DynamoDB, default)` or `dimension_values(default, ..., ..., ...)`. + Name | Description ------- | -------- *regions()* | Returns a list of regions AWS provides their service. *namespaces()* | Returns a list of namespaces CloudWatch support. -*metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region for custom metrics) +*metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region or use "default" for custom metrics) *dimension_keys(namespace)* | Returns a list of dimension keys in the namespace. *dimension_values(region, namespace, metric, dimension_key)* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`. *ebs_volume_ids(region, instance_id)* | Returns a list of volume ids matching the specified `region`, `instance_id`. diff --git a/pkg/tsdb/cloudwatch/credentials.go b/pkg/tsdb/cloudwatch/credentials.go index 784f3b729ac..0c142bd4ea0 100644 --- a/pkg/tsdb/cloudwatch/credentials.go +++ b/pkg/tsdb/cloudwatch/credentials.go @@ -141,6 +141,11 @@ func ec2RoleProvider(sess *session.Session) credentials.Provider { } func (e *CloudWatchExecutor) getDsInfo(region string) *DatasourceInfo { + defaultRegion := e.DataSource.JsonData.Get("defaultRegion").MustString() + if region == "default" { + region = defaultRegion + } + authType := e.DataSource.JsonData.Get("authType").MustString() assumeRoleArn := e.DataSource.JsonData.Get("assumeRoleArn").MustString() accessKey := "" diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index ac4573ef43a..b0d37fd2186 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -39,7 +39,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { !!item.metricName && !_.isEmpty(item.statistics); }).map(function (item) { - item.region = templateSrv.replace(item.region, options.scopedVars); + item.region = templateSrv.replace(self.getActualRegion(item.region), options.scopedVars); item.namespace = templateSrv.replace(item.namespace, options.scopedVars); item.metricName = templateSrv.replace(item.metricName, options.scopedVars); item.dimensions = self.convertDimensionFormat(item.dimensions, options.scopeVars); @@ -165,21 +165,21 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { this.getMetrics = function (namespace, region) { return this.doMetricQueryRequest('metrics', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), namespace: templateSrv.replace(namespace) }); }; this.getDimensionKeys = function(namespace, region) { return this.doMetricQueryRequest('dimension_keys', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), namespace: templateSrv.replace(namespace) }); }; this.getDimensionValues = function(region, namespace, metricName, dimensionKey, filterDimensions) { return this.doMetricQueryRequest('dimension_values', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), namespace: templateSrv.replace(namespace), metricName: templateSrv.replace(metricName), dimensionKey: templateSrv.replace(dimensionKey), @@ -189,14 +189,14 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { this.getEbsVolumeIds = function(region, instanceId) { return this.doMetricQueryRequest('ebs_volume_ids', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), instanceId: templateSrv.replace(instanceId) }); }; this.getEc2InstanceAttribute = function(region, attributeName, filters) { return this.doMetricQueryRequest('ec2_instance_attribute', { - region: templateSrv.replace(region), + region: templateSrv.replace(this.getActualRegion(region)), attributeName: templateSrv.replace(attributeName), filters: filters }); @@ -267,7 +267,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { period = parseInt(period, 10); var parameters = { prefixMatching: annotation.prefixMatching, - region: templateSrv.replace(annotation.region), + region: templateSrv.replace(this.getActualRegion(annotation.region)), namespace: templateSrv.replace(annotation.namespace), metricName: templateSrv.replace(annotation.metricName), dimensions: this.convertDimensionFormat(annotation.dimensions, {}), @@ -341,6 +341,13 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { return this.defaultRegion; }; + this.getActualRegion = function(region) { + if (region === 'default' || _.isEmpty(region)) { + return this.getDefaultRegion(); + } + return region; + }; + this.getExpandedVariables = function(target, dimensionKey, variable, templateSrv) { /* if the all checkbox is marked we should add all values to the targets */ var allSelected = _.find(variable.options, {'selected': true, 'text': 'All'}); diff --git a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts index 57a5fba443b..6bf22b0f2e7 100644 --- a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts @@ -28,7 +28,7 @@ export class CloudWatchQueryParameterCtrl { target.statistics = target.statistics || ['Average']; target.dimensions = target.dimensions || {}; target.period = target.period || ''; - target.region = target.region || ''; + target.region = target.region || 'default'; $scope.regionSegment = uiSegmentSrv.getSegmentForValue($scope.target.region, 'select region'); $scope.namespaceSegment = uiSegmentSrv.getSegmentForValue($scope.target.namespace, 'select namespace'); @@ -51,7 +51,7 @@ export class CloudWatchQueryParameterCtrl { $scope.removeStatSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove stat --'}); if (_.isEmpty($scope.target.region)) { - $scope.target.region = $scope.datasource.getDefaultRegion(); + $scope.target.region = 'default'; } if (!$scope.onChange) { @@ -148,6 +148,10 @@ export class CloudWatchQueryParameterCtrl { $scope.getRegions = function() { return $scope.datasource.metricFindQuery('regions()') + .then(function(results) { + results.unshift({ text: 'default'}); + return results; + }) .then($scope.transformToSegments(true)); }; diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts index f278ce9305d..5eda60d5b9e 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts @@ -165,6 +165,55 @@ describe('CloudWatchDatasource', function() { }); }); + describe('When query region is "default"', function () { + it('should return the datasource region if empty or "default"', function() { + var defaultRegion = instanceSettings.jsonData.defaultRegion; + + expect(ctx.ds.getActualRegion()).to.be(defaultRegion); + expect(ctx.ds.getActualRegion('')).to.be(defaultRegion); + expect(ctx.ds.getActualRegion("default")).to.be(defaultRegion); + }); + + it('should return the specified region if specified', function() { + expect(ctx.ds.getActualRegion('some-fake-region-1')).to.be('some-fake-region-1'); + }); + + var requestParams; + beforeEach(function() { + ctx.ds.performTimeSeriesQuery = function(request) { + requestParams = request; + return ctx.$q.when({data: {}}); + }; + }); + + it('should query for the datasource region if empty or "default"', function(done) { + var query = { + range: { from: 'now-1h', to: 'now' }, + rangeRaw: { from: 1483228800, to: 1483232400 }, + targets: [ + { + region: 'default', + namespace: 'AWS/EC2', + metricName: 'CPUUtilization', + dimensions: { + InstanceId: 'i-12345678' + }, + statistics: ['Average'], + period: 300 + } + ] + }; + + ctx.ds.query(query).then(function(result) { + expect(requestParams.queries[0].region).to.be(instanceSettings.jsonData.defaultRegion); + done(); + }); + ctx.$rootScope.$apply(); + }); + + + }); + describe('When performing CloudWatch query for extended statistics', function() { var requestParams; @@ -348,6 +397,26 @@ describe('CloudWatchDatasource', function() { }); }); + describeMetricFindQuery('dimension_values(default,AWS/EC2,CPUUtilization,InstanceId)', scenario => { + scenario.setup(() => { + scenario.requestResponse = { + results: { + metricFindQuery: { + tables: [ + { rows: [['i-12345678', 'i-12345678']] } + ] + } + } + }; + }); + + it('should call __ListMetrics and return result', () => { + expect(scenario.result[0].text).to.contain('i-12345678'); + expect(scenario.request.queries[0].type).to.be('metricFindQuery'); + expect(scenario.request.queries[0].subtype).to.be('dimension_values'); + }); + }); + it('should caclculate the correct period', function () { var hourSec = 60 * 60; var daySec = hourSec * 24; From 66bc1fea2da651c28b288ebf7e062f0c68b26154 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 11 Dec 2017 09:45:17 +0100 Subject: [PATCH 7/7] changelog: adds note about closing #10131 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b1dc8bdce..6ba7b4a1aff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,9 @@ From `/etc/grafana/datasources` to `/etc/grafana/provisioning/datasources` when * **Dashboard**: Make it possible to start dashboards from search and dashboard list panel [#1871](https://github.com/grafana/grafana/issues/1871) * **Annotations**: Posting annotations now return the id of the annotation [#9798](https://github.com/grafana/grafana/issues/9798) * **Systemd**: Use systemd notification ready flag [#10024](https://github.com/grafana/grafana/issues/10024), thx [@jgrassler](https://github.com/jgrassler) -* **Github**: Use organizations_url provided from github to verify user belongs in org. [#10111](https://github.com/grafana/grafana/issues/10111), thx [@adiletmaratov](https://github.com/adiletmaratov) +* **Github**: Use organizations_url provided from github to verify user belongs in org. [#10111](https://github.com/grafana/grafana/issues/10111), thx +[@adiletmaratov](https://github.com/adiletmaratov) +* **Backend**: Fixed bug where Grafana exited before all sub routines where finished [#10131](https://github.com/grafana/grafana/issues/10131) ## Tech * **RabbitMq**: Remove support for publishing events to RabbitMQ [#9645](https://github.com/grafana/grafana/issues/9645)