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:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user