Prometheus: Fix Azure authentication support (#44407)

Re-adding back Azure authentication support to Prometheus datasource 
after the datasource query logic was rewritten from plugin.json routes to 
Go backend.

Ref #35857
This commit is contained in:
Sergey Kostrukov 2022-02-03 08:06:31 -08:00 committed by GitHub
parent 6d931226d8
commit 85ea1a5d64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 12 deletions

View File

@ -4,8 +4,8 @@ import (
"strings" "strings"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/tsdb/prometheus/middleware" "github.com/grafana/grafana/pkg/tsdb/prometheus/middleware"
"github.com/grafana/grafana/pkg/util/maputil"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/httpclient"
@ -16,30 +16,28 @@ import (
type Provider struct { type Provider struct {
settings backend.DataSourceInstanceSettings settings backend.DataSourceInstanceSettings
jsonData JsonData jsonData map[string]interface{}
httpMethod string
clientProvider httpclient.Provider clientProvider httpclient.Provider
log log.Logger log log.Logger
} }
func NewProvider( func NewProvider(
settings backend.DataSourceInstanceSettings, settings backend.DataSourceInstanceSettings,
jsonData JsonData, jsonData map[string]interface{},
clientProvider httpclient.Provider, clientProvider httpclient.Provider,
log log.Logger, log log.Logger,
) *Provider { ) *Provider {
httpMethod, _ := maputil.GetStringOptional(jsonData, "httpMethod")
return &Provider{ return &Provider{
settings: settings, settings: settings,
jsonData: jsonData, jsonData: jsonData,
httpMethod: httpMethod,
clientProvider: clientProvider, clientProvider: clientProvider,
log: log, log: log,
} }
} }
type JsonData struct {
Method string `json:"httpMethod"`
TimeInterval string `json:"timeInterval"`
}
func (p *Provider) GetClient(headers map[string]string) (apiv1.API, error) { func (p *Provider) GetClient(headers map[string]string) (apiv1.API, error) {
opts, err := p.settings.HTTPClientOptions() opts, err := p.settings.HTTPClientOptions()
if err != nil { if err != nil {
@ -54,6 +52,12 @@ func (p *Provider) GetClient(headers map[string]string) (apiv1.API, error) {
opts.SigV4.Service = "aps" opts.SigV4.Service = "aps"
} }
// Azure authentication
err = p.configureAzureAuthentication(opts)
if err != nil {
return nil, err
}
roundTripper, err := p.clientProvider.GetTransport(opts) roundTripper, err := p.clientProvider.GetTransport(opts)
if err != nil { if err != nil {
return nil, err return nil, err
@ -77,7 +81,7 @@ func (p *Provider) middlewares() []sdkhttpclient.Middleware {
middleware.CustomQueryParameters(p.log), middleware.CustomQueryParameters(p.log),
sdkhttpclient.CustomHeadersMiddleware(), sdkhttpclient.CustomHeadersMiddleware(),
} }
if strings.ToLower(p.jsonData.Method) == "get" { if strings.ToLower(p.httpMethod) == "get" {
middlewares = append(middlewares, middleware.ForceHttpGet(p.log)) middlewares = append(middlewares, middleware.ForceHttpGet(p.log))
} }

View File

@ -0,0 +1,32 @@
package promclient
import (
"fmt"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials"
"github.com/grafana/grafana/pkg/util/maputil"
)
func (p *Provider) configureAzureAuthentication(opts sdkhttpclient.Options) error {
credentials, err := azcredentials.FromDatasourceData(p.jsonData, p.settings.DecryptedSecureJSONData)
if err != nil {
err = fmt.Errorf("invalid Azure credentials: %s", err)
return err
}
if credentials != nil {
opts.CustomOptions["_azureCredentials"] = credentials
resourceId, err := maputil.GetStringOptional(p.jsonData, "azureEndpointResourceId")
if err != nil {
return err
}
if resourceId != "" {
opts.CustomOptions["azureEndpointResourceId"] = resourceId
}
}
return nil
}

View File

@ -135,7 +135,7 @@ func setup(jsonData ...string) *testContext {
rawData = []byte(jsonData[0]) rawData = []byte(jsonData[0])
} }
var jd promclient.JsonData var jd map[string]interface{}
_ = json.Unmarshal(rawData, &jd) _ = json.Unmarshal(rawData, &jd)
settings := backend.DataSourceInstanceSettings{URL: "test-url", JSONData: rawData} settings := backend.DataSourceInstanceSettings{URL: "test-url", JSONData: rawData}

View File

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/tsdb/intervalv2" "github.com/grafana/grafana/pkg/tsdb/intervalv2"
"github.com/grafana/grafana/pkg/util/maputil"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1" apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
) )
@ -42,7 +43,7 @@ func ProvideService(httpClientProvider httpclient.Provider, tracer tracing.Trace
func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc { func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
var jsonData promclient.JsonData var jsonData map[string]interface{}
err := json.Unmarshal(settings.JSONData, &jsonData) err := json.Unmarshal(settings.JSONData, &jsonData)
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading settings: %w", err) return nil, fmt.Errorf("error reading settings: %w", err)
@ -54,10 +55,15 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
return nil, err return nil, err
} }
timeInterval, err := maputil.GetStringOptional(jsonData, "timeInterval")
if err != nil {
return nil, err
}
mdl := DatasourceInfo{ mdl := DatasourceInfo{
ID: settings.ID, ID: settings.ID,
URL: settings.URL, URL: settings.URL,
TimeInterval: jsonData.TimeInterval, TimeInterval: timeInterval,
getClient: pc.GetClient, getClient: pc.GetClient,
} }

View File

@ -0,0 +1,73 @@
package maputil
import "fmt"
func GetMap(obj map[string]interface{}, key string) (map[string]interface{}, error) {
if untypedValue, ok := obj[key]; ok {
if value, ok := untypedValue.(map[string]interface{}); ok {
return value, nil
} else {
err := fmt.Errorf("the field '%s' should be an object", key)
return nil, err
}
} else {
err := fmt.Errorf("the field '%s' should be set", key)
return nil, err
}
}
func GetBool(obj map[string]interface{}, key string) (bool, error) {
if untypedValue, ok := obj[key]; ok {
if value, ok := untypedValue.(bool); ok {
return value, nil
} else {
err := fmt.Errorf("the field '%s' should be a bool", key)
return false, err
}
} else {
err := fmt.Errorf("the field '%s' should be set", key)
return false, err
}
}
func GetBoolOptional(obj map[string]interface{}, key string) (bool, error) {
if untypedValue, ok := obj[key]; ok {
if value, ok := untypedValue.(bool); ok {
return value, nil
} else {
err := fmt.Errorf("the field '%s' should be a bool", key)
return false, err
}
} else {
// Value optional, not error
return false, nil
}
}
func GetString(obj map[string]interface{}, key string) (string, error) {
if untypedValue, ok := obj[key]; ok {
if value, ok := untypedValue.(string); ok {
return value, nil
} else {
err := fmt.Errorf("the field '%s' should be a string", key)
return "", err
}
} else {
err := fmt.Errorf("the field '%s' should be set", key)
return "", err
}
}
func GetStringOptional(obj map[string]interface{}, key string) (string, error) {
if untypedValue, ok := obj[key]; ok {
if value, ok := untypedValue.(string); ok {
return value, nil
} else {
err := fmt.Errorf("the field '%s' should be a string", key)
return "", err
}
} else {
// Value optional, not error
return "", nil
}
}