pluggable

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Apr 28, 2022 License: MIT Imports: 11 Imported by: 0

README

Bhojpur ISO - Pluggable Framework

The pluggable is a light Bus-event driven plugin library. It implements the event/sub pattern to extend your project with external binary plugins that can be written in any language.

import "github.com/bhojpur/iso/pkg/manager/pluggable"

func main() {

    var myEv pluggableEventType = "something.to.hook.on"
    temp := "/usr/custom/bin"

    m = pluggable.NewManager(
        []pluggable.EventType{
            myEv,
        },
    )
        
    // Load plugins
    m.Autoload("test", temp) // Scan for binary plugins with the "test" prefix. E.g. 'test-foo'
    m.Plugin = append(m.Plugin, pluggable.Plugin{ Name: "foo" , Executable: "path" }) // manually add a Plugin
    m.Load("my-binary", "my-binary-2"...) // Load individually, scanning $PATH

    // Register to events and initialize the manager
    m.Register()

    // Optionally process plugin results response
    // The plugins has to return as output a json in stdout in the format { 'state': "somestate", data: "some data", error: "some error" }
    // e.g. with jq:  
    // jq --arg key0   'state' \
    // --arg value0 '' \
    // --arg key1   'data' \
    // --arg value1 "" \
    // --arg key2   'error' \
    // --arg value2 '' \
    // '. | .[$key0]=$value0 | .[$key1]=$value1 | .[$key2]=$value2' \
    // <<<'{}'
    m.Response(myEv, func(p *pluggable.Plugin, r *pluggable.EventResponse) { ... }) 

    // Emit events, they are encoded and passed as JSON payloads to the plugins.
    // In our case, test-foo will receive the map as JSON
    m.Publish(myEv,  map[string]string{"foo": "bar"})
}

Plugin processed data

The interface passed to Publish gets marshalled in JSON in a event struct of the following form:

type Event struct {
	Name EventType `json:"name"`
	Data string    `json:"data"`
	File string    `json:"file"`
}

An example bash plugin could be, for example:

#!/bin/bash

event="$1"
payload="$2"
if [ "$event" == "something.to.hook.on" ]; then
  custom_data=$(echo "$payload" | jq -r .data | jq -r .foo )
  ...
fi

which could be called by

m.Publish(myEv,  map[string]string{"foo": "bar"})

NOTE: when the payload exceeds the threshold size the payload published with Publish is written into a temporary file and the file location is sent to the plugin with the Event file field, so for example, a plugin should expect data in a file if the publisher expects to send big chunk of data:

#!/bin/bash
data_file="$(echo $2 | jq -r .file)"
if [ -n "${data_file}" ]; then
    payload="$(cat $data_file)"

...
fi

Writing Plugin

It is present a FactoryPlugin, which allows to create plugins, consider:

import "github.com/bhojpur/iso/pkg/manager/pluggable"

func main() {
    var myEv pluggableEventType = "event"

    factory := pluggable.NewPluginFactory()
    factory.Add(myEv, func(e *Event) EventResponse { return EventResponse{ ... })

    factory.Run(os.Args[1], os.Args[2], os.Stdout)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Event

type Event struct {
	Name EventType `json:"name"`
	Data string    `json:"data"`
	File string    `json:"file"` // If Data >> 10K write content to file instead
}

Event describes the event structure. Contains a Name field and a Data field which is marshalled in JSON

func NewEvent

func NewEvent(name EventType, obj interface{}) (*Event, error)

NewEvent returns a new event which can be used for publishing the obj gets automatically serialized in json.

func (Event) Copy

func (e Event) Copy() *Event

Copy returns a copy of Event

func (Event) JSON

func (e Event) JSON() (string, error)

JSON returns the stringified JSON of the Event

func (Event) ResponseEventName

func (e Event) ResponseEventName(s string) EventType

type EventResponse

type EventResponse struct {
	State string `json:"state"`
	Data  string `json:"data"`
	Error string `json:"error"`
}

EventResponse describes the event response structure It represent the JSON response from plugins

func (EventResponse) Errored

func (r EventResponse) Errored() bool

Errored returns true if the response contains an error

func (EventResponse) Unmarshal

func (r EventResponse) Unmarshal(i interface{}) error

Unmarshal decodes the json payload in the given parameteer

type EventType

type EventType string

EventType describes an event type

type FactoryPlugin

type FactoryPlugin struct {
	EventType     EventType
	PluginHandler PluginHandler
}

type Manager

type Manager struct {
	Plugins []Plugin
	Events  []EventType
	Bus     *emission.Emitter
}

Manager describes a set of Plugins and a set of Event types which are subscribed to a message bus

func NewManager

func NewManager(events []EventType) *Manager

NewManager returns a manager instance with a new bus and

func (*Manager) Autoload

func (m *Manager) Autoload(prefix string, extensionpath ...string) *Manager

Autoload automatically loads plugins binaries prefixed by 'prefix' in the current path optionally takes a list of paths to look also into

func (*Manager) Load

func (m *Manager) Load(extname ...string) *Manager

Load finds the binaries given as parameter (without path) and scan the system $PATH to retrieve those automatically

func (*Manager) Publish

func (m *Manager) Publish(event EventType, obj interface{}) (*Manager, error)

Publish is a wrapper around NewEvent and the Manager internal Bus publishing system It accepts optionally a list of functions that are called with the plugin result (only once)

func (*Manager) Register

func (m *Manager) Register() *Manager

Register subscribes the plugin to its internal bus

func (*Manager) Response

func (m *Manager) Response(event EventType, listener ...func(p *Plugin, r *EventResponse)) *Manager

Response binds a set of listeners to an event type. The listeners are called for each result from every plugin when Publish is called.

func (*Manager) Subscribe

func (m *Manager) Subscribe(b *emission.Emitter) *Manager

Subscribe subscribes the plugin to the events in the given bus

type Plugin

type Plugin struct {
	Name       string
	Executable string
}

Plugin describes binaries to be hooked on events, with common js input, and common js output

func (Plugin) Run

func (p Plugin) Run(e Event) (EventResponse, error)

Run runs the Event on the plugin, and returns an EventResponse

type PluginFactory

type PluginFactory map[EventType]PluginHandler

PluginFactory is a collection of handlers for a given event type. a plugin has to respond to multiple events and it always needs to return an Event response as result

func NewPluginFactory

func NewPluginFactory(p ...FactoryPlugin) PluginFactory

func (PluginFactory) Add

func (p PluginFactory) Add(ev EventType, ph PluginHandler)

Add associates an handler to an event type

func (PluginFactory) Run

func (p PluginFactory) Run(name EventType, payload string, w io.Writer) error

Run runs the PluginHandler given a event type and a payload

The result is written to the writer provided as argument.

type PluginHandler

type PluginHandler func(*Event) EventResponse

PluginHandler represent a generic plugin which talks go-pluggable API It receives an event, and is always expected to give a response

Jump to

Keyboard shortcuts

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