From 99a8bf2195b487893815a5cfafd0ff8ace436d14 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 17 Oct 2018 12:30:07 +0200 Subject: [PATCH 01/22] Use closure for calling interpolateVariable (cherry picked from commit ec0fd96f08f017a2d3ea694bed35b99437233d7d) --- 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 b0f91f3a3eff8c6586b83e042edaa1168a2a468b Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 17 Oct 2018 13:30:07 +0200 Subject: [PATCH 02/22] postgres: use arrow function declaration of interpolateVariable (cherry picked from commit 7b656097a72396bb351dec391578860918619e33) --- 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 02a3e117080e093ed6ebade9b7d6f0252abb295e Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Tue, 16 Oct 2018 11:39:10 +0900 Subject: [PATCH 03/22] fix concurrent map writes (cherry picked from commit 48aef0c50e07b791196caa4ac40cef2c935ef288) --- pkg/tsdb/cloudwatch/cloudwatch.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 61bbc04394a..437457df52a 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -86,9 +86,10 @@ func (e *CloudWatchExecutor) Query(ctx context.Context, dsInfo *models.DataSourc } func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) { - result := &tsdb.Response{ + results := &tsdb.Response{ Results: make(map[string]*tsdb.QueryResult), } + resultChan := make(chan *tsdb.QueryResult, len(queryContext.Queries)) eg, ectx := errgroup.WithContext(ctx) @@ -102,10 +103,10 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo RefId := queryContext.Queries[i].RefId query, err := parseQuery(queryContext.Queries[i].Model) if err != nil { - result.Results[RefId] = &tsdb.QueryResult{ + results.Results[RefId] = &tsdb.QueryResult{ Error: err, } - return result, nil + return results, nil } query.RefId = RefId @@ -118,10 +119,10 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo } if query.Id == "" && query.Expression != "" { - result.Results[query.RefId] = &tsdb.QueryResult{ + results.Results[query.RefId] = &tsdb.QueryResult{ Error: fmt.Errorf("Invalid query: id should be set if using expression"), } - return result, nil + return results, nil } eg.Go(func() error { @@ -130,12 +131,13 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo return err } if err != nil { - result.Results[query.RefId] = &tsdb.QueryResult{ + resultChan <- &tsdb.QueryResult{ + RefId: query.RefId, Error: err, } return nil } - result.Results[queryRes.RefId] = queryRes + resultChan <- queryRes return nil }) } @@ -149,10 +151,10 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo return err } for _, queryRes := range queryResponses { - result.Results[queryRes.RefId] = queryRes if err != nil { - result.Results[queryRes.RefId].Error = err + queryRes.Error = err } + resultChan <- queryRes } return nil }) @@ -162,8 +164,12 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo if err := eg.Wait(); err != nil { return nil, err } + close(resultChan) + for result := range resultChan { + results.Results[result.RefId] = result + } - return result, nil + return results, nil } func (e *CloudWatchExecutor) executeQuery(ctx context.Context, query *CloudWatchQuery, queryContext *tsdb.TsdbQuery) (*tsdb.QueryResult, error) { From e8e8b014f6fc9990564e0ab4f5aaf6666426e57f Mon Sep 17 00:00:00 2001 From: Adrien Fillon Date: Thu, 18 Oct 2018 12:34:53 +0200 Subject: [PATCH 04/22] fix LDAP Grafana admin logic Co-authored-by: Adrien Fillon Co-authored-by: Remi Buisson (cherry picked from commit 781e66ba3cb9baf767051fdac42d811d2e7feb27) --- pkg/login/ldap.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/login/ldap.go b/pkg/login/ldap.go index 43f45f900d9..4c71ab3cd5f 100644 --- a/pkg/login/ldap.go +++ b/pkg/login/ldap.go @@ -185,7 +185,9 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo if ldapUser.isMemberOf(group.GroupDN) { extUser.OrgRoles[group.OrgId] = group.OrgRole - extUser.IsGrafanaAdmin = group.IsGrafanaAdmin + if extUser.IsGrafanaAdmin == nil || *extUser.IsGrafanaAdmin == false { + extUser.IsGrafanaAdmin = group.IsGrafanaAdmin + } } } From 112fa2b8b92d737fc7519d72f562867e532e61fd Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 15 Oct 2018 13:53:37 +0200 Subject: [PATCH 05/22] Escape values in metric segment and sql part (cherry picked from commit 3a25a0de8306d493f333e1adb2c64d58bed8fe2c) --- public/app/core/components/sql_part/sql_part_editor.ts | 5 +++-- public/app/core/directives/metric_segment.ts | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/public/app/core/components/sql_part/sql_part_editor.ts b/public/app/core/components/sql_part/sql_part_editor.ts index 8097dddeb3b..1d29c577560 100644 --- a/public/app/core/components/sql_part/sql_part_editor.ts +++ b/public/app/core/components/sql_part/sql_part_editor.ts @@ -109,12 +109,12 @@ export function sqlPartEditorDirective($compile, templateSrv) { $scope.$apply(() => { $scope.handleEvent({ $event: { name: 'get-param-options', param: param } }).then(result => { const dynamicOptions = _.map(result, op => { - return op.value; + return _.escape(op.value); }); // add current value to dropdown if it's not in dynamicOptions if (_.indexOf(dynamicOptions, part.params[paramIndex]) === -1) { - dynamicOptions.unshift(part.params[paramIndex]); + dynamicOptions.unshift(_.escape(part.params[paramIndex])); } callback(dynamicOptions); @@ -129,6 +129,7 @@ export function sqlPartEditorDirective($compile, templateSrv) { minLength: 0, items: 1000, updater: value => { + value = _.unescape(value); if (value === part.params[paramIndex]) { clearTimeout(cancelBlur); $input.focus(); diff --git a/public/app/core/directives/metric_segment.ts b/public/app/core/directives/metric_segment.ts index de904e95fc6..4a20f4e3de5 100644 --- a/public/app/core/directives/metric_segment.ts +++ b/public/app/core/directives/metric_segment.ts @@ -56,7 +56,7 @@ export function metricSegment($compile, $sce) { } } else if (segment.custom !== 'false') { segment.value = value; - segment.html = $sce.trustAsHtml(value); + segment.html = _.escape(value); segment.expandable = true; segment.fake = false; } @@ -95,7 +95,7 @@ export function metricSegment($compile, $sce) { // add custom values if (segment.custom !== 'false') { if (!segment.fake && _.indexOf(options, segment.value) === -1) { - options.unshift(segment.value); + options.unshift(_.escape(segment.value)); } } @@ -105,6 +105,7 @@ export function metricSegment($compile, $sce) { }; $scope.updater = value => { + value = _.unescape(value); if (value === segment.value) { clearTimeout(cancelBlur); $input.focus(); @@ -219,7 +220,7 @@ export function metricSegmentModel(uiSegmentSrv, $q) { cachedOptions = $scope.options; return $q.when( _.map($scope.options, option => { - return { value: option.text }; + return { value: _.escape(option.text) }; }) ); } else { @@ -229,7 +230,7 @@ export function metricSegmentModel(uiSegmentSrv, $q) { if (option.html) { return option; } - return { value: option.text }; + return { value: _.escape(option.text) }; }); }); } From 02b4cf392d5c56b19d75d43037041f5f15158409 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 15 Oct 2018 14:05:07 +0200 Subject: [PATCH 06/22] Escape typeahead values in query_part (cherry picked from commit 20c1a58488e61ab28d7dc4702cfb60474b4a802f) --- public/app/core/components/query_part/query_part_editor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/core/components/query_part/query_part_editor.ts b/public/app/core/components/query_part/query_part_editor.ts index 6181d020471..2cab966ed46 100644 --- a/public/app/core/components/query_part/query_part_editor.ts +++ b/public/app/core/components/query_part/query_part_editor.ts @@ -103,7 +103,7 @@ export function queryPartEditorDirective($compile, templateSrv) { $scope.$apply(() => { $scope.handleEvent({ $event: { name: 'get-param-options' } }).then(result => { const dynamicOptions = _.map(result, op => { - return op.value; + return _.escape(op.value); }); callback(dynamicOptions); }); @@ -117,6 +117,7 @@ export function queryPartEditorDirective($compile, templateSrv) { minLength: 0, items: 1000, updater: value => { + value = _.unescape(value); setTimeout(() => { inputBlur.call($input[0], paramIndex); }, 0); From 7ddccdba08870084ede7c76eb12392941b2aa4fb Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Thu, 18 Oct 2018 15:04:54 +0200 Subject: [PATCH 07/22] Fix variable highlighting (cherry picked from commit 2803cdca400bce62db6540a53a1ad09c06f4e7c7) --- public/app/core/directives/metric_segment.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/public/app/core/directives/metric_segment.ts b/public/app/core/directives/metric_segment.ts index 4a20f4e3de5..85576dcffee 100644 --- a/public/app/core/directives/metric_segment.ts +++ b/public/app/core/directives/metric_segment.ts @@ -3,7 +3,7 @@ import $ from 'jquery'; import coreModule from '../core_module'; /** @ngInject */ -export function metricSegment($compile, $sce) { +export function metricSegment($compile, $sce, templateSrv) { const inputTemplate = ' { const selected = _.find($scope.altSegments, { value: value }); if (selected) { segment.value = selected.value; - segment.html = selected.html || selected.value; + segment.html = selected.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(selected.value)); segment.fake = false; segment.expandable = selected.expandable; @@ -56,7 +54,7 @@ export function metricSegment($compile, $sce) { } } else if (segment.custom !== 'false') { segment.value = value; - segment.html = _.escape(value); + segment.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(value)); segment.expandable = true; segment.fake = false; } @@ -220,7 +218,7 @@ export function metricSegmentModel(uiSegmentSrv, $q) { cachedOptions = $scope.options; return $q.when( _.map($scope.options, option => { - return { value: _.escape(option.text) }; + return { value: option.text }; }) ); } else { @@ -230,7 +228,7 @@ export function metricSegmentModel(uiSegmentSrv, $q) { if (option.html) { return option; } - return { value: _.escape(option.text) }; + return { value: option.text }; }); }); } From 3891b8244311dde15ead4a8f81c6afb8ba8cd664 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 18 Oct 2018 14:28:41 +0900 Subject: [PATCH 08/22] don't overwrite unit if user set (cherry picked from commit 287ba77abff0c9ba46fea36b7dfd883181022fd8) --- public/app/plugins/panel/graph/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index f0751ddd816..a6c80d6e03a 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -229,7 +229,7 @@ class GraphCtrl extends MetricsPanelCtrl { for (const series of this.seriesList) { series.applySeriesOverrides(this.panel.seriesOverrides); - if (series.unit) { + if (this.panel.yaxes[series.yaxis - 1].format === 'none' && series.unit) { this.panel.yaxes[series.yaxis - 1].format = series.unit; } } From 5b9116bf803c1d1949de77ce306760f6149e53d8 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 18 Oct 2018 20:45:03 +0900 Subject: [PATCH 09/22] Revert "don't overwrite unit if user set" This reverts commit 9dd33b79e037fc75ddc5f3a6b294edba99e99b94. (cherry picked from commit e465b2d53ab59f8a004db580fdd3902d76536878) --- public/app/plugins/panel/graph/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index a6c80d6e03a..f0751ddd816 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -229,7 +229,7 @@ class GraphCtrl extends MetricsPanelCtrl { for (const series of this.seriesList) { series.applySeriesOverrides(this.panel.seriesOverrides); - if (this.panel.yaxes[series.yaxis - 1].format === 'none' && series.unit) { + if (series.unit) { this.panel.yaxes[series.yaxis - 1].format = series.unit; } } From f213f664cee6e70fb669af8d38341abb37816212 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 18 Oct 2018 20:51:36 +0900 Subject: [PATCH 10/22] allow unit override if cloudwatch response unit is none (cherry picked from commit 4687ce2f7b884fd87f23a6d11bb9d3d0d64dcdc6) --- public/app/plugins/datasource/cloudwatch/datasource.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index e096e44ac25..d8929e770ca 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -131,7 +131,11 @@ export default class CloudWatchDatasource { if (res.results) { _.forEach(res.results, queryRes => { _.forEach(queryRes.series, series => { - data.push({ target: series.name, datapoints: series.points, unit: queryRes.meta.unit || 'none' }); + const s = { target: series.name, datapoints: series.points } as any; + if (queryRes.meta.unit) { + s.unit = queryRes.meta.unit; + } + data.push(s); }); }); } From 72e60346bc22d65186019890a04c015ef8321f2c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 18 Oct 2018 16:42:08 +0200 Subject: [PATCH 11/22] stackdriver: make sure unit is not returned to the panel if mapping from stackdriver unit to grafana unit can't be made (cherry picked from commit d1740f090a0a0479a9e4999d7225c0a5c4ba8faf) --- .../app/plugins/datasource/stackdriver/datasource.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index 7ea748e1082..581c4f5002c 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -89,7 +89,7 @@ export default class StackdriverDatasource { } resolvePanelUnitFromTargets(targets: any[]) { - let unit = 'none'; + let unit; if (targets.length > 0 && targets.every(t => t.unit === targets[0].unit)) { if (stackdriverUnitMappings.hasOwnProperty(targets[0].unit)) { unit = stackdriverUnitMappings[targets[0].unit]; @@ -109,13 +109,17 @@ export default class StackdriverDatasource { const unit = this.resolvePanelUnitFromTargets(options.targets); queryRes.series.forEach(series => { - result.push({ + let timeSerie = { target: series.name, datapoints: series.points, refId: queryRes.refId, meta: queryRes.meta, unit, - }); + }; + if (unit) { + timeSerie = { ...timeSerie, unit }; + } + result.push(timeSerie); }); }); } From 20a47ed3d65352281a576e82494ca0c4e91219a2 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 18 Oct 2018 16:45:46 +0200 Subject: [PATCH 12/22] stackdriver: fix failing tests (cherry picked from commit 0f0763b6b8cc3036d7f19f185c3be1778bbfe607) --- .../datasource/stackdriver/specs/datasource.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts index 3117be402a9..ab0c0653816 100644 --- a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts +++ b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts @@ -235,8 +235,8 @@ describe('StackdriverDataSource', () => { beforeEach(() => { res = ds.resolvePanelUnitFromTargets([{ unit: 'megaseconds' }]); }); - it('should return none', () => { - expect(res).toEqual('none'); + it('should return undefined', () => { + expect(res).toBeUndefined(); }); }); describe('and the stackdriver unit has a corresponding grafana unit', () => { @@ -262,16 +262,16 @@ describe('StackdriverDataSource', () => { beforeEach(() => { res = ds.resolvePanelUnitFromTargets([{ unit: 'megaseconds' }, { unit: 'megaseconds' }]); }); - it('should return the default value - none', () => { - expect(res).toEqual('none'); + it('should return the default value of undefined', () => { + expect(res).toBeUndefined(); }); }); describe('and all target units are not the same', () => { beforeEach(() => { res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }, { unit: 'min' }]); }); - it('should return the default value - none', () => { - expect(res).toEqual('none'); + it('should return the default value of undefined', () => { + expect(res).toBeUndefined(); }); }); }); From 487a8585c6afe4c8ed59a2a4be7f0b71ce6925c3 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 19 Oct 2018 10:42:57 +0200 Subject: [PATCH 13/22] stackdriver: only add unit to resonse obj if it has a value (cherry picked from commit b2932058c7988e350d687fb60926e26519db41bb) --- public/app/plugins/datasource/stackdriver/datasource.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index 581c4f5002c..b77abdbdab3 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -109,12 +109,11 @@ export default class StackdriverDatasource { const unit = this.resolvePanelUnitFromTargets(options.targets); queryRes.series.forEach(series => { - let timeSerie = { + let timeSerie: any = { target: series.name, datapoints: series.points, refId: queryRes.refId, meta: queryRes.meta, - unit, }; if (unit) { timeSerie = { ...timeSerie, unit }; From 5daf8424319a737af9af8887caa31ba886791495 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 22 Oct 2018 10:39:25 +0200 Subject: [PATCH 14/22] add debug logging of folder/dashbord permission checks (cherry picked from commit b371f2d91f5398068775e1c2c8d42e10198765a3) --- pkg/services/guardian/guardian.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/pkg/services/guardian/guardian.go b/pkg/services/guardian/guardian.go index 7506338c5f0..366bc90fc37 100644 --- a/pkg/services/guardian/guardian.go +++ b/pkg/services/guardian/guardian.go @@ -40,7 +40,7 @@ var New = func(dashId int64, orgId int64, user *m.SignedInUser) DashboardGuardia user: user, dashId: dashId, orgId: orgId, - log: log.New("guardians.dashboard"), + log: log.New("dashboard.permissions"), } } @@ -66,15 +66,30 @@ func (g *dashboardGuardianImpl) CanAdmin() (bool, error) { func (g *dashboardGuardianImpl) HasPermission(permission m.PermissionType) (bool, error) { if g.user.OrgRole == m.ROLE_ADMIN { - return true, nil + return g.logHasPermissionResult(permission, true, nil) } acl, err := g.GetAcl() if err != nil { - return false, err + return g.logHasPermissionResult(permission, false, err) } - return g.checkAcl(permission, acl) + result, err := g.checkAcl(permission, acl) + return g.logHasPermissionResult(permission, result, err) +} + +func (g *dashboardGuardianImpl) logHasPermissionResult(permission m.PermissionType, hasPermission bool, err error) (bool, error) { + if err != nil { + return hasPermission, err + } + + if hasPermission { + g.log.Debug("User granted access to execute action", "userId", g.user.UserId, "orgId", g.orgId, "uname", g.user.Login, "dashId", g.dashId, "action", permission) + } else { + g.log.Debug("User denied access to execute action", "userId", g.user.UserId, "orgId", g.orgId, "uname", g.user.Login, "dashId", g.dashId, "action", permission) + } + + return hasPermission, err } func (g *dashboardGuardianImpl) checkAcl(permission m.PermissionType, acl []*m.DashboardAclInfoDTO) (bool, error) { From 6c3202b1b617bad5111a9651e3fb0ec270a07468 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Tue, 23 Oct 2018 14:05:10 +0200 Subject: [PATCH 15/22] =?UTF-8?q?fix:=20Text=20box=20variables=20with=20em?= =?UTF-8?q?pty=20values=20should=20not=20be=20considered=20fa=E2=80=A6=20(?= =?UTF-8?q?#13791)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: text box template variable doesn't work properly without a default value (cherry picked from commit 22a0f3cf943a1465626ba2fbf0d073454611f1bb) --- .../templating/specs/template_srv.test.ts | 10 ++++++++++ public/app/features/templating/template_srv.ts | 15 ++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/public/app/features/templating/specs/template_srv.test.ts b/public/app/features/templating/specs/template_srv.test.ts index 7f5ff959216..d279029d64d 100644 --- a/public/app/features/templating/specs/template_srv.test.ts +++ b/public/app/features/templating/specs/template_srv.test.ts @@ -429,6 +429,11 @@ describe('templateSrv', () => { name: 'period', current: { value: '$__auto_interval_interval', text: 'auto' }, }, + { + type: 'textbox', + name: 'empty_on_init', + current: { value: '', text: '' }, + }, ]); _templateSrv.setGrafanaVariable('$__auto_interval_interval', '13m'); _templateSrv.updateTemplateData(); @@ -438,6 +443,11 @@ describe('templateSrv', () => { const target = _templateSrv.replaceWithText('Server: $server, period: $period'); expect(target).toBe('Server: All, period: 13m'); }); + + it('should replace empty string-values with an empty string', () => { + const target = _templateSrv.replaceWithText('Hello $empty_on_init'); + expect(target).toBe('Hello '); + }); }); describe('built in interval variables', () => { diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts index 70fd287402f..11d235f5a09 100644 --- a/public/app/features/templating/template_srv.ts +++ b/public/app/features/templating/template_srv.ts @@ -30,17 +30,14 @@ export class TemplateSrv { } updateTemplateData() { - this.index = {}; + const existsOrEmpty = value => value || value === ''; - for (let i = 0; i < this.variables.length; i++) { - const variable = this.variables[i]; - - if (!variable.current || (!variable.current.isNone && !variable.current.value)) { - continue; + this.index = this.variables.reduce((acc, currentValue) => { + if (currentValue.current && !currentValue.current.isNone && existsOrEmpty(currentValue.current.value)) { + acc[currentValue.name] = currentValue; } - - this.index[variable.name] = variable; - } + return acc; + }, {}); } variableInitialized(variable) { From c9591f8a8c083f0889de58461865ee2ce1a1a8a2 Mon Sep 17 00:00:00 2001 From: Adam Palaniuk Date: Thu, 11 Oct 2018 14:00:34 -0500 Subject: [PATCH 16/22] Update check for invalid percentile statistics (cherry picked from commit 58a156ba03879b43ec521131d32188b93790defd) --- public/app/plugins/datasource/cloudwatch/datasource.ts | 8 +++++++- .../datasource/cloudwatch/specs/datasource.test.ts | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index d8929e770ca..283b1683a69 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -44,7 +44,13 @@ export default class CloudWatchDatasource { // valid ExtendedStatistics is like p90.00, check the pattern const hasInvalidStatistics = item.statistics.some(s => { - return s.indexOf('p') === 0 && !/p\d{2}\.\d{2}/.test(s); + if (s.indexOf('p') === 0) { + const matches = /^p\d{2}(?:\.\d{1,2})?$/.exec(s); + + return !matches || matches[0] !== s; + } + + return false; }); if (hasInvalidStatistics) { throw { message: 'Invalid extended statistics' }; diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts index 2825539f223..512767075a9 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts @@ -122,7 +122,7 @@ describe('CloudWatchDatasource', () => { }); }); - it('should cancel query for invalid extended statistics', () => { + it.each(['pNN.NN', 'p9', 'p99.', 'p99.999'])('should cancel query for invalid extended statistics (%s)', stat => { const query = { range: { from: 'now-1h', to: 'now' }, rangeRaw: { from: 1483228800, to: 1483232400 }, @@ -134,7 +134,7 @@ describe('CloudWatchDatasource', () => { dimensions: { InstanceId: 'i-12345678', }, - statistics: ['pNN.NN'], + statistics: [stat], period: '60s', }, ], From 0d8c7573f36370ecbefeca2e6821ba1ee2656225 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 23 Oct 2018 16:10:23 +0200 Subject: [PATCH 17/22] docker: adds curl back into the docker image for utility. (#13794) (cherry picked from commit 4cc89f1753367db73feea9b5fcae293d58c06491) --- packaging/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index 890d6a4fb11..dc8972b0ba0 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -25,7 +25,7 @@ ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bi WORKDIR $GF_PATHS_HOME -RUN apt-get update && apt-get install -qq -y libfontconfig ca-certificates && \ +RUN apt-get update && apt-get install -qq -y libfontconfig ca-certificates curl && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* From cb1eedb5f74c6523e0ea8044f5ca30920bbd9d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 23 Oct 2018 15:37:11 +0200 Subject: [PATCH 18/22] fix: kiosk url fix, fixes #13764 (cherry picked from commit 8a1e0cd83b3434a548aa7f9777031565b8b3057e) --- public/app/core/components/grafana_app.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index 4272c8a0b71..b301a89cd3a 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -81,7 +81,7 @@ function setViewModeBodyClass(body, mode, sidemenuOpen: boolean) { break; } // 1 & true for legacy states - case 1: + case '1': case true: { body.removeClass('sidemenu-open'); body.addClass('view-mode--kiosk'); @@ -174,11 +174,11 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop switch (search.kiosk) { case 'tv': { - search.kiosk = 1; + search.kiosk = true; appEvents.emit('alert-success', ['Press ESC to exit Kiosk mode']); break; } - case 1: + case '1': case true: { delete search.kiosk; break; From 7be0716752ff0f9be5587bb776f6f73d22922291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 23 Oct 2018 16:22:00 +0200 Subject: [PATCH 19/22] fix: another fix for #13764 , #13793 (cherry picked from commit 53d9619cb9951b564920d0c324208d185705a370) --- public/app/core/components/grafana_app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index b301a89cd3a..f42ee5d9619 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -169,7 +169,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop const search = $location.search(); if (options && options.exit) { - search.kiosk = 1; + search.kiosk = '1'; } switch (search.kiosk) { From a54fa3858eb4dc667b76fe7fdf380047cabea0f0 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Wed, 24 Oct 2018 12:06:09 +0200 Subject: [PATCH 20/22] =?UTF-8?q?Move=20the=20variable=20regex=20to=20cons?= =?UTF-8?q?tants=20to=20make=20sure=20we=20use=20the=20same=20reg=E2=80=A6?= =?UTF-8?q?=20(#13801)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 38c155403ecd497e94f19d30f0c4b0a902726078) --- .../templating/specs/variable.test.ts | 15 ++++++++ .../app/features/templating/template_srv.ts | 9 ++--- public/app/features/templating/variable.ts | 36 +++++++++++++------ 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/public/app/features/templating/specs/variable.test.ts b/public/app/features/templating/specs/variable.test.ts index 6d5e88fa4bd..83f4af8bca9 100644 --- a/public/app/features/templating/specs/variable.test.ts +++ b/public/app/features/templating/specs/variable.test.ts @@ -22,6 +22,11 @@ describe('containsVariable', () => { expect(contains).toBe(true); }); + it('should find it with [[var:option]] syntax', () => { + const contains = containsVariable('this.[[test:csv]].filters', 'test'); + expect(contains).toBe(true); + }); + it('should find it when part of segment', () => { const contains = containsVariable('metrics.$env.$group-*', 'group'); expect(contains).toBe(true); @@ -36,6 +41,16 @@ describe('containsVariable', () => { const contains = containsVariable('asd', 'asd2.$env', 'env'); expect(contains).toBe(true); }); + + it('should find it with ${var} syntax', () => { + const contains = containsVariable('this.${test}.filters', 'test'); + expect(contains).toBe(true); + }); + + it('should find it with ${var:option} syntax', () => { + const contains = containsVariable('this.${test:csv}.filters', 'test'); + expect(contains).toBe(true); + }); }); }); diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts index 11d235f5a09..61326ad63ec 100644 --- a/public/app/features/templating/template_srv.ts +++ b/public/app/features/templating/template_srv.ts @@ -1,5 +1,6 @@ import kbn from 'app/core/utils/kbn'; import _ from 'lodash'; +import { variableRegex } from 'app/features/templating/variable'; function luceneEscape(value) { return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1'); @@ -8,13 +9,7 @@ function luceneEscape(value) { export class TemplateSrv { variables: any[]; - /* - * This regex matches 3 types of variable reference with an optional format specifier - * \$(\w+) $var1 - * \[\[([\s\S]+?)(?::(\w+))?\]\] [[var2]] or [[var2:fmt2]] - * \${(\w+)(?::(\w+))?} ${var3} or ${var3:fmt3} - */ - private regex = /\$(\w+)|\[\[([\s\S]+?)(?::(\w+))?\]\]|\${(\w+)(?::(\w+))?}/g; + private regex = variableRegex; private index = {}; private grafanaVariables = {}; private builtIns = {}; diff --git a/public/app/features/templating/variable.ts b/public/app/features/templating/variable.ts index 412426fb294..1994e86eff0 100644 --- a/public/app/features/templating/variable.ts +++ b/public/app/features/templating/variable.ts @@ -1,6 +1,19 @@ -import kbn from 'app/core/utils/kbn'; import { assignModelProperties } from 'app/core/utils/model_utils'; +/* + * This regex matches 3 types of variable reference with an optional format specifier + * \$(\w+) $var1 + * \[\[([\s\S]+?)(?::(\w+))?\]\] [[var2]] or [[var2:fmt2]] + * \${(\w+)(?::(\w+))?} ${var3} or ${var3:fmt3} + */ +export const variableRegex = /\$(\w+)|\[\[([\s\S]+?)(?::(\w+))?\]\]|\${(\w+)(?::(\w+))?}/g; + +// Helper function since lastIndex is not reset +export const variableRegexExec = (variableString: string) => { + variableRegex.lastIndex = 0; + return variableRegex.exec(variableString); +}; + export interface Variable { setValue(option); updateOptions(); @@ -14,15 +27,16 @@ export let variableTypes = {}; export { assignModelProperties }; export function containsVariable(...args: any[]) { - let variableName = args[args.length - 1]; - let str = args[0] || ''; + const variableName = args[args.length - 1]; + const variableString = args.slice(0, -1).join(' '); + const matches = variableString.match(variableRegex); + const isMatchingVariable = + matches !== null + ? matches.find(match => { + const varMatch = variableRegexExec(match); + return varMatch !== null && varMatch.indexOf(variableName) > -1; + }) + : false; - for (let i = 1; i < args.length - 1; i++) { - str += ' ' + args[i] || ''; - } - - variableName = kbn.regexEscape(variableName); - const findVarRegex = new RegExp('\\$(' + variableName + ')(?:\\W|$)|\\[\\[(' + variableName + ')\\]\\]', 'g'); - const match = findVarRegex.exec(str); - return match !== null; + return !!isMatchingVariable; } From d20c7260b3f7c6128460759b83b9ce7d85f81679 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 24 Oct 2018 11:18:49 +0200 Subject: [PATCH 21/22] Resource type filter (#13784) * stackdriver: add resource type to filter and group bys * stackdriver: remove not used param * stackdriver: refactor filter and group by code * stackdriver: remove resource type if its already in filter list * stackdriver: remove debug logging * stackdriver: remove more debug logging * stackdriver: append resource type to legend name if there are more than one type present in the response * stackdriver: only make new request if filter has real value * stackdriver: format legend support for resource type * stackdriver: add resource type to documentation * stackdriver: not returning promise from query function * stackdriver: fix refactoring bug * stackdriver: remove not used import (cherry picked from commit c5af0bf1c5f625c5f1ba13781ff89d53f1724a85) --- .../features/datasources/stackdriver.md | 10 +++ pkg/tsdb/stackdriver/stackdriver.go | 23 ++++- .../datasource/stackdriver/datasource.ts | 6 +- .../datasource/stackdriver/filter_segments.ts | 2 +- .../stackdriver/partials/query.filter.html | 2 +- .../datasource/stackdriver/query_ctrl.ts | 1 - .../stackdriver/query_filter_ctrl.ts | 88 ++++++++++++------- 7 files changed, 92 insertions(+), 40 deletions(-) diff --git a/docs/sources/features/datasources/stackdriver.md b/docs/sources/features/datasources/stackdriver.md index c525130aebb..d7091b7ece7 100644 --- a/docs/sources/features/datasources/stackdriver.md +++ b/docs/sources/features/datasources/stackdriver.md @@ -134,6 +134,16 @@ Example Alias By: `{{metric.type}} - {{metric.labels.instance_name}}` Example Result: `compute.googleapis.com/instance/cpu/usage_time - server1-prod` +It is also possible to resolve the name of the Monitored Resource Type. + +| Alias Pattern Format | Description | Example Result | +| ------------------------ | ------------------------------------------------| ---------------- | +| `{{resource.type}}` | returns the name of the monitored resource type | `gce_instance` | + +Example Alias By: `{{resource.type}} - {{metric.type}}` + +Example Result: `gce_instance - compute.googleapis.com/instance/cpu/usage_time` + ## Templating Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index 96242dfdec4..1a9bb93fd3d 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -337,11 +337,21 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (Stackdriver func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data StackdriverResponse, query *StackdriverQuery) error { metricLabels := make(map[string][]string) resourceLabels := make(map[string][]string) + var resourceTypes []string + + for _, series := range data.TimeSeries { + if !containsLabel(resourceTypes, series.Resource.Type) { + resourceTypes = append(resourceTypes, series.Resource.Type) + } + } for _, series := range data.TimeSeries { points := make([]tsdb.TimePoint, 0) defaultMetricName := series.Metric.Type + if len(resourceTypes) > 1 { + defaultMetricName += " " + series.Resource.Type + } for key, value := range series.Metric.Labels { if !containsLabel(metricLabels[key], value) { @@ -385,7 +395,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta points = append(points, tsdb.NewTimePoint(null.FloatFrom(value), float64((point.Interval.EndTime).Unix())*1000)) } - metricName := formatLegendKeys(series.Metric.Type, defaultMetricName, series.Metric.Labels, series.Resource.Labels, make(map[string]string), query) + metricName := formatLegendKeys(series.Metric.Type, defaultMetricName, series.Resource.Type, series.Metric.Labels, series.Resource.Labels, make(map[string]string), query) queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{ Name: metricName, @@ -411,7 +421,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta bucketBound := calcBucketBound(point.Value.DistributionValue.BucketOptions, i) additionalLabels := map[string]string{"bucket": bucketBound} buckets[i] = &tsdb.TimeSeries{ - Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), + Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Resource.Type, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), Points: make([]tsdb.TimePoint, 0), } if maxKey < i { @@ -427,7 +437,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta bucketBound := calcBucketBound(point.Value.DistributionValue.BucketOptions, i) additionalLabels := map[string]string{"bucket": bucketBound} buckets[i] = &tsdb.TimeSeries{ - Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), + Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Resource.Type, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), Points: make([]tsdb.TimePoint, 0), } } @@ -442,6 +452,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta queryRes.Meta.Set("resourceLabels", resourceLabels) queryRes.Meta.Set("metricLabels", metricLabels) queryRes.Meta.Set("groupBys", query.GroupBys) + queryRes.Meta.Set("resourceTypes", resourceTypes) return nil } @@ -455,7 +466,7 @@ func containsLabel(labels []string, newLabel string) bool { return false } -func formatLegendKeys(metricType string, defaultMetricName string, metricLabels map[string]string, resourceLabels map[string]string, additionalLabels map[string]string, query *StackdriverQuery) string { +func formatLegendKeys(metricType string, defaultMetricName string, resourceType string, metricLabels map[string]string, resourceLabels map[string]string, additionalLabels map[string]string, query *StackdriverQuery) string { if query.AliasBy == "" { return defaultMetricName } @@ -469,6 +480,10 @@ func formatLegendKeys(metricType string, defaultMetricName string, metricLabels return []byte(metricType) } + if metaPartName == "resource.type" && resourceType != "" { + return []byte(resourceType) + } + metricPart := replaceWithMetricPart(metaPartName, metricType) if metricPart != nil { diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index b77abdbdab3..6955d15cba6 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -106,7 +106,6 @@ export default class StackdriverDatasource { if (!queryRes.series) { return; } - const unit = this.resolvePanelUnitFromTargets(options.targets); queryRes.series.forEach(series => { let timeSerie: any = { @@ -121,9 +120,10 @@ export default class StackdriverDatasource { result.push(timeSerie); }); }); + return { data: result }; + } else { + return { data: [] }; } - - return { data: result }; } async annotationQuery(options) { diff --git a/public/app/plugins/datasource/stackdriver/filter_segments.ts b/public/app/plugins/datasource/stackdriver/filter_segments.ts index 9eb27f31975..5adb56e2fcf 100644 --- a/public/app/plugins/datasource/stackdriver/filter_segments.ts +++ b/public/app/plugins/datasource/stackdriver/filter_segments.ts @@ -44,7 +44,7 @@ export class FilterSegments { this.removeSegment.value = DefaultRemoveFilterValue; return Promise.resolve([this.removeSegment]); } else { - return this.getFilterKeysFunc(); + return this.getFilterKeysFunc(segment, DefaultRemoveFilterValue); } } diff --git a/public/app/plugins/datasource/stackdriver/partials/query.filter.html b/public/app/plugins/datasource/stackdriver/partials/query.filter.html index 9ec59005a0b..5043161c492 100644 --- a/public/app/plugins/datasource/stackdriver/partials/query.filter.html +++ b/public/app/plugins/datasource/stackdriver/partials/query.filter.html @@ -28,7 +28,7 @@
Group By
- +
diff --git a/public/app/plugins/datasource/stackdriver/query_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_ctrl.ts index 8e1f24edeb7..75c5035eede 100644 --- a/public/app/plugins/datasource/stackdriver/query_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_ctrl.ts @@ -101,6 +101,5 @@ export class StackdriverQueryCtrl extends QueryCtrl { this.lastQueryError = jsonBody.error.message; } } - console.error(err); } } diff --git a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts index 786b2831e89..3ebff09f3de 100644 --- a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts @@ -1,6 +1,6 @@ import angular from 'angular'; import _ from 'lodash'; -import { FilterSegments, DefaultRemoveFilterValue } from './filter_segments'; +import { FilterSegments } from './filter_segments'; import appEvents from 'app/core/app_events'; export class StackdriverFilter { @@ -26,8 +26,10 @@ export class StackdriverFilter { export class StackdriverFilterCtrl { metricLabels: { [key: string]: string[] }; resourceLabels: { [key: string]: string[] }; + resourceTypes: string[]; defaultRemoveGroupByValue = '-- remove group by --'; + resourceTypeValue = 'resource.type'; loadLabelsPromise: Promise; service: string; @@ -72,7 +74,7 @@ export class StackdriverFilterCtrl { this.filterSegments = new FilterSegments( this.uiSegmentSrv, this.target, - this.getGroupBys.bind(this, null, null, DefaultRemoveFilterValue, false), + this.getFilterKeys.bind(this), this.getFilterValues.bind(this) ); this.filterSegments.buildSegmentModel(); @@ -141,6 +143,7 @@ export class StackdriverFilterCtrl { const data = await this.datasource.getLabels(this.target.metricType, this.target.refId); this.metricLabels = data.results[this.target.refId].meta.metricLabels; this.resourceLabels = data.results[this.target.refId].meta.resourceLabels; + this.resourceTypes = data.results[this.target.refId].meta.resourceTypes; resolve(); } catch (error) { if (error.data && error.data.message) { @@ -181,45 +184,66 @@ export class StackdriverFilterCtrl { this.$rootScope.$broadcast('metricTypeChanged'); } - async getGroupBys(segment, index, removeText?: string, removeUsed = true) { + async createLabelKeyElements() { await this.loadLabelsPromise; - const metricLabels = Object.keys(this.metricLabels || {}) - .filter(ml => { - if (!removeUsed) { - return true; - } - return this.target.aggregation.groupBys.indexOf('metric.label.' + ml) === -1; - }) - .map(l => { - return this.uiSegmentSrv.newSegment({ - value: `metric.label.${l}`, - expandable: false, - }); + let elements = Object.keys(this.metricLabels || {}).map(l => { + return this.uiSegmentSrv.newSegment({ + value: `metric.label.${l}`, + expandable: false, }); + }); - const resourceLabels = Object.keys(this.resourceLabels || {}) - .filter(ml => { - if (!removeUsed) { - return true; - } - - return this.target.aggregation.groupBys.indexOf('resource.label.' + ml) === -1; - }) - .map(l => { + elements = [ + ...elements, + ...Object.keys(this.resourceLabels || {}).map(l => { return this.uiSegmentSrv.newSegment({ value: `resource.label.${l}`, expandable: false, }); - }); + }), + ]; - const noValueOrPlusButton = !segment || segment.type === 'plus-button'; - if (noValueOrPlusButton && metricLabels.length === 0 && resourceLabels.length === 0) { - return Promise.resolve([]); + if (this.resourceTypes && this.resourceTypes.length > 0) { + elements = [ + ...elements, + this.uiSegmentSrv.newSegment({ + value: this.resourceTypeValue, + expandable: false, + }), + ]; } - this.removeSegment.value = removeText || this.defaultRemoveGroupByValue; - return Promise.resolve([...metricLabels, ...resourceLabels, this.removeSegment]); + return elements; + } + + async getFilterKeys(segment, removeText?: string) { + let elements = await this.createLabelKeyElements(); + + if (this.target.filters.indexOf(this.resourceTypeValue) !== -1) { + elements = elements.filter(e => e.value !== this.resourceTypeValue); + } + + const noValueOrPlusButton = !segment || segment.type === 'plus-button'; + if (noValueOrPlusButton && elements.length === 0) { + return []; + } + + this.removeSegment.value = removeText; + return [...elements, this.removeSegment]; + } + + async getGroupBys(segment) { + let elements = await this.createLabelKeyElements(); + + elements = elements.filter(e => this.target.aggregation.groupBys.indexOf(e.value) === -1); + const noValueOrPlusButton = !segment || segment.type === 'plus-button'; + if (noValueOrPlusButton && elements.length === 0) { + return []; + } + + this.removeSegment.value = this.defaultRemoveGroupByValue; + return [...elements, this.removeSegment]; } groupByChanged(segment, index) { @@ -263,6 +287,10 @@ export class StackdriverFilterCtrl { return this.resourceLabels[shortKey]; } + if (filterKey === this.resourceTypeValue) { + return this.resourceTypes; + } + return []; } From dc70298210aca0fbbd38588d8eec8a9417eb34af Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 24 Oct 2018 13:27:02 +0200 Subject: [PATCH 22/22] release 5.3.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f4ca54c2926..85546d0813c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Grafana Labs" }, "name": "grafana", - "version": "5.3.1", + "version": "5.3.2", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git"