Datasource: Shared HTTP client provider for core backend data sources and any data source using the data source proxy (#33439)

Uses new httpclient package from grafana-plugin-sdk-go introduced 
via grafana/grafana-plugin-sdk-go#328. 
Replaces the GetHTTPClient, GetTransport, GetTLSConfig methods defined 
on DataSource model.
Longer-term the goal is to migrate core HTTP backend data sources to use the 
SDK contracts and using httpclient.Provider for creating HTTP clients and such.

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Marcus Efraimsson
2021-05-19 23:53:41 +02:00
committed by GitHub
co-authored by Arve Knudsen
parent 7a83d1f9ff
commit 348e76fc8e
46 changed files with 1082 additions and 467 deletions
@@ -241,8 +241,6 @@ func (e *ApplicationInsightsDatasource) createRequest(ctx context.Context, dsInf
return nil, errutil.Wrap("Failed to create request", err)
}
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
pluginproxy.ApplyRoute(ctx, req, proxyPass, appInsightsRoute, dsInfo, e.cfg)
return req, nil
@@ -221,7 +221,6 @@ func (e *AzureLogAnalyticsDatasource) createRequest(ctx context.Context, dsInfo
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
// find plugin
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
@@ -254,7 +254,6 @@ func (e *AzureMonitorDatasource) createRequest(ctx context.Context, dsInfo *mode
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
pluginproxy.ApplyRoute(ctx, req, proxyPass, azureMonitorRoute, dsInfo, e.cfg)
+5 -3
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"regexp"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -29,8 +30,9 @@ func init() {
}
type Service struct {
PluginManager plugins.Manager `inject:""`
Cfg *setting.Cfg `inject:""`
PluginManager plugins.Manager `inject:""`
HTTPClientProvider httpclient.Provider `inject:""`
Cfg *setting.Cfg `inject:""`
}
func (s *Service) Init() error {
@@ -48,7 +50,7 @@ type AzureMonitorExecutor struct {
// NewAzureMonitorExecutor initializes a http client
//nolint: staticcheck // plugins.DataPlugin deprecated
func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
httpClient, err := dsInfo.GetHttpClient()
httpClient, err := dsInfo.GetHTTPClient(s.HTTPClientProvider)
if err != nil {
return nil, err
}
@@ -215,8 +215,6 @@ func (e *InsightsAnalyticsDatasource) createRequest(ctx context.Context, dsInfo
return nil, errutil.Wrap("Failed to create request", err)
}
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
pluginproxy.ApplyRoute(ctx, req, proxyPass, appInsightsRoute, dsInfo, e.cfg)
return req, nil
+5 -4
View File
@@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
@@ -75,8 +76,9 @@ func init() {
}
type Service struct {
PluginManager plugins.Manager `inject:""`
Cfg *setting.Cfg `inject:""`
PluginManager plugins.Manager `inject:""`
HTTPClientProvider httpclient.Provider `inject:""`
Cfg *setting.Cfg `inject:""`
}
func (s *Service) Init() error {
@@ -94,7 +96,7 @@ type Executor struct {
// NewExecutor returns an Executor.
//nolint: staticcheck // plugins.DataPlugin deprecated
func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
httpClient, err := dsInfo.GetHttpClient()
httpClient, err := dsInfo.GetHTTPClient(s.HTTPClientProvider)
if err != nil {
return nil, err
}
@@ -540,7 +542,6 @@ func (e *Executor) createRequest(ctx context.Context, dsInfo *models.DataSource,
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
// find plugin
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
+13 -22
View File
@@ -15,6 +15,7 @@ import (
"github.com/Masterminds/semver"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/tsdb/interval"
@@ -29,8 +30,8 @@ var (
clientLog = log.New(loggerName)
)
var newDatasourceHttpClient = func(ds *models.DataSource) (*http.Client, error) {
return ds.GetHttpClient()
var newDatasourceHttpClient = func(httpClientProvider httpclient.Provider, ds *models.DataSource) (*http.Client, error) {
return ds.GetHTTPClient(httpClientProvider)
}
// Client represents a client which can interact with elasticsearch api
@@ -72,7 +73,7 @@ func coerceVersion(v *simplejson.Json) (*semver.Version, error) {
}
// NewClient creates a new elasticsearch client
var NewClient = func(ctx context.Context, ds *models.DataSource, timeRange plugins.DataTimeRange) (Client, error) {
var NewClient = func(ctx context.Context, httpClientProvider httpclient.Provider, ds *models.DataSource, timeRange plugins.DataTimeRange) (Client, error) {
version, err := coerceVersion(ds.JsonData.Get("esVersion"))
if err != nil {
@@ -108,13 +109,14 @@ var NewClient = func(ctx context.Context, ds *models.DataSource, timeRange plugi
}
type baseClientImpl struct {
ctx context.Context
ds *models.DataSource
version *semver.Version
timeField string
indices []string
timeRange plugins.DataTimeRange
debugEnabled bool
ctx context.Context
httpClientProvider httpclient.Provider
ds *models.DataSource
version *semver.Version
timeField string
indices []string
timeRange plugins.DataTimeRange
debugEnabled bool
}
func (c *baseClientImpl) GetVersion() *semver.Version {
@@ -208,20 +210,9 @@ func (c *baseClientImpl) executeRequest(method, uriPath, uriQuery string, body [
}
}
req.Header.Set("User-Agent", "Grafana")
req.Header.Set("Content-Type", "application/x-ndjson")
if c.ds.BasicAuth {
clientLog.Debug("Request configured to use basic authentication")
req.SetBasicAuth(c.ds.BasicAuthUser, c.ds.DecryptedBasicAuthPassword())
}
if !c.ds.BasicAuth && c.ds.User != "" {
clientLog.Debug("Request configured to use basic authentication")
req.SetBasicAuth(c.ds.User, c.ds.DecryptedPassword())
}
httpClient, err := newDatasourceHttpClient(c.ds)
httpClient, err := newDatasourceHttpClient(c.httpClientProvider, c.ds)
if err != nil {
return nil, err
}
+13 -12
View File
@@ -11,6 +11,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/tsdb/interval"
@@ -24,7 +25,7 @@ func TestNewClient(t *testing.T) {
JsonData: simplejson.NewFromAny(make(map[string]interface{})),
}
_, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
})
@@ -35,7 +36,7 @@ func TestNewClient(t *testing.T) {
}),
}
_, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
})
@@ -48,7 +49,7 @@ func TestNewClient(t *testing.T) {
}),
}
_, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
})
@@ -60,7 +61,7 @@ func TestNewClient(t *testing.T) {
}),
}
c, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.NoError(t, err)
assert.Equal(t, "2.0.0", c.GetVersion().String())
})
@@ -73,7 +74,7 @@ func TestNewClient(t *testing.T) {
}),
}
c, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.NoError(t, err)
assert.Equal(t, "5.0.0", c.GetVersion().String())
})
@@ -86,7 +87,7 @@ func TestNewClient(t *testing.T) {
}),
}
c, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.NoError(t, err)
assert.Equal(t, "5.6.0", c.GetVersion().String())
})
@@ -99,7 +100,7 @@ func TestNewClient(t *testing.T) {
}),
}
c, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.NoError(t, err)
assert.Equal(t, "6.0.0", c.GetVersion().String())
})
@@ -112,7 +113,7 @@ func TestNewClient(t *testing.T) {
}),
}
c, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.NoError(t, err)
assert.Equal(t, "7.0.0", c.GetVersion().String())
})
@@ -127,7 +128,7 @@ func TestNewClient(t *testing.T) {
}),
}
c, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.NoError(t, err)
assert.Equal(t, version, c.GetVersion().String())
})
@@ -141,7 +142,7 @@ func TestNewClient(t *testing.T) {
}),
}
_, err := NewClient(context.Background(), ds, plugins.DataTimeRange{})
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
})
}
@@ -408,14 +409,14 @@ func httpClientScenario(t *testing.T, desc string, ds *models.DataSource, fn sce
toStr := fmt.Sprintf("%d", to.UnixNano()/int64(time.Millisecond))
timeRange := plugins.NewDataTimeRange(fromStr, toStr)
c, err := NewClient(context.Background(), ds, timeRange)
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, timeRange)
require.NoError(t, err)
require.NotNil(t, c)
sc.client = c
currentNewDatasourceHTTPClient := newDatasourceHttpClient
newDatasourceHttpClient = func(ds *models.DataSource) (*http.Client, error) {
newDatasourceHttpClient = func(httpClientProvider httpclient.Provider, ds *models.DataSource) (*http.Client, error) {
return ts.Client(), nil
}
+13 -8
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
@@ -12,15 +13,20 @@ import (
// ElasticsearchExecutor represents a handler for handling elasticsearch datasource request
type Executor struct {
httpClientProvider httpclient.Provider
intervalCalculator interval.Calculator
}
// NewExecutor creates a new Executor.
//nolint: staticcheck // plugins.DataPlugin deprecated
func NewExecutor(*models.DataSource) (plugins.DataPlugin, error) {
return &Executor{
intervalCalculator: interval.NewCalculator(),
}, nil
// New creates a new Executor func.
// nolint:staticcheck // plugins.DataPlugin deprecated
func New(httpClientProvider httpclient.Provider) func(*models.DataSource) (plugins.DataPlugin, error) {
// nolint:staticcheck // plugins.DataPlugin deprecated
return func(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
return &Executor{
httpClientProvider: httpClientProvider,
intervalCalculator: interval.NewCalculator(),
}, nil
}
}
// Query handles an elasticsearch datasource request
@@ -31,8 +37,7 @@ func (e *Executor) DataQuery(ctx context.Context, dsInfo *models.DataSource,
return plugins.DataResponse{}, fmt.Errorf("query contains no queries")
}
client, err := es.NewClient(ctx, dsInfo, *tsdbQuery.TimeRange)
client, err := es.NewClient(ctx, e.httpClientProvider, dsInfo, *tsdbQuery.TimeRange)
if err != nil {
return plugins.DataResponse{}, err
}
+11 -5
View File
@@ -15,6 +15,7 @@ import (
"golang.org/x/net/context/ctxhttp"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -23,12 +24,17 @@ import (
)
type GraphiteExecutor struct {
HttpClient *http.Client
httpClientProvider httpclient.Provider
}
//nolint: staticcheck // plugins.DataPlugin deprecated
func NewExecutor(*models.DataSource) (plugins.DataPlugin, error) {
return &GraphiteExecutor{}, nil
// nolint:staticcheck // plugins.DataPlugin deprecated
func New(httpClientProvider httpclient.Provider) func(*models.DataSource) (plugins.DataPlugin, error) {
// nolint:staticcheck // plugins.DataPlugin deprecated
return func(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
return &GraphiteExecutor{
httpClientProvider: httpClientProvider,
}, nil
}
}
var glog = log.New("tsdb.graphite")
@@ -91,7 +97,7 @@ func (e *GraphiteExecutor) DataQuery(ctx context.Context, dsInfo *models.DataSou
return plugins.DataResponse{}, err
}
httpClient, err := dsInfo.GetHttpClient()
httpClient, err := dsInfo.GetHTTPClient(e.httpClientProvider)
if err != nil {
return plugins.DataResponse{}, err
}
+2 -1
View File
@@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/experimental"
"github.com/grafana/grafana/pkg/components/securejsondata"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -226,7 +227,7 @@ func TestRealQuery(t *testing.T) {
}),
}
runner, err := runnerFromDataSource(dsInfo)
runner, err := runnerFromDataSource(httpclient.NewProvider(), dsInfo)
require.NoError(t, err)
dr := executeQuery(context.Background(), queryModel{
+5 -4
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -22,13 +23,13 @@ func init() {
// Query builds flux queries, executes them, and returns the results.
//nolint: staticcheck // plugins.DataQuery deprecated
func Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery plugins.DataQuery) (
func Query(ctx context.Context, httpClientProvider httpclient.Provider, dsInfo *models.DataSource, tsdbQuery plugins.DataQuery) (
plugins.DataResponse, error) {
glog.Debug("Received a query", "query", tsdbQuery)
tRes := plugins.DataResponse{
Results: make(map[string]plugins.DataQueryResult),
}
r, err := runnerFromDataSource(dsInfo)
r, err := runnerFromDataSource(httpClientProvider, dsInfo)
if err != nil {
return plugins.DataResponse{}, err
}
@@ -69,7 +70,7 @@ func (r *runner) runQuery(ctx context.Context, fluxQuery string) (*api.QueryTabl
}
// runnerFromDataSource creates a runner from the datasource model (the datasource instance's configuration).
func runnerFromDataSource(dsInfo *models.DataSource) (*runner, error) {
func runnerFromDataSource(httpClientProvider httpclient.Provider, dsInfo *models.DataSource) (*runner, error) {
org := dsInfo.JsonData.Get("organization").MustString("")
if org == "" {
return nil, fmt.Errorf("missing organization in datasource configuration")
@@ -85,7 +86,7 @@ func runnerFromDataSource(dsInfo *models.DataSource) (*runner, error) {
}
opts := influxdb2.DefaultOptions()
hc, err := dsInfo.GetHttpClient()
hc, err := dsInfo.GetHTTPClient(httpClientProvider)
if err != nil {
return nil, err
}
+16 -11
View File
@@ -9,6 +9,7 @@ import (
"path"
"strings"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -17,17 +18,21 @@ import (
)
type Executor struct {
// *models.DataSource
QueryParser *InfluxdbQueryParser
ResponseParser *ResponseParser
httpClientProvider httpclient.Provider
QueryParser *InfluxdbQueryParser
ResponseParser *ResponseParser
}
//nolint: staticcheck // plugins.DataPlugin deprecated
func NewExecutor(*models.DataSource) (plugins.DataPlugin, error) {
return &Executor{
QueryParser: &InfluxdbQueryParser{},
ResponseParser: &ResponseParser{},
}, nil
// nolint:staticcheck // plugins.DataPlugin deprecated
func New(httpClientProvider httpclient.Provider) func(*models.DataSource) (plugins.DataPlugin, error) {
// nolint:staticcheck // plugins.DataPlugin deprecated
return func(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
return &Executor{
httpClientProvider: httpClientProvider,
QueryParser: &InfluxdbQueryParser{},
ResponseParser: &ResponseParser{},
}, nil
}
}
var (
@@ -47,7 +52,7 @@ func (e *Executor) DataQuery(ctx context.Context, dsInfo *models.DataSource, tsd
version := dsInfo.JsonData.Get("version").MustString("")
if version == "Flux" {
return flux.Query(ctx, dsInfo, tsdbQuery)
return flux.Query(ctx, e.httpClientProvider, dsInfo, tsdbQuery)
}
glog.Debug("Making a non-Flux type query")
@@ -74,7 +79,7 @@ func (e *Executor) DataQuery(ctx context.Context, dsInfo *models.DataSource, tsd
return plugins.DataResponse{}, err
}
httpClient, err := dsInfo.GetHttpClient()
httpClient, err := dsInfo.GetHTTPClient(e.httpClientProvider)
if err != nil {
return plugins.DataResponse{}, err
}
+12 -10
View File
@@ -9,6 +9,7 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -23,16 +24,17 @@ import (
type LokiExecutor struct {
intervalCalculator interval.Calculator
httpClientProvider httpclient.Provider
}
//nolint: staticcheck // plugins.DataPlugin deprecated
func NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
return newExecutor(), nil
}
func newExecutor() *LokiExecutor {
return &LokiExecutor{
intervalCalculator: interval.NewCalculator(interval.CalculatorOptions{MinInterval: time.Second * 1}),
// nolint:staticcheck // plugins.DataPlugin deprecated
func New(httpClientProvider httpclient.Provider) func(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
// nolint:staticcheck // plugins.DataPlugin deprecated
return func(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
return &LokiExecutor{
intervalCalculator: interval.NewCalculator(interval.CalculatorOptions{MinInterval: time.Second * 1}),
httpClientProvider: httpClientProvider,
}, nil
}
}
@@ -49,12 +51,12 @@ func (e *LokiExecutor) DataQuery(ctx context.Context, dsInfo *models.DataSource,
Results: map[string]plugins.DataQueryResult{},
}
tlsConfig, err := dsInfo.GetTLSConfig()
tlsConfig, err := dsInfo.GetTLSConfig(e.httpClientProvider)
if err != nil {
return plugins.DataResponse{}, err
}
transport, err := dsInfo.GetHttpTransport()
transport, err := dsInfo.GetHTTPTransport(e.httpClientProvider)
if err != nil {
return plugins.DataResponse{}, err
}
+8 -5
View File
@@ -5,6 +5,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/loki/pkg/loghttp"
@@ -60,9 +61,10 @@ func TestLoki(t *testing.T) {
TimeRange: &timeRange,
}
exe := newExecutor()
exe, err := New(httpclient.NewProvider())(dsInfo)
require.NoError(t, err)
models, err := exe.parseQuery(dsInfo, queryContext)
lokiExecutor := exe.(*LokiExecutor)
models, err := lokiExecutor.parseQuery(dsInfo, queryContext)
require.NoError(t, err)
require.Equal(t, time.Second*30, models[0].Step)
})
@@ -82,15 +84,16 @@ func TestLoki(t *testing.T) {
{Model: jsonModel},
},
}
exe := newExecutor()
exe, err := New(httpclient.NewProvider())(dsInfo)
require.NoError(t, err)
models, err := exe.parseQuery(dsInfo, queryContext)
lokiExecutor := exe.(*LokiExecutor)
models, err := lokiExecutor.parseQuery(dsInfo, queryContext)
require.NoError(t, err)
require.Equal(t, time.Minute*2, models[0].Step)
timeRange = plugins.NewDataTimeRange("1h", "now")
queryContext.TimeRange = &timeRange
models, err = exe.parseQuery(dsInfo, queryContext)
models, err = lokiExecutor.parseQuery(dsInfo, queryContext)
require.NoError(t, err)
require.Equal(t, time.Second*2, models[0].Step)
})
+41 -37
View File
@@ -12,6 +12,7 @@ import (
"github.com/VividCortex/mysqlerr"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/setting"
"github.com/go-sql-driver/mysql"
@@ -32,52 +33,55 @@ func characterEscape(s string, escapeChar string) string {
}
//nolint: staticcheck // plugins.DataPlugin deprecated
func NewExecutor(datasource *models.DataSource) (plugins.DataPlugin, error) {
logger := log.New("tsdb.mysql")
func New(httpClientProvider httpclient.Provider) func(datasource *models.DataSource) (plugins.DataPlugin, error) {
//nolint: staticcheck // plugins.DataPlugin deprecated
return func(datasource *models.DataSource) (plugins.DataPlugin, error) {
logger := log.New("tsdb.mysql")
protocol := "tcp"
if strings.HasPrefix(datasource.Url, "/") {
protocol = "unix"
}
protocol := "tcp"
if strings.HasPrefix(datasource.Url, "/") {
protocol = "unix"
}
cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC&allowNativePasswords=true",
characterEscape(datasource.User, ":"),
datasource.DecryptedPassword(),
protocol,
characterEscape(datasource.Url, ")"),
characterEscape(datasource.Database, "?"),
)
cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC&allowNativePasswords=true",
characterEscape(datasource.User, ":"),
datasource.DecryptedPassword(),
protocol,
characterEscape(datasource.Url, ")"),
characterEscape(datasource.Database, "?"),
)
tlsConfig, err := datasource.GetTLSConfig()
if err != nil {
return nil, err
}
if tlsConfig.RootCAs != nil || len(tlsConfig.Certificates) > 0 {
tlsConfigString := fmt.Sprintf("ds%d", datasource.Id)
if err := mysql.RegisterTLSConfig(tlsConfigString, tlsConfig); err != nil {
tlsConfig, err := datasource.GetTLSConfig(httpClientProvider)
if err != nil {
return nil, err
}
cnnstr += "&tls=" + tlsConfigString
}
if setting.Env == setting.Dev {
logger.Debug("getEngine", "connection", cnnstr)
}
if tlsConfig.RootCAs != nil || len(tlsConfig.Certificates) > 0 {
tlsConfigString := fmt.Sprintf("ds%d", datasource.Id)
if err := mysql.RegisterTLSConfig(tlsConfigString, tlsConfig); err != nil {
return nil, err
}
cnnstr += "&tls=" + tlsConfigString
}
config := sqleng.DataPluginConfiguration{
DriverName: "mysql",
ConnectionString: cnnstr,
Datasource: datasource,
TimeColumnNames: []string{"time", "time_sec"},
MetricColumnTypes: []string{"CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"},
}
if setting.Env == setting.Dev {
logger.Debug("getEngine", "connection", cnnstr)
}
rowTransformer := mysqlQueryResultTransformer{
log: logger,
}
config := sqleng.DataPluginConfiguration{
DriverName: "mysql",
ConnectionString: cnnstr,
Datasource: datasource,
TimeColumnNames: []string{"time", "time_sec"},
MetricColumnTypes: []string{"CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"},
}
return sqleng.NewDataPlugin(config, &rowTransformer, newMysqlMacroEngine(logger), logger)
rowTransformer := mysqlQueryResultTransformer{
log: logger,
}
return sqleng.NewDataPlugin(config, &rowTransformer, newMysqlMacroEngine(logger), logger)
}
}
type mysqlQueryResultTransformer struct {
+2 -1
View File
@@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/components/securejsondata"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/sqlstore"
@@ -56,7 +57,7 @@ func TestMySQL(t *testing.T) {
return sql, nil
}
exe, err := NewExecutor(&models.DataSource{
exe, err := New(httpclient.NewProvider())(&models.DataSource{
JsonData: simplejson.New(),
SecureJsonData: securejsondata.SecureJsonData{},
})
+11 -4
View File
@@ -15,6 +15,7 @@ import (
"net/url"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -22,11 +23,17 @@ import (
)
type OpenTsdbExecutor struct {
httpClientProvider httpclient.Provider
}
// nolint:staticcheck // plugins.DataQueryResult deprecated
func NewExecutor(*models.DataSource) (plugins.DataPlugin, error) {
return &OpenTsdbExecutor{}, nil
//nolint: staticcheck // plugins.DataPlugin deprecated
func New(httpClientProvider httpclient.Provider) func(*models.DataSource) (plugins.DataPlugin, error) {
//nolint: staticcheck // plugins.DataPlugin deprecated
return func(*models.DataSource) (plugins.DataPlugin, error) {
return &OpenTsdbExecutor{
httpClientProvider: httpClientProvider,
}, nil
}
}
var (
@@ -56,7 +63,7 @@ func (e *OpenTsdbExecutor) DataQuery(ctx context.Context, dsInfo *models.DataSou
return plugins.DataResponse{}, err
}
httpClient, err := dsInfo.GetHttpClient()
httpClient, err := dsInfo.GetHTTPClient(e.httpClientProvider)
if err != nil {
return plugins.DataResponse{}, err
}
+15 -7
View File
@@ -14,6 +14,7 @@ import (
"net/http"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -65,13 +66,20 @@ func (transport *prometheusTransport) RoundTrip(req *http.Request) (*http.Respon
}
//nolint: staticcheck // plugins.DataPlugin deprecated
func NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
return &PrometheusExecutor{
intervalCalculator: interval.NewCalculator(interval.CalculatorOptions{MinInterval: time.Second * 1}),
baseRoundTripperFactory: func(ds *models.DataSource) (http.RoundTripper, error) {
return ds.GetHttpTransport()
},
}, nil
func New(provider httpclient.Provider) func(*models.DataSource) (plugins.DataPlugin, error) {
return func(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
transport, err := dsInfo.GetHTTPTransport(provider)
if err != nil {
return nil, err
}
return &PrometheusExecutor{
intervalCalculator: interval.NewCalculator(interval.CalculatorOptions{MinInterval: time.Second * 1}),
baseRoundTripperFactory: func(ds *models.DataSource) (http.RoundTripper, error) {
return transport, nil
},
}, nil
}
}
var (
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
p "github.com/prometheus/common/model"
@@ -21,7 +22,7 @@ func TestPrometheus(t *testing.T) {
dsInfo := &models.DataSource{
JsonData: json,
}
plug, err := NewExecutor(dsInfo)
plug, err := New(httpclient.NewProvider())(dsInfo)
require.NoError(t, err)
executor := plug.(*PrometheusExecutor)
+10 -8
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry"
@@ -47,6 +48,7 @@ type Service struct {
CloudMonitoringService *cloudmonitoring.Service `inject:""`
AzureMonitorService *azuremonitor.Service `inject:""`
PluginManager plugins.Manager `inject:""`
HTTPClientProvider httpclient.Provider `inject:""`
//nolint: staticcheck // plugins.DataPlugin deprecated
registry map[string]func(*models.DataSource) (plugins.DataPlugin, error)
@@ -54,18 +56,18 @@ type Service struct {
// Init initialises the service.
func (s *Service) Init() error {
s.registry["graphite"] = graphite.NewExecutor
s.registry["opentsdb"] = opentsdb.NewExecutor
s.registry["prometheus"] = prometheus.NewExecutor
s.registry["influxdb"] = influxdb.NewExecutor
s.registry["graphite"] = graphite.New(s.HTTPClientProvider)
s.registry["opentsdb"] = opentsdb.New(s.HTTPClientProvider)
s.registry["prometheus"] = prometheus.New(s.HTTPClientProvider)
s.registry["influxdb"] = influxdb.New(s.HTTPClientProvider)
s.registry["mssql"] = mssql.NewExecutor
s.registry["postgres"] = s.PostgresService.NewExecutor
s.registry["mysql"] = mysql.NewExecutor
s.registry["elasticsearch"] = elasticsearch.NewExecutor
s.registry["mysql"] = mysql.New(s.HTTPClientProvider)
s.registry["elasticsearch"] = elasticsearch.New(s.HTTPClientProvider)
s.registry["stackdriver"] = s.CloudMonitoringService.NewExecutor
s.registry["grafana-azure-monitor-datasource"] = s.AzureMonitorService.NewExecutor
s.registry["loki"] = loki.NewExecutor
s.registry["tempo"] = tempo.NewExecutor
s.registry["loki"] = loki.New(s.HTTPClientProvider)
s.registry["tempo"] = tempo.New(s.HTTPClientProvider)
return nil
}
+13 -9
View File
@@ -7,6 +7,7 @@ import (
"net/http"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -18,17 +19,20 @@ type tempoExecutor struct {
httpClient *http.Client
}
// NewExecutor returns a tempoExecutor.DataQueryResult
// NewExecutor returns a tempoExecutor.
//nolint: staticcheck // plugins.DataPlugin deprecated
func NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
httpClient, err := dsInfo.GetHttpClient()
if err != nil {
return nil, err
}
func New(httpClientProvider httpclient.Provider) func(*models.DataSource) (plugins.DataPlugin, error) {
//nolint: staticcheck // plugins.DataPlugin deprecated
return func(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
httpClient, err := dsInfo.GetHTTPClient(httpClientProvider)
if err != nil {
return nil, err
}
return &tempoExecutor{
httpClient: httpClient,
}, nil
return &tempoExecutor{
httpClient: httpClient,
}, nil
}
}
var (
+2 -1
View File
@@ -4,13 +4,14 @@ import (
"context"
"testing"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTempo(t *testing.T) {
plug, err := NewExecutor(&models.DataSource{})
plug, err := New(httpclient.NewProvider())(&models.DataSource{})
executor := plug.(*tempoExecutor)
require.NoError(t, err)