Plugins: Make it possible to support multiple plugin versions (#82116)

* first pass

* use version in more places

* add comment

* update installer

* fix wire

* fix tests

* tidy

* simplify changes

* fix in mem

* remove unused step

* fix step dupe logic for child plugins + add tests
This commit is contained in:
Will Browne
2024-02-12 12:47:49 +01:00
committed by GitHub
parent 730e1d2485
commit 788b9afda3
24 changed files with 292 additions and 131 deletions
@@ -3,6 +3,7 @@ package pipeline
import (
"context"
"errors"
"slices"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/plugins"
@@ -11,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization"
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/validation"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
"github.com/grafana/grafana/pkg/plugins/plugindef"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -195,3 +197,54 @@ func (c *AsExternal) Filter(cl plugins.Class, bundles []*plugins.FoundBundle) ([
}
return bundles, nil
}
// DuplicatePluginIDValidation is a filter step that will filter out any plugins that are already registered with the same
// plugin ID. This includes both the primary plugin and child plugins, which are matched using the plugin.json plugin
// ID field.
type DuplicatePluginIDValidation struct {
registry registry.Service
log log.Logger
}
// NewDuplicatePluginIDFilterStep returns a new DuplicatePluginIDValidation.
func NewDuplicatePluginIDFilterStep(registry registry.Service) *DuplicatePluginIDValidation {
return &DuplicatePluginIDValidation{
registry: registry,
log: log.New("plugins.dedupe"),
}
}
// Filter will filter out any plugins that have already been registered under the same plugin ID.
func (d *DuplicatePluginIDValidation) Filter(ctx context.Context, bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) {
res := make([]*plugins.FoundBundle, 0, len(bundles))
var matchesPluginIDFunc = func(fp plugins.FoundPlugin) func(p *plugins.Plugin) bool {
return func(p *plugins.Plugin) bool {
return p.ID == fp.JSONData.ID
}
}
for _, b := range bundles {
ps := d.registry.Plugins(ctx)
if slices.ContainsFunc(ps, matchesPluginIDFunc(b.Primary)) {
d.log.Warn("Skipping loading of plugin as it's a duplicate", "pluginId", b.Primary.JSONData.ID)
continue
}
var nonDupeChildren []*plugins.FoundPlugin
for _, child := range b.Children {
if slices.ContainsFunc(ps, matchesPluginIDFunc(*child)) {
d.log.Warn("Skipping loading of child plugin as it's a duplicate", "pluginId", child.JSONData.ID)
continue
}
nonDupeChildren = append(nonDupeChildren, child)
}
res = append(res, &plugins.FoundBundle{
Primary: b.Primary,
Children: nonDupeChildren,
})
}
return res, nil
}