From 040dbfb5e3e317a3f02f8f90afbe1f787b4b20c5 Mon Sep 17 00:00:00 2001 From: Will Browne Date: Thu, 11 Dec 2025 15:34:55 +0000 Subject: [PATCH] tidy --- apps/plugins/pkg/app/meta/catalog.go | 8 +- apps/plugins/pkg/app/meta/converter.go | 16 +-- apps/plugins/pkg/app/meta/core.go | 126 ++++++++++++++++++--- pkg/tests/apis/plugins/metas_test.go | 10 +- pkg/tests/apis/plugins/pluginmetas_test.go | 85 -------------- 5 files changed, 127 insertions(+), 118 deletions(-) delete mode 100644 pkg/tests/apis/plugins/pluginmetas_test.go diff --git a/apps/plugins/pkg/app/meta/catalog.go b/apps/plugins/pkg/app/meta/catalog.go index 1b72942eada..ab71e85746f 100644 --- a/apps/plugins/pkg/app/meta/catalog.go +++ b/apps/plugins/pkg/app/meta/catalog.go @@ -87,7 +87,7 @@ func (p *CatalogProvider) GetMeta(ctx context.Context, pluginID, version string) return nil, fmt.Errorf("failed to decode response: %w", err) } - spec := pluginsv0alpha1.MetaSpec{ + metaSpec := pluginsv0alpha1.MetaSpec{ PluginJson: gcomMeta.JSON, } @@ -118,16 +118,16 @@ func (p *CatalogProvider) GetMeta(ctx context.Context, pluginID, version string) signature.Org = &gcomMeta.SignedByOrg } - spec.Signature = signature + metaSpec.Signature = signature } // Set angular info - spec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{ + metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{ Detected: gcomMeta.AngularDetected, } return &Result{ - Meta: spec, + Meta: metaSpec, TTL: p.ttl, }, nil } diff --git a/apps/plugins/pkg/app/meta/converter.go b/apps/plugins/pkg/app/meta/converter.go index 33a1ccd8234..728a52c4db1 100644 --- a/apps/plugins/pkg/app/meta/converter.go +++ b/apps/plugins/pkg/app/meta/converter.go @@ -488,7 +488,7 @@ func jsonDataToPluginMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.Met // This is similar to pluginToPluginMetaSpec but works with the plugin store DTO. // loadingStrategy and moduleHash are optional calculated values that can be provided. func pluginStorePluginToMeta(plugin pluginstore.Plugin, loadingStrategy plugins.LoadingStrategy, moduleHash string) pluginsv0alpha1.MetaSpec { - spec := pluginsv0alpha1.MetaSpec{ + metaSpec := pluginsv0alpha1.MetaSpec{ PluginJson: jsonDataToPluginMetaJSONData(plugin.JSONData), } @@ -509,11 +509,11 @@ func pluginStorePluginToMeta(plugin pluginstore.Plugin, loadingStrategy plugins. } module.LoadingStrategy = &ls } - spec.Module = module + metaSpec.Module = module } if plugin.BaseURL != "" { - spec.BaseURL = &plugin.BaseURL + metaSpec.BaseURL = &plugin.BaseURL } if plugin.Signature != "" { @@ -530,22 +530,22 @@ func pluginStorePluginToMeta(plugin pluginstore.Plugin, loadingStrategy plugins. signature.Org = &plugin.SignatureOrg } - spec.Signature = signature + metaSpec.Signature = signature } if len(plugin.Children) > 0 { - spec.Children = plugin.Children + metaSpec.Children = plugin.Children } - spec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{ + metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{ Detected: plugin.Angular.Detected, } if len(plugin.Translations) > 0 { - spec.Translations = plugin.Translations + metaSpec.Translations = plugin.Translations } - return spec + return metaSpec } // convertSignatureStatus converts plugins.SignatureStatus to pluginsv0alpha1.MetaV0alpha1SpecSignatureStatus. diff --git a/apps/plugins/pkg/app/meta/core.go b/apps/plugins/pkg/app/meta/core.go index c93dcf5d008..9f2805b6a11 100644 --- a/apps/plugins/pkg/app/meta/core.go +++ b/apps/plugins/pkg/app/meta/core.go @@ -13,7 +13,15 @@ import ( pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1" "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/config" + pluginsLoader "github.com/grafana/grafana/pkg/plugins/manager/loader" + "github.com/grafana/grafana/pkg/plugins/manager/pipeline/bootstrap" + "github.com/grafana/grafana/pkg/plugins/manager/pipeline/discovery" + "github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization" + "github.com/grafana/grafana/pkg/plugins/manager/pipeline/termination" + "github.com/grafana/grafana/pkg/plugins/manager/pipeline/validation" "github.com/grafana/grafana/pkg/plugins/manager/sources" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs" ) const ( @@ -26,6 +34,7 @@ type CoreProvider struct { loadedPlugins map[string]pluginsv0alpha1.MetaSpec initialized bool ttl time.Duration + loader pluginsLoader.Service } // NewCoreProvider creates a new CoreProvider for core plugins. @@ -35,9 +44,13 @@ func NewCoreProvider() *CoreProvider { // NewCoreProviderWithTTL creates a new CoreProvider with a custom TTL. func NewCoreProviderWithTTL(ttl time.Duration) *CoreProvider { + cfg := &config.PluginManagementCfg{ + Features: config.Features{}, + } return &CoreProvider{ loadedPlugins: make(map[string]pluginsv0alpha1.MetaSpec), ttl: ttl, + loader: createLoader(cfg), } } @@ -86,8 +99,8 @@ func (p *CoreProvider) GetMeta(ctx context.Context, pluginID, _ string) (*Result return nil, ErrMetaNotFound } -// loadPlugins discovers and caches all core plugins. -// Returns an error if the static root path cannot be found or if plugin discovery fails. +// loadPlugins discovers and caches all core plugins by fully loading them. +// Returns an error if the static root path cannot be found or if plugin loading fails. // This error will be handled gracefully by GetMeta, which will return ErrMetaNotFound // to allow other providers to handle the request. func (p *CoreProvider) loadPlugins(ctx context.Context) error { @@ -108,32 +121,113 @@ func (p *CoreProvider) loadPlugins(ctx context.Context) error { panelPath := filepath.Join(staticRootPath, "app", "plugins", "panel") src := sources.NewLocalSource(plugins.ClassCore, []string{datasourcePath, panelPath}) - ps, err := src.Discover(ctx) + loadedPlugins, err := p.loader.Load(ctx, src) if err != nil { return err } - if len(ps) == 0 { - logging.DefaultLogger.Warn("CoreProvider: no core plugins found during discovery") + if len(loadedPlugins) == 0 { + logging.DefaultLogger.Warn("CoreProvider: no core plugins found during loading") return nil } - for _, bundle := range ps { - spec := pluginsv0alpha1.MetaSpec{ - PluginJson: jsonDataToMetaJSONData(bundle.Primary.JSONData), - Module: nil, - BaseURL: nil, - Signature: nil, - Angular: nil, - Translations: nil, - Children: nil, - } - p.loadedPlugins[bundle.Primary.JSONData.ID] = spec + for _, plugin := range loadedPlugins { + metaSpec := p.pluginToMetaSpec(plugin) + p.loadedPlugins[plugin.ID] = metaSpec } return nil } +// createLoader creates a loader service configured for core plugins. +func createLoader(cfg *config.PluginManagementCfg) pluginsLoader.Service { + d := discovery.New(cfg, discovery.Opts{ + FilterFuncs: []discovery.FilterFunc{ + // Allow all plugin types for core plugins + }, + }) + b := bootstrap.New(cfg, bootstrap.Opts{ + DecorateFuncs: []bootstrap.DecorateFunc{}, // no decoration required for metadata + }) + v := validation.New(cfg, validation.Opts{ + ValidateFuncs: []validation.ValidateFunc{ + // Skip validation for core plugins - they're trusted + }, + }) + i := initialization.New(cfg, initialization.Opts{ + InitializeFuncs: []initialization.InitializeFunc{ + // Skip initialization - we only need metadata, not running plugins + }, + }) + t, _ := termination.New(cfg, termination.Opts{ + TerminateFuncs: []termination.TerminateFunc{ + // No termination needed for metadata-only loading + }, + }) + + et := pluginerrs.ProvideErrorTracker() + + return pluginsLoader.New(cfg, d, b, v, i, t, et) +} + +// pluginToMetaSpec converts a fully loaded *plugins.Plugin to a pluginsv0alpha1.MetaSpec. +func (p *CoreProvider) pluginToMetaSpec(plugin *plugins.Plugin) pluginsv0alpha1.MetaSpec { + metaSpec := pluginsv0alpha1.MetaSpec{ + PluginJson: jsonDataToMetaJSONData(plugin.JSONData), + } + + // Set module information + if plugin.Module != "" { + module := &pluginsv0alpha1.MetaV0alpha1SpecModule{ + Path: plugin.Module, + } + + loadingStrategy := pluginsv0alpha1.MetaV0alpha1SpecModuleLoadingStrategyScript + module.LoadingStrategy = &loadingStrategy + + metaSpec.Module = module + } + + // Set BaseURL + if plugin.BaseURL != "" { + metaSpec.BaseURL = &plugin.BaseURL + } + + // Set signature information + signature := &pluginsv0alpha1.MetaV0alpha1SpecSignature{ + Status: convertSignatureStatus(plugin.Signature), + } + + if plugin.SignatureType != "" { + sigType := convertSignatureType(plugin.SignatureType) + signature.Type = &sigType + } + + if plugin.SignatureOrg != "" { + signature.Org = &plugin.SignatureOrg + } + + metaSpec.Signature = signature + + if len(plugin.Children) > 0 { + children := make([]string, 0, len(plugin.Children)) + for _, child := range plugin.Children { + children = append(children, child.ID) + } + metaSpec.Children = children + } + + metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{ + Detected: plugin.Angular.Detected, + } + + if len(plugin.Translations) > 0 { + metaSpec.Translations = plugin.Translations + } + + return metaSpec +} + // jsonDataToMetaJSONData converts a plugins.JSONData to a pluginsv0alpha1.MetaJSONData. // nolint:gocyclo func jsonDataToMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.MetaJSONData { diff --git a/pkg/tests/apis/plugins/metas_test.go b/pkg/tests/apis/plugins/metas_test.go index 44676c4a14a..4ca1a52d5c4 100644 --- a/pkg/tests/apis/plugins/metas_test.go +++ b/pkg/tests/apis/plugins/metas_test.go @@ -57,11 +57,11 @@ func TestIntegrationPluginMeta(t *testing.T) { foundIDs := make(map[string]bool) for _, item := range response.Result.Items { - require.NotNil(t, item.Spec.PluginJSON) - foundIDs[item.Spec.PluginJSON.Id] = true - require.NotEmpty(t, item.Spec.PluginJSON.Id) - require.NotEmpty(t, item.Spec.PluginJSON.Type) - require.NotEmpty(t, item.Spec.PluginJSON.Name) + require.NotNil(t, item.Spec.PluginJson) + foundIDs[item.Spec.PluginJson.Id] = true + require.NotEmpty(t, item.Spec.PluginJson.Id) + require.NotEmpty(t, item.Spec.PluginJson.Type) + require.NotEmpty(t, item.Spec.PluginJson.Name) } require.True(t, foundIDs["grafana-piechart-panel"]) require.True(t, foundIDs["grafana-clock-panel"]) diff --git a/pkg/tests/apis/plugins/pluginmetas_test.go b/pkg/tests/apis/plugins/pluginmetas_test.go deleted file mode 100644 index 46a306be219..00000000000 --- a/pkg/tests/apis/plugins/pluginmetas_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package plugins - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1" - "github.com/grafana/grafana/pkg/tests/apis" - "github.com/grafana/grafana/pkg/util/testutil" -) - -func TestIntegrationPluginMetas(t *testing.T) { - testutil.SkipIntegrationTestInShortMode(t) - - t.Run("list plugin metas", func(t *testing.T) { - helper := setupHelper(t) - ctx := context.Background() - client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Admin, - GVR: gvrPlugins, - }) - - plugin1Name := "test-plugin-metas-1" - plugin1 := helper.LoadYAMLOrJSON(fmt.Sprintf(`{ - "apiVersion": "plugins.grafana.app/v0alpha1", - "kind": "Plugin", - "metadata": {"name": "%s"}, - "spec": {"id": "grafana-piechart-panel", "version": "1.0.0"} - }`, plugin1Name)) - _, err := client.Resource.Create(ctx, plugin1, metav1.CreateOptions{}) - require.NoError(t, err) - - plugin2Name := "test-plugin-metas-2" - plugin2 := helper.LoadYAMLOrJSON(fmt.Sprintf(`{ - "apiVersion": "plugins.grafana.app/v0alpha1", - "kind": "Plugin", - "metadata": {"name": "%s"}, - "spec": {"id": "grafana-clock-panel", "version": "1.0.0"} - }`, plugin2Name)) - _, err = client.Resource.Create(ctx, plugin2, metav1.CreateOptions{}) - require.NoError(t, err) - - namespace := helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()) - path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/pluginmetas", namespace) - response := apis.DoRequest(helper, apis.RequestParams{ - User: helper.Org1.Admin, - Method: "GET", - Path: path, - }, &pluginsv0alpha1.PluginMetaList{}) - - require.NotNil(t, response.Result) - require.NotNil(t, response.Result.Items) - require.GreaterOrEqual(t, len(response.Result.Items), 2) - - foundIDs := make(map[string]bool) - for _, item := range response.Result.Items { - require.NotNil(t, item.Spec.PluginJson) - foundIDs[item.Spec.PluginJson.Id] = true - require.NotEmpty(t, item.Spec.PluginJson.Id) - require.NotEmpty(t, item.Spec.PluginJson.Type) - require.NotEmpty(t, item.Spec.PluginJson.Name) - } - require.True(t, foundIDs["grafana-piechart-panel"]) - require.True(t, foundIDs["grafana-clock-panel"]) - }) - - t.Run("list plugin metas with no plugins", func(t *testing.T) { - helper := setupHelper(t) - namespace := helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()) - path := fmt.Sprintf("/apis/plugins.grafana.app/v0alpha1/namespaces/%s/pluginmetas", namespace) - response := apis.DoRequest(helper, apis.RequestParams{ - User: helper.Org1.Admin, - Method: "GET", - Path: path, - }, &pluginsv0alpha1.PluginMetaList{}) - - require.NotNil(t, response.Result) - require.NotNil(t, response.Result.Items) - require.GreaterOrEqual(t, len(response.Result.Items), 0) - }) -}