diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index 49885732eae..21466fcb2a2 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -22,7 +22,6 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/quota" - "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/setting" ) @@ -76,7 +75,6 @@ type API struct { DataProxy *datasourceproxy.DataSourceProxyService MultiOrgAlertmanager *notifier.MultiOrgAlertmanager StateManager *state.Manager - SecretsService secrets.Service AccessControl accesscontrol.AccessControl Policies *provisioning.NotificationPolicyService ContactPointService *provisioning.ContactPointService @@ -128,7 +126,7 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) { DatasourceCache: api.DatasourceCache, log: logger, accessControl: api.AccessControl, - evaluator: eval.NewEvaluator(api.Cfg, log.New("ngalert.eval"), api.DatasourceCache, api.SecretsService, api.ExpressionService), + evaluator: eval.NewEvaluator(api.Cfg, log.New("ngalert.eval"), api.DatasourceCache, api.ExpressionService), }), m) api.RegisterConfigurationApiEndpoints(NewConfiguration( &ConfigSrv{ diff --git a/pkg/services/ngalert/eval/eval.go b/pkg/services/ngalert/eval/eval.go index 9a281517a9b..cd96ac80126 100644 --- a/pkg/services/ngalert/eval/eval.go +++ b/pkg/services/ngalert/eval/eval.go @@ -11,14 +11,12 @@ import ( "strings" "time" - "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/expr/classic" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/org" - "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" @@ -38,7 +36,6 @@ type evaluatorImpl struct { cfg *setting.Cfg log log.Logger dataSourceCache datasources.CacheService - secretsService secrets.Service expressionService *expr.Service } @@ -46,13 +43,11 @@ func NewEvaluator( cfg *setting.Cfg, log log.Logger, datasourceCache datasources.CacheService, - secretsService secrets.Service, expressionService *expr.Service) Evaluator { return &evaluatorImpl{ cfg: cfg, log: log, dataSourceCache: datasourceCache, - secretsService: secretsService, expressionService: expressionService, } } @@ -164,7 +159,7 @@ type AlertExecCtx struct { } // getExprRequest validates the condition, gets the datasource information and creates an expr.Request from it. -func getExprRequest(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, dsCacheService datasources.CacheService, secretsService secrets.Service) (*expr.Request, error) { +func getExprRequest(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, dsCacheService datasources.CacheService) (*expr.Request, error) { req := &expr.Request{ OrgId: ctx.OrgID, Headers: map[string]string{ @@ -207,19 +202,6 @@ func getExprRequest(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, d datasources[q.DatasourceUID] = ds } - // If the datasource has been configured with custom HTTP headers - // then we need to add these to the request - decryptedData, err := secretsService.DecryptJsonData(ctx.Ctx, ds.SecureJsonData) - if err != nil { - return nil, err - } - customHeaders := getCustomHeaders(ds.JsonData, decryptedData) - for k, v := range customHeaders { - if _, ok := req.Headers[k]; !ok { - req.Headers[k] = v - } - } - req.Queries = append(req.Queries, expr.Query{ TimeRange: expr.TimeRange{ From: q.RelativeTimeRange.ToTimeRange(now).From, @@ -236,32 +218,6 @@ func getExprRequest(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, d return req, nil } -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 NumberValueCapture struct { Var string // RefID Labels data.Labels @@ -347,7 +303,7 @@ func queryDataResponseToExecutionResults(c models.Condition, execResp *backend.Q return result } -func executeQueriesAndExpressions(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, exprService *expr.Service, dsCacheService datasources.CacheService, secretsService secrets.Service) (resp *backend.QueryDataResponse, err error) { +func executeQueriesAndExpressions(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, exprService *expr.Service, dsCacheService datasources.CacheService) (resp *backend.QueryDataResponse, err error) { defer func() { if e := recover(); e != nil { ctx.Log.Error("alert rule panic", "error", e, "stack", string(debug.Stack())) @@ -360,7 +316,7 @@ func executeQueriesAndExpressions(ctx AlertExecCtx, data []models.AlertQuery, no } }() - queryDataReq, err := getExprRequest(ctx, data, now, dsCacheService, secretsService) + queryDataReq, err := getExprRequest(ctx, data, now, dsCacheService) if err != nil { return nil, err } @@ -611,7 +567,7 @@ func (e *evaluatorImpl) QueriesAndExpressionsEval(ctx context.Context, orgID int alertExecCtx := AlertExecCtx{OrgID: orgID, Ctx: alertCtx, ExpressionsEnabled: e.cfg.ExpressionsEnabled, Log: e.log} - execResult, err := executeQueriesAndExpressions(alertExecCtx, data, now, e.expressionService, e.dataSourceCache, e.secretsService) + execResult, err := executeQueriesAndExpressions(alertExecCtx, data, now, e.expressionService, e.dataSourceCache) if err != nil { return nil, fmt.Errorf("failed to execute conditions: %w", err) } diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index 4adf7d008c6..ea89adf8563 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -158,7 +158,7 @@ func (ng *AlertNG) init() error { Cfg: ng.Cfg.UnifiedAlerting, C: clk, Logger: ng.Log, - Evaluator: eval.NewEvaluator(ng.Cfg, ng.Log, ng.DataSourceCache, ng.SecretsService, ng.ExpressionService), + Evaluator: eval.NewEvaluator(ng.Cfg, ng.Log, ng.DataSourceCache, ng.ExpressionService), InstanceStore: store, RuleStore: store, Metrics: ng.Metrics.GetSchedulerMetrics(), @@ -194,7 +194,6 @@ func (ng *AlertNG) init() error { Schedule: ng.schedule, DataProxy: ng.DataProxy, QuotaService: ng.QuotaService, - SecretsService: ng.SecretsService, TransactionManager: store, InstanceStore: store, RuleStore: store, diff --git a/pkg/services/ngalert/schedule/schedule_unit_test.go b/pkg/services/ngalert/schedule/schedule_unit_test.go index 1c10d241356..634af0379ef 100644 --- a/pkg/services/ngalert/schedule/schedule_unit_test.go +++ b/pkg/services/ngalert/schedule/schedule_unit_test.go @@ -30,8 +30,6 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/services/ngalert/store" - "github.com/grafana/grafana/pkg/services/secrets/fakes" - secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -501,8 +499,7 @@ func setupScheduler(t *testing.T, rs *store.FakeRuleStore, is *store.FakeInstanc var evaluator eval.Evaluator = evalMock if evalMock == nil { - secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) - evaluator = eval.NewEvaluator(&setting.Cfg{ExpressionsEnabled: true}, logger, nil, secretsService, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil)) + evaluator = eval.NewEvaluator(&setting.Cfg{ExpressionsEnabled: true}, logger, nil, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil)) } if registry == nil { diff --git a/pkg/services/query/query.go b/pkg/services/query/query.go index d4da79482d0..52d51f7eb7a 100644 --- a/pkg/services/query/query.go +++ b/pkg/services/query/query.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net/http" - "strings" "time" "github.com/grafana/grafana/pkg/api/dtos" @@ -27,11 +26,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" ) -const ( - headerName = "httpHeaderName" - headerValue = "httpHeaderValue" -) - func ProvideService( cfg *setting.Cfg, dataSourceCache datasources.CacheService, @@ -185,10 +179,6 @@ func (s *Service) handleQueryData(ctx context.Context, user *user.SignedInUser, } } - for k, v := range customHeaders(ds.JsonData, instanceSettings.DecryptedSecureJSONData) { - req.Headers[k] = v - } - if parsedReq.httpRequest != nil { proxyutil.ClearCookieHeader(parsedReq.httpRequest, ds.AllowedCookies()) if cookieStr := parsedReq.httpRequest.Header.Get("Cookie"); cookieStr != "" { @@ -216,26 +206,6 @@ type parsedRequest struct { httpRequest *http.Request } -func customHeaders(jsonData *simplejson.Json, decryptedJsonData map[string]string) map[string]string { - if jsonData == nil { - return nil - } - - data := jsonData.MustMap() - - headers := map[string]string{} - for k := range data { - if strings.HasPrefix(k, headerName) { - if header, ok := data[k].(string); ok { - valueKey := strings.ReplaceAll(k, headerName, headerValue) - headers[header] = decryptedJsonData[valueKey] - } - } - } - - return headers -} - func (s *Service) parseMetricRequest(ctx context.Context, user *user.SignedInUser, skipCache bool, reqDTO dtos.MetricRequest) (*parsedRequest, error) { if len(reqDTO.Queries) == 0 { return nil, NewErrBadQuery("no queries found") diff --git a/pkg/services/query/query_test.go b/pkg/services/query/query_test.go index e0db5a3467b..bcb0105674f 100644 --- a/pkg/services/query/query_test.go +++ b/pkg/services/query/query_test.go @@ -2,7 +2,6 @@ package query_test import ( "context" - "encoding/json" "net/http" "testing" @@ -27,22 +26,6 @@ import ( ) func TestQueryData(t *testing.T) { - t.Run("it attaches custom headers to the request", func(t *testing.T) { - tc := setup(t) - tc.dataSourceCache.ds.JsonData = simplejson.NewFromAny(map[string]interface{}{"httpHeaderName1": "foo", "httpHeaderName2": "bar"}) - - secureJsonData, err := json.Marshal(map[string]string{"httpHeaderValue1": "test-header", "httpHeaderValue2": "test-header2"}) - require.NoError(t, err) - - err = tc.secretStore.Set(context.Background(), tc.dataSourceCache.ds.OrgId, tc.dataSourceCache.ds.Name, "datasource", string(secureJsonData)) - require.NoError(t, err) - - _, err = tc.queryService.QueryData(context.Background(), nil, true, metricRequest(), false) - require.Nil(t, err) - - require.Equal(t, map[string]string{"foo": "test-header", "bar": "test-header2"}, tc.pluginContext.req.Headers) - }) - t.Run("it auth custom headers to the request", func(t *testing.T) { token := &oauth2.Token{ TokenType: "bearer", diff --git a/pkg/tests/api/prometheus/prometheus_test.go b/pkg/tests/api/prometheus/prometheus_test.go new file mode 100644 index 00000000000..c8db8a4fe11 --- /dev/null +++ b/pkg/tests/api/prometheus/prometheus_test.go @@ -0,0 +1,221 @@ +package prometheus + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/grafana/grafana/pkg/api/dtos" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/org" + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/tests/testinfra" + "github.com/stretchr/testify/require" +) + +func TestIntegrationPrometheusBuffered(t *testing.T) { + dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ + DisableAnonymous: true, + }) + + grafanaListeningAddr, testEnv := testinfra.StartGrafanaEnv(t, dir, path) + ctx := context.Background() + + createUser(t, testEnv.SQLStore, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleAdmin), + Password: "admin", + Login: "admin", + }) + + var outgoingRequest *http.Request + outgoingServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + outgoingRequest = r + w.WriteHeader(http.StatusUnauthorized) + })) + t.Cleanup(outgoingServer.Close) + + jsonData := simplejson.NewFromAny(map[string]interface{}{ + "httpMethod": "post", + "httpHeaderName1": "X-CUSTOM-HEADER", + "customQueryParameters": "q1=1&q2=2", + }) + secureJSONData := map[string]string{ + "basicAuthPassword": "basicAuthPassword", + "httpHeaderValue1": "custom-header-value", + } + + uid := "prometheus" + err := testEnv.Server.HTTPServer.DataSourcesService.AddDataSource(ctx, &datasources.AddDataSourceCommand{ + OrgId: 1, + Access: datasources.DS_ACCESS_PROXY, + Name: "Prometheus", + Type: datasources.DS_PROMETHEUS, + Uid: uid, + Url: outgoingServer.URL, + BasicAuth: true, + BasicAuthUser: "basicAuthUser", + JsonData: jsonData, + SecureJsonData: secureJSONData, + }) + require.NoError(t, err) + + t.Run("When calling /api/ds/query should set expected headers on outgoing HTTP request", func(t *testing.T) { + query := simplejson.NewFromAny(map[string]interface{}{ + "datasource": map[string]interface{}{ + "uid": uid, + }, + "expr": "up", + "instantQuery": true, + }) + buf1 := &bytes.Buffer{} + err = json.NewEncoder(buf1).Encode(dtos.MetricRequest{ + From: "now-1h", + To: "now", + Queries: []*simplejson.Json{query}, + }) + require.NoError(t, err) + u := fmt.Sprintf("http://admin:admin@%s/api/ds/query", grafanaListeningAddr) + // nolint:gosec + resp, err := http.Post(u, "application/json", buf1) + require.NoError(t, err) + require.Equal(t, http.StatusBadRequest, resp.StatusCode) + t.Cleanup(func() { + err := resp.Body.Close() + require.NoError(t, err) + }) + _, err = io.ReadAll(resp.Body) + require.NoError(t, err) + + require.NotNil(t, outgoingRequest) + require.Equal(t, "/api/v1/query_range?q1=1&q2=2", outgoingRequest.URL.String()) + require.Equal(t, "custom-header-value", outgoingRequest.Header.Get("X-CUSTOM-HEADER")) + username, pwd, ok := outgoingRequest.BasicAuth() + require.True(t, ok) + require.Equal(t, "basicAuthUser", username) + require.Equal(t, "basicAuthPassword", pwd) + }) +} + +func TestIntegrationPrometheusClient(t *testing.T) { + dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ + EnableFeatureToggles: []string{"prometheusStreamingJSONParser"}, + }) + + grafanaListeningAddr, testEnv := testinfra.StartGrafanaEnv(t, dir, path) + ctx := context.Background() + + createUser(t, testEnv.SQLStore, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleAdmin), + Password: "admin", + Login: "admin", + }) + + var outgoingRequest *http.Request + outgoingServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + outgoingRequest = r + w.WriteHeader(http.StatusUnauthorized) + })) + t.Cleanup(outgoingServer.Close) + + jsonData := simplejson.NewFromAny(map[string]interface{}{ + "httpMethod": "post", + "httpHeaderName1": "X-CUSTOM-HEADER", + "customQueryParameters": "q1=1&q2=2", + }) + secureJSONData := map[string]string{ + "basicAuthPassword": "basicAuthPassword", + "httpHeaderValue1": "custom-header-value", + } + + uid := "prometheus" + err := testEnv.Server.HTTPServer.DataSourcesService.AddDataSource(ctx, &datasources.AddDataSourceCommand{ + OrgId: 1, + Access: datasources.DS_ACCESS_PROXY, + Name: "Prometheus", + Type: datasources.DS_PROMETHEUS, + Uid: uid, + Url: outgoingServer.URL, + BasicAuth: true, + BasicAuthUser: "basicAuthUser", + JsonData: jsonData, + SecureJsonData: secureJSONData, + }) + require.NoError(t, err) + + t.Run("When calling /api/ds/query should set expected headers on outgoing HTTP request", func(t *testing.T) { + query := simplejson.NewFromAny(map[string]interface{}{ + "datasource": map[string]interface{}{ + "uid": uid, + }, + "expr": "up", + "instantQuery": true, + }) + buf1 := &bytes.Buffer{} + err = json.NewEncoder(buf1).Encode(dtos.MetricRequest{ + From: "now-1h", + To: "now", + Queries: []*simplejson.Json{query}, + }) + require.NoError(t, err) + u := fmt.Sprintf("http://admin:admin@%s/api/ds/query", grafanaListeningAddr) + // nolint:gosec + resp, err := http.Post(u, "application/json", buf1) + require.NoError(t, err) + require.Equal(t, http.StatusInternalServerError, resp.StatusCode) + t.Cleanup(func() { + err := resp.Body.Close() + require.NoError(t, err) + }) + _, err = io.ReadAll(resp.Body) + require.NoError(t, err) + + require.NotNil(t, outgoingRequest) + require.Equal(t, "/api/v1/query_range", outgoingRequest.URL.Path) + require.Contains(t, outgoingRequest.URL.String(), "&q1=1&q2=2") + require.Equal(t, "custom-header-value", outgoingRequest.Header.Get("X-CUSTOM-HEADER")) + username, pwd, ok := outgoingRequest.BasicAuth() + require.True(t, ok) + require.Equal(t, "basicAuthUser", username) + require.Equal(t, "basicAuthPassword", pwd) + }) + + t.Run("When calling /api/datasources/uid/{uid}/resources/api/v1/labels should set expected headers on outgoing HTTP request", func(t *testing.T) { + u := fmt.Sprintf("http://%s/api/datasources/uid/%s/resources/api/v1/labels", grafanaListeningAddr, uid) + // nolint:gosec + resp, err := http.Post(u, "application/json", nil) + require.NoError(t, err) + require.Equal(t, http.StatusUnauthorized, resp.StatusCode) + t.Cleanup(func() { + err := resp.Body.Close() + require.NoError(t, err) + }) + _, err = io.ReadAll(resp.Body) + require.NoError(t, err) + + require.NotNil(t, outgoingRequest) + require.Equal(t, "/api/v1/labels?q1=1&q2=2", outgoingRequest.URL.String()) + require.Equal(t, "custom-header-value", outgoingRequest.Header.Get("X-CUSTOM-HEADER")) + username, pwd, ok := outgoingRequest.BasicAuth() + require.True(t, ok) + require.Equal(t, "basicAuthUser", username) + require.Equal(t, "basicAuthPassword", pwd) + }) +} + +func createUser(t *testing.T, store *sqlstore.SQLStore, cmd user.CreateUserCommand) int64 { + t.Helper() + + store.Cfg.AutoAssignOrg = true + store.Cfg.AutoAssignOrgId = 1 + + u, err := store.CreateUser(context.Background(), cmd) + require.NoError(t, err) + return u.ID +} diff --git a/pkg/tsdb/legacydata/service/service.go b/pkg/tsdb/legacydata/service/service.go index b5a8e6df967..0779eff7176 100644 --- a/pkg/tsdb/legacydata/service/service.go +++ b/pkg/tsdb/legacydata/service/service.go @@ -3,12 +3,10 @@ package service import ( "context" "fmt" - "strings" "time" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/adapters" "github.com/grafana/grafana/pkg/services/datasources" @@ -16,11 +14,6 @@ import ( "github.com/grafana/grafana/pkg/tsdb/legacydata" ) -const ( - headerName = "httpHeaderName" - headerValue = "httpHeaderValue" -) - var oAuthIsOAuthPassThruEnabledFunc = func(oAuthTokenService oauthtoken.OAuthTokenService, ds *datasources.DataSource) bool { return oAuthTokenService.IsOAuthPassThruEnabled(ds) } @@ -126,11 +119,6 @@ func generateRequest(ctx context.Context, ds *datasources.DataSource, decryptedJ Headers: query.Headers, } - // Apply Configured Custom Headers to query request. - for k, v := range customHeaders(ds.JsonData, instanceSettings.DecryptedSecureJSONData) { - req.Headers[k] = v - } - for _, q := range query.Queries { modelJSON, err := q.Model.MarshalJSON() if err != nil { @@ -151,24 +139,4 @@ func generateRequest(ctx context.Context, ds *datasources.DataSource, decryptedJ return req, nil } -func customHeaders(jsonData *simplejson.Json, decryptedJsonData map[string]string) map[string]string { - if jsonData == nil { - return nil - } - - data := jsonData.MustMap() - - headers := map[string]string{} - for k := range data { - if strings.HasPrefix(k, headerName) { - if header, ok := data[k].(string); ok { - valueKey := strings.ReplaceAll(k, headerName, headerValue) - headers[header] = decryptedJsonData[valueKey] - } - } - } - - return headers -} - var _ legacydata.RequestHandler = &Service{} diff --git a/pkg/tsdb/legacydata/service/service_test.go b/pkg/tsdb/legacydata/service/service_test.go index 90ebc640eb1..a4c4c1a1daa 100644 --- a/pkg/tsdb/legacydata/service/service_test.go +++ b/pkg/tsdb/legacydata/service/service_test.go @@ -64,39 +64,6 @@ func TestHandleRequest(t *testing.T) { }) } -func Test_generateRequest(t *testing.T) { - t.Run("Should attach custom headers to request if present", func(t *testing.T) { - jsonData := simplejson.New() - jsonData.Set(headerName+"testOne", "x-test-one") - jsonData.Set("testOne", "x-test-wrong") - jsonData.Set(headerName+"testTwo", "x-test-two") - - decryptedJsonData := map[string]string{ - headerValue + "testOne": "secret-value-one", - headerValue + "testTwo": "secret-value-two", - "something": "else", - } - - ds := &datasources.DataSource{Id: 12, Type: "unregisteredType", JsonData: jsonData} - query := legacydata.DataQuery{ - TimeRange: &legacydata.DataTimeRange{}, - Queries: []legacydata.DataSubQuery{ - {RefID: "A", DataSource: &datasources.DataSource{Id: 1, Type: "test"}, Model: simplejson.New()}, - {RefID: "B", DataSource: &datasources.DataSource{Id: 1, Type: "test"}, Model: simplejson.New()}, - }, - } - - req, err := generateRequest(context.Background(), ds, decryptedJsonData, query) - require.NoError(t, err) - require.NotNil(t, req) - require.EqualValues(t, - map[string]string{ - "x-test-one": "secret-value-one", - "x-test-two": "secret-value-two", - }, req.Headers) - }) -} - type fakePluginsClient struct { plugins.Client backend.QueryDataHandlerFunc