plugin
packageAPI reference for the plugin
package.
Imports
(10)LoadSo
LoadSo loads a Go plugin (.so) exposing a PluginFactory symbol.
Parameters
Returns
func LoadSo(path string) (Factory, error)
{
p, err := stdplugin.Open(path)
if err != nil {
return nil, err
}
symbol, err := p.Lookup("PluginFactory")
if err != nil {
return nil, err
}
if factory, ok := symbol.(func() Plugin); ok {
return factory, nil
}
if factory, ok := symbol.(*func() Plugin); ok {
return *factory, nil
}
return nil, errors.New("plugin: PluginFactory has wrong type")
}
Uses
Factory
Factory creates plugin instances on demand.
type Factory func() Plugin
FactoryRegistry
FactoryRegistry stores plugin factories by name.
type FactoryRegistry struct
Methods
Fields
| Name | Type | Description |
|---|---|---|
| byName | map[string]Factory |
NewFactoryRegistry
NewFactoryRegistry creates an empty factory registry.
Returns
func NewFactoryRegistry() *FactoryRegistry
{
return &FactoryRegistry{byName: make(map[string]Factory)}
}
Registry
Registry manages plugin registration and lifecycle in a deterministic order.
type Registry struct
Methods
DiscoverFromValues inspects provided values and registers any that implement Plugin.
Parameters
Returns
func (*Registry) DiscoverFromValues(values ...interface{}) int
{
if r == nil {
return 0
}
pluginType := reflect.TypeOf((*Plugin)(nil)).Elem()
count := 0
for _, value := range values {
if value == nil {
continue
}
t := reflect.TypeOf(value)
if t.Implements(pluginType) {
p, _ := value.(Plugin)
if p != nil && r.Register(p) == nil {
count++
}
continue
}
if t.Kind() == reflect.Ptr && t.Elem().Implements(pluginType) {
pluginValue, _ := reflect.ValueOf(value).Elem().Interface().(Plugin)
if pluginValue != nil && r.Register(pluginValue) == nil {
count++
}
}
}
return count
}
Register adds a plugin to the registry.
Parameters
Returns
func (*Registry) Register(p Plugin) error
{
r.mu.Lock()
defer r.mu.Unlock()
name := p.Name()
if _, ok := r.byName[name]; ok {
return ErrAlreadyRegistered
}
r.byName[name] = p
r.order = append(r.order, name)
return nil
}
Unregister removes a plugin by name.
Parameters
func (*Registry) Unregister(name string)
{
r.mu.Lock()
defer r.mu.Unlock()
delete(r.byName, name)
for i, current := range r.order {
if current == name {
r.order = append(r.order[:i], r.order[i+1:]...)
break
}
}
}
Get returns a plugin and whether it exists.
Parameters
Returns
func (*Registry) Get(name string) (Plugin, bool)
{
r.mu.RLock()
defer r.mu.RUnlock()
plugin, ok := r.byName[name]
return plugin, ok
}
Names returns registered plugin names in insertion order.
Returns
func (*Registry) Names() []string
{
r.mu.RLock()
defer r.mu.RUnlock()
names := make([]string, len(r.order))
copy(names, r.order)
return names
}
StartAll starts plugins in insertion order and returns any collected errors.
Returns
func (*Registry) StartAll() []error
{
names := r.Names()
var errs []error
for _, name := range names {
p, ok := r.Get(name)
if !ok {
continue
}
if err := p.Start(); err != nil {
errs = append(errs, err)
}
}
return errs
}
StopAll stops plugins in reverse insertion order and returns any collected errors.
Returns
func (*Registry) StopAll() []error
{
names := r.Names()
var errs []error
for i := len(names) - 1; i >= 0; i-- {
p, ok := r.Get(names[i])
if !ok {
continue
}
if err := p.Stop(); err != nil {
errs = append(errs, err)
}
}
return errs
}
Fields
| Name | Type | Description |
|---|---|---|
| mu | sync.RWMutex | |
| order | []string | |
| byName | map[string]Plugin |
NewRegistry
NewRegistry creates an empty plugin registry.
Returns
func NewRegistry() *Registry
{
return &Registry{byName: make(map[string]Plugin)}
}
RegisterRouteProviders
RegisterRouteProviders inspects registered plugins and forwards any routes they expose.
Parameters
func RegisterRouteProviders(r *Registry)
{
if r == nil {
return
}
for _, name := range r.Names() {
p, ok := r.Get(name)
if !ok {
continue
}
if provider, ok := p.(interface{ Routes() []registry.Route }); ok {
routes := provider.Routes()
if len(routes) > 0 {
registry.RegisterRoutes(func() []registry.Route { return routes })
}
}
}
}
ExecSandbox
ExecSandbox runs a plugin as an external process using a simple JSON-over-stdio protocol.
type ExecSandbox struct
Methods
Start launches the external process and waits for a ready signal.
Returns
func (*ExecSandbox) Start() error
{
e.mu.Lock()
defer e.mu.Unlock()
if e.cmd.Process != nil {
return errors.New("plugin: sandbox already started")
}
in, err := e.cmd.StdinPipe()
if err != nil {
return err
}
out, err := e.cmd.StdoutPipe()
if err != nil {
return err
}
e.in = in
e.out = out
if err := e.cmd.Start(); err != nil {
return err
}
decoder := json.NewDecoder(e.out)
ready := make(chan error, 1)
go func() {
var msg map[string]any
if err := decoder.Decode(&msg); err != nil {
ready <- err
return
}
if ok, _ := msg["ready"].(bool); ok {
ready <- nil
return
}
ready <- errors.New("plugin: unexpected ready message")
}()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case err := <-ready:
return err
case <-ctx.Done():
return ctx.Err()
}
}
Stop sends a stop command and waits for the external process to exit.
Returns
func (*ExecSandbox) Stop() error
{
e.mu.Lock()
defer e.mu.Unlock()
if e.cmd.Process == nil {
return errors.New("plugin: sandbox not started")
}
if err := json.NewEncoder(e.in).Encode(map[string]any{"cmd": "stop"}); err != nil {
return err
}
return e.cmd.Wait()
}
Fields
| Name | Type | Description |
|---|---|---|
| cmd | *exec.Cmd | |
| in | io.WriteCloser | |
| out | io.ReadCloser | |
| mu | sync.Mutex |
NewExecSandbox
NewExecSandbox creates a sandbox around the provided binary path and arguments.
Parameters
Returns
func NewExecSandbox(path string, args ...string) *ExecSandbox
{
return &ExecSandbox{cmd: exec.Command(path, args...)}
}