mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-21 10:27:46 +08:00
plugins: add support for plugin configs
For now, configs specified in `daemon --init-config` and `init CONFIG` are not available. We should fix this eventually but isn't necessary for now (and supporting this will be annoying).
This commit is contained in:
parent
d2a1ce3c5c
commit
150b6dd1bd
@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"time"
|
||||
@ -46,22 +45,9 @@ const (
|
||||
)
|
||||
|
||||
func loadPlugins(repoPath string) (*loader.PluginLoader, error) {
|
||||
pluginpath := filepath.Join(repoPath, "plugins")
|
||||
|
||||
plugins, err := loader.NewPluginLoader()
|
||||
plugins, err := loader.NewPluginLoader(repoPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading preloaded plugins: %s", err)
|
||||
}
|
||||
|
||||
// check if repo is accessible before loading plugins
|
||||
ok, err := checkPermissions(repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
if err := plugins.LoadDirectory(pluginpath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("error loading plugins: %s", err)
|
||||
}
|
||||
|
||||
if err := plugins.Initialize(); err != nil {
|
||||
@ -282,20 +268,6 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
|
||||
return http.NewClient(host, opts...), nil
|
||||
}
|
||||
|
||||
func checkPermissions(path string) (bool, error) {
|
||||
_, err := os.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
// repo does not exist yet - don't load plugins, but also don't fail
|
||||
return false, nil
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
// repo is not accessible. error out.
|
||||
return false, fmt.Errorf("error opening repository at %s: permission denied", path)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// commandDetails returns a command's details for the command given by |path|.
|
||||
func commandDetails(path []string) cmdDetails {
|
||||
if len(path) == 0 {
|
||||
|
||||
@ -57,6 +57,12 @@ func (c *Context) GetNode() (*core.IpfsNode, error) {
|
||||
return nil, errors.New("nil ConstructNode function")
|
||||
}
|
||||
c.node, err = c.ConstructNode()
|
||||
if err == nil {
|
||||
// Pre-load the config from the repo to avoid re-parsing it from disk.
|
||||
if cfg, err := c.node.Repo.Config(); err != nil {
|
||||
c.config = cfg
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.node, err
|
||||
}
|
||||
|
||||
@ -21,6 +21,9 @@ directory (by default `~/.ipfs/plugins`).
|
||||
|
||||
## Plugin Types
|
||||
|
||||
Plugins can implement one or more plugin types, defined in the
|
||||
[plugin](https://godoc.org/github.com/ipfs/go-ipfs/plugin) package.
|
||||
|
||||
### IPLD
|
||||
|
||||
IPLD plugins add support for additional formats to `ipfs dag` and other IPLD
|
||||
|
||||
2
go.mod
2
go.mod
@ -31,7 +31,7 @@ require (
|
||||
github.com/ipfs/go-ipfs-blockstore v0.1.0
|
||||
github.com/ipfs/go-ipfs-chunker v0.0.1
|
||||
github.com/ipfs/go-ipfs-cmds v0.1.0
|
||||
github.com/ipfs/go-ipfs-config v0.0.6
|
||||
github.com/ipfs/go-ipfs-config v0.0.11
|
||||
github.com/ipfs/go-ipfs-ds-help v0.0.1
|
||||
github.com/ipfs/go-ipfs-exchange-interface v0.0.1
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.0.1
|
||||
|
||||
4
go.sum
4
go.sum
@ -251,8 +251,8 @@ github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcB
|
||||
github.com/ipfs/go-ipfs-cmds v0.1.0 h1:0CEde9EcxByej8+L6d1PST57J4ambRPyCTjLG5Ymou8=
|
||||
github.com/ipfs/go-ipfs-cmds v0.1.0/go.mod h1:TiK4e7/V31tuEb8YWDF8lN3qrnDH+BS7ZqWIeYJlAs8=
|
||||
github.com/ipfs/go-ipfs-config v0.0.5/go.mod h1:IGkVTacurWv9WFKc7IBPjHGM/7hi6+PEClqUb/l2BIM=
|
||||
github.com/ipfs/go-ipfs-config v0.0.6 h1:jzK9Tl8S0oWBir3F5ObtGgnHRPdqQ0MYiCmwXtV3Ps4=
|
||||
github.com/ipfs/go-ipfs-config v0.0.6/go.mod h1:IGkVTacurWv9WFKc7IBPjHGM/7hi6+PEClqUb/l2BIM=
|
||||
github.com/ipfs/go-ipfs-config v0.0.11 h1:5/4nas2CQXiKr2/MLxU24GDGTBvtstQIQezuk7ltOQQ=
|
||||
github.com/ipfs/go-ipfs-config v0.0.11/go.mod h1:wveA8UT5ywN26oKStByzmz1CO6cXwLKKM6Jn/Hfw08I=
|
||||
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
|
||||
github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
|
||||
github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
|
||||
|
||||
@ -3,8 +3,11 @@ package loader
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
config "github.com/ipfs/go-ipfs-config"
|
||||
cserialize "github.com/ipfs/go-ipfs-config/serialize"
|
||||
coredag "github.com/ipfs/go-ipfs/core/coredag"
|
||||
plugin "github.com/ipfs/go-ipfs/plugin"
|
||||
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||
@ -83,16 +86,32 @@ type PluginLoader struct {
|
||||
state loaderState
|
||||
plugins map[string]plugin.Plugin
|
||||
started []plugin.Plugin
|
||||
config config.Plugins
|
||||
repo string
|
||||
}
|
||||
|
||||
// NewPluginLoader creates new plugin loader
|
||||
func NewPluginLoader() (*PluginLoader, error) {
|
||||
loader := &PluginLoader{plugins: make(map[string]plugin.Plugin, len(preloadPlugins))}
|
||||
func NewPluginLoader(repo string) (*PluginLoader, error) {
|
||||
loader := &PluginLoader{plugins: make(map[string]plugin.Plugin, len(preloadPlugins)), repo: repo}
|
||||
if repo != "" {
|
||||
cfg, err := cserialize.Load(filepath.Join(repo, config.DefaultConfigFile))
|
||||
switch err {
|
||||
case cserialize.ErrNotInitialized:
|
||||
case nil:
|
||||
loader.config = cfg.Plugins
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, v := range preloadPlugins {
|
||||
if err := loader.Load(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := loader.LoadDirectory(filepath.Join(repo, "plugins")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loader, nil
|
||||
}
|
||||
|
||||
@ -125,6 +144,10 @@ func (loader *PluginLoader) Load(pl plugin.Plugin) error {
|
||||
"while trying to load dynamically: %s",
|
||||
name, ppl.Version(), pl.Version())
|
||||
}
|
||||
if loader.config.Plugins[name].Disabled {
|
||||
log.Infof("not loading disabled plugin %s", name)
|
||||
return nil
|
||||
}
|
||||
loader.plugins[name] = pl
|
||||
return nil
|
||||
}
|
||||
@ -164,8 +187,11 @@ func (loader *PluginLoader) Initialize() error {
|
||||
if err := loader.transition(loaderLoading, loaderInitializing); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range loader.plugins {
|
||||
err := p.Init()
|
||||
for name, p := range loader.plugins {
|
||||
err := p.Init(&plugin.Environment{
|
||||
Repo: loader.repo,
|
||||
Config: loader.config.Plugins[name].Config,
|
||||
})
|
||||
if err != nil {
|
||||
loader.state = loaderFailed
|
||||
return err
|
||||
|
||||
@ -1,12 +1,25 @@
|
||||
package plugin
|
||||
|
||||
// Environment is the environment passed into the plugin on init.
|
||||
type Environment struct {
|
||||
// Path to the IPFS repo.
|
||||
Repo string
|
||||
|
||||
// The plugin's config, if specified.
|
||||
Config interface{}
|
||||
}
|
||||
|
||||
// Plugin is base interface for all kinds of go-ipfs plugins
|
||||
// It will be included in interfaces of different Plugins
|
||||
type Plugin interface {
|
||||
// Name should return unique name of the plugin
|
||||
Name() string
|
||||
|
||||
// Version returns current version of the plugin
|
||||
Version() string
|
||||
|
||||
// Init is called once when the Plugin is being loaded
|
||||
Init() error
|
||||
// The plugin is passed an environment containing the path to the
|
||||
// (possibly uninitialized) IPFS repo and the plugin's config.
|
||||
Init(env *Environment) error
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ func (*badgerdsPlugin) Version() string {
|
||||
return "0.1.0"
|
||||
}
|
||||
|
||||
func (*badgerdsPlugin) Init() error {
|
||||
func (*badgerdsPlugin) Init(_ *plugin.Environment) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ func (*flatfsPlugin) Version() string {
|
||||
return "0.1.0"
|
||||
}
|
||||
|
||||
func (*flatfsPlugin) Init() error {
|
||||
func (*flatfsPlugin) Init(_ *plugin.Environment) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ func (*gitPlugin) Version() string {
|
||||
return "0.0.1"
|
||||
}
|
||||
|
||||
func (*gitPlugin) Init() error {
|
||||
func (*gitPlugin) Init(_ *plugin.Environment) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ func (*leveldsPlugin) Version() string {
|
||||
return "0.1.0"
|
||||
}
|
||||
|
||||
func (*leveldsPlugin) Init() error {
|
||||
func (*leveldsPlugin) Init(_ *plugin.Environment) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ var measureConfig = []byte(`{
|
||||
}`)
|
||||
|
||||
func TestDefaultDatastoreConfig(t *testing.T) {
|
||||
loader, err := loader.NewPluginLoader()
|
||||
loader, err := loader.NewPluginLoader("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -22,9 +22,9 @@ test_expect_success "ipfs init fails" '
|
||||
# Under Windows/Cygwin the error message is different,
|
||||
# so we use the STD_ERR_MSG prereq.
|
||||
if test_have_prereq STD_ERR_MSG; then
|
||||
init_err_msg="Error: error opening repository at $IPFS_PATH: permission denied"
|
||||
init_err_msg="Error: error loading plugins: open $IPFS_PATH/config: permission denied"
|
||||
else
|
||||
init_err_msg="Error: mkdir $IPFS_PATH: The system cannot find the path specified."
|
||||
init_err_msg="Error: error loading plugins: open $IPFS_PATH/config: The system cannot find the path specified."
|
||||
fi
|
||||
|
||||
test_expect_success "ipfs init output looks good" '
|
||||
|
||||
30
test/sharness/t0280-plugin-data/example.go
Normal file
30
test/sharness/t0280-plugin-data/example.go
Normal file
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ipfs/go-ipfs/plugin"
|
||||
)
|
||||
|
||||
var Plugins = []plugin.Plugin{
|
||||
&testPlugin{},
|
||||
}
|
||||
|
||||
var _ = Plugins // used
|
||||
|
||||
type testPlugin struct{}
|
||||
|
||||
func (*testPlugin) Name() string {
|
||||
return "test-plugin"
|
||||
}
|
||||
|
||||
func (*testPlugin) Version() string {
|
||||
return "0.1.0"
|
||||
}
|
||||
|
||||
func (*testPlugin) Init(env *plugin.Environment) error {
|
||||
fmt.Fprintf(os.Stderr, "testplugin %s\n", env.Repo)
|
||||
fmt.Fprintf(os.Stderr, "testplugin %v\n", env.Config)
|
||||
return nil
|
||||
}
|
||||
@ -8,6 +8,12 @@ test_description="Test plugin loading"
|
||||
|
||||
. lib/test-lib.sh
|
||||
|
||||
if ! test_have_prereq PLUGIN; then
|
||||
skip_all='skipping plugin tests, plugins not available'
|
||||
|
||||
test_done
|
||||
fi
|
||||
|
||||
test_init_ipfs
|
||||
|
||||
test_expect_success "ipfs id succeeds" '
|
||||
@ -28,4 +34,59 @@ test_expect_success "cleanup bad plugin" '
|
||||
rm "$IPFS_PATH/plugins/foo.so"
|
||||
'
|
||||
|
||||
test_expect_success "install test plugin" '
|
||||
go build \
|
||||
-asmflags=all="-trimpath=${GOPATH}" -gcflags=all="-trimpath=${GOPATH}" \
|
||||
-buildmode=plugin -o "$IPFS_PATH/plugins/example.so" ../t0280-plugin-data/example.go &&
|
||||
chmod +x "$IPFS_PATH/plugins/example.so"
|
||||
'
|
||||
|
||||
test_plugin() {
|
||||
local loads="$1"
|
||||
local repo="$2"
|
||||
local config="$3"
|
||||
|
||||
rm -f id_raw_output id_output id_output_expected
|
||||
|
||||
test_expect_success "id runs" '
|
||||
ipfs id 2>id_raw_output >/dev/null
|
||||
'
|
||||
|
||||
test_expect_success "filter test plugin output" '
|
||||
sed -ne "s/^testplugin //p" id_raw_output >id_output
|
||||
'
|
||||
|
||||
if [ "$loads" != "true" ]; then
|
||||
test_expect_success "plugin doesn't load" '
|
||||
test_must_be_empty id_output
|
||||
'
|
||||
else
|
||||
test_expect_success "plugin produces the correct output" '
|
||||
echo "$repo" >id_output_expected &&
|
||||
echo "$config" >>id_output_expected &&
|
||||
test_cmp id_output id_output_expected
|
||||
'
|
||||
fi
|
||||
}
|
||||
|
||||
test_plugin true "$IPFS_PATH" "<nil>"
|
||||
|
||||
test_expect_success "disable the plugin" '
|
||||
ipfs config --json Plugins.Plugins.test-plugin.Disabled true
|
||||
'
|
||||
|
||||
test_plugin false
|
||||
|
||||
test_expect_success "re-enable the plugin" '
|
||||
ipfs config --json Plugins.Plugins.test-plugin.Disabled false
|
||||
'
|
||||
|
||||
test_plugin true "$IPFS_PATH" "<nil>"
|
||||
|
||||
test_expect_success "configure the plugin" '
|
||||
ipfs config Plugins.Plugins.test-plugin.Config foobar
|
||||
'
|
||||
|
||||
test_plugin true "$IPFS_PATH" "foobar"
|
||||
|
||||
test_done
|
||||
|
||||
Loading…
Reference in New Issue
Block a user