pluginmanager

package module
v0.0.0-...-0c80b31 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2024 License: MIT Imports: 22 Imported by: 0

README

Plugin Manager for Go

A robust and flexible plugin management library for Go applications.

Features
  • Dynamic Loading and Unloading

  • Versioning and Dependency Management

  • Hot-Reloading and Lazy Loading

  • Event System for Lifecycle Events

  • Enhanced Sandboxing for improved security

  • Metrics Collection for Plugin Performance

Deployment and Updates Simplified
  • Automatic Discovery (directory)
  • Remote Plugin Repository
  • Automated Plugin Update System
  • Digital Signature Verification

Easy deployment of your plugin repository with Redbean!

Getting Started

Obtain the latest version of the plugin-manager library with the Go package manager (recommended):

go get github.com/matt-dunleavy/plugin-manager

Or clone the repository to your local machine using the Git Command Line by running the following command in your terminal:

git clone https://github.com/matt-dunleavy/plugin-manager.git
cd plugin-manager

After you've obtained the library, import the package into your source code:

import "github.com/matt-dunleavy/plugin-manager"

Usage

Visit to the examples directory for full-featured implementations.

Initialize a New Plugin Manager

Create a new plugin manager instance at the start of your application.

manager, err := pm.NewManager("plugins.json", "./plugins", "public_key.pem")

Parameters:

  • configPath (string): Path to the JSON configuration file for managing enabled/disabled plugins. ("plugins.json")
  • pluginDir (string): Directory where plugins are stored. ("./plugins")
  • publicKeyPath (string): Path to the public key file used for verifying plugin signatures. ("public_key.pem")

Returns:

  • *Manager: Pointer to the newly created Manager instance.
  • error: Any error encountered during initialization.
Load a Plugin

Load a plugin from the specified path into memory, making it available for execution.

err = manager.LoadPlugin("./plugins/myplugin.so")

Parameters:

  • path (string): Path to the plugin file (.so extension).

Returns:

  • error: Any error encountered during the loading process.
Execute (Run) a Plugin

Run a loaded plugin's Execute() method.

err = manager.ExecutePlugin("MyPlugin")

Parameters:

  • name (string): Name of the plugin to execute.

Returns:

  • error: Any error encountered during plugin execution.
Unload a Plugin

Safely remove a plugin from memory when it's no longer needed.

err = manager.UnloadPlugin("MyPlugin")

Parameters:

  • name (string): Name of the plugin to unload.

Returns:

  • error: Any error encountered during the unloading process.
Hot-Reload a Plugin

Update a loaded plugin to a new version while the application is running (without stopping the application).

err = manager.HotReload("MyPlugin", "./plugins/myplugin_v2.so")

Parameters:

  • name (string): Name of the plugin to hot-reload.
  • path (string): Path to the new version of the plugin.

Returns:

  • error: Any error encountered during the hot-reload process.
Enable Automatic Plugin Discovery

Automatically discover and load all plugins from a specified directory.

err = manager.DiscoverPlugins("./plugins")

Parameters:

  • dir (string): Directory to search for plugins.

Returns:

  • error: Any error encountered during the discovery process.
Subscribe to Plugin Events

Subscribes to a specific plugin event, executing the provided function when the event occurs. Use this function to set up event handlers for various plugin lifecycle events.

manager.SubscribeToEvent("PluginLoaded", func(e pm.Event) {
	fmt.Printf("Plugin loaded: %s\n", e.(pm.PluginLoadedEvent).PluginName)
})

Parameters:

  • eventName (string): Name of the event to subscribe to (e.g., "PluginLoaded").
  • handler (func(Event)): Function to execute when the event occurs.

Returns: None

Creating a plugin

Plugins must implement the Plugin interface.

Plugin (struct)

The Plugin struct defines the basic structure of your plugin. It can be empty if your plugin doesn't need to store any state, or you can add fields if your plugin needs to maintain state across method calls.

type MyPlugin struct{}
Metadata()

