diff --git a/core/builder.go b/core/builder.go index 2b84c6d8399..aac0dc9fa50 100644 --- a/core/builder.go +++ b/core/builder.go @@ -15,6 +15,17 @@ import ( "go.uber.org/fx" ) +type fxOptFunc func([]fx.Option) ([]fx.Option, error) + +var fxOptionFuncs []fxOptFunc + +// RegisterFXOptionFunc registers a function that is run before the fx app is initialized, +// with the list of all fx options that will be passed during initialization, and that +// returns a new list of all fx options. +func RegisterFXOptionFunc(optFunc fxOptFunc) { + fxOptionFuncs = append(fxOptionFuncs, optFunc) +} + // from https://stackoverflow.com/a/59348871 type valueContext struct { context.Context @@ -41,12 +52,21 @@ func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) { ctx: ctx, } - app := fx.New( + opts := []fx.Option{ node.IPFS(ctx, cfg), - fx.NopLogger, - fx.Extract(n), - ) + } + for _, optFunc := range fxOptionFuncs { + var err error + opts, err = optFunc(opts) + if err != nil { + cancel() + return nil, fmt.Errorf("building fx opts: %w", err) + } + } + opts = append(opts, fx.Extract(n)) + + app := fx.New(opts...) var once sync.Once var stopErr error diff --git a/go.mod b/go.mod index 52488fdc107..d8920933018 100644 --- a/go.mod +++ b/go.mod @@ -113,7 +113,7 @@ require ( go.uber.org/zap v1.21.0 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a ) require ( diff --git a/go.sum b/go.sum index 35c276367a3..67f99c15dd7 100644 --- a/go.sum +++ b/go.sum @@ -1924,8 +1924,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= diff --git a/plugin/fx.go b/plugin/fx.go new file mode 100644 index 00000000000..ec1dfacf379 --- /dev/null +++ b/plugin/fx.go @@ -0,0 +1,16 @@ +package plugin + +import ( + "go.uber.org/fx" +) + +// PluginFx can be used to customize the fx options passed to the go-ipfs app when it is initialized. +// +// This is invasive and depends on internal details such as the structure of the dependency graph, +// so breaking changes might occur between releases. +// So it's recommended to keep this as simple as possible, and stick to overriding interfaces +// with fx.Replace() or fx.Decorate(). +type PluginFx interface { + Plugin + Options(opts []fx.Option) ([]fx.Option, error) +} diff --git a/plugin/loader/loader.go b/plugin/loader/loader.go index 116c3dfce25..bb9c15befee 100644 --- a/plugin/loader/loader.go +++ b/plugin/loader/loader.go @@ -241,7 +241,6 @@ func (loader *PluginLoader) Inject() error { for _, pl := range loader.plugins { if pl, ok := pl.(plugin.PluginIPLD); ok { - err := injectIPLDPlugin(pl) if err != nil { loader.state = loaderFailed @@ -262,6 +261,13 @@ func (loader *PluginLoader) Inject() error { return err } } + if pl, ok := pl.(plugin.PluginFx); ok { + err := injectFxPlugin(pl) + if err != nil { + loader.state = loaderFailed + return err + } + } } return loader.transition(loaderInjecting, loaderInjected) @@ -347,3 +353,8 @@ func injectTracerPlugin(pl plugin.PluginTracer) error { opentracing.SetGlobalTracer(tracer) return nil } + +func injectFxPlugin(pl plugin.PluginFx) error { + core.RegisterFXOptionFunc(pl.Options) + return nil +} diff --git a/plugin/loader/preload.go b/plugin/loader/preload.go index 6b1607dc539..4304862119d 100644 --- a/plugin/loader/preload.go +++ b/plugin/loader/preload.go @@ -4,6 +4,7 @@ import ( pluginbadgerds "github.com/ipfs/kubo/plugin/plugins/badgerds" pluginiplddagjose "github.com/ipfs/kubo/plugin/plugins/dagjose" pluginflatfs "github.com/ipfs/kubo/plugin/plugins/flatfs" + pluginfxtest "github.com/ipfs/kubo/plugin/plugins/fxtest" pluginipldgit "github.com/ipfs/kubo/plugin/plugins/git" pluginlevelds "github.com/ipfs/kubo/plugin/plugins/levelds" pluginpeerlog "github.com/ipfs/kubo/plugin/plugins/peerlog" @@ -20,4 +21,5 @@ func init() { Preload(pluginflatfs.Plugins...) Preload(pluginlevelds.Plugins...) Preload(pluginpeerlog.Plugins...) + Preload(pluginfxtest.Plugins...) } diff --git a/plugin/loader/preload_list b/plugin/loader/preload_list index 048f4fd28e8..c18ea80ccd5 100644 --- a/plugin/loader/preload_list +++ b/plugin/loader/preload_list @@ -10,3 +10,4 @@ badgerds github.com/ipfs/kubo/plugin/plugins/badgerds * flatfs github.com/ipfs/kubo/plugin/plugins/flatfs * levelds github.com/ipfs/kubo/plugin/plugins/levelds * peerlog github.com/ipfs/kubo/plugin/plugins/peerlog * +fxtest github.com/ipfs/kubo/plugin/plugins/fxtest * diff --git a/plugin/plugins/fxtest/fxtest.go b/plugin/plugins/fxtest/fxtest.go new file mode 100644 index 00000000000..aef2bc82e36 --- /dev/null +++ b/plugin/plugins/fxtest/fxtest.go @@ -0,0 +1,42 @@ +package fxtest + +import ( + "os" + + logging "github.com/ipfs/go-log" + "github.com/ipfs/kubo/plugin" + "go.uber.org/fx" +) + +var log = logging.Logger("fxtestplugin") + +var Plugins = []plugin.Plugin{ + &fxtestPlugin{}, +} + +// fxtestPlugin is used for testing the fx plugin. +// It merely adds an fx option that logs a debug statement, so we can verify that it works in tests. +type fxtestPlugin struct{} + +var _ plugin.PluginFx = (*fxtestPlugin)(nil) + +func (p *fxtestPlugin) Name() string { + return "fx-test" +} + +func (p *fxtestPlugin) Version() string { + return "0.1.0" +} + +func (p *fxtestPlugin) Init(env *plugin.Environment) error { + return nil +} + +func (p *fxtestPlugin) Options(opts []fx.Option) ([]fx.Option, error) { + if os.Getenv("TEST_FX_PLUGIN") != "" { + opts = append(opts, fx.Invoke(func() { + log.Debug("invoked test fx function") + })) + } + return opts, nil +} diff --git a/test/sharness/t0280-plugin-fx.sh b/test/sharness/t0280-plugin-fx.sh new file mode 100755 index 00000000000..ca4d45f0784 --- /dev/null +++ b/test/sharness/t0280-plugin-fx.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +test_description="Test fx plugin" + +. lib/test-lib.sh + +test_init_ipfs + +export GOLOG_LOG_LEVEL="fxtestplugin=debug" +export TEST_FX_PLUGIN=1 +test_launch_ipfs_daemon + +test_expect_success "expected log entry should be present" ' + fgrep "invoked test fx function" daemon_err >/dev/null +' + +test_kill_ipfs_daemon + +test_done