diff --git a/apps/plugins/pkg/app/meta/local.go b/apps/plugins/pkg/app/meta/local.go index af40316cd73..80b1c212620 100644 --- a/apps/plugins/pkg/app/meta/local.go +++ b/apps/plugins/pkg/app/meta/local.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" ) @@ -12,25 +11,17 @@ const ( defaultLocalTTL = 1 * time.Hour ) -// PluginAssetsCalculator is an interface for calculating plugin asset information. -// LocalProvider requires this to calculate loading strategy. -type PluginAssetsCalculator interface { - LoadingStrategy(ctx context.Context, p pluginstore.Plugin) plugins.LoadingStrategy -} - // LocalProvider retrieves plugin metadata for locally installed plugins. // It uses the plugin store to access plugins that have already been loaded. type LocalProvider struct { - store pluginstore.Store - pluginAssets PluginAssetsCalculator + store pluginstore.Store } // NewLocalProvider creates a new LocalProvider for locally installed plugins. -// pluginAssets is required for calculating loading strategy. -func NewLocalProvider(pluginStore pluginstore.Store, pluginAssets PluginAssetsCalculator) *LocalProvider { +// pluginAssets is kept for backward compatibility but is no longer used. +func NewLocalProvider(pluginStore pluginstore.Store) *LocalProvider { return &LocalProvider{ - store: pluginStore, - pluginAssets: pluginAssets, + store: pluginStore, } } @@ -41,10 +32,7 @@ func (p *LocalProvider) GetMeta(ctx context.Context, pluginID, version string) ( return nil, ErrMetaNotFound } - loadingStrategy := p.pluginAssets.LoadingStrategy(ctx, plugin) - moduleHash := plugin.ModuleHash - - spec := pluginStorePluginToMeta(plugin, loadingStrategy, moduleHash) + spec := pluginStorePluginToMeta(plugin, plugin.LoadingStrategy, plugin.ModuleHash) return &Result{ Meta: spec, TTL: defaultLocalTTL, diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 64165cfb98a..b37ac34b5bb 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -170,7 +170,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro Signature: string(panel.Signature), Sort: getPanelSort(panel.ID), Angular: panel.Angular, - LoadingStrategy: hs.pluginAssets.LoadingStrategy(c.Req.Context(), panel), + LoadingStrategy: panel.LoadingStrategy, Translations: panel.Translations, } } @@ -531,7 +531,7 @@ func (hs *HTTPServer) getFSDataSources(c *contextmodel.ReqContext, availablePlug BaseURL: plugin.BaseURL, Angular: plugin.Angular, MultiValueFilterOperators: plugin.MultiValueFilterOperators, - LoadingStrategy: hs.pluginAssets.LoadingStrategy(c.Req.Context(), plugin), + LoadingStrategy: plugin.LoadingStrategy, Translations: plugin.Translations, } @@ -638,7 +638,7 @@ func (hs *HTTPServer) newAppDTO(ctx context.Context, plugin pluginstore.Plugin, Path: plugin.Module, Preload: false, Angular: plugin.Angular, - LoadingStrategy: hs.pluginAssets.LoadingStrategy(ctx, plugin), + LoadingStrategy: plugin.LoadingStrategy, Extensions: plugin.Extensions, Dependencies: plugin.Dependencies, ModuleHash: plugin.ModuleHash, diff --git a/pkg/api/frontendsettings_test.go b/pkg/api/frontendsettings_test.go index 4741f6b10bc..9b8d42c5f45 100644 --- a/pkg/api/frontendsettings_test.go +++ b/pkg/api/frontendsettings_test.go @@ -31,7 +31,6 @@ import ( "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" - "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/services/rendering" @@ -44,7 +43,7 @@ import ( "github.com/grafana/grafana/pkg/web" ) -func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.FeatureToggles, pstore pluginstore.Store, psettings pluginsettings.Service, passets *pluginassets.Service) (*web.Mux, *HTTPServer) { +func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.FeatureToggles, pstore pluginstore.Store, psettings pluginsettings.Service) (*web.Mux, *HTTPServer) { t.Helper() db.InitTestDB(t) // nolint:staticcheck @@ -75,10 +74,6 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F pluginsSettings = &pluginsettings.FakePluginSettings{} } - var pluginsAssets = passets - if pluginsAssets == nil { - pluginsAssets = pluginassets.ProvideService(pluginsCfg, pluginsCDN, pluginStore) - } hs := &HTTPServer{ authnService: &authntest.FakeService{}, @@ -96,7 +91,6 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F AccessControl: accesscontrolmock.New(), PluginSettings: pluginsSettings, pluginsCDNService: pluginsCDN, - pluginAssets: pluginsAssets, namespacer: request.GetNamespaceMapper(cfg), SocialService: socialimpl.ProvideService(cfg, features, &usagestats.UsageStatsMock{}, supportbundlestest.NewFakeBundleService(), remotecache.NewFakeCacheStorage(), nil, ssosettingstests.NewFakeService()), managedPluginsService: managedplugins.NewNoop(), @@ -129,7 +123,7 @@ func TestIntegrationHTTPServer_GetFrontendSettings_hideVersionAnonymous(t *testi cfg.BuildVersion = "7.8.9" cfg.BuildCommit = "01234567" - m, hs := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil, nil) + m, hs := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil) @@ -221,7 +215,7 @@ func TestIntegrationHTTPServer_GetFrontendSettings_pluginsCDNBaseURL(t *testing. if test.mutateCfg != nil { test.mutateCfg(cfg) } - m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil, nil) + m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil) recorder := httptest.NewRecorder() @@ -246,7 +240,6 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { desc string pluginStore func() pluginstore.Store pluginSettings func() pluginsettings.Service - pluginAssets func() *pluginassets.Service expected settings }{ { @@ -263,7 +256,8 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Type: plugins.TypeApp, Preload: true, }, - FS: &pluginfakes.FakePluginFS{}, + FS: &pluginfakes.FakePluginFS{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, } @@ -273,7 +267,6 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", false), } }, - pluginAssets: newPluginAssets(), expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { @@ -301,7 +294,8 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Type: plugins.TypeApp, Preload: true, }, - FS: &pluginfakes.FakePluginFS{}, + FS: &pluginfakes.FakePluginFS{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, } @@ -311,7 +305,6 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", true), } }, - pluginAssets: newPluginAssets(), expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { @@ -338,8 +331,9 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Type: plugins.TypeApp, Preload: true, }, - Angular: plugins.AngularMeta{Detected: true}, - FS: &pluginfakes.FakePluginFS{}, + Angular: plugins.AngularMeta{Detected: true}, + FS: &pluginfakes.FakePluginFS{}, + LoadingStrategy: plugins.LoadingStrategyFetch, }, }, } @@ -349,7 +343,6 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", true), } }, - pluginAssets: newPluginAssets(), expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { @@ -376,6 +369,7 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Type: plugins.TypeApp, Preload: true, }, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, } @@ -385,13 +379,6 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", true), } }, - pluginAssets: newPluginAssetsWithConfig(&config.PluginManagementCfg{ - PluginSettings: map[string]map[string]string{ - "test-app": { - pluginassets.CreatePluginVersionCfgKey: pluginassets.CreatePluginVersionScriptSupportEnabled, - }, - }, - }), expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { @@ -421,6 +408,7 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { FS: &pluginfakes.FakePluginFS{TypeFunc: func() plugins.FSType { return plugins.FSTypeCDN }}, + LoadingStrategy: plugins.LoadingStrategyFetch, }, }, } @@ -430,7 +418,6 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { Plugins: newAppSettings("test-app", true), } }, - pluginAssets: newPluginAssets(), expected: settings{ Apps: map[string]*plugins.AppDTO{ "test-app": { @@ -448,7 +435,7 @@ func TestIntegrationHTTPServer_GetFrontendSettings_apps(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { cfg := setting.NewCfg() - m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), test.pluginStore(), test.pluginSettings(), test.pluginAssets()) + m, _ := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), test.pluginStore(), test.pluginSettings()) req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil) recorder := httptest.NewRecorder() @@ -549,7 +536,8 @@ func TestIntegrationHTTPServer_GetFrontendSettings_translations(t *testing.T) { "en-US": "public/plugins/test-app/locales/en-US/test-app.json", "pt-BR": "public/plugins/test-app/locales/pt-BR/test-app.json", }, - FS: &pluginfakes.FakePluginFS{}, + FS: &pluginfakes.FakePluginFS{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, } @@ -599,7 +587,8 @@ func TestIntegrationHTTPServer_GetFrontendSettings_translations(t *testing.T) { "en-US": "public/plugins/test-app/locales/en-US/test-app.json", "pt-BR": "public/plugins/test-app/locales/pt-BR/test-app.json", }, - FS: &pluginfakes.FakePluginFS{}, + FS: &pluginfakes.FakePluginFS{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, } @@ -639,7 +628,8 @@ func TestIntegrationHTTPServer_GetFrontendSettings_translations(t *testing.T) { "en-US": "public/plugins/test-app/locales/en-US/test-app.json", "pt-BR": "public/plugins/test-app/locales/pt-BR/test-app.json", }, - FS: &pluginfakes.FakePluginFS{}, + FS: &pluginfakes.FakePluginFS{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, } @@ -667,7 +657,7 @@ func TestIntegrationHTTPServer_GetFrontendSettings_translations(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { cfg := setting.NewCfg() - m, hs := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), test.pluginStore(), nil, nil) + m, hs := setupTestEnvironment(t, cfg, featuremgmt.WithFeatures(), test.pluginStore(), nil) // Create a request with the appropriate context req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil) @@ -705,12 +695,3 @@ func TestIntegrationHTTPServer_GetFrontendSettings_translations(t *testing.T) { } } -func newPluginAssets() func() *pluginassets.Service { - return newPluginAssetsWithConfig(&config.PluginManagementCfg{}) -} - -func newPluginAssetsWithConfig(pCfg *config.PluginManagementCfg) func() *pluginassets.Service { - return func() *pluginassets.Service { - return pluginassets.ProvideService(pCfg, pluginscdn.ProvideService(pCfg), &pluginstore.FakePluginStore{}) - } -} diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 591941d8bf5..abb0b1cc9b8 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -82,7 +82,6 @@ import ( "github.com/grafana/grafana/pkg/services/playlist" "github.com/grafana/grafana/pkg/services/plugindashboards" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" - "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" @@ -151,7 +150,6 @@ type HTTPServer struct { pluginDashboardService plugindashboards.Service pluginStaticRouteResolver plugins.StaticRouteResolver pluginErrorResolver plugins.ErrorResolver - pluginAssets *pluginassets.Service pluginPreinstall pluginchecker.Preinstall SearchService search.Service ShortURLService shorturls.Service @@ -255,7 +253,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi encryptionService encryption.Internal, grafanaUpdateChecker *updatemanager.GrafanaService, pluginsUpdateChecker *updatemanager.PluginsService, searchUsersService searchusers.Service, dataSourcesService datasources.DataSourceService, queryDataService query.Service, pluginFileStore plugins.FileStore, - serviceaccountsService serviceaccounts.Service, pluginAssets *pluginassets.Service, + serviceaccountsService serviceaccounts.Service, authInfoService login.AuthInfoService, storageService store.StorageService, notificationService notifications.Service, dashboardService dashboards.DashboardService, dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service, @@ -294,7 +292,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi pluginStore: pluginStore, pluginStaticRouteResolver: pluginStaticRouteResolver, pluginDashboardService: pluginDashboardService, - pluginAssets: pluginAssets, pluginErrorResolver: pluginErrorResolver, pluginFileStore: pluginFileStore, grafanaUpdateChecker: grafanaUpdateChecker, diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index 079dae066bb..935e91f0a56 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -209,7 +209,7 @@ func (hs *HTTPServer) GetPluginSettingByID(c *contextmodel.ReqContext) response. SignatureOrg: plugin.SignatureOrg, SecureJsonFields: map[string]bool{}, AngularDetected: plugin.Angular.Detected, - LoadingStrategy: hs.pluginAssets.LoadingStrategy(c.Req.Context(), plugin), + LoadingStrategy: plugin.LoadingStrategy, Extensions: plugin.Extensions, Translations: plugin.Translations, } diff --git a/pkg/api/plugins_test.go b/pkg/api/plugins_test.go index 238b0b3390f..0403fcaf12d 100644 --- a/pkg/api/plugins_test.go +++ b/pkg/api/plugins_test.go @@ -41,7 +41,6 @@ import ( "github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol" - "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" @@ -675,9 +674,10 @@ func Test_PluginsList_AccessControl(t *testing.T) { func createPlugin(jd plugins.JSONData, class plugins.Class, files plugins.FS) *plugins.Plugin { return &plugins.Plugin{ - JSONData: jd, - Class: class, - FS: files, + JSONData: jd, + Class: class, + FS: files, + LoadingStrategy: plugins.LoadingStrategyScript, } } @@ -844,9 +844,6 @@ func Test_PluginsSettings(t *testing.T) { ErrorCode: tc.errCode, }) } - pCfg := &config.PluginManagementCfg{} - pluginCDN := pluginscdn.ProvideService(pCfg) - hs.pluginAssets = pluginassets.ProvideService(pCfg, pluginCDN, hs.pluginStore) hs.pluginErrorResolver = pluginerrs.ProvideStore(errTracker) hs.pluginsUpdateChecker, err = updatemanager.ProvidePluginsService( hs.Cfg, diff --git a/pkg/plugins/manager/loader/loader_test.go b/pkg/plugins/manager/loader/loader_test.go index f4c63e71e15..d7e7e6e1d2e 100644 --- a/pkg/plugins/manager/loader/loader_test.go +++ b/pkg/plugins/manager/loader/loader_test.go @@ -129,6 +129,7 @@ func TestLoader_Load(t *testing.T) { Class: plugins.ClassCore, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -242,6 +243,7 @@ func TestLoader_Load(t *testing.T) { SignatureOrg: "Grafana Labs", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -294,6 +296,7 @@ func TestLoader_Load(t *testing.T) { Signature: "unsigned", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -353,6 +356,7 @@ func TestLoader_Load(t *testing.T) { Signature: plugins.SignatureStatusUnsigned, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -451,6 +455,7 @@ func TestLoader_Load(t *testing.T) { BaseURL: "public/plugins/test-app", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, diff --git a/pkg/plugins/manager/pipeline/bootstrap/steps.go b/pkg/plugins/manager/pipeline/bootstrap/steps.go index 5ab85dc6e43..a8b657e8815 100644 --- a/pkg/plugins/manager/pipeline/bootstrap/steps.go +++ b/pkg/plugins/manager/pipeline/bootstrap/steps.go @@ -36,6 +36,7 @@ func DefaultDecorateFuncs(cfg *config.PluginManagementCfg, cdn *pluginscdn.Servi AppChildDecorateFunc(), SkipHostEnvVarsDecorateFunc(cfg), ModuleHashDecorateFunc(cfg, cdn), + LoadingStrategyDecorateFunc(cfg, cdn), } } @@ -166,3 +167,11 @@ func ModuleHashDecorateFunc(cfg *config.PluginManagementCfg, cdn *pluginscdn.Ser return p, nil } } + +// LoadingStrategyDecorateFunc returns a DecorateFunc that calculates and sets the loading strategy for the plugin. +func LoadingStrategyDecorateFunc(cfg *config.PluginManagementCfg, cdn *pluginscdn.Service) DecorateFunc { + return func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { + p.LoadingStrategy = pluginassets.CalculateLoadingStrategy(p, cfg, cdn) + return p, nil + } +} diff --git a/pkg/plugins/pluginassets/loadingstrategy.go b/pkg/plugins/pluginassets/loadingstrategy.go new file mode 100644 index 00000000000..c5b0edf5e99 --- /dev/null +++ b/pkg/plugins/pluginassets/loadingstrategy.go @@ -0,0 +1,67 @@ +package pluginassets + +import ( + "github.com/Masterminds/semver/v3" + + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/config" + "github.com/grafana/grafana/pkg/plugins/pluginscdn" +) + +const ( + CreatePluginVersionCfgKey = "create_plugin_version" + CreatePluginVersionScriptSupportEnabled = "4.15.0" +) + +var ( + scriptLoadingMinSupportedVersion = semver.MustParse(CreatePluginVersionScriptSupportEnabled) +) + +// CalculateLoadingStrategy calculates the loading strategy for a plugin. +// If a plugin has plugin setting `create_plugin_version` >= 4.15.0, set loadingStrategy to "script". +// If a plugin is not loaded via the CDN and is not Angular, set loadingStrategy to "script". +// Otherwise, set loadingStrategy to "fetch". +func CalculateLoadingStrategy(p *plugins.Plugin, cfg *config.PluginManagementCfg, cdn *pluginscdn.Service) plugins.LoadingStrategy { + if cfg != nil && cfg.PluginSettings != nil { + if pCfg, ok := cfg.PluginSettings[p.ID]; ok { + if compatibleCreatePluginVersion(pCfg) { + return plugins.LoadingStrategyScript + } + } + + // If the plugin has a parent + if p.Parent != nil { + // Check the parent's create_plugin_version setting + if pCfg, ok := cfg.PluginSettings[p.Parent.ID]; ok { + if compatibleCreatePluginVersion(pCfg) { + return plugins.LoadingStrategyScript + } + } + + // Since the parent plugin is not explicitly configured as script loading compatible, + // If the plugin is either loaded from the CDN (via its parent) or contains Angular, we should use fetch + if cdnEnabled(p.Parent, cdn) || p.Angular.Detected { + return plugins.LoadingStrategyFetch + } + } + } + + if !cdnEnabled(p, cdn) && !p.Angular.Detected { + return plugins.LoadingStrategyScript + } + + return plugins.LoadingStrategyFetch +} + +// compatibleCreatePluginVersion checks if the create_plugin_version setting is >= 4.15.0 +func compatibleCreatePluginVersion(ps map[string]string) bool { + if cpv, ok := ps[CreatePluginVersionCfgKey]; ok { + createPluginVer, err := semver.NewVersion(cpv) + if err != nil { + // Invalid semver, treat as incompatible + return false + } + return !createPluginVer.LessThan(scriptLoadingMinSupportedVersion) + } + return false +} diff --git a/pkg/services/pluginsintegration/pluginassets/pluginassets_test.go b/pkg/plugins/pluginassets/loadingstrategy_test.go similarity index 59% rename from pkg/services/pluginsintegration/pluginassets/pluginassets_test.go rename to pkg/plugins/pluginassets/loadingstrategy_test.go index 91b8b515bb1..0246f633396 100644 --- a/pkg/services/pluginsintegration/pluginassets/pluginassets_test.go +++ b/pkg/plugins/pluginassets/loadingstrategy_test.go @@ -1,20 +1,25 @@ package pluginassets import ( - "context" "testing" "github.com/stretchr/testify/assert" - "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/config" - "github.com/grafana/grafana/pkg/plugins/manager/pluginfakes" "github.com/grafana/grafana/pkg/plugins/pluginscdn" - "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" ) -func TestService_Calculate(t *testing.T) { +// cdnFS is a simple mock FS that returns CDN type +type cdnFS struct { + plugins.FS +} + +func (f *cdnFS) Type() plugins.FSType { + return plugins.FSTypeCDN +} + +func TestCalculateLoadingStrategy(t *testing.T) { const pluginID = "grafana-test-datasource" const ( @@ -26,7 +31,7 @@ func TestService_Calculate(t *testing.T) { tcs := []struct { name string pluginSettings config.PluginSettings - plugin pluginstore.Plugin + plugin *plugins.Plugin expected plugins.LoadingStrategy }{ { @@ -34,7 +39,7 @@ func TestService_Calculate(t *testing.T) { pluginSettings: newPluginSettings(pluginID, map[string]string{ CreatePluginVersionCfgKey: compatVersion, }), - plugin: newPlugin(pluginID, withAngular(false)), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false)), expected: plugins.LoadingStrategyScript, }, { @@ -42,9 +47,11 @@ func TestService_Calculate(t *testing.T) { pluginSettings: newPluginSettings("parent-datasource", map[string]string{ CreatePluginVersionCfgKey: compatVersion, }), - plugin: newPlugin(pluginID, withAngular(false), func(p pluginstore.Plugin) pluginstore.Plugin { - p.Parent = &pluginstore.ParentPlugin{ID: "parent-datasource"} - return p + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), func(p *plugins.Plugin) { + p.Parent = &plugins.Plugin{ + JSONData: plugins.JSONData{ID: "parent-datasource"}, + FS: plugins.NewFakeFS(), + } }), expected: plugins.LoadingStrategyScript, }, @@ -53,24 +60,21 @@ func TestService_Calculate(t *testing.T) { pluginSettings: newPluginSettings(pluginID, map[string]string{ CreatePluginVersionCfgKey: futureVersion, }), - plugin: newPlugin(pluginID, withAngular(false), withFS(plugins.NewFakeFS())), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), withFSForLoadingStrategy(plugins.NewFakeFS())), expected: plugins.LoadingStrategyScript, }, { name: "Expected LoadingStrategyScript when create-plugin version is not provided, plugin is not angular and is not configured as CDN enabled", - pluginSettings: newPluginSettings(pluginID, map[string]string{ - // NOTE: cdn key is not set - }), - plugin: newPlugin(pluginID, withAngular(false), withFS(plugins.NewFakeFS())), - expected: plugins.LoadingStrategyScript, + pluginSettings: newPluginSettings(pluginID, map[string]string{}), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), withFSForLoadingStrategy(plugins.NewFakeFS())), + expected: plugins.LoadingStrategyScript, }, { name: "Expected LoadingStrategyScript when create-plugin version is not compatible, plugin is not angular, is not configured as CDN enabled and does not have a CDN fs", pluginSettings: newPluginSettings(pluginID, map[string]string{ CreatePluginVersionCfgKey: incompatVersion, - // NOTE: cdn key is not set }), - plugin: newPlugin(pluginID, withAngular(false), withClass(plugins.ClassExternal), withFS(plugins.NewFakeFS())), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), withClassForLoadingStrategy(plugins.ClassExternal), withFSForLoadingStrategy(plugins.NewFakeFS())), expected: plugins.LoadingStrategyScript, }, { @@ -80,9 +84,11 @@ func TestService_Calculate(t *testing.T) { "cdn": "true", }, }, - plugin: newPlugin(pluginID, withAngular(false), func(p pluginstore.Plugin) pluginstore.Plugin { - p.Parent = &pluginstore.ParentPlugin{ID: "parent-datasource"} - return p + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), func(p *plugins.Plugin) { + p.Parent = &plugins.Plugin{ + JSONData: plugins.JSONData{ID: "parent-datasource"}, + FS: plugins.NewFakeFS(), + } }), expected: plugins.LoadingStrategyFetch, }, @@ -93,18 +99,22 @@ func TestService_Calculate(t *testing.T) { "cdn": "true", }, }, - plugin: newPlugin(pluginID, withAngular(true), func(p pluginstore.Plugin) pluginstore.Plugin { - p.Parent = &pluginstore.ParentPlugin{ID: "parent-datasource"} - return p + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(true), func(p *plugins.Plugin) { + p.Parent = &plugins.Plugin{ + JSONData: plugins.JSONData{ID: "parent-datasource"}, + FS: plugins.NewFakeFS(), + } }), expected: plugins.LoadingStrategyFetch, }, { name: "Expected LoadingStrategyFetch when parent create-plugin version is not set, is not configured as CDN enabled and plugin is angular", pluginSettings: config.PluginSettings{}, - plugin: newPlugin(pluginID, withAngular(true), withFS(plugins.NewFakeFS()), func(p pluginstore.Plugin) pluginstore.Plugin { - p.Parent = &pluginstore.ParentPlugin{ID: "parent-datasource"} - return p + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(true), withFSForLoadingStrategy(plugins.NewFakeFS()), func(p *plugins.Plugin) { + p.Parent = &plugins.Plugin{ + JSONData: plugins.JSONData{ID: "parent-datasource"}, + FS: plugins.NewFakeFS(), + } }), expected: plugins.LoadingStrategyFetch, }, @@ -114,7 +124,7 @@ func TestService_Calculate(t *testing.T) { "cdn": "true", CreatePluginVersionCfgKey: incompatVersion, }), - plugin: newPlugin(pluginID, withAngular(false), withClass(plugins.ClassExternal)), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), withClassForLoadingStrategy(plugins.ClassExternal), withFSForLoadingStrategy(plugins.NewFakeFS())), expected: plugins.LoadingStrategyFetch, }, { @@ -122,7 +132,7 @@ func TestService_Calculate(t *testing.T) { pluginSettings: newPluginSettings(pluginID, map[string]string{ CreatePluginVersionCfgKey: incompatVersion, }), - plugin: newPlugin(pluginID, withAngular(true), withFS(plugins.NewFakeFS())), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(true), withFSForLoadingStrategy(plugins.NewFakeFS())), expected: plugins.LoadingStrategyFetch, }, { @@ -131,7 +141,7 @@ func TestService_Calculate(t *testing.T) { "cdn": "true", CreatePluginVersionCfgKey: incompatVersion, }), - plugin: newPlugin(pluginID, withAngular(false)), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), withFSForLoadingStrategy(plugins.NewFakeFS())), expected: plugins.LoadingStrategyFetch, }, { @@ -139,12 +149,8 @@ func TestService_Calculate(t *testing.T) { pluginSettings: newPluginSettings(pluginID, map[string]string{ CreatePluginVersionCfgKey: incompatVersion, }), - plugin: newPlugin(pluginID, withAngular(false), withFS( - &pluginfakes.FakePluginFS{ - TypeFunc: func() plugins.FSType { - return plugins.FSTypeCDN - }, - }, + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), withFSForLoadingStrategy( + &cdnFS{FS: plugins.NewFakeFS()}, )), expected: plugins.LoadingStrategyFetch, }, @@ -153,63 +159,53 @@ func TestService_Calculate(t *testing.T) { pluginSettings: newPluginSettings(pluginID, map[string]string{ CreatePluginVersionCfgKey: "invalidSemver", }), - plugin: newPlugin(pluginID, withAngular(false), withFS(plugins.NewFakeFS())), + plugin: newPluginForLoadingStrategy(pluginID, withAngularForLoadingStrategy(false), withFSForLoadingStrategy(plugins.NewFakeFS())), expected: plugins.LoadingStrategyScript, }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - s := &Service{ - cfg: newCfg(tc.pluginSettings), - cdn: pluginscdn.ProvideService(&config.PluginManagementCfg{ - PluginsCDNURLTemplate: "http://cdn.example.com", // required for cdn.PluginSupported check - PluginSettings: tc.pluginSettings, - }), - log: log.NewNopLogger(), + cfg := &config.PluginManagementCfg{ + PluginSettings: tc.pluginSettings, } + cdn := pluginscdn.ProvideService(&config.PluginManagementCfg{ + PluginsCDNURLTemplate: "http://cdn.example.com", // required for cdn.PluginSupported check + PluginSettings: tc.pluginSettings, + }) - got := s.LoadingStrategy(context.Background(), tc.plugin) + got := CalculateLoadingStrategy(tc.plugin, cfg, cdn) assert.Equal(t, tc.expected, got, "unexpected loading strategy") }) } } -func newPlugin(pluginID string, cbs ...func(p pluginstore.Plugin) pluginstore.Plugin) pluginstore.Plugin { - p := pluginstore.Plugin{ +func newPluginForLoadingStrategy(pluginID string, cbs ...func(*plugins.Plugin)) *plugins.Plugin { + p := &plugins.Plugin{ JSONData: plugins.JSONData{ ID: pluginID, }, } for _, cb := range cbs { - p = cb(p) + cb(p) } return p } -func withFS(fs plugins.FS) func(p pluginstore.Plugin) pluginstore.Plugin { - return func(p pluginstore.Plugin) pluginstore.Plugin { - p.FS = fs - return p - } -} - -func withAngular(angular bool) func(p pluginstore.Plugin) pluginstore.Plugin { - return func(p pluginstore.Plugin) pluginstore.Plugin { +func withAngularForLoadingStrategy(angular bool) func(*plugins.Plugin) { + return func(p *plugins.Plugin) { p.Angular = plugins.AngularMeta{Detected: angular} - return p } } -func withClass(class plugins.Class) func(p pluginstore.Plugin) pluginstore.Plugin { - return func(p pluginstore.Plugin) pluginstore.Plugin { +func withFSForLoadingStrategy(fs plugins.FS) func(*plugins.Plugin) { + return func(p *plugins.Plugin) { + p.FS = fs + } +} + +func withClassForLoadingStrategy(class plugins.Class) func(*plugins.Plugin) { + return func(p *plugins.Plugin) { p.Class = class - return p - } -} - -func newCfg(ps config.PluginSettings) *config.PluginManagementCfg { - return &config.PluginManagementCfg{ - PluginSettings: ps, } } diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index e1c26b4f87d..1a75dac2afc 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -49,9 +49,10 @@ type Plugin struct { Error *Error // SystemJS fields - Module string - ModuleHash string - BaseURL string + Module string + ModuleHash string + LoadingStrategy LoadingStrategy + BaseURL string Angular AngularMeta diff --git a/pkg/registry/apps/plugins/register.go b/pkg/registry/apps/plugins/register.go index d29da1eb6c4..daffd3867ce 100644 --- a/pkg/registry/apps/plugins/register.go +++ b/pkg/registry/apps/plugins/register.go @@ -14,7 +14,6 @@ import ( "github.com/grafana/grafana/pkg/services/apiserver/appinstaller" grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer" "github.com/grafana/grafana/pkg/services/featuremgmt" - "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" ) @@ -35,7 +34,6 @@ func ProvideAppInstaller( cfgProvider configprovider.ConfigProvider, restConfigProvider apiserver.RestConfigProvider, pluginStore pluginstore.Store, - pluginAssetsService *pluginassets.Service, accessControlService accesscontrol.Service, accessClient authlib.AccessClient, features featuremgmt.FeatureToggles, ) (*AppInstaller, error) { @@ -46,7 +44,7 @@ func ProvideAppInstaller( } } - localProvider := meta.NewLocalProvider(pluginStore, pluginAssetsService) + localProvider := meta.NewLocalProvider(pluginStore) metaProviderManager := meta.NewProviderManager(localProvider) authorizer := grafanaauthorizer.NewResourceAuthorizer(accessClient) i, err := pluginsapp.ProvideAppInstaller(authorizer, metaProviderManager) diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index 5b4f3bed5bb..b19b3d87a5b 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -187,7 +187,6 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" "github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol" - pluginassets2 "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig" "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" @@ -715,7 +714,6 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api if err != nil { return nil, err } - pluginassetsService := pluginassets2.ProvideService(pluginManagementCfg, pluginscdnService, pluginstoreService) avatarCacheServer := avatar.ProvideAvatarCacheServer(cfg) prefService := prefimpl.ProvideService(sqlStore, cfg) dashboardPermissionsService, err := ossaccesscontrol.ProvideDashboardPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, dashboardService, folderimplService, acimplService, teamService, userService, actionSetService, dashboardServiceImpl, eventualRestConfigProvider) @@ -753,7 +751,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api } idimplService := idimpl.ProvideService(cfg, localSigner, remoteCache, authnService, registerer, tracer) verifier := userimpl.ProvideVerifier(cfg, userService, tempuserService, notificationService, idimplService) - httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationService, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, managedpluginsNoop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokenService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) + httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, authinfoimplService, storageService, notificationService, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, managedpluginsNoop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokenService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) if err != nil { return nil, err } @@ -786,7 +784,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api if err != nil { return nil, err } - appInstaller, err := plugins.ProvideAppInstaller(configProvider, eventualRestConfigProvider, pluginstoreService, pluginassetsService, acimplService, accessClient, featureToggles) + appInstaller, err := plugins.ProvideAppInstaller(configProvider, eventualRestConfigProvider, pluginstoreService, acimplService, accessClient, featureToggles) if err != nil { return nil, err } @@ -1383,7 +1381,6 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac if err != nil { return nil, err } - pluginassetsService := pluginassets2.ProvideService(pluginManagementCfg, pluginscdnService, pluginstoreService) avatarCacheServer := avatar.ProvideAvatarCacheServer(cfg) prefService := prefimpl.ProvideService(sqlStore, cfg) dashboardPermissionsService, err := ossaccesscontrol.ProvideDashboardPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, dashboardService, folderimplService, acimplService, teamService, userService, actionSetService, dashboardServiceImpl, eventualRestConfigProvider) @@ -1421,7 +1418,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac } idimplService := idimpl.ProvideService(cfg, localSigner, remoteCache, authnService, registerer, tracer) verifier := userimpl.ProvideVerifier(cfg, userService, tempuserService, notificationServiceMock, idimplService) - httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationServiceMock, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, managedpluginsNoop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokentestService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) + httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, authinfoimplService, storageService, notificationServiceMock, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, managedpluginsNoop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokentestService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) if err != nil { return nil, err } @@ -1454,7 +1451,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac if err != nil { return nil, err } - appInstaller, err := plugins.ProvideAppInstaller(configProvider, eventualRestConfigProvider, pluginstoreService, pluginassetsService, acimplService, accessClient, featureToggles) + appInstaller, err := plugins.ProvideAppInstaller(configProvider, eventualRestConfigProvider, pluginstoreService, acimplService, accessClient, featureToggles) if err != nil { return nil, err } diff --git a/pkg/services/pluginsintegration/loader/loader_test.go b/pkg/services/pluginsintegration/loader/loader_test.go index eee04acedfc..da5836f27bc 100644 --- a/pkg/services/pluginsintegration/loader/loader_test.go +++ b/pkg/services/pluginsintegration/loader/loader_test.go @@ -127,6 +127,7 @@ func TestLoader_Load(t *testing.T) { Signature: plugins.SignatureStatusInternal, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -240,6 +241,7 @@ func TestLoader_Load(t *testing.T) { SignatureOrg: "Grafana Labs", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -292,6 +294,7 @@ func TestLoader_Load(t *testing.T) { Signature: "unsigned", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -357,6 +360,7 @@ func TestLoader_Load(t *testing.T) { Signature: plugins.SignatureStatusUnsigned, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -479,6 +483,7 @@ func TestLoader_Load(t *testing.T) { BaseURL: "public/plugins/test-app", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -571,6 +576,7 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) { }, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, } @@ -688,6 +694,7 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) { SignatureOrg: "Will Browne", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, pluginErrors: map[string]*plugins.Error{ @@ -822,6 +829,7 @@ func TestLoader_Load_RBACReady(t *testing.T) { BaseURL: "public/plugins/test-app", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, }, }, @@ -906,6 +914,7 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) { Module: "public/plugins/test-datasource/module.js", BaseURL: "public/plugins/test-datasource", Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, } @@ -1010,6 +1019,7 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) { BaseURL: "public/plugins/test-app", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, } @@ -1118,6 +1128,7 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) { BaseURL: "public/plugins/test-app", SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, }, } @@ -1295,6 +1306,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) { Class: plugins.ClassExternal, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, } child := &plugins.Plugin{ @@ -1354,6 +1366,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) { Class: plugins.ClassExternal, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, } parent.Children = []*plugins.Plugin{child} @@ -1520,6 +1533,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) { Class: plugins.ClassExternal, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, } child := &plugins.Plugin{ @@ -1587,6 +1601,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) { Class: plugins.ClassExternal, SkipHostEnvVars: true, Translations: map[string]string{}, + LoadingStrategy: plugins.LoadingStrategyScript, } parent.Children = []*plugins.Plugin{child} diff --git a/pkg/services/pluginsintegration/pluginassets/pluginassets.go b/pkg/services/pluginsintegration/pluginassets/pluginassets.go deleted file mode 100644 index 8735f7a4354..00000000000 --- a/pkg/services/pluginsintegration/pluginassets/pluginassets.go +++ /dev/null @@ -1,90 +0,0 @@ -package pluginassets - -import ( - "context" - - "github.com/Masterminds/semver/v3" - - "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/plugins/config" - "github.com/grafana/grafana/pkg/plugins/pluginscdn" - "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" -) - -const ( - CreatePluginVersionCfgKey = "create_plugin_version" - CreatePluginVersionScriptSupportEnabled = "4.15.0" -) - -var ( - scriptLoadingMinSupportedVersion = semver.MustParse(CreatePluginVersionScriptSupportEnabled) -) - -func ProvideService(cfg *config.PluginManagementCfg, cdn *pluginscdn.Service, store pluginstore.Store) *Service { - return &Service{ - cfg: cfg, - cdn: cdn, - store: store, - log: log.New("pluginassets"), - } -} - -type Service struct { - cfg *config.PluginManagementCfg - cdn *pluginscdn.Service - store pluginstore.Store - log log.Logger -} - -// LoadingStrategy calculates the loading strategy for a plugin. -// If a plugin has plugin setting `create_plugin_version` >= 4.15.0, set loadingStrategy to "script". -// If a plugin is not loaded via the CDN and is not Angular, set loadingStrategy to "script". -// Otherwise, set loadingStrategy to "fetch". -func (s *Service) LoadingStrategy(_ context.Context, p pluginstore.Plugin) plugins.LoadingStrategy { - if pCfg, ok := s.cfg.PluginSettings[p.ID]; ok { - if s.compatibleCreatePluginVersion(pCfg) { - return plugins.LoadingStrategyScript - } - } - - // If the plugin has a parent - if p.Parent != nil { - // Check the parent's create_plugin_version setting - if pCfg, ok := s.cfg.PluginSettings[p.Parent.ID]; ok { - if s.compatibleCreatePluginVersion(pCfg) { - return plugins.LoadingStrategyScript - } - } - - // Since the parent plugin is not explicitly configured as script loading compatible, - // If the plugin is either loaded from the CDN (via its parent) or contains Angular, we should use fetch - if s.cdnEnabled(p.Parent.ID, p.FS) || p.Angular.Detected { - return plugins.LoadingStrategyFetch - } - } - - if !s.cdnEnabled(p.ID, p.FS) && !p.Angular.Detected { - return plugins.LoadingStrategyScript - } - - return plugins.LoadingStrategyFetch -} - -func (s *Service) compatibleCreatePluginVersion(ps map[string]string) bool { - if cpv, ok := ps[CreatePluginVersionCfgKey]; ok { - createPluginVer, err := semver.NewVersion(cpv) - if err != nil { - s.log.Warn("Failed to parse create plugin version setting as semver", "version", cpv, "error", err) - } else { - if !createPluginVer.LessThan(scriptLoadingMinSupportedVersion) { - return true - } - } - } - return false -} - -func (s *Service) cdnEnabled(pluginID string, fs plugins.FS) bool { - return s.cdn.PluginSupported(pluginID) || fs.Type().CDN() -} diff --git a/pkg/services/pluginsintegration/pluginsintegration.go b/pkg/services/pluginsintegration/pluginsintegration.go index c0b3a2547cb..ae028c34411 100644 --- a/pkg/services/pluginsintegration/pluginsintegration.go +++ b/pkg/services/pluginsintegration/pluginsintegration.go @@ -46,7 +46,6 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsintegration/loader" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" "github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline" - "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig" "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" @@ -131,7 +130,6 @@ var WireSet = wire.NewSet( plugincontext.ProvideBaseService, wire.Bind(new(plugincontext.BasePluginContextProvider), new(*plugincontext.BaseProvider)), plugininstaller.ProvideService, - pluginassets.ProvideService, pluginchecker.ProvidePreinstall, wire.Bind(new(pluginchecker.Preinstall), new(*pluginchecker.PreinstallImpl)), advisor.ProvideService, diff --git a/pkg/services/pluginsintegration/pluginstore/plugins.go b/pkg/services/pluginsintegration/pluginstore/plugins.go index 77fe1365fc3..52bae98545d 100644 --- a/pkg/services/pluginsintegration/pluginstore/plugins.go +++ b/pkg/services/pluginsintegration/pluginstore/plugins.go @@ -30,9 +30,10 @@ type Plugin struct { Error *plugins.Error // SystemJS fields - Module string - BaseURL string - ModuleHash string + Module string + LoadingStrategy plugins.LoadingStrategy + BaseURL string + ModuleHash string Angular plugins.AngularMeta @@ -77,6 +78,7 @@ func ToGrafanaDTO(p *plugins.Plugin) Plugin { SignatureOrg: p.SignatureOrg, Error: p.Error, Module: p.Module, + LoadingStrategy: p.LoadingStrategy, BaseURL: p.BaseURL, ExternalService: p.ExternalService, Angular: p.Angular, diff --git a/public/app/features/plugins/loader/systemjsHooks.ts b/public/app/features/plugins/loader/systemjsHooks.ts index 8e925e6635a..28016fd0901 100644 --- a/public/app/features/plugins/loader/systemjsHooks.ts +++ b/public/app/features/plugins/loader/systemjsHooks.ts @@ -21,7 +21,7 @@ export function initSystemJSHooks() { // This instructs SystemJS to load plugin assets using fetch and eval if it returns a truthy value, otherwise // it will load the plugin using a script tag. The logic that sets loadingStrategy comes from the backend. - // See: pkg/services/pluginsintegration/pluginassets/pluginassets.go + // Loading strategy is calculated during bootstrap in pkg/plugins/pluginassets/loadingstrategy.go systemJSPrototype.shouldFetch = function (url) { const pluginInfo = getPluginInfoFromCache(url); const jsTypeRegEx = /^[^#?]+\.(js)([?#].*)?$/;