plugins

package module
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Jan 17, 2021 License: MIT Imports: 15 Imported by: 1

Documentation

Overview

Package plugins allows you to load arbitrary Go code after compilation. Plugins are ran through Yaegi (https://github.com/traefik/yaegi), the Go interpreter.

How it Works

Plugins are downloaded as zip files and should be saved to a specified folder (e.g. /usr/lib/program/plugins). Once you run PluginHost.LoadPlugins() the hash for the zip files are collected and stored. If there are any plugins in the cache folder, the hashes for their corresponding zip files are collected from the configuration (more on that later). The hashes are compared against each other, if there is a hash in the plugins cache that isn't in the list of zip hashes, that plugin is removed (plugins with the tag local set to true are ignored). If there is a hash for a zip file that isn't in the plugins cache, that plugin is unzipped.

Once the plugins cache has been updated, the plugins are loaded by setting the GOPATH for the interpreter to the plugin folder and importing the plugin module as specifed by the import tag. The interpreter than retrieves the module.Plugin struct from the module. This struct should implement the plugin type as declared in the plugin configuration.

Now that the plugins have been loaded, you can run PluginHost.GetPlugins() to get a list of all loaded plugins. Alternatively, you could run PluginHost.GetPluginsForType() to get a list of all loaded plugins that satisfy a certain plugin type. Once you know that, you can pass the name of the plugin you'd like to use to PluginHost.GetPlugin() which returns an interface that can be bound to the desired plugin type interface. From there you can use it however you'd like.

Usage

Usage is rather simple:

 import "github.com/chabad360/plugins"

 // GreeterPlugin is an example plugin interface
 type GreeterPlugin interface {
 	// Greet takes a string and outputs it somehow.
 	Greet(string)
 }

 func main() {
		pluginHost := plugins.NewPluginHost("./plugins", "./plugins-cache")
		pluginHost.AddPluginType((*GreeterPlugin)(nil))
		pluginType.LoadPlugins()

		pluginI, err := pluginHost.GetPlugin("hello")
		if err != nil {
			panic(err)
		}

		This can be done safely, because during LoadPlugins(), we check to ensure that the plugins fulfill the type requirements.
		plugin := pluginI.(GreeterPlugin)

		plugin.Greet("Hello")
		// Output: Hello
 }

Plugin Format

Plugins are zip files with the following structure:

plugin.zip/ (name is arbitrary)
 └ pluginFolder/ (name is arbitrary)
    ├ plugin.yml
    ├ vendor/ (required if the plugin has non-stdlib dependencies)
    ├ go.mod
    ├ main.go (name is arbitrary)
    ┊

plugin.yml example:

name: Plugin
description: This is a plugin that does plugin things.
import: github.com/user/plugin
type: middleware

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrLoading is returned when there is an issue loading the plugin files.
	ErrLoading = errors.New("error loading plugin")
	// ErrInvalidType is returned when the plugin type specified by the plugin is invalid.
	ErrInvalidType = errors.New("invalid plugin type")
	// ErrValidatingPlugin is returned when the plugin fails to fully implement the interface of the plugin type.
	ErrValidatingPlugin = errors.New("plugin does not implement type")
	// ErrNoDirectorySpecified is returned when LoadPlugin is called and the plugin folders are set to "".
	ErrNoDirectorySpecified = errors.New("plugin directory not specified")
)

Functions

This section is empty.

Types

type PluginConfig

type PluginConfig struct {
	// ImportPath is the module path i.e. "github.com/user/module"
	ImportPath string `yaml:"import" json:"import_path,omitempty"`
	// PluginType is the type of plugin, this plugin is checked against that type.
	// The available types are specified by the implementor of this package.
	PluginType string `yaml:"type" json:"plugin_type"`
	// Name is the of the plugin, it's used to identify the plugin.
	Name string `yaml:"name" json:"name"`
	// Local is set if the plugin is sourced from a local directory.
	// This must be set to true if there is no matching zip file in the plugins folder, otherwise the plugin will be deleted.
	Local bool `yaml:"local" json:"local_plugin"`
	// Internal is set if the plugin is loaded using AddInternalPlugin().
	Internal bool `yaml:"-" json:"internal_plugin"`
	// Description is a purely aesthetic field to to fill with information about the plugin.
	Description string `yaml:"description" json:"description"`
	// Hash is automatically filled by the plugins module. DO NOT TOUCH!!!
	// It is corresponding to the zip file that it came from.
	// If that zip file is missing (i.e. it's hash isn't in the plugins folder), it gets deleted.
	//
	// Again: DO NOT TOUCH!!!
	Hash string `yaml:"hash,omitempty" json:"plugin_id,omitempty"`
}

PluginConfig Describes the configuration for the plugin.yml file to be found at the root of the plugin folder.

type PluginHost

type PluginHost struct {
	// PluginDir is where the zipped plugins are stored.
	PluginDir string
	// PluginCacheDir is the location where the plugins are unzipped too.
	PluginCacheDir string
	// Plugins contains a map of the plugins, you can run operations directly on this.
	Plugins map[string]plugin
	// PluginTypes is a list of plugins types that plugins have to use at least one of.
	PluginTypes map[string]reflect.Type
	// Symbols is the map of symbols generated by yaegi extract
	Symbols map[string]map[string]reflect.Value
}

PluginHost manages loading the plugins.

func NewPluginHost

func NewPluginHost(pluginDir string, pluginCacheDir string, symbols map[string]map[string]reflect.Value) (*PluginHost, error)

NewPluginHost initializes a PluginHost.

func (*PluginHost) AddInternalPlugin added in v0.1.3

func (h *PluginHost) AddInternalPlugin(v reflect.Value, conf PluginConfig) error

AddInternalPlugin adds an compiled plugin as a valid plugin is the plugin system.

func (*PluginHost) AddPluginType

func (h *PluginHost) AddPluginType(name string, pluginType interface{})

AddPluginType adds a plugin type to the list. The interface for the pluginType parameter should be a nil of the plugin type interface:

(*PluginInterface)(nil)

func (*PluginHost) GetPlugin

func (h *PluginHost) GetPlugin(pluginName string) (reflect.Value, bool)

GetPlugin returns a plugin as an reflect.Value, provided you know what your getting, you can safely bind the value.Interface() to an interface.

func (*PluginHost) GetPlugins

func (h *PluginHost) GetPlugins() (list []string)

GetPlugins returns a list of the plugins by name.

func (*PluginHost) GetPluginsForType

func (h *PluginHost) GetPluginsForType(pluginType string) (list []string)

GetPluginsForType returns all the plugins that are of type pluginType or empty if the pluginType doesn't exist.

func (*PluginHost) LoadPlugins

func (h *PluginHost) LoadPlugins() error

LoadPlugins loads up the plugins in the plugin directory.

Jump to

Keyboard shortcuts

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