mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
6dbe3b555f
Adding support for backend plugin client middlewares. This allows headers in outgoing backend plugin and HTTP requests to be modified using client middlewares. The following client middlewares added: Forward cookies: Will forward incoming HTTP request Cookies to outgoing plugins.Client and HTTP requests if the datasource has enabled forwarding of cookies (keepCookies). Forward OAuth token: Will set OAuth token headers on outgoing plugins.Client and HTTP requests if the datasource has enabled Forward OAuth Identity (oauthPassThru). Clear auth headers: Will clear any outgoing HTTP headers that was part of the incoming HTTP request and used when authenticating to Grafana. The current suggested way to register client middlewares is to have a separate package, pluginsintegration, responsible for bootstrap/instantiate the backend plugin client with middlewares and/or longer term bootstrap/instantiate plugin management. Fixes #54135 Related to #47734 Related to #57870 Related to #41623 Related to #57065
160 lines
3.8 KiB
Go
160 lines
3.8 KiB
Go
package expr
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
)
|
|
|
|
var (
|
|
expressionsQuerySummary *prometheus.SummaryVec
|
|
)
|
|
|
|
func init() {
|
|
expressionsQuerySummary = prometheus.NewSummaryVec(
|
|
prometheus.SummaryOpts{
|
|
Name: "expressions_queries_duration_milliseconds",
|
|
Help: "Expressions query summary",
|
|
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
|
|
},
|
|
[]string{"status"},
|
|
)
|
|
|
|
prometheus.MustRegister(expressionsQuerySummary)
|
|
}
|
|
|
|
// Request is similar to plugins.DataQuery but with the Time Ranges is per Query.
|
|
type Request struct {
|
|
Headers map[string]string
|
|
Debug bool
|
|
OrgId int64
|
|
Queries []Query
|
|
User *backend.User
|
|
}
|
|
|
|
// Query is like plugins.DataSubQuery, but with a a time range, and only the UID
|
|
// for the data source. Also interval is a time.Duration.
|
|
type Query struct {
|
|
RefID string
|
|
TimeRange TimeRange
|
|
DataSource *datasources.DataSource `json:"datasource"`
|
|
JSON json.RawMessage
|
|
Interval time.Duration
|
|
QueryType string
|
|
MaxDataPoints int64
|
|
}
|
|
|
|
// TimeRange is a time.Time based TimeRange.
|
|
type TimeRange interface {
|
|
AbsoluteTime(now time.Time) backend.TimeRange
|
|
}
|
|
|
|
type AbsoluteTimeRange struct {
|
|
From time.Time
|
|
To time.Time
|
|
}
|
|
|
|
func (r AbsoluteTimeRange) AbsoluteTime(_ time.Time) backend.TimeRange {
|
|
return backend.TimeRange{
|
|
From: r.From,
|
|
To: r.To,
|
|
}
|
|
}
|
|
|
|
// RelativeTimeRange is a time range relative to some absolute time.
|
|
type RelativeTimeRange struct {
|
|
From time.Duration
|
|
To time.Duration
|
|
}
|
|
|
|
func (r RelativeTimeRange) AbsoluteTime(t time.Time) backend.TimeRange {
|
|
return backend.TimeRange{
|
|
From: t.Add(r.From),
|
|
To: t.Add(r.To),
|
|
}
|
|
}
|
|
|
|
// TransformData takes Queries which are either expressions nodes
|
|
// or are datasource requests.
|
|
func (s *Service) TransformData(ctx context.Context, now time.Time, req *Request) (r *backend.QueryDataResponse, err error) {
|
|
if s.isDisabled() {
|
|
return nil, fmt.Errorf("server side expressions are disabled")
|
|
}
|
|
|
|
start := time.Now()
|
|
defer func() {
|
|
var respStatus string
|
|
switch {
|
|
case err == nil:
|
|
respStatus = "success"
|
|
default:
|
|
respStatus = "failure"
|
|
}
|
|
duration := float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond)
|
|
expressionsQuerySummary.WithLabelValues(respStatus).Observe(duration)
|
|
}()
|
|
|
|
// Build the pipeline from the request, checking for ordering issues (e.g. loops)
|
|
// and parsing graph nodes from the queries.
|
|
pipeline, err := s.BuildPipeline(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Execute the pipeline
|
|
responses, err := s.ExecutePipeline(ctx, now, pipeline)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get which queries have the Hide property so they those queries' results
|
|
// can be excluded from the response.
|
|
hidden, err := hiddenRefIDs(req.Queries)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(hidden) != 0 {
|
|
filteredRes := backend.NewQueryDataResponse()
|
|
for refID, res := range responses.Responses {
|
|
if _, ok := hidden[refID]; !ok {
|
|
filteredRes.Responses[refID] = res
|
|
}
|
|
}
|
|
responses = filteredRes
|
|
}
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
func hiddenRefIDs(queries []Query) (map[string]struct{}, error) {
|
|
hidden := make(map[string]struct{})
|
|
|
|
for _, query := range queries {
|
|
hide := struct {
|
|
Hide bool `json:"hide"`
|
|
}{}
|
|
|
|
if err := json.Unmarshal(query.JSON, &hide); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if hide.Hide {
|
|
hidden[query.RefID] = struct{}{}
|
|
}
|
|
}
|
|
return hidden, nil
|
|
}
|
|
|
|
func (s *Service) decryptSecureJsonDataFn(ctx context.Context) func(ds *datasources.DataSource) (map[string]string, error) {
|
|
return func(ds *datasources.DataSource) (map[string]string, error) {
|
|
return s.dataSourceService.DecryptedValues(ctx, ds)
|
|
}
|
|
}
|