pingo

package module
v0.0.0-...-b2bbeb6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 20, 2024 License: MIT Imports: 17 Imported by: 0

README

Pingo: Plugins for Go

Pingo is a simple standalone library to create plugins for your Go program. As Go is statically linked, all plugins run as external processes.

The library aims to be as simple as possible and to mimic the standard RPC package to be immediately familiar to most developers.

Pingo supports both TCP and Unix as communication protocols. However, remote plugins are currently not supported. Remote plugins might be implemented if requested.

Example

Create a new plugin. Make a directory named after the plugin (for example plugins/hello-world) and write main.go as follows:

// Always create a new binary
package main

import "github.com/dullgiulio/pingo"

// Create an object to be exported
type MyPlugin struct{}

// Exported method, with a RPC signature
func (p *MyPlugin) SayHello(name string, msg *string) error {
    *msg = "Hello, " + name
    return nil
}

func main() {
	plugin := &MyPlugin{}

	// Register the objects to be exported
	pingo.Register(plugin)
	// Run the main events handler
	pingo.Run()
}

And compile it:

$ cd plugins/hello-world
$ go build

You should get an executable called hello-world. Congratulations, this is your plugin.

Now, time to use the newly create plugin.

In your main executable, invoke the plugin you have just created:

package main

import (
	"log"
	"github.com/golango-cn/pingo"
)

func main() {
	// Make a new plugin from the executable we created. Connect to it via TCP
	p := pingo.NewPlugin("tcp", "plugins/hello-world/hello-world")
	// Actually start the plugin
	p.Start()
	// Remember to stop the plugin when done using it
	defer p.Stop()

	var resp string

	// Call a function from the object we created previously
	if err := p.Call("MyPlugin.SayHello", "Go developer", &resp); err != nil {
		log.Print(err)
	} else {
		log.Print(resp)
	}
}

Now, build your executable and all should work! Remember to use the correct path to your plugins when you make the Plugin object. Ideally, always pass an absolute path.

Unix or TCP?

When allocating a new plugin (via NewPlugin), you have to choose whether to use Unix or TCP.

In general, prefer Unix: it has way less overhead. However, if you choose Unix you should provide a writable directory where to place the temporary socket. Do so using SetSocketDirectory before you call Start.

If you do not specify a directory, the default temporary directory for your OS will be used. Note, however, that for security reasons, it might not be possible to create a socket there. It is advised to always specify a local directory.

Otherwise, the overhead of using TCP locally is negligible.

Your Pingo plugin will not accept non-local connections even via TCP.

Bugs

Report bugs in Github. Pull requests are welcome!

TODO

  • Automatically restart crashed plugins
  • Automatically switch between unix and TCP if setup of one fails

License

MIT

Documentation

Overview

Package pingo implements the basics for creating and running subprocesses as plugins. The subprocesses will communicate via either TCP or Unix socket to implement an interface that mimics the standard RPC package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Register

func Register(obj interface{})

Register a new object this plugin exports. The object must be an exported symbol and obey all rules an object in the standard "rpc" module has to obey.

Register will panic if called after Run.

func Run

func Run() error

Run will start all the necessary steps to make the plugin available.

Types

type DefaultErrorHandler

type DefaultErrorHandler struct{}

Default error handler implementation. Uses the default logging facility from the Go standard library.

func NewDefaultErrorHandler

func NewDefaultErrorHandler() *DefaultErrorHandler

Constructor for default error handler.

func (*DefaultErrorHandler) Error

func (e *DefaultErrorHandler) Error(err error)

Log via default standard library facility prepending the "error: " string.

func (*DefaultErrorHandler) Print

func (e *DefaultErrorHandler) Print(s interface{})

Log via default standard library facility.

type ErrConnectionFailed

type ErrConnectionFailed error

Error reported when connection to the external plugin has failed.

type ErrHttpServe

type ErrHttpServe error

Error reported when the external plugin cannot start listening for calls.

type ErrInvalidMessage

type ErrInvalidMessage error

Error reported when an invalid message is printed by the external plugin.

type ErrRegistrationTimeout

type ErrRegistrationTimeout error

Error reported when the plugin fails to register before the registration timeout expires.

type ErrorHandler

type ErrorHandler interface {
	// Error is called whenever a non-fatal error occurs in the plugin subprocess.
	Error(error)
	// Print is called for each line of output received from the plugin subprocess.
	Print(interface{})
}

ErrorHandler is the interface used by Plugin to report non-fatal errors and any other output from the plugin.

A default implementation is provided and used if none is specified on plugin creation.

type PingoRpc

type PingoRpc struct{}

Internal object for plugin control

func NewPingoRpc

func NewPingoRpc() *PingoRpc

Default constructor for interal object. Do not call manually.

func (*PingoRpc) Exit

func (s *PingoRpc) Exit(status int, unused *int) error

Internal RPC call to shut down a plugin. Do not call manually.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Represents a plugin. After being created the plugin is not started or ready to run.

Additional configuration (ErrorHandler and Timeout) can be set after initialization.

Use Start() to make the plugin available.

func NewPlugin

func NewPlugin(proto, path string, params ...string) *Plugin

NewPlugin create a new plugin ready to be started, or returns an error if the initial setup fails.

The first argument specifies the protocol. It can be either set to "unix" for communication on an ephemeral local socket, or "tcp" for network communication on the local host (using a random unprivileged port.)

This constructor will panic if the proto argument is neither "unix" nor "tcp".

The path to the plugin executable should be absolute. Any path accepted by the "exec" package in the standard library is accepted and the same rules for execution are applied.

Optionally some parameters might be passed to the plugin executable.

func (*Plugin) Call

func (p *Plugin) Call(name string, args interface{}, resp interface{}) error

Call performs an RPC call to the plugin. Prior to calling Call, the plugin must have been initialized by calling Start.

Call will hang until a plugin has been initialized; it will return any error that happens either when performing the call or during plugin initialization via Start.

Please refer to the "rpc" package from the standard library for more information on the semantics of this function.

func (*Plugin) Objects

func (p *Plugin) Objects() ([]string, error)

Objects returns a list of the exported objects from the plugin. Exported objects used internally are not reported.

Like Call, Objects returns any error happened on initialization if called after Start.

func (*Plugin) SetErrorHandler

func (p *Plugin) SetErrorHandler(h ErrorHandler)

Set the error (and output) handler implementation. Use this to set a custom implementation. By default, standard logging is used. See ErrorHandler.

Panics if called after Start.

func (*Plugin) SetSocketDirectory

func (p *Plugin) SetSocketDirectory(dir string)

func (*Plugin) SetTimeout

func (p *Plugin) SetTimeout(t time.Duration)

Set the maximum time a plugin is allowed to start up and to shut down. Empty timeout (zero) is not allowed, default will be used.

Default is two seconds.

Panics if called after Start.

func (*Plugin) Start

func (p *Plugin) Start()

Start will execute the plugin as a subprocess. Start will return immediately. Any first call to the plugin will reveal eventual errors occurred at initialization.

Calls subsequent to Start will hang until the plugin has been properly initialized.

func (*Plugin) Stop

func (p *Plugin) Stop()

Stop attemps to stop cleanly or kill the running plugin, then will free all resources. Stop returns when the plugin as been shut down and related routines have exited.

func (*Plugin) String

func (p *Plugin) String() string

Default string representation

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL