Skip to content

plugin: add a daemon plugin with access to the CoreAPI #5955

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

Merged
merged 4 commits into from
Jan 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
oldcmds "github.com/ipfs/go-ipfs/commands"
"github.com/ipfs/go-ipfs/core"
commands "github.com/ipfs/go-ipfs/core/commands"
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
nodeMount "github.com/ipfs/go-ipfs/fuse/node"
Expand All @@ -25,6 +26,7 @@ import (

ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr"
cmds "gx/ipfs/QmR77mMvvh8mJBBWQmBfQBu8oD38NUN4KE9SL2gDgAQNc6/go-ipfs-cmds"
goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
"gx/ipfs/QmTQuFQWHAWy4wMH6ZyPfGiawA5u9T8rs79FENoV8yXaoS/client_golang/prometheus"
mprome "gx/ipfs/QmVMcMs6duiwLzvhF6xWM3yc4GgjpNoctKFhvtBch5tpgo/go-metrics-prometheus"
"gx/ipfs/QmZcLBXKaFe8ND5YHPkJRAwmhJGrVsi1JqDZNyJ4nRK5Mj/go-multiaddr-net"
Expand Down Expand Up @@ -355,6 +357,18 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
return node, nil
}

// Start "core" plugins. We want to do this *before* starting the HTTP
// API as the user may be relying on these plugins.
api, err := coreapi.NewCoreAPI(node)
if err != nil {
return err
}
err = cctx.Plugins.Start(api)
if err != nil {
return err
}
node.Process().AddChild(goprocess.WithTeardown(cctx.Plugins.Close))

