glick

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

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

Go to latest
Published: May 3, 2016 License: MIT Imports: 21 Imported by: 8

README

Versatile plugin framework for Go

This repository contains the "glick" plug-in framework, which is a work-in-progress.

Why "glick"? Well the framework is written in "go" and intended to be as easy to build with as lego bricks which "click" together, hence "glick".

The key features of Glick are:

  • Named APIs, described as Go types in and out of a plugin.
  • Different "actions" on that same API, but running different code.
  • Plugins can be configured using a simple JSON configuration file.
  • At runtime, the context of the call can be used to re-route it to different plugin code.

Plugin code can run:

  • within the application;
  • as a sub-process, either simple or structured as an RPC;
  • or remotely via: simple URL get, standard go RPC, via go-kit or using gRPC (with more to come).

For a more detailed overivew of the package see: doc.go

For a simple example see: examples_test.go

Dependencies

Besides the standard packages, "glick" relies on the Context of a request: https://golang.org/x/net/context and https://golang.org/x/net/context/ctxhttp

Additionally, "glick/glpie" provides an interface to Nate Finch's PIE package: https://github.com/natefinch/pie

The tests in "glick/glgrpc" provide example code to interface with gRPC, Go package at: https://google.golang.org/grpc

The package "glick/glkit" provides an interface to go-kit and alse requires: https://gopkg.in/logfmt.v0 and https://gopkg.in/stack.v1

Testing

In order to run the tests for the "glick/glpie" sub-package the server counterpart executables need to be built. "glick/glpie/_test/build_tests.sh" provides a bash script for doing this, it must be run from the directory it is in.

The code has only been tested on OSX & Ubuntu.

Documentation

Overview

Package glick provides a simple plug-in environment.

The central feature of glick is the Library which contains example types for the input and output of each API on the system. Each of these APIs can have a number of "actions" upon them, for example a file conversion API may have one action for each of the file formats to be convereted. Using the Run() method of glick.Library, a given API/Action combination runs the code in a function of Go type Plugin.

Although it is easy to create your own plugins, there are three types built-in: Remote Procedure Calls (RPC), simple URL fetch (URL) and OS commands (CMD). A number of sub-packages simplify the use of third-party libraries when providing further types of plugin.

The mapping of which plugin code to run occurs at three levels:

1) Intialisation and set-up code for the application will establish the glick.Library using glick.New(), then add API specifications using RegAPI(), it may also add the application's base plugins using RegPlugin().

2) The base set-up can be extended and overloaded using a JSON format configuration description (probaly held in a file) by calling the Config() method of glick.Library. This configuration process is extensible, using the AddConfigurator() method - see the glick/glpie or glick/glkit sub-pakages for examples.

3) Which plugin to use can also be set-up or overloaded at runtime within Run(). Each call to a plugin includes a Context (as described in https://blog.golang.org/context). This context can contain for example user details, which could be matched against a database to see if that user should be directed to one plugin for a given action, rather than another. It could also be used to wrap every plugin call by a particular user with some other code, for example to log or meter activity.

Example
package main

import (
	"fmt"
	"time"

	"github.com/documize/glick"
	"golang.org/x/net/context"
)

func main() {

	goDatePlugin := func(ctx context.Context, in interface{}) (interface{}, error) {
		return time.Now().String(), nil
	}

	runtimeRerouter := func(ctx context.Context, api, action string, handler glick.Plugin) (context.Context, glick.Plugin, error) {
		// if we hit a particular set of circumstances return the go version
		if ctx.Value("bingo") != nil && api == "timeNow" && action == "lookup" {
			return ctx, goDatePlugin, nil
		}
		// otherwise return what we we were planning to do anyway
		return ctx, handler, nil
	}

	lib, nerr := glick.New(runtimeRerouter)
	if nerr != nil {
		fmt.Println(nerr)
		return
	}

	timeNowAPIproto := ""
	if err := lib.RegAPI("timeNow", timeNowAPIproto,
		func() interface{} { return timeNowAPIproto },
		time.Second); err != nil {
		fmt.Println(err)
		return
	}

	// the set-up version of the plugin, in Go
	if err := lib.RegPlugin("timeNow", "lookup", goDatePlugin, nil); err != nil {
		fmt.Println(err)
		return
	}

	ctx := context.Background()

	lookup := func() {
		if S, err := lib.Run(ctx, "timeNow", "lookup", ""); err != nil {
			fmt.Println(err)
		} else {
			fmt.Println(S)
		}
	}

	lookup() // should run the go version

	// now overload an os version of timeNow/lookup via a JSON config
	if err := lib.Configure([]byte(`[
{"Plugin":"OS-style-date","API":"timeNow","Actions":["lookup"],"Type":"CMD","Cmd":["date"]}
		]`)); err != nil {
		fmt.Println(err)
	}

	lookup() // should run the os command 'date' and print the output

	// now set a specific context to be picked-up in runtimeRerouter
	ctx = context.WithValue(ctx, "bingo", "house")

	lookup() // should run the go version again after being re-routed

}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNilLib means the library pointer is nil.
	ErrNilLib = errors.New("nil library")
	// ErrNilAPI means an API value is nil.
	ErrNilAPI = errors.New("nil api")
)
View Source
var ErrNotText = errors.New("interface value is not string or []byte, or pointer to string or []byte")

