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

@ -20,7 +20,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/log/logtest"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/pluginscdn"
@ -270,14 +269,9 @@ func Test_GetPluginAssets(t *testing.T) {
requestedFile := filepath.Clean(tmpFile.Name())
t.Run("Given a request for an existing plugin file", func(t *testing.T) {
p := &plugins.Plugin{
JSONData: plugins.JSONData{
ID: pluginID,
},
PluginDir: pluginDir,
}
p := createPluginDTO(plugins.JSONData{ID: pluginID}, plugins.External, plugins.NewLocalFS(map[string]struct{}{requestedFile: {}}, filepath.Dir(requestedFile)))
service := &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{p.ToDTO()},
PluginList: []plugins.PluginDTO{p},
}
url := fmt.Sprintf("/public/plugins/%s/%s", pluginID, requestedFile)
@ -291,7 +285,7 @@ func Test_GetPluginAssets(t *testing.T) {
})
t.Run("Given a request for a relative path", func(t *testing.T) {
p := createPluginDTO(plugins.JSONData{ID: pluginID}, plugins.External, pluginDir)
p := createPluginDTO(plugins.JSONData{ID: pluginID}, plugins.External, plugins.NewLocalFS(map[string]struct{}{}, ""))
service := &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{p},
}
@ -305,8 +299,26 @@ func Test_GetPluginAssets(t *testing.T) {
})
})
t.Run("Given a request for an existing plugin file that is not listed as a signature covered file", func(t *testing.T) {
p := createPluginDTO(plugins.JSONData{ID: pluginID}, plugins.Core, plugins.NewLocalFS(map[string]struct{}{
requestedFile: {},
}, ""))
service := &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{p},
}
url := fmt.Sprintf("/public/plugins/%s/%s", pluginID, requestedFile)
pluginAssetScenario(t, "When calling GET on", url, "/public/plugins/:pluginId/*",
setting.NewCfg(), service, func(sc *scenarioContext) {
callGetPluginAsset(sc)
require.Equal(t, 200, sc.resp.Code)
assert.Equal(t, expectedBody, sc.resp.Body.String())
})
})
t.Run("Given a request for an non-existing plugin file", func(t *testing.T) {
p := createPluginDTO(plugins.JSONData{ID: pluginID}, plugins.External, pluginDir)
p := createPluginDTO(plugins.JSONData{ID: pluginID}, plugins.External, plugins.NewLocalFS(map[string]struct{}{}, ""))
service := &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{p},
}
@ -329,7 +341,6 @@ func Test_GetPluginAssets(t *testing.T) {
service := &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{},
}
l := &logtest.Fake{}
requestedFile := "nonExistent"
url := fmt.Sprintf("/public/plugins/%s/%s", pluginID, requestedFile)
@ -342,29 +353,6 @@ func Test_GetPluginAssets(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 404, sc.resp.Code)
require.Equal(t, "Plugin not found", respJson["message"])
require.Zero(t, l.WarnLogs.Calls)
})
})
t.Run("Given a request for a core plugin's file", func(t *testing.T) {
service := &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{
{
JSONData: plugins.JSONData{ID: pluginID},
Class: plugins.Core,
},
},
}
l := &logtest.Fake{}
url := fmt.Sprintf("/public/plugins/%s/%s", pluginID, requestedFile)
pluginAssetScenario(t, "When calling GET on", url, "/public/plugins/:pluginId/*",
setting.NewCfg(), service, func(sc *scenarioContext) {
callGetPluginAsset(sc)
require.Equal(t, 200, sc.resp.Code)
require.Equal(t, expectedBody, sc.resp.Body.String())
require.Zero(t, l.WarnLogs.Calls)
})
})
}
@ -546,40 +534,19 @@ func (c *fakePluginClient) QueryData(ctx context.Context, req *backend.QueryData
}
func Test_PluginsList_AccessControl(t *testing.T) {
p1 := &plugins.Plugin{
PluginDir: "/grafana/plugins/test-app/dist",
Class: plugins.External,
DefaultNavURL: "/plugins/test-app/page/test",
Signature: plugins.SignatureUnsigned,
Module: "plugins/test-app/module",
BaseURL: "public/plugins/test-app",
JSONData: plugins.JSONData{
ID: "test-app",
Type: plugins.App,
Name: "test-app",
Info: plugins.Info{
Version: "1.0.0",
},
},
}
p2 := &plugins.Plugin{
PluginDir: "/grafana/public/app/plugins/datasource/mysql",
Class: plugins.Core,
Pinned: false,
Signature: plugins.SignatureInternal,
Module: "app/plugins/datasource/mysql/module",
BaseURL: "public/app/plugins/datasource/mysql",
JSONData: plugins.JSONData{
ID: "mysql",
Type: plugins.DataSource,
Name: "MySQL",
p1 := createPluginDTO(plugins.JSONData{
ID: "test-app", Type: "app", Name: "test-app",
Info: plugins.Info{
Version: "1.0.0",
}}, plugins.External, plugins.NewLocalFS(map[string]struct{}{}, ""))
p2 := createPluginDTO(
plugins.JSONData{ID: "mysql", Type: "datasource", Name: "MySQL",
Info: plugins.Info{
Author: plugins.InfoLink{Name: "Grafana Labs", URL: "https://grafana.com"},
Description: "Data source for MySQL databases",
},
},
}
pluginStore := plugins.FakePluginStore{PluginList: []plugins.PluginDTO{p1.ToDTO(), p2.ToDTO()}}
}}, plugins.Core, plugins.NewLocalFS(map[string]struct{}{}, ""))
pluginStore := plugins.FakePluginStore{PluginList: []plugins.PluginDTO{p1, p2}}
pluginSettings := pluginsettings.FakePluginSettings{Plugins: map[string]*pluginsettings.DTO{
"test-app": {ID: 0, OrgID: 1, PluginID: "test-app", PluginVersion: "1.0.0", Enabled: true},
@ -630,11 +597,12 @@ func Test_PluginsList_AccessControl(t *testing.T) {
}
}
func createPluginDTO(jd plugins.JSONData, class plugins.Class, pluginDir string) plugins.PluginDTO {
func createPluginDTO(jd plugins.JSONData, class plugins.Class, files plugins.FS) plugins.PluginDTO {
p := &plugins.Plugin{
JSONData: jd,
Class: class,
PluginDir: pluginDir,
JSONData: jd,
Class: class,
FS: files,
}
return p.ToDTO()
}