On Fri, Apr 10, 2020 at 02:51:52PM +0100, Richard W.M. Jones wrote:
Similar to C, OCaml and Rust, this is not a plugin per se. Instead
it's more of a method and set of tests around writing plugins in
golang. They are standalone programs that compile into shared objects
that nbdkit can then load (so there is no "go plugin" between nbdkit
and the user plugin, unlike in scripting languages like Perl).
Why did you choose this approach ?
Looking at the code below there's the general boilerplate that
will be approx the same for all plugins with cut+paste tedium
across projects, and then there's the "interesting" code in
test.go
The methods in test.go though look quite unappealing from a
Go programmer's POV, as the API contracts are full of CGo
types and unsafe pointers.
To make something that is attractive for Go programmers, I
think it needs to hide all the low level CGo stuff entirely.
Implementing a nbdkit plugin should require nothing more
that providing pure Go code that satisfies a well defined
Go "interface" type definition. There should not be any
copy+paste of example boilerplate, nor any use of CGo.
As an illustration, consider an interface and basic infrastructure:
package nbdkitplugin
type NBDKitFeature int
const (
NBDKitFeatureGetSize NBDKitFeature = iota
NBDKitFeaturePread
...other optional methods...
)
type NBDKitPlugin interface {
NBDGetFeatures() []NBDKitFeature
NBDOpen(readonly bool)
NBDClose()
NBDGetSize() int64
NBDPRead(count uint32, offset uint64, flags uint32) ([]byte)
....many other methods...
}
var plugin *C.struct_nbdkit_plugin
var pluginImpl NBDKitPlugin
func PluginInit(name string, impl NBDKitPlugin) {
pluginImpl = impl
features = impl.GetFeatures()
plugin.name = C.CString(name)
plugin.open = (*[0]byte)(C.open_wrapper)
plugin.close = (*[0]byte)(C.close_wrapper)
for _, feature := range features {
switch feature {
case NBDKitFeatureGetSize:
plugin.get_size = (*[0]byte)(C.get_size_wrapper)
case NBDKitFeaturePread:
plugin.pread = (*[0]byte)(C.pread_wrapper)
}
}
}
....all the methods like C.pread_wrapper/C.get_size_wrapper
need to invoke pluginImpl methods....
// This type implements all methods in NBDKitPlugin, with
// no-op impls. This means that people implementing plugins
// don't need to implement every method in the interface,
// only the few they care about
type PluginBase struct {
}
// We don't implement anytrhing by default
func (plugin *PluginBase) NBDGetFeatures() []NBDKitFeature {
return []NBDKitFeature{}
}
func (plugin *PluginBase) NBDOpen(readonly bool) {}
func (plugin *PluginBase) NBDClose() {}
func (plugin *PluginBase) NBDGetSize() int64 { return 0 }
}
func (plugin *PluginBase) NBDPRead(count uint32, offset uint64, flags uint32) []byte {
return []byte{}
}
... no-op impls of all other methods for NBDKitPlugin interface...
This code above would all be a standalone go module that is just
imported.
Now an impl of a plugin becomes just one single file
import (
libguestfs.org/nbdkitplugin
)
type TestPlugin struct {
nbdkitplugin.PluginBase // This provides no-op impls of all methods
...blah...
}
func NewTestPlugin() NBDKitPlugin {
return &TestPlugin{
....blah...
}
}
// This declares which methods we're actually implementing
func (plugin *TestPlugin) NBDGetFeatures() []NBDKitFeature {
return []NBDKitFeature{
NBDKitFeatureGetSize,
NBDKitFeaturePread,
}
}
func (plugin *TestPlugin) NBDOpen(readonly bool) {
nbdkit.Debug("golang code running in the .open callback")
}
func (plugin *TestPlugin) NBDClose() {
nbdkit.Debug("golang code running in the .close callback")
}
func (plugin *TestPlugin) NBDGetSize() int64 {
nbdkit.Debug("golang code running in the .get_size callback")
return 1024 * 1024
}
func (plugin *TestPlugin) NBDPRead(count uint32, offset uint64, flags uint32) []byte {
nbdkit.Debug("golang code running in the .pread callback")
return []byte{}
}
... don't need to implement any other methods, since PluginBase
satisfies the interface contract....
func init() {
nbdkitplugin.PluginInitialize("test", NewTestPlugin())
}
Regards,
Daniel
--
|:
https://berrange.com -o-
https://www.flickr.com/photos/dberrange :|
|:
https://libvirt.org -o-
https://fstop138.berrange.com :|
|:
https://entangle-photo.org -o-
https://www.instagram.com/dberrange :|