-
-
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
add experimental daemon plugin for 9P2000.L support #6612
Changes from 78 commits
2a02a91
ef75786
43a38b6
7a80532
2d99302
75618a5
07534a3
0d28489
7bb358f
6a17332
090f7cd
19d5332
353007e
6e7ea19
28c5cff
81b9a06
864dbfe
35eee21
483845c
3876302
d9fd656
f48f4a8
4bb5712
760d3c6
6e87be8
7ff0d1a
99df410
dc6287d
b8c4a3c
0997454
3ee383a
15e894a
e5ec672
c653fc5
b6d1506
fbf5bca
888c33d
3017586
6507b34
4a471d6
afd9cb1
e8c1a5e
a2a9e8b
0a6110d
1dd3125
7b10b5f
82780a1
4c9a790
fb20a44
5ad516c
02f69b8
eaff0c9
3ab6029
b6dee76
770bbce
4717f73
21b2d2c
62560e4
11c5add
dd3a894
1636e1b
63f6d27
97f36a9
3c56f5f
4c1ad5d
e3db71a
b2a43b8
9cc06ae
e79e371
d1a1170
be83005
fbac898
62cf47e
e8ccc6b
6d92612
294c3ea
8ce5e51
8328485
db0bcba
7bb94a4
e029c7d
8cee086
2a495c7
161d86a
9ef8e6a
545d666
9024b95
a0465f4
2aead06
ec9f8ea
c744a00
65b677f
7ce0e9b
4c6b8ee
6e654db
3501592
07db2b9
984f155
0fa5983
c9faaa7
c7cfd71
c294ac8
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 |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
Package filesystem is an experimental package that implements the go-ipfs daemon plugin interface | ||
and defines the plugin's config structure. The plugin itself exposes file system services over a multiaddr listener. | ||
|
||
By default, we try to expose the IPFS namespace using the 9P2000.L protocol, over a unix domain socket | ||
(located at $IPFS_PATH/filesystem.9P.sock) | ||
|
||
To set the multiaddr listen address, you may use the environment variable $IPFS_FS_ADDR, or set the option in the node's config file | ||
via `ipfs config --json 'Plugins.Plugins.filesystem.Config "Config":{"Service":{"9P":"/ip4/127.0.0.1/tcp/564"}}'` | ||
To disable this plugin entirely, use: `ipfs config --json Plugins.Plugins.filesystem.Disabled true` | ||
*/ | ||
package filesystem |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package filesystem | ||
|
||
import ( | ||
"context" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/djdv/p9/p9" | ||
plugin "github.com/ipfs/go-ipfs/plugin" | ||
fsnodes "github.com/ipfs/go-ipfs/plugin/plugins/filesystem/nodes" | ||
nodeopts "github.com/ipfs/go-ipfs/plugin/plugins/filesystem/nodes/options" | ||
logging "github.com/ipfs/go-log" | ||
coreiface "github.com/ipfs/interface-go-ipfs-core" | ||
"github.com/mitchellh/mapstructure" | ||
"github.com/multiformats/go-multiaddr" | ||
manet "github.com/multiformats/go-multiaddr-net" | ||
) | ||
|
||
var ( | ||
_ plugin.PluginDaemon = (*FileSystemPlugin)(nil) // impl check | ||
|
||
// Plugins is an exported list of plugins that will be loaded by go-ipfs. | ||
Plugins = []plugin.Plugin{ | ||
&FileSystemPlugin{}, //TODO: individually name implementations: &P9{} | ||
} | ||
|
||
logger logging.EventLogger | ||
) | ||
|
||
func init() { | ||
logger = logging.Logger("plugin/filesystem") | ||
} | ||
|
||
type FileSystemPlugin struct { | ||
ctx context.Context | ||
cancel context.CancelFunc | ||
|
||
addr multiaddr.Multiaddr | ||
listener manet.Listener | ||
errorChan chan error | ||
} | ||
|
||
func (*FileSystemPlugin) Name() string { | ||
return PluginName | ||
} | ||
|
||
func (*FileSystemPlugin) Version() string { | ||
return PluginVersion | ||
} | ||
|
||
func (fs *FileSystemPlugin) Init(env *plugin.Environment) error { | ||
djdv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
logger.Info("Initializing 9P resource server...") | ||
|
||
// stabilise repo path; our template depends on this | ||
if !filepath.IsAbs(env.Repo) { | ||
absRepo, err := filepath.Abs(env.Repo) | ||
if err != nil { | ||
return err | ||
} | ||
env.Repo = absRepo | ||
} | ||
|
||
cfg := &Config{} | ||
// load config from file or initialize it | ||
if env.Config != nil { | ||
if err := mapstructure.Decode(env.Config, cfg); err != nil { | ||
return err | ||
} | ||
} else { | ||
cfg = defaultConfig() | ||
} | ||
|
||
var addrString string | ||
// allow environment variable to override config values | ||
if envAddr := os.ExpandEnv(EnvAddr); envAddr != "" { | ||
addrString = EnvAddr | ||
} else { | ||
addrString = cfg.Service[defaultService] | ||
} | ||
|
||
// expand string templates and initialize listening addr | ||
templateRepoPath := env.Repo | ||
if strings.HasPrefix(addrString, "/unix") { | ||
// prevent template from expanding to double slashed paths like `/unix//home/...` | ||
// but allow it to expand to `/unix/C:\Users...` | ||
templateRepoPath = strings.TrimPrefix(templateRepoPath, "/") | ||
} | ||
|
||
addrString = os.Expand(addrString, configVarMapper(templateRepoPath)) | ||
|
||
ma, err := multiaddr.NewMultiaddr(addrString) | ||
if err != nil { | ||
return err | ||
} | ||
fs.addr = ma | ||
|
||
logger.Info("9P resource server okay for launch") | ||
return nil | ||
} | ||
|
||
func (fs *FileSystemPlugin) Start(core coreiface.CoreAPI) error { | ||
logger.Info("Starting 9P resource server...") | ||
|
||
// make sure sockets are not in use already (if we're using them) | ||
err := removeUnixSockets(fs.addr) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fs.ctx, fs.cancel = context.WithCancel(context.Background()) | ||
fs.errorChan = make(chan error, 1) | ||
|
||
// launch the listener | ||
listener, err := manet.Listen(fs.addr) | ||
if err != nil { | ||
logger.Errorf("9P listen error: %s\n", err) | ||
return err | ||
} | ||
fs.listener = listener | ||
|
||
// construct and launch the 9P resource server | ||
opts := []nodeopts.AttachOption{nodeopts.Logger(logging.Logger("9root"))} | ||
s := p9.NewServer(fsnodes.RootAttacher(fs.ctx, core, opts...)) | ||
go func() { | ||
fs.errorChan <- s.Serve(manet.NetListener(fs.listener)) | ||
}() | ||
|
||
logger.Infof("9P service is listening on %s\n", fs.listener.Addr()) | ||
return nil | ||
} | ||
|
||
func (fs *FileSystemPlugin) Close() error { | ||
Stebalien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
logger.Info("9P server requested to close") | ||
fs.listener.Close() | ||
djdv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fs.cancel() | ||
return <-fs.errorChan | ||
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. nit: this should probably be idempotent over multiple close calls (instead of just hanging forever). The usual way to do this is to set a variable and then close a "closed" chan. To read the error, wait for the closed chan to close then read the error from the variable (no lock necessary). 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. Maybe, I don't know how we're actually using this. If it's not idempotent, we should document that. 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. You're right. However, I also had to add in a bool to signify close intent. I'm wondering if there's a better way to handle this 8cee086#diff-8d243ab8514fa1b77d41df37a81641c9R146 specifically. 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. I usually use the flag (bool) approach. However, it needs to be atomic. What if we:
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. |
||
} |
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.
Calling this returns
Error: failed to get config value: " key has no attributes"
when using the default config.Is there some flag we can pass to force creation of keys that don't exist? If not should we add one?
Otherwise, we need more instructing for the user. As it stands they'd need to manually add the Plugin section + our plugin's config within it.
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.
Not yet. Really, we should change how we do config type checking (see https://github.com/ipfs/go-ipfs/blob/7e7b76259f655c5aa321e622a38619587a5a02a8/repo/fsrepo/fsrepo.go#L589). Basically, instead of manually type checking like that, we should just set the key and see if it serializes correctly.
The tricky part will be making sure the error doesn't suck.