Plugins: Remove pkg/infra/fs, pkg/infra/tracing and pkg/infra/process dependencies from pkg/plugins (#115798)
* remove dependency on packages * update tests * trigger
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
package coreplugin_test
|
||||
package coreplugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/pluginfakes"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCorePlugin(t *testing.T) {
|
||||
t.Run("New core plugin with empty opts should return expected values", func(t *testing.T) {
|
||||
factory := coreplugin.New(backend.ServeOpts{})
|
||||
factory := New(backend.ServeOpts{})
|
||||
p, err := factory("plugin", log.New("test"), pluginfakes.InitializeNoopTracerForTest(), nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
@@ -36,7 +36,7 @@ func TestCorePlugin(t *testing.T) {
|
||||
t.Run("New core plugin with handlers set in opts should return expected values", func(t *testing.T) {
|
||||
checkHealthCalled := false
|
||||
callResourceCalled := false
|
||||
factory := coreplugin.New(backend.ServeOpts{
|
||||
factory := New(backend.ServeOpts{
|
||||
CheckHealthHandler: backend.CheckHealthHandlerFunc(func(ctx context.Context,
|
||||
req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
checkHealthCalled = true
|
||||
|
||||
@@ -5,18 +5,16 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
sdklog "github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||
sdktracing "github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||
cloudmonitoring "github.com/grafana/grafana/pkg/tsdb/cloud-monitoring"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||
@@ -204,7 +202,7 @@ var ErrCorePluginNotFound = errors.New("core plugin not found")
|
||||
|
||||
// NewPlugin factory for creating and initializing a single core plugin.
|
||||
// Note: cfg only needed for mssql connection pooling defaults.
|
||||
func NewPlugin(pluginID string, cfg *setting.Cfg, httpClientProvider *httpclient.Provider, tracer trace.Tracer, features featuremgmt.FeatureToggles) (*plugins.Plugin, error) {
|
||||
func NewPlugin(pluginID string, httpClientProvider *httpclient.Provider, tracer trace.Tracer) (*plugins.Plugin, error) {
|
||||
jsonData := plugins.JSONData{
|
||||
ID: pluginID,
|
||||
AliasIDs: []string{},
|
||||
@@ -241,7 +239,7 @@ func NewPlugin(pluginID string, cfg *setting.Cfg, httpClientProvider *httpclient
|
||||
case MySQL:
|
||||
svc = mysql.ProvideService()
|
||||
case MSSQL:
|
||||
svc = mssql.ProvideService(cfg)
|
||||
svc = mssql.ProvideService()
|
||||
case Pyroscope:
|
||||
svc = pyroscope.ProvideService(httpClientProvider)
|
||||
case Parca:
|
||||
|
||||
@@ -4,11 +4,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewPlugin(t *testing.T) {
|
||||
@@ -46,7 +45,7 @@ func TestNewPlugin(t *testing.T) {
|
||||
tc.ExpectedID = tc.ID
|
||||
}
|
||||
|
||||
p, err := NewPlugin(tc.ID, setting.NewCfg(), httpclient.NewProvider(), tracing.NoopTracer(), featuremgmt.WithFeatures())
|
||||
p, err := NewPlugin(tc.ID, httpclient.NewProvider(), tracing.NoopTracer())
|
||||
if tc.ExpectedNotFoundErr {
|
||||
require.ErrorIs(t, err, ErrCorePluginNotFound)
|
||||
require.Nil(t, p)
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// PluginManagementCfg is the configuration for the plugin management system.
|
||||
// It includes settings which are used to configure different components of plugin management.
|
||||
type PluginManagementCfg struct {
|
||||
@@ -11,7 +7,7 @@ type PluginManagementCfg struct {
|
||||
|
||||
PluginsPath string
|
||||
|
||||
PluginSettings setting.PluginSettings
|
||||
PluginSettings PluginSettings
|
||||
PluginsAllowUnsigned []string
|
||||
DisablePlugins []string
|
||||
ForwardHostEnvVars []string
|
||||
@@ -35,8 +31,11 @@ type Features struct {
|
||||
TempoAlertingEnabled bool
|
||||
}
|
||||
|
||||
// PluginSettings maps plugin id to map of key/value settings.
|
||||
type PluginSettings map[string]map[string]string
|
||||
|
||||
// NewPluginManagementCfg returns a new PluginManagementCfg.
|
||||
func NewPluginManagementCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string,
|
||||
func NewPluginManagementCfg(devMode bool, pluginsPath string, pluginSettings PluginSettings, pluginsAllowUnsigned []string,
|
||||
pluginsCDNURLTemplate string, appURL string, features Features,
|
||||
grafanaComAPIURL string, disablePlugins []string, forwardHostEnvVars []string, grafanaComAPIToken string,
|
||||
) *PluginManagementCfg {
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ErrWalkSkipDir is the Error returned when we want to skip descending into a directory
|
||||
var ErrWalkSkipDir = errors.New("skip this directory")
|
||||
|
||||
// WalkFunc is a callback function called for each path as a directory is walked
|
||||
// If resolvedPath != "", then we are following symbolic links.
|
||||
type WalkFunc func(resolvedPath string, info os.FileInfo, err error) error
|
||||
|
||||
type walker struct {
|
||||
rootDir string
|
||||
}
|
||||
|
||||
// newWalker creates a new walker
|
||||
func newWalker(rootDir string) *walker {
|
||||
return &walker{rootDir: rootDir}
|
||||
}
|
||||
|
||||
// Walk walks a path, optionally following symbolic links, and for each path,
|
||||
// it calls the walkFn passed.
|
||||
//
|
||||
// It is similar to filepath.Walk, except that it supports symbolic links and
|
||||
// can detect infinite loops while following symlinks.
|
||||
// It solves the issue where your WalkFunc needs a path relative to the symbolic link
|
||||
// (resolving links within walkfunc loses the path to the symbolic link for each traversal).
|
||||
func Walk(path string, followSymlinks bool, detectSymlinkInfiniteLoop bool, walkFn WalkFunc) error {
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var symlinkPathsFollowed map[string]bool
|
||||
var resolvedPath string
|
||||
if followSymlinks {
|
||||
resolvedPath = path
|
||||
if detectSymlinkInfiniteLoop {
|
||||
symlinkPathsFollowed = make(map[string]bool, 8)
|
||||
}
|
||||
}
|
||||
|
||||
return newWalker(path).walk(path, info, resolvedPath, symlinkPathsFollowed, walkFn)
|
||||
}
|
||||
|
||||
// walk walks the path. It is a helper/sibling function to Walk.
|
||||
// It takes a resolvedPath into consideration. This way, paths being walked are
|
||||
// always relative to the path argument, even if symbolic links were resolved).
|
||||
//
|
||||
// If resolvedPath is "", then we are not following symbolic links.
|
||||
// If symlinkPathsFollowed is not nil, then we need to detect infinite loop.
|
||||
func (w *walker) walk(path string, info os.FileInfo, resolvedPath string, symlinkPathsFollowed map[string]bool, walkFn WalkFunc) error {
|
||||
if info == nil {
|
||||
return errors.New("walk: Nil FileInfo passed")
|
||||
}
|
||||
err := walkFn(resolvedPath, info, nil)
|
||||
if err != nil {
|
||||
if info.IsDir() && errors.Is(err, ErrWalkSkipDir) {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if resolvedPath != "" && info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
// We only want to lstat on directories. If this entry is a symbolic link to a file, no need to recurse.
|
||||
statInfo, err := os.Stat(resolvedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !statInfo.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
path2, err := filepath.EvalSymlinks(resolvedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// vout("SymLink Path: %v, links to: %v", resolvedPath, path2)
|
||||
if symlinkPathsFollowed != nil {
|
||||
if _, ok := symlinkPathsFollowed[path2]; ok {
|
||||
errMsg := "potential symLink infinite loop, path: %v, link to: %v"
|
||||
return fmt.Errorf(errMsg, resolvedPath, path2)
|
||||
}
|
||||
symlinkPathsFollowed[path2] = true
|
||||
}
|
||||
info2, err := os.Lstat(path2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return w.walk(path, info2, path2, symlinkPathsFollowed, walkFn)
|
||||
} else if info.IsDir() {
|
||||
list, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return walkFn(resolvedPath, info, err)
|
||||
}
|
||||
var subFiles = make([]subFile, 0)
|
||||
for _, file := range list {
|
||||
path2 := filepath.Join(path, file.Name())
|
||||
var resolvedPath2 string
|
||||
if resolvedPath != "" {
|
||||
resolvedPath2 = filepath.Join(resolvedPath, file.Name())
|
||||
}
|
||||
fileInfo, err := file.Info()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read file info: %v, path: %v", file.Name(), path2)
|
||||
}
|
||||
|
||||
subFiles = append(subFiles, subFile{path: path2, resolvedPath: resolvedPath2, fileInfo: fileInfo})
|
||||
}
|
||||
|
||||
if w.containsDistFolder(subFiles) {
|
||||
err := w.walk(
|
||||
filepath.Join(path, "dist"),
|
||||
info,
|
||||
filepath.Join(resolvedPath, "dist"),
|
||||
symlinkPathsFollowed,
|
||||
walkFn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
for _, p := range subFiles {
|
||||
err = w.walk(p.path, p.fileInfo, p.resolvedPath, symlinkPathsFollowed, walkFn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// containsDistFolder returns true if the provided subFiles is a folder named "dist".
|
||||
func (w *walker) containsDistFolder(subFiles []subFile) bool {
|
||||
for _, p := range subFiles {
|
||||
if p.fileInfo.IsDir() && p.fileInfo.Name() == "dist" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type subFile struct {
|
||||
path, resolvedPath string
|
||||
fileInfo os.FileInfo
|
||||
}
|
||||
|
||||
// CleanRelativePath returns the shortest path name equivalent to path
|
||||
// by purely lexical processing. It makes sure the provided path is rooted
|
||||
// and then uses filepath.Clean and filepath.Rel to make sure the path
|
||||
// doesn't include any separators or elements that shouldn't be there
|
||||
// like ., .., //.
|
||||
func CleanRelativePath(path string) (string, error) {
|
||||
cleanPath := filepath.Clean(filepath.Join("/", path))
|
||||
rel, err := filepath.Rel("/", cleanPath)
|
||||
if err != nil {
|
||||
// slash is prepended above therefore this is not expected to fail
|
||||
return "", err
|
||||
}
|
||||
|
||||
return rel, nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCleanRelativePath(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input string
|
||||
expectedPath string
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expectedPath: ".",
|
||||
},
|
||||
{
|
||||
input: filepath.Join(string(filepath.Separator), "test", "test.txt"),
|
||||
expectedPath: filepath.Join("test", "test.txt"),
|
||||
},
|
||||
{
|
||||
input: filepath.Join("..", "..", "test", "test.txt"),
|
||||
expectedPath: filepath.Join("test", "test.txt"),
|
||||
},
|
||||
{
|
||||
// since filepath.Join will remove the leading dot, we need to build the path manually
|
||||
input: "." + string(filepath.Separator) + filepath.Join("..", "test", "test.txt"),
|
||||
expectedPath: filepath.Join("test", "test.txt"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testcases {
|
||||
path, err := CleanRelativePath(tt.input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedPath, path)
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -110,7 +108,7 @@ func (f LocalFS) walkFunc(basePath string, acc map[string]struct{}) filepath.Wal
|
||||
// If a nil error is returned, the caller should take care of calling Close() the returned fs.File.
|
||||
// If the file does not exist, ErrFileNotExist is returned.
|
||||
func (f LocalFS) Open(name string) (fs.File, error) {
|
||||
cleanPath, err := util.CleanRelativePath(name)
|
||||
cleanPath, err := CleanRelativePath(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -155,7 +153,7 @@ func (f LocalFS) Files() ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clenRelPath, err := util.CleanRelativePath(relPath)
|
||||
clenRelPath, err := CleanRelativePath(relPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -124,7 +123,7 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
|
||||
res.Headers = map[string][]string{}
|
||||
}
|
||||
|
||||
proxyutil.SetProxyResponseHeaders(res.Headers)
|
||||
SetCSPHeader(res.Headers)
|
||||
ensureContentTypeHeader(res)
|
||||
}
|
||||
|
||||
@@ -281,6 +280,10 @@ func (s *Service) ValidateAdmission(ctx context.Context, req *backend.AdmissionR
|
||||
return plugin.ValidateAdmission(ctx, req)
|
||||
}
|
||||
|
||||
func SetCSPHeader(header http.Header) {
|
||||
header.Set("Content-Security-Policy", "sandbox")
|
||||
}
|
||||
|
||||
// plugin finds a plugin with `pluginID` from the registry that is not decommissioned
|
||||
func (s *Service) plugin(ctx context.Context, pluginID, pluginVersion string) (*plugins.Plugin, bool) {
|
||||
p, exists := s.pluginRegistry.Plugin(ctx, pluginID, pluginVersion)
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/pluginfakes"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestQueryData(t *testing.T) {
|
||||
@@ -157,9 +157,7 @@ func TestCheckHealth(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationCallResource(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
func TestCallResource(t *testing.T) {
|
||||
registry := pluginfakes.NewFakePluginRegistry()
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
|
||||
@@ -13,10 +13,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var walk = util.Walk
|
||||
var walk = plugins.Walk
|
||||
|
||||
var (
|
||||
ErrInvalidPluginJSONFilePath = errors.New("invalid plugin.json filepath was provided")
|
||||
@@ -215,7 +214,7 @@ func (s *LocalSource) getAbsPluginJSONPaths(path string) ([]string, error) {
|
||||
}
|
||||
|
||||
if fi.Name() == "node_modules" {
|
||||
return util.ErrWalkSkipDir
|
||||
return plugins.ErrWalkSkipDir
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
cfg *config.PluginManagementCfg
|
||||
staticRootPath string
|
||||
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, pCcfg *config.PluginManagementCfg) *Service {
|
||||
return &Service{
|
||||
cfg: pCcfg,
|
||||
staticRootPath: cfg.StaticRootPath,
|
||||
log: log.New("plugin.sources"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) List(_ context.Context) []plugins.PluginSource {
|
||||
r := []plugins.PluginSource{
|
||||
NewLocalSource(
|
||||
plugins.ClassCore,
|
||||
s.corePluginPaths(),
|
||||
),
|
||||
}
|
||||
r = append(r, s.externalPluginSources()...)
|
||||
r = append(r, s.pluginSettingSources()...)
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *Service) externalPluginSources() []plugins.PluginSource {
|
||||
localSrcs, err := DirAsLocalSources(s.cfg, s.cfg.PluginsPath, plugins.ClassExternal)
|
||||
if err != nil {
|
||||
s.log.Error("Failed to load external plugins", "error", err)
|
||||
return []plugins.PluginSource{}
|
||||
}
|
||||
|
||||
srcs := make([]plugins.PluginSource, len(localSrcs))
|
||||
for i, src := range localSrcs {
|
||||
srcs[i] = src
|
||||
}
|
||||
|
||||
return srcs
|
||||
}
|
||||
|
||||
func (s *Service) pluginSettingSources() []plugins.PluginSource {
|
||||
sources := make([]plugins.PluginSource, 0, len(s.cfg.PluginSettings))
|
||||
for _, ps := range s.cfg.PluginSettings {
|
||||
path, exists := ps["path"]
|
||||
if !exists || path == "" {
|
||||
continue
|
||||
}
|
||||
if s.cfg.DevMode {
|
||||
sources = append(sources, NewUnsafeLocalSource(plugins.ClassExternal, []string{path}))
|
||||
} else {
|
||||
sources = append(sources, NewLocalSource(plugins.ClassExternal, []string{path}))
|
||||
}
|
||||
}
|
||||
|
||||
return sources
|
||||
}
|
||||
|
||||
// corePluginPaths provides a list of the Core plugin file system paths
|
||||
func (s *Service) corePluginPaths() []string {
|
||||
datasourcePaths := filepath.Join(s.staticRootPath, "app", "plugins", "datasource")
|
||||
panelsPath := filepath.Join(s.staticRootPath, "app", "plugins", "panel")
|
||||
return []string{datasourcePaths, panelsPath}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestSources_List(t *testing.T) {
|
||||
t.Run("Plugin sources are populated by default and listed in specific order", func(t *testing.T) {
|
||||
testdata, err := filepath.Abs("../testdata")
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
StaticRootPath: testdata,
|
||||
}
|
||||
|
||||
pCfg := &config.PluginManagementCfg{
|
||||
PluginsPath: filepath.Join(testdata, "pluginRootWithDist"),
|
||||
PluginSettings: setting.PluginSettings{
|
||||
"foo": map[string]string{
|
||||
"path": filepath.Join(testdata, "test-app"),
|
||||
},
|
||||
"bar": map[string]string{
|
||||
"url": "https://grafana.plugin",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
s := ProvideService(cfg, pCfg)
|
||||
srcs := s.List(context.Background())
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
require.Len(t, srcs, 5)
|
||||
|
||||
require.Equal(t, srcs[0].PluginClass(ctx), plugins.ClassCore)
|
||||
if localSrc, ok := srcs[0].(*LocalSource); ok {
|
||||
require.Equal(t, localSrc.Paths(), []string{
|
||||
filepath.Join(testdata, "app", "plugins", "datasource"),
|
||||
filepath.Join(testdata, "app", "plugins", "panel"),
|
||||
})
|
||||
} else {
|
||||
t.Fatalf("Expected LocalSource, got %T", srcs[0])
|
||||
}
|
||||
sig, exists := srcs[0].DefaultSignature(ctx, "")
|
||||
require.True(t, exists)
|
||||
require.Equal(t, plugins.SignatureStatusInternal, sig.Status)
|
||||
require.Equal(t, plugins.SignatureType(""), sig.Type)
|
||||
require.Equal(t, "", sig.SigningOrg)
|
||||
|
||||
require.Equal(t, srcs[1].PluginClass(ctx), plugins.ClassExternal)
|
||||
if localSrc, ok := srcs[1].(*LocalSource); ok {
|
||||
require.Equal(t, localSrc.Paths(), []string{
|
||||
filepath.Join(testdata, "pluginRootWithDist", "datasource"),
|
||||
})
|
||||
} else {
|
||||
t.Fatalf("Expected LocalSource, got %T", srcs[1])
|
||||
}
|
||||
sig, exists = srcs[1].DefaultSignature(ctx, "")
|
||||
require.False(t, exists)
|
||||
require.Equal(t, plugins.Signature{}, sig)
|
||||
|
||||
require.Equal(t, srcs[2].PluginClass(ctx), plugins.ClassExternal)
|
||||
if localSrc, ok := srcs[2].(*LocalSource); ok {
|
||||
require.Equal(t, localSrc.Paths(), []string{
|
||||
filepath.Join(testdata, "pluginRootWithDist", "dist"),
|
||||
})
|
||||
} else {
|
||||
t.Fatalf("Expected LocalSource, got %T", srcs[2])
|
||||
}
|
||||
sig, exists = srcs[2].DefaultSignature(ctx, "")
|
||||
require.False(t, exists)
|
||||
require.Equal(t, plugins.Signature{}, sig)
|
||||
|
||||
require.Equal(t, srcs[3].PluginClass(ctx), plugins.ClassExternal)
|
||||
if localSrc, ok := srcs[3].(*LocalSource); ok {
|
||||
require.Equal(t, localSrc.Paths(), []string{
|
||||
filepath.Join(testdata, "pluginRootWithDist", "panel"),
|
||||
})
|
||||
} else {
|
||||
t.Fatalf("Expected LocalSource, got %T", srcs[3])
|
||||
}
|
||||
sig, exists = srcs[3].DefaultSignature(ctx, "")
|
||||
require.False(t, exists)
|
||||
require.Equal(t, plugins.Signature{}, sig)
|
||||
})
|
||||
|
||||
t.Run("Plugin sources are populated with symbolic links", func(t *testing.T) {
|
||||
testdata, err := filepath.Abs("../testdata")
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
StaticRootPath: testdata,
|
||||
}
|
||||
|
||||
pCfg := &config.PluginManagementCfg{
|
||||
PluginsPath: filepath.Join(testdata, "symbolic-plugin-dirs"),
|
||||
}
|
||||
|
||||
s := ProvideService(cfg, pCfg)
|
||||
ctx := context.Background()
|
||||
srcs := s.List(ctx)
|
||||
uris := map[plugins.Class]map[string]struct{}{}
|
||||
for _, src := range srcs {
|
||||
class := src.PluginClass(ctx)
|
||||
if _, exists := uris[class]; !exists {
|
||||
uris[class] = map[string]struct{}{}
|
||||
}
|
||||
if localSrc, ok := src.(*LocalSource); ok {
|
||||
for _, path := range localSrc.Paths() {
|
||||
uris[class][path] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(t, uris[plugins.ClassCore], map[string]struct{}{
|
||||
filepath.Join(testdata, "app", "plugins", "datasource"): {},
|
||||
filepath.Join(testdata, "app", "plugins", "panel"): {},
|
||||
}, "should include core plugins")
|
||||
|
||||
require.Equal(t, uris[plugins.ClassExternal], map[string]struct{}{
|
||||
filepath.Join(testdata, "symbolic-plugin-dirs", "plugin"): {},
|
||||
}, "should include external symlinked plugin")
|
||||
})
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -414,7 +413,7 @@ func (p *Plugin) ConvertObjects(ctx context.Context, req *backend.ConversionRequ
|
||||
}
|
||||
|
||||
func (p *Plugin) File(name string) (fs.File, error) {
|
||||
cleanPath, err := util.CleanRelativePath(name)
|
||||
cleanPath, err := CleanRelativePath(name)
|
||||
if err != nil {
|
||||
// CleanRelativePath should clean and make the path relative so this is not expected to fail
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user