ErrNotText not a simple text value held in string or []byte, or a pointer to them.

View Source
var InsecureSkipVerifyTLS = false

InsecureSkipVerifyTLS should only be set to true when testing.

Functions

func ConfigCmd

func ConfigCmd(lib *Library) error

ConfigCmd provides the Configurator for plugins that run operating system commands.

func ConfigGetURL

func ConfigGetURL(lib *Library) error

ConfigGetURL provides the Configurator for the URL class of plugins that fetch the content of URLs.

func ConfigRPC

func ConfigRPC(lib *Library) error

ConfigRPC provides the Configurator for the RPC class of plugin.

func IsText

func IsText(t interface{}) bool

IsText defines what can be a textual value, that is one of: string, *string, []byte or *[]byte.

func Port

func Port(configJSONpath, pluginServerName string) (string, error)

Port returns the first port number it comes across for a given Plugin name in a json config file, in the form: ":9999". TODO add tests for this code.

func TextBytes

func TextBytes(t interface{}) ([]byte, error)

TextBytes returns a []byte when given a textual value that is one of: string, *string, []byte or *[]byte.

func TextConvert

func TextConvert(b []byte, model interface{}) (interface{}, error)

TextConvert takes a []byte value and returs a new textual value of the same type as the model, that is one of: string, *string, []byte or *[]byte.

func TextReader

func TextReader(t interface{}) (io.Reader, error)

TextReader returns an io.Reader when given a textual value that is one of: string, *string, []byte or *[]byte.

Types

type Config

type Config struct {
	Plugin  string   // name of the plugin server, used to configure URL ports.
	API     string   // must already exist.
	Actions []string // these must be unique within the API.
	Token   string   // authorisation string to pass in the API, if it contains a Token field.
	Type    string   // the type of plugin, e.g. "RPC","URL","CMD"...
	Method  string   // the service method to use in the plugin, if relavent.
	Path    string   // path to the end-point for "RPC" or "URL".
	Cmd     []string // command to run to start an image in "CMD", or to start a local "RPC" server.
	Comment string   // a place to put comments about the entry.

	// bools at the end to make the structure smaller
	Disabled bool // disable the plugin(s) or plugin server by setting this to true.
	Gob      bool // should the plugin use GOB encoding rather than JSON, if relavent.
	Static   bool // only used by "URL" to signal a static address.
}

Config defines a line in the JSON configuration file for a glick Libarary.

type Configurator

type Configurator func(lib *Library, line int, cfg *Config) error

Configurator is a type of function that allows plug-in fuctionality to the Config process.

type Library

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

Library holds the registered API and plugin database.

func New

func New(ov Overloader) (*Library, error)

New returns an initialized Library.

func (*Library) Actions

func (l *Library) Actions(api string) ([]string, error)

