Plugins: Add pluginStoreServiceLoading feature toggle (#112588)

This commit is contained in:
Todd Treece
2025-10-17 16:01:43 -04:00
committed by GitHub
parent 626b799cff
commit 69628baa9d
8 changed files with 139 additions and 35 deletions
@@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/manager/loader"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"github.com/grafana/grafana/pkg/plugins/manager/sources"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"golang.org/x/sync/errgroup"
)
@@ -33,11 +34,36 @@ type Service struct {
pluginRegistry registry.Service
pluginLoader loader.Service
pluginSources sources.Registry
loadOnStartup bool
}
func ProvideService(pluginRegistry registry.Service, pluginSources sources.Registry,
pluginLoader loader.Service) *Service {
return New(pluginRegistry, pluginLoader, pluginSources)
pluginLoader loader.Service, features featuremgmt.FeatureToggles) (*Service, error) {
if features.IsEnabledGlobally(featuremgmt.FlagPluginStoreServiceLoading) {
s := New(pluginRegistry, pluginLoader, pluginSources)
s.loadOnStartup = true
return s, nil
}
ctx := context.Background()
start := time.Now()
totalPlugins := 0
logger := log.New("plugin.store")
logger.Info("Loading plugins...")
for _, ps := range pluginSources.List(ctx) {
loadedPlugins, err := pluginLoader.Load(ctx, ps)
if err != nil {
logger.Error("Loading plugin source failed", "source", ps.PluginClass(ctx), "error", err)
return nil, err
}
totalPlugins += len(loadedPlugins)
}
logger.Info("Plugins loaded", "count", totalPlugins, "duration", time.Since(start))
return New(pluginRegistry, pluginLoader, pluginSources), nil
}
func (s *Service) Run(ctx context.Context) error {
@@ -50,6 +76,7 @@ func (s *Service) Run(ctx context.Context) error {
func NewPluginStoreForTest(pluginRegistry registry.Service, pluginLoader loader.Service, pluginSources sources.Registry) (*Service, error) {
s := New(pluginRegistry, pluginLoader, pluginSources)
s.loadOnStartup = true
if err := s.StartAsync(context.Background()); err != nil {
return nil, err
}
@@ -70,6 +97,9 @@ func New(pluginRegistry registry.Service, pluginLoader loader.Service, pluginSou
}
func (s *Service) starting(ctx context.Context) error {
if !s.loadOnStartup {
return nil
}
start := time.Now()
totalPlugins := 0
logger := log.New(ServiceName)
@@ -85,7 +115,6 @@ func (s *Service) starting(ctx context.Context) error {
}
logger.Info("Plugins loaded", "count", totalPlugins, "duration", time.Since(start))
return nil
}
@@ -11,45 +11,83 @@ import (
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/manager/pluginfakes"
"github.com/grafana/grafana/pkg/services/featuremgmt"
)
func TestStore_ProvideService(t *testing.T) {
t.Run("Plugin sources are added in order", func(t *testing.T) {
var loadedSrcs []plugins.Class
l := &pluginfakes.FakeLoader{
LoadFunc: func(ctx context.Context, src plugins.PluginSource) ([]*plugins.Plugin, error) {
loadedSrcs = append(loadedSrcs, src.PluginClass(ctx))
return nil, nil
tests := []struct {
name string
featureEnabled bool
expectedLoadOnStartup bool
expectedBeforeStart []plugins.Class
expectedAfterStart []plugins.Class
}{
{
name: "with FlagPluginStoreServiceLoading disabled",
featureEnabled: false,
expectedLoadOnStartup: false,
expectedBeforeStart: []plugins.Class{"1", "2", "3"},
expectedAfterStart: []plugins.Class{"1", "2", "3"},
},
{
name: "with FlagPluginStoreServiceLoading enabled",
featureEnabled: true,
expectedLoadOnStartup: true,
expectedBeforeStart: nil,
expectedAfterStart: []plugins.Class{"1", "2", "3"},
},
}
srcs := &pluginfakes.FakeSourceRegistry{ListFunc: func(_ context.Context) []plugins.PluginSource {
return []plugins.PluginSource{
&pluginfakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return "1"
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var loadedSrcs []plugins.Class
l := &pluginfakes.FakeLoader{
LoadFunc: func(ctx context.Context, src plugins.PluginSource) ([]*plugins.Plugin, error) {
loadedSrcs = append(loadedSrcs, src.PluginClass(ctx))
return nil, nil
},
},
&pluginfakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return "2"
},
},
&pluginfakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return "3"
},
},
}
}}
}
service := ProvideService(pluginfakes.NewFakePluginRegistry(), srcs, l)
ctx := context.Background()
err := service.StartAsync(ctx)
require.NoError(t, err)
err = service.AwaitRunning(ctx)
require.NoError(t, err)
require.Equal(t, []plugins.Class{"1", "2", "3"}, loadedSrcs)
srcs := &pluginfakes.FakeSourceRegistry{ListFunc: func(_ context.Context) []plugins.PluginSource {
return []plugins.PluginSource{
&pluginfakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return "1"
},
},
&pluginfakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return "2"
},
},
&pluginfakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return "3"
},
},
}
}}
var features featuremgmt.FeatureToggles
if tt.featureEnabled {
features = featuremgmt.WithFeatures(featuremgmt.FlagPluginStoreServiceLoading)
} else {
features = featuremgmt.WithFeatures()
}
service, err := ProvideService(pluginfakes.NewFakePluginRegistry(), srcs, l, features)
require.Equal(t, tt.expectedLoadOnStartup, service.loadOnStartup)
require.Equal(t, tt.expectedBeforeStart, loadedSrcs)
require.NoError(t, err)
ctx := context.Background()
err = service.StartAsync(ctx)
require.NoError(t, err)
err = service.AwaitRunning(ctx)
require.NoError(t, err)
require.Equal(t, tt.expectedAfterStart, loadedSrcs)
})
}
})
}