-
-
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
plugin: create plugin API and loader, add ipld-git plugin #4033
Conversation
cc @kumavis and @hermanjunge, this will be how we load in blockchain support too |
Really cool to have plugin support finally :) However, the following disclaimer exists in the documentation: |
@victorbjelkholm its for all of the above. The plugin stuff only works on linux. If people want this on platforms other than linux, pester the go team. |
cmd/ipfs/plugin_linux.go
Outdated
LoadPlugins = LoadPluginsLinux | ||
} | ||
|
||
func LoadPluginsLinux(cfgroot string) error { |
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.
this can be private.
plugins/git/git.go
Outdated
"github.com/ipfs/go-cid" | ||
"github.com/ipfs/go-ipfs/core/coredag" | ||
git "github.com/ipfs/go-ipld-git" | ||
"gx/ipfs/QmUBtPvHKFAX43XMsyxsYpMi3U5VwZ4jYFTo4kFhvAR33G/go-ipld-format" |
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.
Spacing and go-ipld-git is not imported.
core/coredag/dagtransl.go
Outdated
var DefaultInputEncParsers = InputEncParsers{ | ||
"json": DefaultJsonParsers, | ||
"raw": DefaultRawParsers, | ||
} |
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 am not sure if making this global is the right decision.
This will make testing it harder.
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.
If we dont use a global, registration becomes a much more complicated thing to do. Any ideas on a good way to do that?
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.
Can't we do the same thing we're doing with BlockDecoder
s (pass it to register)?
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.
Yeah, I am planning that.
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.
BlockDecoder is doing the same (it is using DefaultBlockDecoder from go-ipld-format).
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.
Good point. The problem isn't really registration, it's using the the decoders (e.g. in ParseInputs
).
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.
In the Plugin API for IPLD I've sketched out I've made the registration map an argument so at least the plugins can be tested.
We have to figure out how to use the decoders well with non-global states.
cmd/ipfs/plugin_linux.go
Outdated
regfunci, err := plugin.Lookup("Register") | ||
if err != nil { | ||
return fmt.Errorf("ipld plugin had no Register function: %s", err) | ||
} |
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.
Regarding API for plugins themselves, I think it might be better to have a plugin expose a structure of callback functions, implemented interface or overridden structure. This way the API is cleanly defined in Go code instead of having to be written somewhere separately.
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.
Hrm... so we could maybe do something like:
plug, _ := plugin.Lookup("Plugin")
switch plug := plug.(type) {
case IpldPlugin:
case Libp2pPlugin:
case DatastorePlugin:
default:
// unsupported plugin type!
}
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.
That would work.
Once we decide on structure more I will add Makefile support for it. |
This may be beyond the scope of this PR but it would be really nice to make encoders/decoders composable and separate. That is, when we re-encode, always decode to a common (in-memory) format/interface and then re-encode to the target format. Unfortunately, the only way I think of doing this in go is to decode to a dynamically typed |
510f5c1
to
8c84c76
Compare
This should be ready for first CR, I will try adding some tests and more UX regarding plugins on other systems (display warnings if there is a plugin in directory but system is not supported). |
Hah, I can't request review from @whyrusleeping's own PR. |
would be pretty cool if github had a 'take over PR' button |
plugin/loader/load_linux.go
Outdated
typePls, ok := pls.([]iplugin.Plugin) | ||
if !ok { | ||
return nil, errors.New("filed 'Plugins' didn't contain correct type") | ||
} else { |
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.
no need for the else
would be pretty cool if github had a 'take over PR' button. You also probably don't want to commit the plugin |
Oops. |
a91be3c
to
9b2cfae
Compare
License: MIT Signed-off-by: Jeromy <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
Whoa, tests are green. Git IPLD plugin is being loaded dynamically and works (passes sharness tests written by @magik6k). Should be ready for CR: @whyrusleeping @Stebalien This PR contains quite a bit of Makefile magic to glue plugins and tests together, provide option for preloading plugins ( |
I just saw that coverage collection failed, so please do not merge I have to fix it first. |
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.
What I've looked at so far looks good. There are a few things I'd like to see improved eventually but they're not reasons to hold this up.
I'm a bit concerned about loading plugins from a directory owned by the user IPFS is running under but this is very convenient (and probably not a problem). However, in the future, we'll probably want to allow loading plugins out of /usr/lib/plugins/...
(for distros and package managers).
Now, it would be awesome if we could load plugins directly from IPFS but we can discuss that later.
I really don't like having one stream parser per input-output pair. However, doing this the correct way would require quit a bit of work and thought so I'm going to say that this is good enough for now.
plugin/loader/load_linux.go
Outdated
) | ||
|
||
func init() { | ||
loadPluginsFunc = linxuLoadFunc |
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.
s/linxu/linux/g
plugin/loader/initializer.go
Outdated
format "gx/ipfs/QmYNyRZJBUYPNrLszFmrBrPJbsBh2vMsefz5gnDpB5M1P6/go-ipld-format" | ||
) | ||
|
||
func initalize(plugins []plugin.Plugin) error { |
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.
s/initalize/initialize/g
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
I don't think it is possible for me to build plugins with coverage collection. This means I will either have to preload plugins for coverage collection or disable plugins during coverage collection. I think for now I will disable plugins and plugin tests during coverage collection. |
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
Coverage of the patch is low as the sharness won't work well with the plugins. I can't test the dynamic plugin loading in the sharness and this is the part that misses coverage the most. |
return nil | ||
} | ||
|
||
func runIPLDPlugin(pl plugin.Plugin) error { |
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.
do a type switch up in the 'run' function, and have this function accept the proper type. Then we can error out if the types don't match correctly instead of silently ignoring the problem (which could easily happen with a mis-compiled plugin)
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 was thinking about it, but a Plugin could in theory implement multiple interfaces, then typeswich isn't really helping us as it will choose just one interface (the first in the switch statement).
Miss-compiled plugin will error out or log if: the plugin lib can't be loaded, there is no Plugins
variable, it has wrong type.
plugin/loader/initializer.go
Outdated
} | ||
|
||
var err error | ||
err = ipldpl.RegisterBlockDecoders(format.DefaultBlockDecoder) |
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.
why not just err := ....
?
plugin/loader/initializer.go
Outdated
return err | ||
} | ||
|
||
err = ipldpl.RegisterInputEncParsers(coredag.DefaultInputEncParsers) |
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.
return ipldpl.Register....
@@ -0,0 +1,2 @@ | |||
*.so | |||
*/main |
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.
what is this main ignore for?
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.
This is autogenerated package in directory of every plugin. This is what I have to do so the plugin can be either pre-loaded (statically compiled with go-ipfs, for non-linux builds) or be compiled for dynamic linking as plugin.
Go requires plugins to be defined in package named main
, and at the same time it doesn't allow for static linking of main
package. So I have main plugin inside its own package and I generate main
package that rebinds Plugins
variable.
test/sharness/t0280-plugin-git.sh
Outdated
@@ -0,0 +1,57 @@ | |||
#!/bin/sh | |||
# | |||
# Copyright (c) 2016 Jakub Sztandera |
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.
its 2017 btw
This LGTM, great work @Kubuxu :) I'll wait for your confirmation before merging, but I think this is ready to go. |
License: MIT Signed-off-by: Jakub Sztandera <[email protected]>
This is very much a work in progress, but it works and is pretty fun to play around with.
If you want to try it, pull this branch down and:
Then start ipfs, it will now be loaded with git support. You can install https://github.com/magik6k/git-remote-ipld and then try out
git push ipld::
to push a git repo into ipfs and thengit clone ipld::GITSHA
to clone it from ipfs.