From 18e4271abdabaa111feabf656d3f1bc0f8bf0355 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 1 Jun 2018 10:34:57 +0200 Subject: [PATCH 001/300] added span with folder title that is shown for recently and starred, created a new class for folder title --- public/app/core/components/search/search_results.html | 2 +- public/sass/components/_search.scss | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/public/app/core/components/search/search_results.html b/public/app/core/components/search/search_results.html index 7435f8d0b7e..9f266ed3a6b 100644 --- a/public/app/core/components/search/search_results.html +++ b/public/app/core/components/search/search_results.html @@ -33,7 +33,7 @@ -
{{::item.title}}
+
{{::item.title}} {{::item.folderTitle}}
diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 8338a5d72ae..b00168505fa 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -208,6 +208,12 @@ color: $list-item-link-color; } +.search-item__body-folder-title { + color: $text-color-weak; + font-style: italic; + padding-left: 0.25rem; +} + .search-item__icon { padding: 5px; flex: 0 0 auto; From 83a73327cfb42ed5a3bea73497a5b4c7303a020e Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 1 Jun 2018 15:16:22 +0200 Subject: [PATCH 002/300] removed italic --- public/sass/components/_search.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index b00168505fa..3b6c1fbcce6 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -210,7 +210,6 @@ .search-item__body-folder-title { color: $text-color-weak; - font-style: italic; padding-left: 0.25rem; } From 8419cc05531a8db0bd3d3ce0a809096189ab3f33 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Mon, 4 Jun 2018 13:32:19 +0200 Subject: [PATCH 003/300] made folder text smaller --- public/sass/components/_search.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 3b6c1fbcce6..e2e3336db05 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -211,6 +211,7 @@ .search-item__body-folder-title { color: $text-color-weak; padding-left: 0.25rem; + font-size: $font-size-xs; } .search-item__icon { From c4308fedea8ee48973d39284e60562db1228fe6b Mon Sep 17 00:00:00 2001 From: Josh Dadak Date: Wed, 25 Jul 2018 14:02:36 +0100 Subject: [PATCH 004/300] Update Configuration.md Perhaps not worded as best it could be, however it would be good to include some information here about the importance of having your Grafana SERVER_ROOT_URL being the same URL listed in your Return URLs in Azure Application. Otherwise Azure Active Directory Auth will not work correctly resulting in an error page being displayed. --- docs/sources/installation/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 2a799b044b3..8eee32bd616 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -629,7 +629,7 @@ allowed_organizations = team_ids = allowed_organizations = ``` - +Note: It's important to ensure that the SERVER_ROOT_URL in Grafana is set in your Azure Application Return URLs
## [auth.basic] From 87745e6e447f0f4acfd01d9d0984a03477c88c76 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Fri, 10 Aug 2018 16:41:21 +0200 Subject: [PATCH 005/300] Explore: label selector for logging - query all available label keys for logs - query all values for each key - build cascader options with label values by key - lots of temporarily added conditions to reuse the promquery field --- public/app/containers/Explore/Explore.tsx | 1 + .../app/containers/Explore/PromQueryField.tsx | 82 +++++++++++++++++-- public/app/containers/Explore/QueryRows.tsx | 3 +- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/public/app/containers/Explore/Explore.tsx b/public/app/containers/Explore/Explore.tsx index 9620ac4f91b..bd52cd5ba05 100644 --- a/public/app/containers/Explore/Explore.tsx +++ b/public/app/containers/Explore/Explore.tsx @@ -564,6 +564,7 @@ export class Explore extends React.Component { onClickHintFix={this.onModifyQueries} onExecuteQuery={this.onSubmit} onRemoveQueryRow={this.onRemoveQueryRow} + supportsLogs={supportsLogs} />
{supportsGraph ? ( diff --git a/public/app/containers/Explore/PromQueryField.tsx b/public/app/containers/Explore/PromQueryField.tsx index 1b3ff33971d..ee9496fb024 100644 --- a/public/app/containers/Explore/PromQueryField.tsx +++ b/public/app/containers/Explore/PromQueryField.tsx @@ -137,12 +137,14 @@ interface PromQueryFieldProps { onQueryChange?: (value: string, override?: boolean) => void; portalPrefix?: string; request?: (url: string) => any; + supportsLogs?: boolean; // To be removed after Logging gets its own query field } interface PromQueryFieldState { histogramMetrics: string[]; labelKeys: { [index: string]: string[] }; // metric -> [labelKey,...] labelValues: { [index: string]: { [index: string]: string[] } }; // metric -> labelKey -> [labelValue,...] + logLabelOptions: any[]; metrics: string[]; metricsByPrefix: CascaderOption[]; } @@ -171,16 +173,41 @@ class PromQueryField extends React.Component { + let query; + if (selectedOptions.length === 1) { + if (selectedOptions[0].children.length === 0) { + query = selectedOptions[0].value; + } else { + // Ignore click on group + return; + } + } else { + const key = selectedOptions[0].value; + const value = selectedOptions[1].value; + query = `{${key}="${value}"}`; + } + this.onChangeQuery(query, true); + }; + onChangeMetrics = (values: string[], selectedOptions: CascaderOption[]) => { let query; if (selectedOptions.length === 1) { @@ -380,7 +407,8 @@ class PromQueryField extends React.Component this.fetchLabelValues(key))); @@ -409,6 +437,38 @@ class PromQueryField extends React.Component ({ label: value, value })), + }); + } + const labelValues = { [EMPTY_SELECTOR]: labelValuesByKey }; + this.setState({ labelKeys: labelKeysBySelector, labelValues, logLabelOptions }); + } catch (e) { + console.error(e); + } + } + async fetchLabelValues(key: string) { const url = `/api/v1/label/${key}/values`; try { @@ -463,8 +523,8 @@ class PromQueryField extends React.Component ({ label: hm, value: hm })); const metricsOptions = [ { label: 'Histograms', value: HISTOGRAM_GROUP, children: histogramOptions }, @@ -474,9 +534,15 @@ class PromQueryField extends React.Component
- - - + {supportsLogs ? ( + + + + ) : ( + + + + )}
diff --git a/public/app/containers/Explore/QueryRows.tsx b/public/app/containers/Explore/QueryRows.tsx index a7d91d59033..51adfa81c68 100644 --- a/public/app/containers/Explore/QueryRows.tsx +++ b/public/app/containers/Explore/QueryRows.tsx @@ -44,7 +44,7 @@ class QueryRow extends PureComponent { }; render() { - const { edited, history, query, queryError, queryHint, request } = this.props; + const { edited, history, query, queryError, queryHint, request, supportsLogs } = this.props; return (
@@ -58,6 +58,7 @@ class QueryRow extends PureComponent { onPressEnter={this.onPressEnter} onQueryChange={this.onChangeQuery} request={request} + supportsLogs={supportsLogs} />
From 953bdc4dc063c85ac00e0e8536f1565e1c236144 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 11 Sep 2018 14:53:38 +0200 Subject: [PATCH 006/300] put folder name under dashboard name, tweaked aliginments in search results --- public/app/core/components/search/search_results.html | 3 ++- public/sass/components/_search.scss | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/app/core/components/search/search_results.html b/public/app/core/components/search/search_results.html index 9f266ed3a6b..45258ded652 100644 --- a/public/app/core/components/search/search_results.html +++ b/public/app/core/components/search/search_results.html @@ -33,7 +33,8 @@ -
{{::item.title}} {{::item.folderTitle}}
+
{{::item.title}}
+ {{::item.folderTitle}}
diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 1589cc1e52c..b1211bcbdee 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -210,18 +210,20 @@ .search-item__body-title { color: $list-item-link-color; + line-height: 14px; } .search-item__body-folder-title { color: $text-color-weak; - padding-left: 0.25rem; font-size: $font-size-xs; + line-height: 11px; } .search-item__icon { padding: 5px; flex: 0 0 auto; font-size: 19px; + line-height: 22px; padding: 5px 2px 5px 10px; } From f0e905f3c9993135603b74bb6de3b9b41e31e404 Mon Sep 17 00:00:00 2001 From: Dan Doyle Date: Wed, 12 Sep 2018 13:17:15 +0000 Subject: [PATCH 007/300] First pass at a text based template var, getting feedback from devs --- .../features/dashboard/submenu/submenu.html | 3 +- public/app/features/templating/all.ts | 2 + .../features/templating/partials/editor.html | 8 +++ .../app/features/templating/text_variable.ts | 58 +++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 public/app/features/templating/text_variable.ts diff --git a/public/app/features/dashboard/submenu/submenu.html b/public/app/features/dashboard/submenu/submenu.html index f240a86efba..9d3332b06e2 100644 --- a/public/app/features/dashboard/submenu/submenu.html +++ b/public/app/features/dashboard/submenu/submenu.html @@ -4,7 +4,8 @@ - + +
diff --git a/public/app/features/templating/all.ts b/public/app/features/templating/all.ts index 16465740642..424494f66b2 100644 --- a/public/app/features/templating/all.ts +++ b/public/app/features/templating/all.ts @@ -9,6 +9,7 @@ import { DatasourceVariable } from './datasource_variable'; import { CustomVariable } from './custom_variable'; import { ConstantVariable } from './constant_variable'; import { AdhocVariable } from './adhoc_variable'; +import { TextVariable } from './text_variable'; coreModule.factory('templateSrv', () => { return templateSrv; @@ -22,4 +23,5 @@ export { CustomVariable, ConstantVariable, AdhocVariable, + TextVariable }; diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index 0d8b0ace327..ed8398738da 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -155,6 +155,14 @@
+
+
Text options
+
+ Value + +
+
+
Query Options
diff --git a/public/app/features/templating/text_variable.ts b/public/app/features/templating/text_variable.ts new file mode 100644 index 00000000000..3459b99f602 --- /dev/null +++ b/public/app/features/templating/text_variable.ts @@ -0,0 +1,58 @@ +import { Variable, assignModelProperties, variableTypes } from './variable'; + +export class TextVariable implements Variable { + query: string; + current: any; + options: any[]; + skipUrlSync: boolean; + + defaults = { + type: 'text', + name: '', + hide: 2, + label: '', + query: '', + current: {}, + options: [], + skipUrlSync: false, + }; + + /** @ngInject */ + constructor(private model, private variableSrv) { + assignModelProperties(this, model, this.defaults); + } + + getSaveModel() { + assignModelProperties(this.model, this, this.defaults); + return this.model; + } + + setValue(option) { + this.variableSrv.setOptionAsCurrent(this, option); + } + + updateOptions() { + this.options = [{ text: this.query.trim(), value: this.query.trim() }]; + this.current = this.options[0]; + return Promise.resolve(); + } + + dependsOn(variable) { + return false; + } + + setValueFromUrl(urlValue) { + this.query = urlValue; + return this.variableSrv.setOptionFromUrl(this, urlValue); + } + + getValueForUrl() { + return this.current.value; + } +} + +variableTypes['text'] = { + name: 'Text', + ctor: TextVariable, + description: 'Define a textbox variable, where users can enter any arbitrary string', +}; From f0f19e0c0334d51ce264d3d151b9d3e4a87a4f9a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 4 Sep 2018 13:21:02 +0200 Subject: [PATCH 008/300] Adds stackdriver frontend skeleton --- .../datasource/stackdriver/config_ctrl.ts | 11 +++ .../datasource/stackdriver/datasource.ts | 13 +++ .../plugins/datasource/stackdriver/module.ts | 14 ++++ .../partials/annotations.editor.html | 13 +++ .../stackdriver/partials/config.html | 6 ++ .../stackdriver/partials/query.editor.html | 81 +++++++++++++++++++ .../datasource/stackdriver/plugin.json | 16 ++++ .../datasource/stackdriver/query_ctrl.ts | 11 +++ 8 files changed, 165 insertions(+) create mode 100644 public/app/plugins/datasource/stackdriver/config_ctrl.ts create mode 100644 public/app/plugins/datasource/stackdriver/datasource.ts create mode 100644 public/app/plugins/datasource/stackdriver/module.ts create mode 100644 public/app/plugins/datasource/stackdriver/partials/annotations.editor.html create mode 100644 public/app/plugins/datasource/stackdriver/partials/config.html create mode 100755 public/app/plugins/datasource/stackdriver/partials/query.editor.html create mode 100644 public/app/plugins/datasource/stackdriver/plugin.json create mode 100644 public/app/plugins/datasource/stackdriver/query_ctrl.ts diff --git a/public/app/plugins/datasource/stackdriver/config_ctrl.ts b/public/app/plugins/datasource/stackdriver/config_ctrl.ts new file mode 100644 index 00000000000..405d91833cd --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/config_ctrl.ts @@ -0,0 +1,11 @@ +export class StackdriverConfigCtrl { + static templateUrl = 'public/app/plugins/datasource/stackdriver/partials/config.html'; + datasourceSrv: any; + current: any; + + /** @ngInject */ + constructor($scope, datasourceSrv) { + this.datasourceSrv = datasourceSrv; + this.current.jsonData = this.current.jsonData || {}; + } +} diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts new file mode 100644 index 00000000000..1bb2d4721a8 --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -0,0 +1,13 @@ +/** @ngInject */ +export function StackdriverDatasource(this: any, instanceSettings, $q, backendSrv, templateSrv) { + // this.basicAuth = instanceSettings.basicAuth; + // this.url = instanceSettings.url; + // this.name = instanceSettings.name; + // this.graphiteVersion = instanceSettings.jsonData.graphiteVersion || '0.9'; + // this.supportsTags = supportsTags(this.graphiteVersion); + // this.cacheTimeout = instanceSettings.cacheTimeout; + // this.withCredentials = instanceSettings.withCredentials; + // this.render_method = instanceSettings.render_method || 'POST'; + // this.funcDefs = null; + // this.funcDefsPromise = null; +} diff --git a/public/app/plugins/datasource/stackdriver/module.ts b/public/app/plugins/datasource/stackdriver/module.ts new file mode 100644 index 00000000000..52d3aa58453 --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/module.ts @@ -0,0 +1,14 @@ +// import { StackdriverDatasource } from './datasource'; +// import { StackdriverQueryCtrl } from './query_ctrl'; +import { StackdriverConfigCtrl } from './config_ctrl'; + +// class AnnotationsQueryCtrl { +// static templateUrl = 'partials/annotations.editor.html'; +// } + +export { + // StackdriverDatasource as Datasource, + // StackdriverQueryCtrl as QueryCtrl, + StackdriverConfigCtrl as ConfigCtrl, + // AnnotationsQueryCtrl, +}; diff --git a/public/app/plugins/datasource/stackdriver/partials/annotations.editor.html b/public/app/plugins/datasource/stackdriver/partials/annotations.editor.html new file mode 100644 index 00000000000..9d228b8e4f9 --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/partials/annotations.editor.html @@ -0,0 +1,13 @@ +
+
+ Graphite query + +
+ +
Or
+ +
+ Graphite events tags + +
+
diff --git a/public/app/plugins/datasource/stackdriver/partials/config.html b/public/app/plugins/datasource/stackdriver/partials/config.html new file mode 100644 index 00000000000..c0af41fb891 --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/partials/config.html @@ -0,0 +1,6 @@ + + + +

Hello Stackdriver

diff --git a/public/app/plugins/datasource/stackdriver/partials/query.editor.html b/public/app/plugins/datasource/stackdriver/partials/query.editor.html new file mode 100755 index 00000000000..51c25100c1e --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/partials/query.editor.html @@ -0,0 +1,81 @@ + + +
+ +
+ +
+
+
+ +
+ +
+ + + + +
+ + + + + +
+ +
+ +
+
+
+
+ +
+
+ +
+ +
+ +
+ + + +
+
+
+
+
+ +
diff --git a/public/app/plugins/datasource/stackdriver/plugin.json b/public/app/plugins/datasource/stackdriver/plugin.json new file mode 100644 index 00000000000..c2d04eb9717 --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "Stackdriver", + "type": "datasource", + "id": "stackdriver", + "metrics": true, + "alerting": false, + "annotations": false, + "queryOptions": { + "maxDataPoints": true, + "cacheTimeout": true + }, + "info": { + "description": "Data Source for Stackdriver", + "version": "1.0.0" + } +} \ No newline at end of file diff --git a/public/app/plugins/datasource/stackdriver/query_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_ctrl.ts new file mode 100644 index 00000000000..94389237eb2 --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/query_ctrl.ts @@ -0,0 +1,11 @@ +import './add_graphite_func'; +import './func_editor'; +import { QueryCtrl } from 'app/plugins/sdk'; + +export class StackdriverQueryCtrl extends QueryCtrl { + static templateUrl = 'partials/query.editor.html'; + /** @ngInject */ + constructor($scope, $injector) { + super($scope, $injector); + } +} From 9ee61b660677f4eb2245264b694d4bbdf4ac8858 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 4 Sep 2018 13:21:58 +0200 Subject: [PATCH 009/300] Add stackdriver backend skeleton --- pkg/cmd/grafana-server/main.go | 1 + pkg/tsdb/stackdriver/stackdriver.go | 177 ++++++++++++++++++++++++++++ pkg/tsdb/stackdriver/types.go | 8 ++ 3 files changed, 186 insertions(+) create mode 100644 pkg/tsdb/stackdriver/stackdriver.go create mode 100644 pkg/tsdb/stackdriver/types.go diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index f1e298671d7..c85270b64d9 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -29,6 +29,7 @@ import ( _ "github.com/grafana/grafana/pkg/tsdb/opentsdb" _ "github.com/grafana/grafana/pkg/tsdb/postgres" _ "github.com/grafana/grafana/pkg/tsdb/prometheus" + _ "github.com/grafana/grafana/pkg/tsdb/stackdriver" _ "github.com/grafana/grafana/pkg/tsdb/testdata" ) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go new file mode 100644 index 00000000000..d755ccf12e2 --- /dev/null +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -0,0 +1,177 @@ +package stackdriver + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "path" + "regexp" + "strings" + + "golang.org/x/net/context/ctxhttp" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/tsdb" + "github.com/opentracing/opentracing-go" +) + +type StackdriverExecutor struct { + HttpClient *http.Client +} + +func NewStackdriverExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) { + return &StackdriverExecutor{}, nil +} + +var glog = log.New("tsdb.stackdriver") + +func init() { + tsdb.RegisterTsdbQueryEndpoint("stackdriver", NewStackdriverExecutor) +} + +func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) { + result := &tsdb.Response{} + + from := "-" + formatTimeRange(tsdbQuery.TimeRange.From) + until := formatTimeRange(tsdbQuery.TimeRange.To) + var target string + + formData := url.Values{ + "from": []string{from}, + "until": []string{until}, + "format": []string{"json"}, + "maxDataPoints": []string{"500"}, + } + + for _, query := range tsdbQuery.Queries { + glog.Info("stackdriver", "query", query.Model) + if fullTarget, err := query.Model.Get("targetFull").String(); err == nil { + target = fixIntervalFormat(fullTarget) + } else { + target = fixIntervalFormat(query.Model.Get("target").MustString()) + } + } + + formData["target"] = []string{target} + + if setting.Env == setting.DEV { + glog.Debug("Graphite request", "params", formData) + } + + req, err := e.createRequest(dsInfo, formData) + if err != nil { + return nil, err + } + + httpClient, err := dsInfo.GetHttpClient() + if err != nil { + return nil, err + } + + span, ctx := opentracing.StartSpanFromContext(ctx, "graphite query") + span.SetTag("target", target) + span.SetTag("from", from) + span.SetTag("until", until) + span.SetTag("datasource_id", dsInfo.Id) + span.SetTag("org_id", dsInfo.OrgId) + + defer span.Finish() + + opentracing.GlobalTracer().Inject( + span.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(req.Header)) + + res, err := ctxhttp.Do(ctx, httpClient, req) + if err != nil { + return nil, err + } + + data, err := e.parseResponse(res) + if err != nil { + return nil, err + } + + result.Results = make(map[string]*tsdb.QueryResult) + queryRes := tsdb.NewQueryResult() + + for _, series := range data { + queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{ + Name: series.Target, + Points: series.DataPoints, + }) + + if setting.Env == setting.DEV { + glog.Debug("Graphite response", "target", series.Target, "datapoints", len(series.DataPoints)) + } + } + + result.Results["A"] = queryRes + return result, nil +} + +func (e *StackdriverExecutor) parseResponse(res *http.Response) ([]TargetResponseDTO, error) { + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + if err != nil { + return nil, err + } + + if res.StatusCode/100 != 2 { + glog.Info("Request failed", "status", res.Status, "body", string(body)) + return nil, fmt.Errorf("Request failed status: %v", res.Status) + } + + var data []TargetResponseDTO + err = json.Unmarshal(body, &data) + if err != nil { + glog.Info("Failed to unmarshal graphite response", "error", err, "status", res.Status, "body", string(body)) + return nil, err + } + + return data, nil +} + +func (e *StackdriverExecutor) createRequest(dsInfo *models.DataSource, data url.Values) (*http.Request, error) { + u, _ := url.Parse(dsInfo.Url) + u.Path = path.Join(u.Path, "render") + + req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(data.Encode())) + if err != nil { + glog.Info("Failed to create request", "error", err) + return nil, fmt.Errorf("Failed to create request. error: %v", err) + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if dsInfo.BasicAuth { + req.SetBasicAuth(dsInfo.BasicAuthUser, dsInfo.BasicAuthPassword) + } + + return req, err +} + +func formatTimeRange(input string) string { + if input == "now" { + return input + } + return strings.Replace(strings.Replace(strings.Replace(input, "now", "", -1), "m", "min", -1), "M", "mon", -1) +} + +func fixIntervalFormat(target string) string { + rMinute := regexp.MustCompile(`'(\d+)m'`) + rMin := regexp.MustCompile("m") + target = rMinute.ReplaceAllStringFunc(target, func(m string) string { + return rMin.ReplaceAllString(m, "min") + }) + rMonth := regexp.MustCompile(`'(\d+)M'`) + rMon := regexp.MustCompile("M") + target = rMonth.ReplaceAllStringFunc(target, func(M string) string { + return rMon.ReplaceAllString(M, "mon") + }) + return target +} diff --git a/pkg/tsdb/stackdriver/types.go b/pkg/tsdb/stackdriver/types.go new file mode 100644 index 00000000000..3fb4cdc43f9 --- /dev/null +++ b/pkg/tsdb/stackdriver/types.go @@ -0,0 +1,8 @@ +package stackdriver + +import "github.com/grafana/grafana/pkg/tsdb" + +type TargetResponseDTO struct { + Target string `json:"target"` + DataPoints tsdb.TimeSeriesPoints `json:"datapoints"` +} From 834d06c35b6c0e5823714b881969ebbf0079de1c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 4 Sep 2018 13:23:14 +0200 Subject: [PATCH 010/300] Build new stackdriver frontend script --- public/app/features/plugins/built_in_plugins.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 2c5bf459eda..e29e1709ccf 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -11,6 +11,7 @@ import * as postgresPlugin from 'app/plugins/datasource/postgres/module'; import * as prometheusPlugin from 'app/plugins/datasource/prometheus/module'; import * as mssqlPlugin from 'app/plugins/datasource/mssql/module'; import * as testDataDSPlugin from 'app/plugins/datasource/testdata/module'; +import * as stackdriverPlugin from 'app/plugins/datasource/stackdriver/module'; import * as textPanel from 'app/plugins/panel/text/module'; import * as graphPanel from 'app/plugins/panel/graph/module'; @@ -36,6 +37,7 @@ const builtInPlugins = { 'app/plugins/datasource/mssql/module': mssqlPlugin, 'app/plugins/datasource/prometheus/module': prometheusPlugin, 'app/plugins/datasource/testdata/module': testDataDSPlugin, + 'app/plugins/datasource/stackdriver/module': stackdriverPlugin, 'app/plugins/panel/text/module': textPanel, 'app/plugins/panel/graph/module': graphPanel, From ef3beb1f0e169df967c2d8eea0fde9cd56c24394 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 4 Sep 2018 13:25:22 +0200 Subject: [PATCH 011/300] Adds poc code for retrieving google auth accesstoken --- pkg/api/pluginproxy/ds_proxy.go | 96 +++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go index fb2cab9b9b1..54c272dd5d6 100644 --- a/pkg/api/pluginproxy/ds_proxy.go +++ b/pkg/api/pluginproxy/ds_proxy.go @@ -2,7 +2,7 @@ package pluginproxy import ( "bytes" - "encoding/json" + "context" "errors" "fmt" "io/ioutil" @@ -22,6 +22,7 @@ import ( "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" + "golang.org/x/oauth2/jwt" ) var ( @@ -353,49 +354,64 @@ func (proxy *DataSourceProxy) applyRoute(req *http.Request) { } func (proxy *DataSourceProxy) getAccessToken(data templateData) (string, error) { - if cachedToken, found := tokenCache[proxy.getAccessTokenCacheKey()]; found { - if cachedToken.ExpiresOn.After(time.Now().Add(time.Second * 10)) { - logger.Info("Using token from cache") - return cachedToken.AccessToken, nil - } + conf := jwt.Config{ + Email: "raintank-production-stackdrive@raintank-production.iam.gserviceaccount.com", + PrivateKey: []byte("-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCb1u1Srw8ICWfX\nb+hh0qyRoWJzHkf4jxdpOjjqiaYqlipf3fCWoyNgHvQSmX2trYbb1Kg+8Nv9/iaz\n6el48MvOlRN6WbcMfhFoT8AKkdjYD3DM+vK3C3uXmWeVMHzQimjECFWsX4WRU4fj\nLJ44B3svFvShe3bRJpt2e5LdfjcEQER7Bvte/zQi8v1jloHmcptyz8wb9NddVHs9\nINwFbrSaUiQnoJTDSIYEMiKDTvZHRenmLz/RexjG5RXbdA+I9ZB5EASoEbG+7Ssc\nsn+Bhu/J29s/+NVM5sYgcjOm54NxCYFwYlApbroa0+KcDDCs4DzgzA41sOcvBWIX\nOZuQRD6BAgMBAAECggEAGXUo28MBP5zZu9XqLmDOFBQ7Evc1ZqNpfaUnOxk1beuO\nDI8jCFiqJL+pu4gbgc3BJBQ/T9jk9z8Xb3iczUb45ExyHCCfyIinq1Sr2I4u0Ezl\nbnboQ4K6s+85fqOnICIcLzn1VO1d0nnEzxWw2xJNy0mCuQaESHJ4Hwjc2xYNQsJD\newfeAQh2bUw7R4xyIJieP5a0fQbZWAENbVKstyfd9NJNM+6wUmwXR4ALbLM31f1n\nExbHfUe8TLp892ZgeSL+C0C31xDqkqi/DfUOFpBt79Rr5p3++rDEe98NKrDmw6yz\nbprZHBslzx612md2L/ljKQROs3tHl6BvGfFtQMOjSQKBgQDXidmYq5FQwRiVkl4r\ny8WzNbflbdfMRxetaPwR7lPWgiyGgU54Y0xUAaCy1bbpmJkfRcxJuSBYpensV9x2\nXwAI8vzuJcmteVAeZ59YYJYOA+AdT8MQW7aCeUK5qgLgpO0dt4wCK4xERdhbQdvC\nMMCu3UfgeVNyo+EhTqM97VuHTwKBgQC5GB8PwM8H7NxaPZhTfy7S54OZHdhoUpTc\nZ+qWTSG6QgjHDzNaqZ+p6ehDCnO9EyuwpYHXcFIavlmSxszUPNy6f3TosvKcvm6q\n5CFzdt4fgev3cgGB+P1mT1gyi6UjntWuAj1fFvxh+o87kq9v4p6YVX+woll10CaH\n++O3QNlpLwKBgQCRV5qM0by26M8MJVwtSkaxdxrfsjdfv9zeibnY2Y5dSwB9Xvqs\nQcF5sHNNxMGIOeefZ/C/EgAW5yKbxg+bHqqmXjxi1sZtnS2CozuXW+Iz5zccbOnL\nwRyMVPrCujsggvaGIHxgBj+a1kJ0Hy/yfe+guwS6APZditbIIAACRWmADwKBgAuA\nHC32ZOaxKN/Sg+xsMpSYHe0dlZylxOoM6t573GSeRb1YjHBNqcX86pl/xMEyt7w6\nDF8+c1uGCDq+b2ugfHZ6BOGQfNKQYn/rvMhX0mVSxT6SrtVMizIYK/q4AoK8E7rE\nGNwXqYbM8qlY692fzwrYBR8Md1KCpGI+nF9+gAOxAoGAAwJO+jr44SRldSSsli0d\nDbGRmlMc093MebjwNsO2NGoF8uTRyYIzchP2l57PMPKFX/r5IcchjVh3wHWwtzMS\nhB8zfRDj7RFjW0H8U1Qf5k2ID3ACJtxnt+o744Lggqzf9puf4RKwwAjIwJy2lhbi\nyA+63fAHMAwG+k22IkBqcu4=\n-----END PRIVATE KEY-----\n"), + Scopes: []string{"https://www.googleapis.com/auth/monitoring.read"}, + TokenURL: "https://oauth2.googleapis.com/token", } - - urlInterpolated, err := interpolateString(proxy.route.TokenAuth.Url, data) + ctx := context.Background() + tokenSrc := conf.TokenSource(ctx) + // logger.Info("Accesstoken", tokenSrc.Token.AccessToken) + token, err := tokenSrc.Token() if err != nil { - return "", err + logger.Info("GetToken", "Error", err) } - - params := make(url.Values) - for key, value := range proxy.route.TokenAuth.Params { - interpolatedParam, err := interpolateString(value, data) - if err != nil { - return "", err - } - params.Add(key, interpolatedParam) - } - - getTokenReq, _ := http.NewRequest("POST", urlInterpolated, bytes.NewBufferString(params.Encode())) - getTokenReq.Header.Add("Content-Type", "application/x-www-form-urlencoded") - getTokenReq.Header.Add("Content-Length", strconv.Itoa(len(params.Encode()))) - - resp, err := client.Do(getTokenReq) - if err != nil { - return "", err - } - - defer resp.Body.Close() - - var token jwtToken - if err := json.NewDecoder(resp.Body).Decode(&token); err != nil { - return "", err - } - - expiresOnEpoch, _ := strconv.ParseInt(token.ExpiresOnString, 10, 64) - token.ExpiresOn = time.Unix(expiresOnEpoch, 0) - tokenCache[proxy.getAccessTokenCacheKey()] = &token - - logger.Info("Got new access token", "ExpiresOn", token.ExpiresOn) + logger.Info("GetToken", "Token", token.AccessToken) return token.AccessToken, nil + // if cachedToken, found := tokenCache[proxy.getAccessTokenCacheKey()]; found { + // if cachedToken.ExpiresOn.After(time.Now().Add(time.Second * 10)) { + // logger.Info("Using token from cache") + // return cachedToken.AccessToken, nil + // } + // } + + // urlInterpolated, err := interpolateString(proxy.route.TokenAuth.Url, data) + // if err != nil { + // return "", err + // } + + // params := make(url.Values) + // for key, value := range proxy.route.TokenAuth.Params { + // interpolatedParam, err := interpolateString(value, data) + // if err != nil { + // return "", err + // } + // params.Add(key, interpolatedParam) + // } + + // getTokenReq, _ := http.NewRequest("POST", urlInterpolated, bytes.NewBufferString(params.Encode())) + // getTokenReq.Header.Add("Content-Type", "application/x-www-form-urlencoded") + // getTokenReq.Header.Add("Content-Length", strconv.Itoa(len(params.Encode()))) + + // resp, err := client.Do(getTokenReq) + // if err != nil { + // return "", err + // } + + // defer resp.Body.Close() + + // var token jwtToken + // if err := json.NewDecoder(resp.Body).Decode(&token); err != nil { + // return "", err + // } + + // expiresOnEpoch, _ := strconv.ParseInt(token.ExpiresOnString, 10, 64) + // token.ExpiresOn = time.Unix(expiresOnEpoch, 0) + // tokenCache[proxy.getAccessTokenCacheKey()] = &token + + // logger.Info("Got new access token", "ExpiresOn", token.ExpiresOn) + // return token.AccessToken, nil } func (proxy *DataSourceProxy) getAccessTokenCacheKey() string { From 4fa4c275372843e15028dcc6882733703a658af8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 4 Sep 2018 14:32:25 +0200 Subject: [PATCH 012/300] Upload: Fixing link function in directive --- public/app/features/dashboard/upload.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/features/dashboard/upload.ts b/public/app/features/dashboard/upload.ts index 974a0c35cd2..5ef329fc76e 100644 --- a/public/app/features/dashboard/upload.ts +++ b/public/app/features/dashboard/upload.ts @@ -1,7 +1,7 @@ import coreModule from 'app/core/core_module'; const template = ` - +