// construct api endpoint - every time
apiErrc, err := serveHTTPApi(req, cctx)
if err != nil {
Expand Down Expand Up @@ -391,6 +405,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
// initialize metrics collector
prometheus.MustRegister(&corehttp.IpfsNodeCollector{Node: node})

// The daemon is *finally* ready.
fmt.Printf("Daemon is ready\n")

// Give the user some immediate feedback when they hit C-c
Expand Down
2 changes: 1 addition & 1 deletion cmd/ipfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func loadPlugins(repoPath string) (*loader.PluginLoader, error) {
log.Error("error initializing plugins: ", err)
}

if err := plugins.Run(); err != nil {
if err := plugins.Inject(); err != nil {
log.Error("error running plugins: ", err)
}
return plugins, nil
Expand Down
16 changes: 16 additions & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ related commands.

Datastore plugins add support for additional datastore backends.

### Tracer

(experimental)

Tracer plugins allow injecting an opentracing backend into go-ipfs.
Copy link
Member

@magik6k magik6k Jan 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaik there is a repo with this plugin somewhere, would be nice to have a link

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linked.


### Daemon

Daemon plugins are started when the go-ipfs daemon is started and are given an
instance of the CoreAPI. This should make it possible to build an ipfs-based
application without IPC and without forking go-ipfs.

Note: We eventually plan to make go-ipfs usable as a library. However, this
plugin type is likely the best interim solution.

## Available Plugins

| Name | Type | Preloaded | Description |
Expand All @@ -38,6 +53,7 @@ Datastore plugins add support for additional datastore backends.
| [badgerds](https://github.com/ipfs/go-ipfs/tree/master/plugin/plugins/badgerds) | Datastore | x | A high performance but experimental datastore. |
| [flatfs](https://github.com/ipfs/go-ipfs/tree/master/plugin/plugins/flatfs) | Datastore | x | A stable filesystem-based datastore. |
| [levelds](https://github.com/ipfs/go-ipfs/tree/master/plugin/plugins/levelds) | Datastore | x | A stable, flexible datastore backend. |
| [jaeger](https://github.com/ipfs/go-jaeger-plugin) | Tracing | | An opentracing backend. |

* **Preloaded** plugins are built into the go-ipfs binary and do not need to be
installed separately. At the moment, all in-tree plugins are preloaded.
Expand Down
14 changes: 14 additions & 0 deletions plugin/daemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package plugin

import (
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
)

// PluginDaemon is an interface for daemon plugins. These plugins will be run on
// the daemon and will be given access to an implementation of the CoreAPI.
type PluginDaemon interface {
Plugin

Start(coreiface.CoreAPI) error
Close() error
}
79 changes: 62 additions & 17 deletions plugin/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package loader

import (
"fmt"
"github.com/ipfs/go-ipfs/core/coredag"
"github.com/ipfs/go-ipfs/plugin"
"github.com/ipfs/go-ipfs/repo/fsrepo"
"os"
"strings"

coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
coredag "github.com/ipfs/go-ipfs/core/coredag"
plugin "github.com/ipfs/go-ipfs/plugin"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"

ipld "gx/ipfs/QmRL22E4paat7ky7vx9MLpR97JHHbFPrg3ytFQw6qp1y1s/go-ipld-format"
opentracing "gx/ipfs/QmWLWmRVSiagqP15jczsGME1qpob6HDbtbHAY2he9W5iUo/opentracing-go"
Expand Down Expand Up @@ -69,7 +72,7 @@ func loadDynamicPlugins(pluginDir string) ([]plugin.Plugin, error) {
return loadPluginsFunc(pluginDir)
}

//Initialize all loaded plugins
// Initialize initializes all loaded plugins
func (loader *PluginLoader) Initialize() error {
for _, p := range loader.plugins {
err := p.Init()
Expand All @@ -81,41 +84,83 @@ func (loader *PluginLoader) Initialize() error {
return nil
}

//Run the plugins
func (loader *PluginLoader) Run() error {
// Inject hooks all the plugins into the appropriate subsystems.
func (loader *PluginLoader) Inject() error {
for _, pl := range loader.plugins {
switch pl := pl.(type) {
case plugin.PluginIPLD:
err := runIPLDPlugin(pl)
if pl, ok := pl.(plugin.PluginIPLD); ok {
err := injectIPLDPlugin(pl)
if err != nil {
return err
}
}
if pl, ok := pl.(plugin.PluginTracer); ok {
err := injectTracerPlugin(pl)
if err != nil {
return err
}
case plugin.PluginTracer:
err := runTracerPlugin(pl)
}
if pl, ok := pl.(plugin.PluginDatastore); ok {
err := injectDatastorePlugin(pl)
if err != nil {
return err
}
case plugin.PluginDatastore:
err := fsrepo.AddDatastoreConfigHandler(pl.DatastoreTypeName(), pl.DatastoreConfigParser())
}
}
return nil
}

// Start starts all long-running plugins.
func (loader *PluginLoader) Start(iface coreiface.CoreAPI) error {
for i, pl := range loader.plugins {
if pl, ok := pl.(plugin.PluginDaemon); ok {
err := pl.Start(iface)
if err != nil {
closePlugins(loader.plugins[i:])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is a bug. It should read loader.plugins[:i] as all plugins before i have been started not those after i.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be the case, do you mind sending a PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll send a PR.

return err
}
default:
panic(pl)
}
}
return nil
}

func runIPLDPlugin(pl plugin.PluginIPLD) error {
// StopDaemon stops all long-running plugins.
func (loader *PluginLoader) Close() error {
return closePlugins(loader.plugins)
}

func closePlugins(plugins []plugin.Plugin) error {
var errs []string
for _, pl := range plugins {
if pl, ok := pl.(plugin.PluginDaemon); ok {
err := pl.Close()
if err != nil {
errs = append(errs, fmt.Sprintf(
"error closing plugin %s: %s",
pl.Name(),
err.Error(),
))
}
}
}
if errs != nil {
return fmt.Errorf(strings.Join(errs, "\n"))
}
return nil
}

func injectDatastorePlugin(pl plugin.PluginDatastore) error {
return fsrepo.AddDatastoreConfigHandler(pl.DatastoreTypeName(), pl.DatastoreConfigParser())
}

func injectIPLDPlugin(pl plugin.PluginIPLD) error {
err := pl.RegisterBlockDecoders(ipld.DefaultBlockDecoder)
if err != nil {
return err
}
return pl.RegisterInputEncParsers(coredag.DefaultInputEncParsers)
}

func runTracerPlugin(pl plugin.PluginTracer) error {
func injectTracerPlugin(pl plugin.PluginTracer) error {
tracer, err := pl.InitTracer()
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion repo/fsrepo/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestDefaultDatastoreConfig(t *testing.T) {
t.Fatal(err)
}

err = loader.Run()
err = loader.Inject()
if err != nil {
t.Fatal(err)
}
Expand Down