diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index a3a5df31583..13934a47fa0 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -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" @@ -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" @@ -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 { @@ -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 diff --git a/plugin/daemon.go b/plugin/daemon.go new file mode 100644 index 00000000000..50aa2ac6c5e --- /dev/null +++ b/plugin/daemon.go @@ -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 +} diff --git a/plugin/loader/loader.go b/plugin/loader/loader.go index 0e63bf1b870..67599e385fb 100644 --- a/plugin/loader/loader.go +++ b/plugin/loader/loader.go @@ -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" @@ -106,6 +109,45 @@ func (loader *PluginLoader) Inject() error { 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:]) + return err + } + } + } + return nil +} + +// 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()) }