Files
grafana/pkg/plugins/manager/store.go
Will Browne 26dfdd5af3 Plugins: Refactor plugin download/installation (#43046)
* installer -> repo

* add semver format checking

* add plugin callbacks in test

* remove newline

* post install only scans new directories

* remove unused stuff

* everything in own package

* add missing cli params

* make grafana version part of the API

* resolve conflicts

* tidy up logger

* fix cli and tidy log statements

* rename log package

* update struct name

* fix linter issue

* fs -> filestore

* reorder imports

* alias import

* fix test

* fix test

* inline var

* revert jsonc file

* make repo dep of manager

* actually inject the thing

* accept all args for compatability checks

* accept compat from store

* pass os + arch vals

* don't inject fs

* tidy up

* tidy up

* merge with main and tidy fs storage

* fix test

* fix packages

* fix comment + field name

* update fs naming

* fixed wire

* remove unused func

* fix mocks

* fix storage test

* renaming

* fix log line

* fix test

* re-order field

* tidying

* add test for update with same version

* fix wire for CLI

* remove use of ioutil

* don't pass field

* small tidy

* ignore code scanning warn

* fix testdata link

* update lgtm code
2022-08-23 11:50:50 +02:00

177 lines
4.7 KiB
Go

package manager
import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/repo"
)
func (m *PluginManager) Plugin(ctx context.Context, pluginID string) (plugins.PluginDTO, bool) {
p, exists := m.plugin(ctx, pluginID)
if !exists {
return plugins.PluginDTO{}, false
}
return p.ToDTO(), true
}
func (m *PluginManager) Plugins(ctx context.Context, pluginTypes ...plugins.Type) []plugins.PluginDTO {
// if no types passed, assume all
if len(pluginTypes) == 0 {
pluginTypes = plugins.PluginTypes
}
var requestedTypes = make(map[plugins.Type]struct{})
for _, pt := range pluginTypes {
requestedTypes[pt] = struct{}{}
}
pluginsList := make([]plugins.PluginDTO, 0)
for _, p := range m.availablePlugins(ctx) {
if _, exists := requestedTypes[p.Type]; exists {
pluginsList = append(pluginsList, p.ToDTO())
}
}
return pluginsList
}
// plugin finds a plugin with `pluginID` from the registry that is not decommissioned
func (m *PluginManager) plugin(ctx context.Context, pluginID string) (*plugins.Plugin, bool) {
p, exists := m.pluginRegistry.Plugin(ctx, pluginID)
if !exists || p.IsDecommissioned() {
return nil, false
}
return p, true
}
// availablePlugins returns all non-decommissioned plugins from the registry
func (m *PluginManager) availablePlugins(ctx context.Context) []*plugins.Plugin {
var res []*plugins.Plugin
for _, p := range m.pluginRegistry.Plugins(ctx) {
if !p.IsDecommissioned() {
res = append(res, p)
}
}
return res
}
// registeredPlugins returns all registered plugins from the registry
func (m *PluginManager) registeredPlugins(ctx context.Context) map[string]struct{} {
pluginsByID := make(map[string]struct{})
for _, p := range m.pluginRegistry.Plugins(ctx) {
pluginsByID[p.ID] = struct{}{}
}
return pluginsByID
}
func (m *PluginManager) Add(ctx context.Context, pluginID, version string, opts plugins.CompatOpts) error {
compatOpts := repo.NewCompatOpts(opts.GrafanaVersion, opts.OS, opts.Arch)
var pluginArchive *repo.PluginArchive
if plugin, exists := m.plugin(ctx, pluginID); exists {
if !plugin.IsExternalPlugin() {
return plugins.ErrInstallCorePlugin
}
if plugin.Info.Version == version {
return plugins.DuplicateError{
PluginID: plugin.ID,
ExistingPluginDir: plugin.PluginDir,
}
}
// get plugin update information to confirm if target update is possible
dlOpts, err := m.pluginRepo.GetPluginDownloadOptions(ctx, pluginID, version, compatOpts)
if err != nil {
return err
}
// if existing plugin version is the same as the target update version
if dlOpts.Version == plugin.Info.Version {
return plugins.DuplicateError{
PluginID: plugin.ID,
ExistingPluginDir: plugin.PluginDir,
}
}
if dlOpts.PluginZipURL == "" && dlOpts.Version == "" {
return fmt.Errorf("could not determine update options for %s", pluginID)
}
// remove existing installation of plugin
err = m.Remove(ctx, plugin.ID)
if err != nil {
return err
}
if dlOpts.PluginZipURL != "" {
pluginArchive, err = m.pluginRepo.GetPluginArchiveByURL(ctx, dlOpts.PluginZipURL, compatOpts)
if err != nil {
return err
}
} else {
pluginArchive, err = m.pluginRepo.GetPluginArchive(ctx, pluginID, dlOpts.Version, compatOpts)
if err != nil {
return err
}
}
} else {
var err error
pluginArchive, err = m.pluginRepo.GetPluginArchive(ctx, pluginID, version, compatOpts)
if err != nil {
return err
}
}
extractedArchive, err := m.pluginStorage.Add(ctx, pluginID, pluginArchive.File)
if err != nil {
return err
}
// download dependency plugins
pathsToScan := []string{extractedArchive.Path}
for _, dep := range extractedArchive.Dependencies {
m.log.Info("Fetching %s dependencies...", dep.ID)
d, err := m.pluginRepo.GetPluginArchive(ctx, dep.ID, dep.Version, compatOpts)
if err != nil {
return fmt.Errorf("%v: %w", fmt.Sprintf("failed to download plugin %s from repository", dep.ID), err)
}
depArchive, err := m.pluginStorage.Add(ctx, dep.ID, d.File)
if err != nil {
return err
}
pathsToScan = append(pathsToScan, depArchive.Path)
}
err = m.loadPlugins(context.Background(), plugins.External, pathsToScan...)
if err != nil {
m.log.Error("Could not load plugins", "paths", pathsToScan, "err", err)
return err
}
return nil
}
func (m *PluginManager) Remove(ctx context.Context, pluginID string) error {
plugin, exists := m.plugin(ctx, pluginID)
if !exists {
return plugins.ErrPluginNotInstalled
}
if !plugin.IsExternalPlugin() {
return plugins.ErrUninstallCorePlugin
}
if err := m.unregisterAndStop(ctx, plugin); err != nil {
return err
}
return m.pluginStorage.Remove(ctx, plugin.ID)
}