| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | package flux | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend" | 
					
						
							| 
									
										
										
										
											2021-05-19 23:53:41 +02:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/httpclient" | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/log" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/models" | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins" | 
					
						
							| 
									
										
										
										
											2020-08-17 08:32:26 +02:00
										 |  |  | 	influxdb2 "github.com/influxdata/influxdb-client-go/v2" | 
					
						
							|  |  |  | 	"github.com/influxdata/influxdb-client-go/v2/api" | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	glog log.Logger | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	glog = log.New("tsdb.influx_flux") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Query builds flux queries, executes them, and returns the results. | 
					
						
							| 
									
										
										
										
											2021-04-22 18:03:11 -07:00
										 |  |  | //nolint: staticcheck // plugins.DataQuery deprecated | 
					
						
							| 
									
										
										
										
											2021-05-19 23:53:41 +02:00
										 |  |  | func Query(ctx context.Context, httpClientProvider httpclient.Provider, dsInfo *models.DataSource, tsdbQuery plugins.DataQuery) ( | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | 	plugins.DataResponse, error) { | 
					
						
							|  |  |  | 	glog.Debug("Received a query", "query", tsdbQuery) | 
					
						
							|  |  |  | 	tRes := plugins.DataResponse{ | 
					
						
							|  |  |  | 		Results: make(map[string]plugins.DataQueryResult), | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-19 23:53:41 +02:00
										 |  |  | 	r, err := runnerFromDataSource(httpClientProvider, dsInfo) | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | 		return plugins.DataResponse{}, err | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-19 09:23:11 +02:00
										 |  |  | 	defer r.client.Close() | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, query := range tsdbQuery.Queries { | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | 		qm, err := getQueryModelTSDB(query, *tsdbQuery.TimeRange, dsInfo) | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | 			tRes.Results[query.RefID] = plugins.DataQueryResult{Error: err} | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-10 15:23:19 +01:00
										 |  |  | 		// If the default changes also update labels/placeholder in config page. | 
					
						
							|  |  |  | 		maxSeries := dsInfo.JsonData.Get("maxSeries").MustInt(1000) | 
					
						
							|  |  |  | 		res := executeQuery(ctx, *qm, r, maxSeries) | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | 		tRes.Results[query.RefID] = backendDataResponseToDataResponse(&res, query.RefID) | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return tRes, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 09:23:11 +02:00
										 |  |  | // runner is an influxdb2 Client with an attached org property and is used | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | // for running flux queries. | 
					
						
							| 
									
										
										
										
											2020-08-19 09:23:11 +02:00
										 |  |  | type runner struct { | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	client influxdb2.Client | 
					
						
							|  |  |  | 	org    string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is an interface to help testing | 
					
						
							|  |  |  | type queryRunner interface { | 
					
						
							| 
									
										
										
										
											2020-06-22 10:19:26 -07:00
										 |  |  | 	runQuery(ctx context.Context, q string) (*api.QueryTableResult, error) | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-13 17:50:53 +02:00
										 |  |  | // runQuery executes fluxQuery against the Runner's organization and returns a Flux typed result. | 
					
						
							| 
									
										
										
										
											2020-08-19 09:23:11 +02:00
										 |  |  | func (r *runner) runQuery(ctx context.Context, fluxQuery string) (*api.QueryTableResult, error) { | 
					
						
							| 
									
										
										
										
											2020-08-14 18:40:02 +02:00
										 |  |  | 	qa := r.client.QueryAPI(r.org) | 
					
						
							| 
									
										
										
										
											2020-08-13 17:50:53 +02:00
										 |  |  | 	return qa.Query(ctx, fluxQuery) | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 09:23:11 +02:00
										 |  |  | // runnerFromDataSource creates a runner from the datasource model (the datasource instance's configuration). | 
					
						
							| 
									
										
										
										
											2021-05-19 23:53:41 +02:00
										 |  |  | func runnerFromDataSource(httpClientProvider httpclient.Provider, dsInfo *models.DataSource) (*runner, error) { | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	org := dsInfo.JsonData.Get("organization").MustString("") | 
					
						
							|  |  |  | 	if org == "" { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("missing organization in datasource configuration") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	url := dsInfo.Url | 
					
						
							|  |  |  | 	if url == "" { | 
					
						
							| 
									
										
										
										
											2020-08-13 17:50:53 +02:00
										 |  |  | 		return nil, fmt.Errorf("missing URL from datasource configuration") | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	token, found := dsInfo.SecureJsonData.DecryptedValue("token") | 
					
						
							|  |  |  | 	if !found { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("token is missing from datasource configuration and is needed to use Flux") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-14 18:40:02 +02:00
										 |  |  | 	opts := influxdb2.DefaultOptions() | 
					
						
							| 
									
										
										
										
											2021-05-19 23:53:41 +02:00
										 |  |  | 	hc, err := dsInfo.GetHTTPClient(httpClientProvider) | 
					
						
							| 
									
										
										
										
											2020-08-14 18:40:02 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	opts.HTTPOptions().SetHTTPClient(hc) | 
					
						
							| 
									
										
										
										
											2020-08-19 09:23:11 +02:00
										 |  |  | 	return &runner{ | 
					
						
							| 
									
										
										
										
											2020-08-14 18:40:02 +02:00
										 |  |  | 		client: influxdb2.NewClientWithOptions(url, token, opts), | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 		org:    org, | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | // backendDataResponseToDataResponse takes the SDK's style response and changes it into a | 
					
						
							|  |  |  | // plugins.DataQueryResult. This is a wrapper so less of existing code needs to be changed. This should | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | // be able to be removed in the near future https://github.com/grafana/grafana/pull/25472. | 
					
						
							| 
									
										
										
										
											2021-04-22 18:03:11 -07:00
										 |  |  | //nolint: staticcheck // plugins.DataQueryResult deprecated | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | func backendDataResponseToDataResponse(dr *backend.DataResponse, refID string) plugins.DataQueryResult { | 
					
						
							|  |  |  | 	qr := plugins.DataQueryResult{ | 
					
						
							|  |  |  | 		RefID: refID, | 
					
						
							|  |  |  | 		Error: dr.Error, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	if dr.Frames != nil { | 
					
						
							| 
									
										
										
										
											2021-03-08 07:02:49 +01:00
										 |  |  | 		qr.Dataframes = plugins.NewDecodedDataFrames(dr.Frames) | 
					
						
							| 
									
										
										
										
											2020-06-10 12:26:24 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return qr | 
					
						
							|  |  |  | } |