mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Make sure that default timeout settings are based on configuration parameters. This now applies for core data sources using old TSDB contracts and new SDK contracts. Before it was only applied for old TSDB contracts. Also moves global setting variables to non-global (setting.Cfg).
270 lines
7.1 KiB
Go
270 lines
7.1 KiB
Go
package models
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
|
)
|
|
|
|
func (ds *DataSource) getTimeout() time.Duration {
|
|
timeout := 0
|
|
if ds.JsonData != nil {
|
|
timeout = ds.JsonData.Get("timeout").MustInt()
|
|
if timeout <= 0 {
|
|
if timeoutStr := ds.JsonData.Get("timeout").MustString(); timeoutStr != "" {
|
|
if t, err := strconv.Atoi(timeoutStr); err == nil {
|
|
timeout = t
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if timeout <= 0 {
|
|
return sdkhttpclient.DefaultTimeoutOptions.Timeout
|
|
}
|
|
|
|
return time.Duration(timeout) * time.Second
|
|
}
|
|
|
|
type proxyTransportCache struct {
|
|
cache map[int64]cachedRoundTripper
|
|
sync.Mutex
|
|
}
|
|
|
|
type cachedRoundTripper struct {
|
|
updated time.Time
|
|
roundTripper http.RoundTripper
|
|
}
|
|
|
|
var ptc = proxyTransportCache{
|
|
cache: make(map[int64]cachedRoundTripper),
|
|
}
|
|
|
|
func (ds *DataSource) GetHTTPClient(provider httpclient.Provider) (*http.Client, error) {
|
|
transport, err := ds.GetHTTPTransport(provider)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &http.Client{
|
|
Timeout: ds.getTimeout(),
|
|
Transport: transport,
|
|
}, nil
|
|
}
|
|
|
|
func (ds *DataSource) GetHTTPTransport(provider httpclient.Provider, customMiddlewares ...sdkhttpclient.Middleware) (http.RoundTripper, error) {
|
|
ptc.Lock()
|
|
defer ptc.Unlock()
|
|
|
|
if t, present := ptc.cache[ds.Id]; present && ds.Updated.Equal(t.updated) {
|
|
return t.roundTripper, nil
|
|
}
|
|
|
|
opts := ds.HTTPClientOptions()
|
|
opts.Middlewares = customMiddlewares
|
|
|
|
rt, err := provider.GetTransport(opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ptc.cache[ds.Id] = cachedRoundTripper{
|
|
roundTripper: rt,
|
|
updated: ds.Updated,
|
|
}
|
|
|
|
return rt, nil
|
|
}
|
|
|
|
func (ds *DataSource) HTTPClientOptions() sdkhttpclient.Options {
|
|
tlsOptions := ds.TLSOptions()
|
|
timeouts := &sdkhttpclient.TimeoutOptions{
|
|
Timeout: ds.getTimeout(),
|
|
DialTimeout: sdkhttpclient.DefaultTimeoutOptions.DialTimeout,
|
|
KeepAlive: sdkhttpclient.DefaultTimeoutOptions.KeepAlive,
|
|
TLSHandshakeTimeout: sdkhttpclient.DefaultTimeoutOptions.TLSHandshakeTimeout,
|
|
ExpectContinueTimeout: sdkhttpclient.DefaultTimeoutOptions.ExpectContinueTimeout,
|
|
MaxConnsPerHost: sdkhttpclient.DefaultTimeoutOptions.MaxConnsPerHost,
|
|
MaxIdleConns: sdkhttpclient.DefaultTimeoutOptions.MaxIdleConns,
|
|
MaxIdleConnsPerHost: sdkhttpclient.DefaultTimeoutOptions.MaxIdleConnsPerHost,
|
|
IdleConnTimeout: sdkhttpclient.DefaultTimeoutOptions.IdleConnTimeout,
|
|
}
|
|
opts := sdkhttpclient.Options{
|
|
Timeouts: timeouts,
|
|
Headers: getCustomHeaders(ds.JsonData, ds.DecryptedValues()),
|
|
Labels: map[string]string{
|
|
"datasource_name": ds.Name,
|
|
"datasource_uid": ds.Uid,
|
|
},
|
|
TLS: &tlsOptions,
|
|
}
|
|
|
|
if ds.JsonData != nil {
|
|
opts.CustomOptions = ds.JsonData.MustMap()
|
|
}
|
|
|
|
if ds.BasicAuth {
|
|
opts.BasicAuth = &sdkhttpclient.BasicAuthOptions{
|
|
User: ds.BasicAuthUser,
|
|
Password: ds.DecryptedBasicAuthPassword(),
|
|
}
|
|
} else if ds.User != "" {
|
|
opts.BasicAuth = &sdkhttpclient.BasicAuthOptions{
|
|
User: ds.User,
|
|
Password: ds.DecryptedPassword(),
|
|
}
|
|
}
|
|
|
|
if ds.JsonData != nil && ds.JsonData.Get("sigV4Auth").MustBool(false) {
|
|
opts.SigV4 = &sdkhttpclient.SigV4Config{
|
|
Service: awsServiceNamespace(ds.Type),
|
|
Region: ds.JsonData.Get("sigV4Region").MustString(),
|
|
AssumeRoleARN: ds.JsonData.Get("sigV4AssumeRoleArn").MustString(),
|
|
AuthType: ds.JsonData.Get("sigV4AuthType").MustString(),
|
|
ExternalID: ds.JsonData.Get("sigV4ExternalId").MustString(),
|
|
Profile: ds.JsonData.Get("sigV4Profile").MustString(),
|
|
}
|
|
|
|
if val, exists := ds.DecryptedValue("sigV4AccessKey"); exists {
|
|
opts.SigV4.AccessKey = val
|
|
}
|
|
|
|
if val, exists := ds.DecryptedValue("sigV4SecretKey"); exists {
|
|
opts.SigV4.SecretKey = val
|
|
}
|
|
}
|
|
|
|
return opts
|
|
}
|
|
|
|
func (ds *DataSource) TLSOptions() sdkhttpclient.TLSOptions {
|
|
var tlsSkipVerify, tlsClientAuth, tlsAuthWithCACert bool
|
|
var serverName string
|
|
|
|
if ds.JsonData != nil {
|
|
tlsClientAuth = ds.JsonData.Get("tlsAuth").MustBool(false)
|
|
tlsAuthWithCACert = ds.JsonData.Get("tlsAuthWithCACert").MustBool(false)
|
|
tlsSkipVerify = ds.JsonData.Get("tlsSkipVerify").MustBool(false)
|
|
serverName = ds.JsonData.Get("serverName").MustString()
|
|
}
|
|
|
|
opts := sdkhttpclient.TLSOptions{
|
|
InsecureSkipVerify: tlsSkipVerify,
|
|
ServerName: serverName,
|
|
}
|
|
|
|
if tlsClientAuth || tlsAuthWithCACert {
|
|
if tlsAuthWithCACert {
|
|
if val, exists := ds.DecryptedValue("tlsCACert"); exists && len(val) > 0 {
|
|
opts.CACertificate = val
|
|
}
|
|
}
|
|
|
|
if tlsClientAuth {
|
|
if val, exists := ds.DecryptedValue("tlsClientCert"); exists && len(val) > 0 {
|
|
opts.ClientCertificate = val
|
|
}
|
|
if val, exists := ds.DecryptedValue("tlsClientKey"); exists && len(val) > 0 {
|
|
opts.ClientKey = val
|
|
}
|
|
}
|
|
}
|
|
|
|
return opts
|
|
}
|
|
|
|
func (ds *DataSource) GetTLSConfig(httpClientProvider httpclient.Provider) (*tls.Config, error) {
|
|
return httpClientProvider.GetTLSConfig(ds.HTTPClientOptions())
|
|
}
|
|
|
|
// getCustomHeaders returns a map with all the to be set headers
|
|
// The map key represents the HeaderName and the value represents this header's value
|
|
func getCustomHeaders(jsonData *simplejson.Json, decryptedValues map[string]string) map[string]string {
|
|
headers := make(map[string]string)
|
|
if jsonData == nil {
|
|
return headers
|
|
}
|
|
|
|
index := 1
|
|
for {
|
|
headerNameSuffix := fmt.Sprintf("httpHeaderName%d", index)
|
|
headerValueSuffix := fmt.Sprintf("httpHeaderValue%d", index)
|
|
|
|
key := jsonData.Get(headerNameSuffix).MustString()
|
|
if key == "" {
|
|
// No (more) header values are available
|
|
break
|
|
}
|
|
|
|
if val, ok := decryptedValues[headerValueSuffix]; ok {
|
|
headers[key] = val
|
|
}
|
|
index++
|
|
}
|
|
|
|
return headers
|
|
}
|
|
|
|
type cachedDecryptedJSON struct {
|
|
updated time.Time
|
|
json map[string]string
|
|
}
|
|
|
|
type secureJSONDecryptionCache struct {
|
|
cache map[int64]cachedDecryptedJSON
|
|
sync.Mutex
|
|
}
|
|
|
|
var dsDecryptionCache = secureJSONDecryptionCache{
|
|
cache: make(map[int64]cachedDecryptedJSON),
|
|
}
|
|
|
|
// DecryptedValues returns cached decrypted values from secureJsonData.
|
|
func (ds *DataSource) DecryptedValues() map[string]string {
|
|
dsDecryptionCache.Lock()
|
|
defer dsDecryptionCache.Unlock()
|
|
|
|
if item, present := dsDecryptionCache.cache[ds.Id]; present && ds.Updated.Equal(item.updated) {
|
|
return item.json
|
|
}
|
|
|
|
json := ds.SecureJsonData.Decrypt()
|
|
dsDecryptionCache.cache[ds.Id] = cachedDecryptedJSON{
|
|
updated: ds.Updated,
|
|
json: json,
|
|
}
|
|
|
|
return json
|
|
}
|
|
|
|
// DecryptedValue returns cached decrypted value from cached secureJsonData.
|
|
func (ds *DataSource) DecryptedValue(key string) (string, bool) {
|
|
value, exists := ds.DecryptedValues()[key]
|
|
return value, exists
|
|
}
|
|
|
|
// ClearDSDecryptionCache clears the datasource decryption cache.
|
|
func ClearDSDecryptionCache() {
|
|
dsDecryptionCache.Lock()
|
|
defer dsDecryptionCache.Unlock()
|
|
|
|
dsDecryptionCache.cache = make(map[int64]cachedDecryptedJSON)
|
|
}
|
|
|
|
func awsServiceNamespace(dsType string) string {
|
|
switch dsType {
|
|
case DS_ES, DS_ES_OPEN_DISTRO:
|
|
return "es"
|
|
case DS_PROMETHEUS:
|
|
return "aps"
|
|
default:
|
|
panic(fmt.Sprintf("Unsupported datasource %q", dsType))
|
|
}
|
|
}
|