Plugins: Add Plugin FS abstraction (#63734)

* unexport pluginDir from dto

* first pass

* tidy

* naming + add mutex

* add dupe checking

* fix func typo

* interface + move logic from renderer

* remote finder

* remote signing

* fix tests

* tidy up

* tidy markdown logic

* split changes

* fix tests

* slim interface down

* fix status code

* tidy exec path func

* fixup

* undo changes

* remove unused func

* remove unused func

* fix goimports

* fetch remotely

* simultaneous support

* fix linter

* use var

* add exception for gosec warning

* fixup

* fix tests

* tidy

* rework cfg pattern

* simplify

* PR feedback

* fix dupe field

* remove g304 nolint

* apply PR feedback

* remove unnecessary gosec nolint

* fix finder loop and update comment

* fix map alloc

* fix test

* remove commented code
This commit is contained in:
Will Browne
2023-03-07 15:47:02 +00:00
committed by GitHub
parent 380138f57b
commit 68df83c86d
22 changed files with 1344 additions and 870 deletions

View File

@ -2,7 +2,7 @@ package loader
import (
"context"
"errors"
"os"
"path/filepath"
"sort"
"testing"
@ -20,11 +20,25 @@ import (
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting"
)
var compareOpts = cmpopts.IgnoreFields(plugins.Plugin{}, "client", "log")
var compareOpts = []cmp.Option{cmpopts.IgnoreFields(plugins.Plugin{}, "client", "log"), localFSComparer}
var localFSComparer = cmp.Comparer(func(fs1 plugins.LocalFS, fs2 plugins.LocalFS) bool {
fs1Files := fs1.Files()
fs2Files := fs2.Files()
sort.SliceStable(fs1Files, func(i, j int) bool {
return fs1Files[i] < fs1Files[j]
})
sort.SliceStable(fs2Files, func(i, j int) bool {
return fs2Files[i] < fs2Files[j]
})
return cmp.Equal(fs1Files, fs2Files) && fs1.Base() == fs2.Base()
})
func TestLoader_Load(t *testing.T) {
corePluginDir, err := filepath.Abs("./../../../../public")
@ -86,9 +100,11 @@ func TestLoader_Load(t *testing.T) {
Backend: true,
QueryOptions: map[string]bool{"minInterval": true},
},
Module: "app/plugins/datasource/cloudwatch/module",
BaseURL: "public/app/plugins/datasource/cloudwatch",
PluginDir: filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch"),
Module: "app/plugins/datasource/cloudwatch/module",
BaseURL: "public/app/plugins/datasource/cloudwatch",
FS: plugins.NewLocalFS(
filesInDir(t, filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")),
filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")),
Signature: plugins.SignatureInternal,
Class: plugins.Core,
},
@ -125,9 +141,12 @@ func TestLoader_Load(t *testing.T) {
Backend: true,
State: "alpha",
},
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
PluginDir: filepath.Join(parentDir, "testdata/valid-v2-signature/plugin/"),
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
FS: plugins.NewLocalFS(
filesInDir(t, filepath.Join(parentDir, "testdata/valid-v2-signature/plugin/")),
filepath.Join(parentDir, "testdata/valid-v2-signature/plugin/"),
),
Signature: "valid",
SignatureType: plugins.GrafanaSignature,
SignatureOrg: "Grafana Labs",
@ -201,10 +220,20 @@ func TestLoader_Load(t *testing.T) {
},
},
},
Class: plugins.External,
Module: "plugins/test-app/module",
BaseURL: "public/plugins/test-app",
PluginDir: filepath.Join(parentDir, "testdata/includes-symlinks"),
Class: plugins.External,
Module: "plugins/test-app/module",
BaseURL: "public/plugins/test-app",
FS: plugins.NewLocalFS(
map[string]struct{}{
filepath.Join(parentDir, "testdata/includes-symlinks", "/MANIFEST.txt"): {},
filepath.Join(parentDir, "testdata/includes-symlinks", "dashboards/connections.json"): {},
filepath.Join(parentDir, "testdata/includes-symlinks", "dashboards/extra/memory.json"): {},
filepath.Join(parentDir, "testdata/includes-symlinks", "plugin.json"): {},
filepath.Join(parentDir, "testdata/includes-symlinks", "symlink_to_txt"): {},
filepath.Join(parentDir, "testdata/includes-symlinks", "text.txt"): {},
},
filepath.Join(parentDir, "testdata/includes-symlinks"),
),
Signature: "valid",
SignatureType: plugins.GrafanaSignature,
SignatureOrg: "Grafana Labs",
@ -241,10 +270,13 @@ func TestLoader_Load(t *testing.T) {
Backend: true,
State: plugins.AlphaRelease,
},
Class: plugins.External,
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
PluginDir: filepath.Join(parentDir, "testdata/unsigned-datasource/plugin"),
Class: plugins.External,
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
FS: plugins.NewLocalFS(
filesInDir(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
filepath.Join(parentDir, "testdata/unsigned-datasource/plugin"),
),
Signature: "unsigned",
},
},
@ -292,10 +324,13 @@ func TestLoader_Load(t *testing.T) {
Backend: true,
State: plugins.AlphaRelease,
},
Class: plugins.External,
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
PluginDir: filepath.Join(parentDir, "testdata/unsigned-datasource/plugin"),
Class: plugins.External,
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
FS: plugins.NewLocalFS(
filesInDir(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
filepath.Join(parentDir, "testdata/unsigned-datasource/plugin"),
),
Signature: plugins.SignatureUnsigned,
},
},
@ -399,11 +434,14 @@ func TestLoader_Load(t *testing.T) {
Backend: false,
},
DefaultNavURL: "/plugins/test-app/page/root-page-react",
PluginDir: filepath.Join(parentDir, "testdata/test-app-with-includes"),
Class: plugins.External,
Signature: plugins.SignatureUnsigned,
Module: "plugins/test-app/module",
BaseURL: "public/plugins/test-app",
FS: plugins.NewLocalFS(map[string]struct{}{
filepath.Join(parentDir, "testdata/test-app-with-includes", "dashboards/memory.json"): {},
filepath.Join(parentDir, "testdata/test-app-with-includes", "plugin.json"): {},
}, filepath.Join(parentDir, "testdata/test-app-with-includes")),
Class: plugins.External,
Signature: plugins.SignatureUnsigned,
Module: "plugins/test-app/module",
BaseURL: "public/plugins/test-app",
},
},
},
@ -454,7 +492,9 @@ func TestLoader_Load(t *testing.T) {
Plugins: []plugins.Dependency{},
},
},
PluginDir: filepath.Join(parentDir, "testdata/cdn/plugin"),
FS: plugins.NewLocalFS(map[string]struct{}{
filepath.Join(parentDir, "testdata/cdn/plugin", "plugin.json"): {},
}, filepath.Join(parentDir, "testdata/cdn/plugin")),
Class: plugins.External,
Signature: plugins.SignatureValid,
BaseURL: "plugin-cdn/grafana-worldmap-panel/0.3.3/public/plugins/grafana-worldmap-panel",
@ -478,8 +518,8 @@ func TestLoader_Load(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
got, err := l.Load(context.Background(), tt.class, tt.pluginPaths)
require.NoError(t, err)
if !cmp.Equal(got, tt.want, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, tt.want, compareOpts))
if !cmp.Equal(got, tt.want, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, tt.want, compareOpts...))
}
pluginErrs := l.PluginErrors()
@ -600,10 +640,13 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
Executable: "test",
State: plugins.AlphaRelease,
},
Class: plugins.External,
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
PluginDir: filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin"),
Class: plugins.External,
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
FS: plugins.NewLocalFS(map[string]struct{}{
filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin/plugin.json"): {},
filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin/MANIFEST.txt"): {},
}, filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin")),
Signature: "valid",
SignatureType: plugins.PrivateSignature,
SignatureOrg: "Will Browne",
@ -641,8 +684,8 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
sort.SliceStable(got, func(i, j int) bool {
return got[i].ID < got[j].ID
})
if !cmp.Equal(got, tt.want, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, tt.want, compareOpts))
if !cmp.Equal(got, tt.want, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, tt.want, compareOpts...))
}
pluginErrs := l.PluginErrors()
require.Equal(t, len(tt.pluginErrors), len(pluginErrs))
@ -717,7 +760,10 @@ func TestLoader_Load_RBACReady(t *testing.T) {
},
Backend: false,
},
PluginDir: pluginDir,
FS: plugins.NewLocalFS(map[string]struct{}{
filepath.Join(pluginDir, "plugin.json"): {},
filepath.Join(pluginDir, "MANIFEST.txt"): {},
}, pluginDir),
Class: plugins.External,
Signature: plugins.SignatureValid,
SignatureType: plugins.PrivateSignature,
@ -749,8 +795,8 @@ func TestLoader_Load_RBACReady(t *testing.T) {
got, err := l.Load(context.Background(), plugins.External, tt.pluginPaths)
require.NoError(t, err)
if !cmp.Equal(got, tt.want, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, tt.want, compareOpts))
if !cmp.Equal(got, tt.want, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, tt.want, compareOpts...))
}
pluginErrs := l.PluginErrors()
require.Len(t, pluginErrs, 0)
@ -799,7 +845,10 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) {
Backend: true,
Executable: "test",
},
PluginDir: filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin"),
FS: plugins.NewLocalFS(map[string]struct{}{
filepath.Join(filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin"), "plugin.json"): {},
filepath.Join(filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin"), "MANIFEST.txt"): {},
}, filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin")),
Class: plugins.External,
Signature: plugins.SignatureValid,
SignatureType: plugins.PrivateSignature,
@ -822,8 +871,8 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) {
got, err := l.Load(context.Background(), plugins.External, paths)
require.NoError(t, err)
if !cmp.Equal(got, expected, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts))
if !cmp.Equal(got, expected, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts...))
}
verifyState(t, expected, reg, procPrvdr, storage, procMgr)
})
@ -878,7 +927,7 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
},
Backend: false,
},
PluginDir: pluginDir,
FS: plugins.NewLocalFS(filesInDir(t, pluginDir), pluginDir),
Class: plugins.External,
Signature: plugins.SignatureValid,
SignatureType: plugins.GrafanaSignature,
@ -901,8 +950,8 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
got, err := l.Load(context.Background(), plugins.External, []string{pluginDir, pluginDir})
require.NoError(t, err)
if !cmp.Equal(got, expected, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts))
if !cmp.Equal(got, expected, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts...))
}
verifyState(t, expected, reg, procPrvdr, storage, procMgr)
@ -939,9 +988,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
},
Backend: true,
},
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
PluginDir: filepath.Join(rootDir, "testdata/nested-plugins/parent"),
Module: "plugins/test-datasource/module",
BaseURL: "public/plugins/test-datasource",
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/nested-plugins/parent")),
filepath.Join(rootDir, "testdata/nested-plugins/parent")),
Signature: plugins.SignatureValid,
SignatureType: plugins.GrafanaSignature,
SignatureOrg: "Grafana Labs",
@ -971,9 +1021,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
Plugins: []plugins.Dependency{},
},
},
Module: "plugins/test-panel/module",
BaseURL: "public/plugins/test-panel",
PluginDir: filepath.Join(rootDir, "testdata/nested-plugins/parent/nested"),
Module: "plugins/test-panel/module",
BaseURL: "public/plugins/test-panel",
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/nested-plugins/parent/nested")),
filepath.Join(rootDir, "testdata/nested-plugins/parent/nested")),
Signature: plugins.SignatureValid,
SignatureType: plugins.GrafanaSignature,
SignatureOrg: "Grafana Labs",
@ -1004,8 +1055,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
})
expected := []*plugins.Plugin{parent, child}
if !cmp.Equal(got, expected, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts))
if !cmp.Equal(got, expected, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts...))
}
verifyState(t, expected, reg, procPrvdr, storage, procMgr)
@ -1019,8 +1070,8 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
return got[i].ID < got[j].ID
})
if !cmp.Equal(got, []*plugins.Plugin{}, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts))
if !cmp.Equal(got, []*plugins.Plugin{}, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts...))
}
verifyState(t, expected, reg, procPrvdr, storage, procMgr)
@ -1098,9 +1149,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
},
Backend: false,
},
Module: "plugins/myorgid-simple-app/module",
BaseURL: "public/plugins/myorgid-simple-app",
PluginDir: filepath.Join(rootDir, "testdata/app-with-child/dist"),
Module: "plugins/myorgid-simple-app/module",
BaseURL: "public/plugins/myorgid-simple-app",
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/app-with-child/dist")),
filepath.Join(rootDir, "testdata/app-with-child/dist")),
DefaultNavURL: "/plugins/myorgid-simple-app/page/root-page-react",
Signature: plugins.SignatureValid,
SignatureType: plugins.GrafanaSignature,
@ -1136,9 +1188,10 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
Plugins: []plugins.Dependency{},
},
},
Module: "plugins/myorgid-simple-app/child/module",
BaseURL: "public/plugins/myorgid-simple-app",
PluginDir: filepath.Join(rootDir, "testdata/app-with-child/dist/child"),
Module: "plugins/myorgid-simple-app/child/module",
BaseURL: "public/plugins/myorgid-simple-app",
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/app-with-child/dist/child")),
filepath.Join(rootDir, "testdata/app-with-child/dist/child")),
IncludedInAppID: parent.ID,
Signature: plugins.SignatureValid,
SignatureType: plugins.GrafanaSignature,
@ -1168,205 +1221,27 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
return got[i].ID < got[j].ID
})
if !cmp.Equal(got, expected, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts))
if !cmp.Equal(got, expected, compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts...))
}
verifyState(t, expected, reg, procPrvdr, storage, procMgr)
t.Run("order of loaded parent and child plugins gives same output", func(t *testing.T) {
parentPluginJSON := filepath.Join(rootDir, "testdata/app-with-child/dist/plugin.json")
childPluginJSON := filepath.Join(rootDir, "testdata/app-with-child/dist/child/plugin.json")
reg = fakes.NewFakePluginRegistry()
storage = fakes.NewFakePluginStorage()
procPrvdr = fakes.NewFakeBackendProcessProvider()
procMgr = fakes.NewFakeProcessManager()
l = newLoader(&config.Cfg{}, func(l *Loader) {
l.pluginRegistry = reg
l.pluginStorage = storage
l.processManager = procMgr
l.pluginInitializer = initializer.New(&config.Cfg{}, procPrvdr, fakes.NewFakeLicensingService())
})
got, err = l.loadPlugins(context.Background(), plugins.External, []string{parentPluginJSON, childPluginJSON})
require.NoError(t, err)
// to ensure we can compare with expected
sort.SliceStable(got, func(i, j int) bool {
return got[i].ID < got[j].ID
})
if !cmp.Equal(got, expected, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts))
}
verifyState(t, expected, reg, procPrvdr, storage, procMgr)
reg = fakes.NewFakePluginRegistry()
storage = fakes.NewFakePluginStorage()
procPrvdr = fakes.NewFakeBackendProcessProvider()
procMgr = fakes.NewFakeProcessManager()
l = newLoader(&config.Cfg{}, func(l *Loader) {
l.pluginRegistry = reg
l.pluginStorage = storage
l.processManager = procMgr
l.pluginInitializer = initializer.New(&config.Cfg{}, procPrvdr, fakes.NewFakeLicensingService())
})
got, err = l.loadPlugins(context.Background(), plugins.External, []string{childPluginJSON, parentPluginJSON})
require.NoError(t, err)
// to ensure we can compare with expected
sort.SliceStable(got, func(i, j int) bool {
return got[i].ID < got[j].ID
})
if !cmp.Equal(got, expected, compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, expected, compareOpts))
}
verifyState(t, expected, reg, procPrvdr, storage, procMgr)
})
})
}
func TestLoader_readPluginJSON(t *testing.T) {
tests := []struct {
name string
pluginPath string
expected plugins.JSONData
failed bool
}{
{
name: "Valid plugin",
pluginPath: "../testdata/test-app/plugin.json",
expected: plugins.JSONData{
ID: "test-app",
Type: "app",
Name: "Test App",
Info: plugins.Info{
Author: plugins.InfoLink{
Name: "Test Inc.",
URL: "http://test.com",
},
Description: "Official Grafana Test App & Dashboard bundle",
Version: "1.0.0",
Links: []plugins.InfoLink{
{Name: "Project site", URL: "http://project.com"},
{Name: "License & Terms", URL: "http://license.com"},
},
Logos: plugins.Logos{
Small: "img/logo_small.png",
Large: "img/logo_large.png",
},
Screenshots: []plugins.Screenshots{
{Path: "img/screenshot1.png", Name: "img1"},
{Path: "img/screenshot2.png", Name: "img2"},
},
Updated: "2015-02-10",
},
Dependencies: plugins.Dependencies{
GrafanaVersion: "3.x.x",
Plugins: []plugins.Dependency{
{Type: "datasource", ID: "graphite", Name: "Graphite", Version: "1.0.0"},
{Type: "panel", ID: "graph", Name: "Graph", Version: "1.0.0"},
},
},
Includes: []*plugins.Includes{
{Name: "Nginx Connections", Path: "dashboards/connections.json", Type: "dashboard", Role: org.RoleViewer},
{Name: "Nginx Memory", Path: "dashboards/memory.json", Type: "dashboard", Role: org.RoleViewer},
{Name: "Nginx Panel", Type: "panel", Role: org.RoleViewer},
{Name: "Nginx Datasource", Type: "datasource", Role: org.RoleViewer},
},
Backend: false,
},
},
{
name: "Invalid plugin JSON",
pluginPath: "../testdata/invalid-plugin-json/plugin.json",
failed: true,
},
{
name: "Non-existing JSON file",
pluginPath: "nonExistingFile.json",
failed: true,
},
}
l := newLoader(nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := l.readPluginJSON(tt.pluginPath)
if (err != nil) && !tt.failed {
t.Errorf("readPluginJSON() error = %v, failed %v", err, tt.failed)
return
}
if !cmp.Equal(got, tt.expected, compareOpts) {
t.Errorf("Unexpected pluginJSONData: %v", cmp.Diff(got, tt.expected, compareOpts))
}
})
}
}
func Test_validatePluginJSON(t *testing.T) {
type args struct {
data plugins.JSONData
}
tests := []struct {
name string
args args
err error
}{
{
name: "Valid case",
args: args{
data: plugins.JSONData{
ID: "grafana-plugin-id",
Type: plugins.DataSource,
},
},
},
{
name: "Invalid plugin ID",
args: args{
data: plugins.JSONData{
Type: plugins.Panel,
},
},
err: ErrInvalidPluginJSON,
},
{
name: "Invalid plugin type",
args: args{
data: plugins.JSONData{
ID: "grafana-plugin-id",
Type: "test",
},
},
err: ErrInvalidPluginJSON,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := validatePluginJSON(tt.args.data); !errors.Is(err, tt.err) {
t.Errorf("validatePluginJSON() = %v, want %v", err, tt.err)
}
})
}
}
func Test_setPathsBasedOnApp(t *testing.T) {
t.Run("When setting paths based on core plugin on Windows", func(t *testing.T) {
child := &plugins.Plugin{
PluginDir: "c:\\grafana\\public\\app\\plugins\\app\\testdata-app\\datasources\\datasource",
FS: fakes.NewFakePluginFiles("c:\\grafana\\public\\app\\plugins\\app\\testdata-app\\datasources\\datasource"),
}
parent := &plugins.Plugin{
JSONData: plugins.JSONData{
Type: plugins.App,
ID: "testdata-app",
},
Class: plugins.Core,
PluginDir: "c:\\grafana\\public\\app\\plugins\\app\\testdata-app",
BaseURL: "public/app/plugins/app/testdata-app",
Class: plugins.Core,
FS: fakes.NewFakePluginFiles("c:\\grafana\\public\\app\\plugins\\app\\testdata-app"),
BaseURL: "public/app/plugins/app/testdata-app",
}
configureAppChildPlugin(parent, child)
@ -1395,8 +1270,8 @@ func verifyState(t *testing.T, ps []*plugins.Plugin, reg *fakes.FakePluginRegist
t.Helper()
for _, p := range ps {
if !cmp.Equal(p, reg.Store[p.ID], compareOpts) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(p, reg.Store[p.ID], compareOpts))
if !cmp.Equal(p, reg.Store[p.ID], compareOpts...) {
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(p, reg.Store[p.ID], compareOpts...))
}
if p.Backend {
@ -1418,3 +1293,40 @@ func verifyState(t *testing.T, ps []*plugins.Plugin, reg *fakes.FakePluginRegist
require.Zero(t, procMngr.Stopped[p.ID])
}
}
func filesInDir(t *testing.T, dir string) map[string]struct{} {
files, err := collectFilesWithin(dir)
if err != nil {
t.Logf("Could not collect plugin file info. Err: %v", err)
return map[string]struct{}{}
}
return files
}
func collectFilesWithin(dir string) (map[string]struct{}, error) {
files := map[string]struct{}{}
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// skip directories
if info.IsDir() {
return nil
}
// verify that file is within plugin directory
//file, err := filepath.Rel(dir, path)
//if err != nil {
// return err
//}
//if strings.HasPrefix(file, ".."+string(filepath.Separator)) {
// return fmt.Errorf("file '%s' not inside of plugin directory", file)
//}
files[path] = struct{}{}
return nil
})
return files, err
}