Actions provides the names of all registered plugin actions for an api.

func (*Library) AddConfigurator

func (l *Library) AddConfigurator(name string, cfg Configurator) error

AddConfigurator adds a type of configuration to the library.

func (*Library) Config

func (l *Library) Config(api, action string) *Config

Config returns a pointer to the JSON Config struct for a given API and Action, or nil if no Config exists.

func (*Library) Configure

func (l *Library) Configure(b []byte) error

Configure takes a JSON-encoded byte slice and configures the plugins for a library from it. NOTE: duplicate actions overload earlier versions.

func (*Library) Disable

func (l *Library) Disable(api string, actions []string)

Disable an existing plugin.

func (*Library) KillSubProcs

func (l *Library) KillSubProcs() error

KillSubProcs created by StartLocalRPCservers() (or eventually maybe elsewhere).

func (*Library) ProtoPlugOut

func (l *Library) ProtoPlugOut(api string) (ppo ProtoPlugOut, err error)

ProtoPlugOut provides the way to return a function to create the output for a plugin.

func (*Library) RegAPI

func (l *Library) RegAPI(api string, inPrototype interface{}, outPlugProto ProtoPlugOut, timeout time.Duration) error

RegAPI allows registration of a named API. The in/out prototype defines the type that must be passed in and out. The timeout gives the maximum time that a Plugin using this API may take to execute.

func (*Library) RegPlugin

func (l *Library) RegPlugin(api, action string, handler Plugin, cfg *Config) error

RegPlugin registers a Plugger to use for this action on an api. Duplicate actions simply overload what is there.

func (*Library) Run

func (l *Library) Run(ctx context.Context, api, action string, in interface{}) (out interface{}, err error)

Run a plugin for a given action on an API, passing data in/out. The library overloader function may decide from the context that a non-standard action should be run.

func (*Library) StartLocalRPCservers

func (l *Library) StartLocalRPCservers(stdOut, stdErr io.Writer) error

StartLocalRPCservers starts up local RPC server plugins. TODO add tests.

func (*Library) Token

func (l *Library) Token(api, action string) string

Token is a convenience function that returns the Token string for a given API and Action, if one exists.

func (*Library) ValidTypes

func (l *Library) ValidTypes() []string

ValidTypes returns all the valid plugin type names.

type Overloader

type Overloader func(ctx context.Context, api, action string, handler Plugin) (context.Context, Plugin, error)

Overloader allows the standard system settings for an API to be overloaded, depending on the context passed in.

type Plugin

type Plugin func(ctx context.Context, in interface{}) (out interface{}, err error)

Plugin type provides the type of the every plugin function, it has the same signature as Endpoint in "github.com/go-kit/kit".

func PluginCmd

func PluginCmd(cmd []string, model interface{}) Plugin

PluginCmd only works with an api with a simple Text/Text signature. it runs the given operating system command using the input string as stdin and putting stdout into the output string. At present, to limit stress on system resources, only one os command can run at a time via this plugin sub-system.

func PluginGetURL

func PluginGetURL(static bool, uri string, model interface{}) Plugin

PluginGetURL fetches the content of a URL, which could be static or dynamic (passed in). It only works with an api with a simple Text/Text signature.

func PluginRPC

func PluginRPC(useJSON bool, serviceMethod, endPoint string, ppo ProtoPlugOut) Plugin

PluginRPC returns a type which implements the Plugger interface for making an RPC. The return type of this class of plugin must be a pointer. The plugin creates a client per call to allow services to go up-and-down between calls.

type ProtoPlugOut

type ProtoPlugOut func() interface{}

ProtoPlugOut provides a prototype for the output of a Plugger

Directories

Path Synopsis
Package glkit enables integration with gokit.io from the glick library.
Package glkit enables integration with gokit.io from the glick library.
Package glpie exists to allow use of "github.com/natefinch/pie" (a toolkit for creating plugins for Go applications) from the glick package.
Package glpie exists to allow use of "github.com/natefinch/pie" (a toolkit for creating plugins for Go applications) from the glick package.

Jump to

Keyboard shortcuts

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