mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 08:42:19 +08:00
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:
@ -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()
|
||||
}
|
||||
|
Reference in New Issue
Block a user