The Metadata() method returns metadata about the plugin, including the Name, Version, and Dependencies (a map of other plugins that this plugin depends on, with the key being the plugin's name, and the value being the version constraint).

func (p *MyPlugin) Metadata() pm.PluginMetadata {
    return pm.PluginMetadata{
        Name:         "MyPlugin",
        Version:      "1.0.0",
        Dependencies: map[string]string{},
    }
}
Preload()

The Preload() method is called before the plugin is fully loaded. Use it for any setup that needs to happen before initialization.

func (p *MyPlugin) PreLoad() error {
    fmt.Println("MyPlugin pre-load")
    return nil
}
Init()

The Init() method is called to initialize the plugin. Use it to set up any resources or state the plugin needs.

func (p *MyPlugin) Init() error {
    fmt.Println("MyPlugin initialized")
    return nil
}
PostLoad()

The PostLoad() method is called after the plugin is fully loaded. Use it for any final setup steps.

func (p *MyPlugin) PostLoad() error {
    fmt.Println("MyPlugin post-load")
    return nil
}
Execute()

Execute() is the main method of your plugin. It is called when the plugin manager executes your plugin.

func (p *MyPlugin) Execute() error {
    fmt.Println("MyPlugin executed")
    return nil
}
PreUnload()

The PreUnload() method is called before the plugin is unloaded. Use it to prepare for shutdown.

func (p *MyPlugin) PreUnload() error {
    fmt.Println("MyPlugin pre-unload")
    return nil
}
Shutdown()

The Shutdown() method is called when the plugin is being unloaded. Use it to clean up any resources the plugin has allocated.

func (p *MyPlugin) Shutdown() error {
    fmt.Println("MyPlugin shut down")
    return nil
}
Plugin Variable

This variable is how the plugin manager discovers your plugin. It must be named Plugin and be of the type that implements the plugin interface.

var Plugin MyPlugin

[!IMPORTANT]

Each of these methods should return an error if something goes wrong during their execution. Returning nil indicates successful completion.

[!NOTE]

When implementing your own plugin, you would replace the fmt.Println statements with your actual plugin logic. The PreLoad, PostLoad, PreUnload, and Shutdown methods allow you to manage the lifecycle of your plugin, while Init and Execute form the core functionality.

Compiling Plugins

Compile a plugin using the standard Go compiler toolchain by setting the -buildmode flag to plugin:

go build -buildmode=plugin -o myplugin.so myplugin.go

Configuration

The plugin manager uses a JSON configuration file to keep track of enabled plugins. Here's an example plugins.json:

{
  "enabled": {
    "MyPlugin": true,
    "AnotherPlugin": false
  }
}

Simplified Deployment Plugin Repositories

An efficient and straightforward way to deploy and manage remote plugin repositories.

Benefits
  1. Simplified Management: Manage your entire plugin repository with a single file.
  2. Reduced Dependencies: No need for complex web server setups or databases.
  3. Easy Updates: Update your plugin repository by simply replacing the Redbean executable.
  4. Scalability: Redbean can handle repositories of various sizes efficiently.
Advantages
  • Security: Redbean includes built-in security features, reducing the attack surface of your plugin repository.
  • Performance: As a compiled C application, Redbean offers excellent performance for serving plugin files.
  • Flexibility: Easily customize your repository structure and access controls.
  • Low Overhead: Minimal resource usage makes it suitable for various hosting environments.
Features
  • Single-File Deployment: Redbean combines the web server and your content into a single executable file, simplifying deployment and distribution.
  • Automatic Download: The plugin manager can automatically download and set up the Redbean server.
  • Easy Repository Deployment: Deploy your plugin repository with a single function call.
  • Cross-Platform Compatibility: Redbean works on various platforms, including Linux, macOS, and Windows.
  • Lightweight: Redbean has a small footprint, making it ideal for plugin repositories of all sizes.
Step-by-Step Guide to Implementing and Deploying Redbean
Setup a Remote Repository
repo, err := manager.SetupRemoteRepository("[email protected]:/path/to/repo", "/path/to/ssh/key")
Prepare Local Directory for Deployment

Create a local directory to store your plugins and repository structure:

localRepoPath := "./repository"
Add Plugins to the Local Repository.

Copy or move your plugin files to the local repository directory.

Deploy the Repository
err = manager.DeployRepository(repo, localRepoPath)

This step will:

  • Download the latest version of Redbean (if not present)
  • Package your plugins and repository structure into a Redbean executable
  • Deploy the Redbean executable to your remote server (if a remote URL was provided)
Verify Deployment

If deployed remotely, SSH into your server and check that the Redbean executable is present and running:

ssh [email protected]
ls /path/to/repo/redbean.com
ps aux | grep redbean
Access Your Plugin Repository

Your plugins are now accessible via HTTP/HTTPS. If Redbean is running on the default port, you can access your plugins at: http://your-server-address:8080/plugins/

Update Repository

To update your repository, simply repeat steps 4-5. The plugin manager will handle updating the Redbean executable and redeploying your changes.

Advanced Configuration

When deploying your plugin repository via redbean, the plugin manager will include a redbean.ini file that you can use to customize your repository's server configuration.

[server]
port = 9000
addr = 127.0.0.1

Refer to the redbean.ini reference for a comprehensive list of commands.

[!IMPORTANT]

  • Always use HTTPS in production environments.
  • Implement proper access controls to restrict repository access.
  • Regularly update both your plugins and the Redbean executable to ensure you have the latest security patches.

API Reference

Management
  • NewManager(configPath string, pluginDir string, publicKeyPath string) (*Manager, error)
  • LoadPlugin(path string) error
  • UnloadPlugin(name string) error
  • ExecutePlugin(name string) error
  • HotReload(name string, path string) error
  • EnablePlugin(name string) error
  • DisablePlugin(name string) error
  • LoadEnabledPlugins(pluginDir string) error
  • ListPlugins() []string
  • GetPluginStats(name string) (*PluginStats, error)
  • SubscribeToEvent(eventName string, handler EventHandler)
Automatic Discovery and Updates
  • DiscoverPlugins(dir string) error
  • CheckForUpdates(repo *PluginRepository) ([]string, error)
  • UpdatePlugin(repo *PluginRepository, pluginName string) error
Remote Repository (via redbean)
  • SetupRemoteRepository(url, sshKeyPath string) (*PluginRepository, error)
  • DeployRepository(repo *PluginRepository, localPath string) error
EventBus
  • Subscribe(eventName string, handler EventHandler)
  • Publish(event Event)
Sandbox
  • Enable() error
  • Disable() error
  • VerifyPluginPath(path string) error

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

View Source
const PluginSymbol = "Plugin"

Variables

View Source
var (
	ErrPluginAlreadyLoaded    = errors.New("plugin already loaded")
	ErrInvalidPluginInterface = errors.New("invalid plugin interface")
	ErrPluginNotFound         = errors.New("plugin not found")
	ErrIncompatibleVersion    = errors.New("incompatible plugin version")
	ErrMissingDependency      = errors.New("missing plugin dependency")
	ErrCircularDependency     = errors.New("circular plugin dependency detected")
	ErrPluginSandboxViolation = errors.New("plugin attempted to violate sandbox")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	Enabled map[string]bool `json:"enabled"`
	// contains filtered or unexported fields
}

func LoadConfig

func LoadConfig(path string) (*Config, error)

func (*Config) DisablePlugin

func (c *Config) DisablePlugin(name string) error

func (*Config) EnablePlugin

func (c *Config) EnablePlugin(name string) error

func (*Config) EnabledPlugins

func (c *Config) EnabledPlugins() []string

func (*Config) Save

func (c *Config) Save() error

type Event

type Event interface {
	Name() string
}

type EventBus

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

func NewEventBus

func NewEventBus() *EventBus

func (*EventBus) Publish

func (eb *EventBus) Publish(event Event)

func (*EventBus) Subscribe

func (eb *EventBus) Subscribe(eventName string, handler EventHandler)

type EventHandler

type EventHandler func(Event)

type LinuxSandbox

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

func NewLinuxSandbox

func NewLinuxSandbox(chrootDir string) *LinuxSandbox

func (*LinuxSandbox) Disable

func (s *LinuxSandbox) Disable() error

func (*LinuxSandbox) Enable

func (s *LinuxSandbox) Enable() error

func (*LinuxSandbox) VerifyPluginPath

func (s *LinuxSandbox) VerifyPluginPath(path string) error

type Manager

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

func NewManager

func NewManager(configPath, pluginDir, publicKeyPath string) (*Manager, error)

func (*Manager) CheckForUpdates

func (m *Manager) CheckForUpdates(repo *PluginRepository) ([]string, error)

func (*Manager) DeployRepository

func (m *Manager) DeployRepository(repo *PluginRepository, localPath string) error

func (*Manager) DisablePlugin

func (m *Manager) DisablePlugin(name string) error

func (*Manager) DiscoverPlugins

func (m *Manager) DiscoverPlugins(dir string) error

func (*Manager) EnablePlugin

func (m *Manager) EnablePlugin(name string) error

func (*Manager) ExecutePlugin

func (m *Manager) ExecutePlugin(name string) error

func (*Manager) GetPluginStats

func (m *Manager) GetPluginStats(name string) (*PluginStats, error)

func (*Manager) HotReload

func (m *Manager) HotReload(name string, path string) error

func (*Manager) ListPlugins

func (m *Manager) ListPlugins() []string

func (*Manager) LoadEnabledPlugins

func (m *Manager) LoadEnabledPlugins(pluginDir string) error

func (*Manager) LoadPlugin

func (m *Manager) LoadPlugin(path string) error

func (*Manager) SetupRemoteRepository

func (m *Manager) SetupRemoteRepository(url, sshKeyPath string) (*PluginRepository, error)

func (*Manager) SubscribeToEvent

func (m *Manager) SubscribeToEvent(eventName string, handler EventHandler)

func (*Manager) UnloadPlugin

func (m *Manager) UnloadPlugin(name string) error

func (*Manager) UpdatePlugin

func (m *Manager) UpdatePlugin(repo *PluginRepository, pluginName string) error

func (*Manager) VerifyPluginSignature

func (m *Manager) VerifyPluginSignature(pluginPath string, publicKeyPath string) error

type Plugin

type Plugin interface {
	Metadata() PluginMetadata
	PreLoad() error
	Init() error
	PostLoad() error
	Execute() error
	PreUnload() error
	Shutdown() error
}

func LoadPlugin

func LoadPlugin(path string) (Plugin, error)

type PluginError

type PluginError struct {
	Op     string
	Err    error
	Plugin string
}

func (*PluginError) Error

func (e *PluginError) Error() string

func (*PluginError) Unwrap

func (e *PluginError) Unwrap() error

type PluginHotReloadedEvent

type PluginHotReloadedEvent struct {
	PluginName string
}

func (PluginHotReloadedEvent) Name

func (e PluginHotReloadedEvent) Name() string

type PluginLoadedEvent

type PluginLoadedEvent struct {
	PluginName string
}

func (PluginLoadedEvent) Name

func (e PluginLoadedEvent) Name() string

type PluginMetadata

type PluginMetadata struct {
	Name         string
	Version      string
	Dependencies map[string]string
	GoVersion    string
	Signature    []byte
}

type PluginRepository

type PluginRepository struct {
	URL       string
	SSHKey    string
	PublicKey ssh.PublicKey
}

type PluginStats

type PluginStats struct {
	ExecutionCount     int64
	LastExecutionTime  time.Duration
	TotalExecutionTime time.Duration
}

type PluginUnloadedEvent

type PluginUnloadedEvent struct {
	PluginName string
}

func (PluginUnloadedEvent) Name

func (e PluginUnloadedEvent) Name() string

type Sandbox

type Sandbox interface {
	Enable() error
	Disable() error
	VerifyPluginPath(path string) error
}

Jump to

Keyboard shortcuts

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