-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
plugins: add support for plugin configs #6613
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was kind of useless so I didn't bother replicating it. Instead of checking permissions, we just do the thing and see if it fails. |
||
if err != nil { | ||
return nil, err | ||
} | ||
if ok { | ||
if err := plugins.LoadDirectory(pluginpath); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Called in |
||
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 { | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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) { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The repo a Plugin receives from the Environment should probably be normalized into absolute paths. Abs patch 1/2
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that we don't do this anywhere else, I'd like to leave this as it is for consistency (that way the repo path is the same everywhere). I believe it'll usually be an absolute path anyways. If we do run into issues, we should be consistent and make this path absolute at a higher layer. |
||||||||||||||
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 { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Abs patch 2/2
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general, I don't like using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically, this should either be left as-is or should be:
|
||||||||||||||
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 | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I needed the repo path so I figured I might as well move a bunch of the plugin loading logic into the loader.