CloudMonitoring: Migrate to use backend plugin SDK contracts (#38650)

* Use SDK contracts for cloudmonitoring

* Get build running, tests passing and do some refactoring (#38754)

* fix build+tests and refactor

* remove alerting stuff

* remove unused field

* fix plugin fetch

* end to end

* resp rename

* tidy annotations

* reformatting

* update refID

* reformat imports

* fix styling

* clean up unmarshalling

* uncomment + fix tests

* appease linter

* remove spaces

* remove old cruft

* add check for empty queries

* update tests

* remove pm as dep

* adjust proxy route contract

* fix service loading

* use UNIX val

* fix endpoint + resp

* h@ckz for frontend

* fix resp

* fix interval

* always set custom meta

* remove unused param

* fix labels fetch

* fix linter

* fix test + remove unused field

* apply pr feedback

* fix grafana-auto intervals

* fix tests

* resolve conflicts

* fix bad merge

* fix conflicts

* remove bad logger import

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Will Browne <will.browne@grafana.com>
This commit is contained in:
idafurjes
2021-10-08 14:46:35 +02:00
committed by GitHub
parent d13c799aa6
commit e822c8a24d
19 changed files with 764 additions and 742 deletions
+13 -13
View File
@@ -6,28 +6,28 @@ import (
"net/http"
"net/url"
"strings"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/encryption"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
type DSInfo struct {
ID int64
Updated time.Time
JSONData map[string]interface{}
DecryptedSecureJSONData map[string]string
}
// ApplyRoute should use the plugin route data to set auth headers and custom headers.
func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route *plugins.AppPluginRoute,
ds *models.DataSource, cfg *setting.Cfg, encryptionService encryption.Service) {
ds DSInfo, cfg *setting.Cfg) {
proxyPath = strings.TrimPrefix(proxyPath, route.Path)
secureJsonData, err := encryptionService.DecryptJsonData(ctx, ds.SecureJsonData, setting.SecretKey)
if err != nil {
logger.Error("Error interpolating proxy url", "error", err)
return
}
data := templateData{
JsonData: ds.JsonData.Interface().(map[string]interface{}),
SecureJsonData: secureJsonData,
JsonData: ds.JSONData,
SecureJsonData: ds.DecryptedSecureJSONData,
}
if len(route.URL) > 0 {
@@ -76,12 +76,12 @@ func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route
}
}
func getTokenProvider(ctx context.Context, cfg *setting.Cfg, ds *models.DataSource, pluginRoute *plugins.AppPluginRoute,
func getTokenProvider(ctx context.Context, cfg *setting.Cfg, ds DSInfo, pluginRoute *plugins.AppPluginRoute,
data templateData) (accessTokenProvider, error) {
authType := pluginRoute.AuthType
// Plugin can override authentication type specified in route configuration
if authTypeOverride := ds.JsonData.Get("authenticationType").MustString(); authTypeOverride != "" {
if authTypeOverride, ok := ds.JSONData["authenticationType"].(string); ok && authTypeOverride != "" {
authType = authTypeOverride
}
+22 -6
View File
@@ -238,16 +238,32 @@ func (proxy *DataSourceProxy) director(req *http.Request) {
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
// Clear Origin and Referer to avoir CORS issues
// Clear Origin and Referer to avoid CORS issues
req.Header.Del("Origin")
req.Header.Del("Referer")
jsonData := make(map[string]interface{})
if proxy.ds.JsonData != nil {
jsonData, err = proxy.ds.JsonData.Map()
if err != nil {
logger.Error("Failed to get json data as map", "jsonData", proxy.ds.JsonData, "error", err)
return
}
}
secureJsonData, err := proxy.dataSourcesService.EncryptionService.DecryptJsonData(req.Context(), proxy.ds.SecureJsonData, setting.SecretKey)
if err != nil {
logger.Error("Error interpolating proxy url", "error", err)
return
}
if proxy.route != nil {
ApplyRoute(
proxy.ctx.Req.Context(), req, proxy.proxyPath,
proxy.route, proxy.ds, proxy.cfg,
proxy.dataSourcesService.EncryptionService,
)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, DSInfo{
ID: proxy.ds.Id,
Updated: proxy.ds.Updated,
JSONData: jsonData,
DecryptedSecureJSONData: secureJsonData,
}, proxy.cfg)
}
if proxy.oAuthTokenService.IsOAuthPassThruEnabled(proxy.ds) {
+29 -7
View File
@@ -99,6 +99,17 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
},
}
jd, err := ds.JsonData.Map()
require.NoError(t, err)
dsInfo := DSInfo{
ID: ds.Id,
Updated: ds.Updated,
JSONData: jd,
DecryptedSecureJSONData: map[string]string{
"key": "123",
},
}
setUp := func() (*models.ReqContext, *http.Request) {
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
require.NoError(t, err)
@@ -117,7 +128,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
require.NoError(t, err)
proxy.route = plugin.Routes[0]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds, cfg, ossencryption.ProvideService())
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, dsInfo, cfg)
assert.Equal(t, "https://www.google.com/some/method", req.URL.String())
assert.Equal(t, "my secret 123", req.Header.Get("x-header"))
@@ -129,7 +140,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
require.NoError(t, err)
proxy.route = plugin.Routes[3]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds, cfg, ossencryption.ProvideService())
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, dsInfo, cfg)
assert.Equal(t, "https://dynamic.grafana.com/some/method?apiKey=123", req.URL.String())
assert.Equal(t, "my secret 123", req.Header.Get("x-header"))
@@ -141,7 +152,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
require.NoError(t, err)
proxy.route = plugin.Routes[4]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds, cfg, ossencryption.ProvideService())
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, dsInfo, cfg)
assert.Equal(t, "http://localhost/asd", req.URL.String())
})
@@ -152,7 +163,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
require.NoError(t, err)
proxy.route = plugin.Routes[5]
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds, cfg, ossencryption.ProvideService())
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, dsInfo, cfg)
content, err := ioutil.ReadAll(req.Body)
require.NoError(t, err)
@@ -262,10 +273,21 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
cfg := &setting.Cfg{}
jd, err := ds.JsonData.Map()
require.NoError(t, err)
dsInfo := DSInfo{
ID: ds.Id,
Updated: ds.Updated,
JSONData: jd,
DecryptedSecureJSONData: map[string]string{
"clientSecret": "123",
},
}
dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService())
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[0], proxy.ds, cfg, ossencryption.ProvideService())
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[0], dsInfo, cfg)
authorizationHeaderCall1 = req.Header.Get("Authorization")
assert.Equal(t, "https://api.nr1.io/some/path", req.URL.String())
@@ -281,7 +303,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService())
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[1], proxy.ds, cfg, ossencryption.ProvideService())
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[1], dsInfo, cfg)
authorizationHeaderCall2 = req.Header.Get("Authorization")
@@ -298,7 +320,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService())
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService)
require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[0], proxy.ds, cfg, ossencryption.ProvideService())
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[0], dsInfo, cfg)
authorizationHeaderCall3 := req.Header.Get("Authorization")
assert.Equal(t, "https://api.nr1.io/some/path", req.URL.String())
+5 -5
View File
@@ -2,25 +2,25 @@ package pluginproxy
import (
"context"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"golang.org/x/oauth2/google"
)
type gceAccessTokenProvider struct {
datasourceId int64
datasourceVersion int
datasourceUpdated time.Time
ctx context.Context
route *plugins.AppPluginRoute
authParams *plugins.JwtTokenAuth
}
func newGceAccessTokenProvider(ctx context.Context, ds *models.DataSource, pluginRoute *plugins.AppPluginRoute,
func newGceAccessTokenProvider(ctx context.Context, ds DSInfo, pluginRoute *plugins.AppPluginRoute,
authParams *plugins.JwtTokenAuth) *gceAccessTokenProvider {
return &gceAccessTokenProvider{
datasourceId: ds.Id,
datasourceVersion: ds.Version,
datasourceId: ds.ID,
datasourceUpdated: ds.Updated,
ctx: ctx,
route: pluginRoute,
authParams: authParams,
@@ -10,7 +10,6 @@ import (
"sync"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
)
@@ -27,7 +26,7 @@ type tokenCacheType struct {
type genericAccessTokenProvider struct {
datasourceId int64
datasourceVersion int
datasourceUpdated time.Time
route *plugins.AppPluginRoute
authParams *plugins.JwtTokenAuth
}
@@ -68,11 +67,11 @@ func (token *jwtToken) UnmarshalJSON(b []byte) error {
return nil
}
func newGenericAccessTokenProvider(ds *models.DataSource, pluginRoute *plugins.AppPluginRoute,
func newGenericAccessTokenProvider(ds DSInfo, pluginRoute *plugins.AppPluginRoute,
authParams *plugins.JwtTokenAuth) *genericAccessTokenProvider {
return &genericAccessTokenProvider{
datasourceId: ds.Id,
datasourceVersion: ds.Version,
datasourceId: ds.ID,
datasourceUpdated: ds.Updated,
route: pluginRoute,
authParams: authParams,
}
@@ -124,5 +123,5 @@ func (provider *genericAccessTokenProvider) GetAccessToken() (string, error) {
}
func (provider *genericAccessTokenProvider) getAccessTokenCacheKey() string {
return fmt.Sprintf("%v_%v_%v_%v", provider.datasourceId, provider.datasourceVersion, provider.route.Path, provider.route.Method)
return fmt.Sprintf("%v_%v_%v_%v", provider.datasourceId, provider.datasourceUpdated.Unix(), provider.route.Path, provider.route.Method)
}
+5 -6
View File
@@ -6,7 +6,6 @@ import (
"sync"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"golang.org/x/oauth2"
"golang.org/x/oauth2/jwt"
@@ -25,17 +24,17 @@ type oauthJwtTokenCacheType struct {
type jwtAccessTokenProvider struct {
datasourceId int64
datasourceVersion int
datasourceUpdated time.Time
ctx context.Context
route *plugins.AppPluginRoute
authParams *plugins.JwtTokenAuth
}
func newJwtAccessTokenProvider(ctx context.Context, ds *models.DataSource, pluginRoute *plugins.AppPluginRoute,
func newJwtAccessTokenProvider(ctx context.Context, ds DSInfo, pluginRoute *plugins.AppPluginRoute,
authParams *plugins.JwtTokenAuth) *jwtAccessTokenProvider {
return &jwtAccessTokenProvider{
datasourceId: ds.Id,
datasourceVersion: ds.Version,
datasourceId: ds.ID,
datasourceUpdated: ds.Updated,
ctx: ctx,
route: pluginRoute,
authParams: authParams,
@@ -93,5 +92,5 @@ var getTokenSource = func(conf *jwt.Config, ctx context.Context) (*oauth2.Token,
}
func (provider *jwtAccessTokenProvider) getAccessTokenCacheKey() string {
return fmt.Sprintf("%v_%v_%v_%v", provider.datasourceId, provider.datasourceVersion, provider.route.Path, provider.route.Method)
return fmt.Sprintf("%v_%v_%v_%v", provider.datasourceId, provider.datasourceUpdated.Unix(), provider.route.Path, provider.route.Method)
}
+3 -4
View File
@@ -12,7 +12,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"golang.org/x/oauth2"
"golang.org/x/oauth2/jwt"
@@ -63,7 +62,7 @@ func TestAccessToken_pluginWithJWTTokenAuthRoute(t *testing.T) {
getTokenSource = fn
}
ds := &models.DataSource{Id: 1, Version: 2}
ds := DSInfo{ID: 1, Updated: time.Now()}
t.Run("should fetch token using JWT private key", func(t *testing.T) {
setUp(t, func(conf *jwt.Config, ctx context.Context) (*oauth2.Token, error) {
@@ -170,7 +169,7 @@ func TestAccessToken_pluginWithTokenAuthRoute(t *testing.T) {
mockTimeNow(time.Now())
defer resetTimeNow()
provider := newGenericAccessTokenProvider(&models.DataSource{}, pluginRoute, authParams)
provider := newGenericAccessTokenProvider(DSInfo{}, pluginRoute, authParams)
testCases := []tokenTestDescription{
{
@@ -252,7 +251,7 @@ func TestAccessToken_pluginWithTokenAuthRoute(t *testing.T) {
mockTimeNow(time.Now())
defer resetTimeNow()
provider := newGenericAccessTokenProvider(&models.DataSource{}, pluginRoute, authParams)
provider := newGenericAccessTokenProvider(DSInfo{}, pluginRoute, authParams)
token = map[string]interface{}{
"access_token": "2YotnFZFEjr1zCsicMWpAA",