plugin

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Feb 17, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

Plugin System

The scafctl plugin system allows extending the provider framework with external plugins using hashicorp/go-plugin.

Architecture

  • hashicorp/go-plugin: Manages plugin lifecycle, process isolation, and crash recovery
  • gRPC: Communication protocol between scafctl and plugins
  • Protocol Buffers: Interface definitions

Plugin Interface

Plugins must implement the ProviderPlugin interface:

type ProviderPlugin interface {
    // GetProviders returns all provider names exposed by this plugin
    GetProviders(ctx context.Context) ([]string, error)

    // GetProviderDescriptor returns metadata for a specific provider
    GetProviderDescriptor(ctx context.Context, providerName string) (*provider.Descriptor, error)

    // ExecuteProvider executes a provider with the given input
    ExecuteProvider(ctx context.Context, providerName string, input map[string]any) (*provider.Output, error)
}

Creating a Plugin

  1. Import the plugin package:
import "github.com/oakwood-commons/scafctl/pkg/plugin"
  1. Implement the ProviderPlugin interface

  2. Call plugin.Serve() in your main function:

func main() {
    plugin.Serve(&YourPlugin{})
}
  1. Build your plugin as an executable:
go build -o my-plugin main.go

Plugin Discovery

Plugins are discovered by scanning configured plugin directories for executable files. The system:

  1. Searches for executable files in plugin directories
  2. Attempts to connect to each potential plugin
  3. Registers providers from successfully loaded plugins
  4. Skips plugins that fail to load

Example Plugin

See examples/plugins/echo/ for a complete example plugin implementation.

Security

  • Plugins run in isolated processes
  • Communication over gRPC provides clear security boundaries
  • Plugins are validated using handshake configuration
  • Failed plugins don't crash the main process

Testing

Use MockProviderPlugin for testing plugin implementations without running actual plugin processes.

Documentation

Index

Constants

View Source
const (
	// PluginName is the name used to identify the provider plugin
	PluginName = "provider"
)

Variables

View Source
var HandshakeConfig = &HandshakeConfigData{
	ProtocolVersion:  1,
	MagicCookieKey:   "SCAFCTL_PLUGIN",
	MagicCookieValue: "scafctl_provider_plugin",
}

HandshakeConfig is used to verify plugin compatibility

Functions

func RegisterPluginProviders

func RegisterPluginProviders(registry *provider.Registry, pluginDirs []string) error

RegisterPluginProviders discovers plugins and registers them with the provider registry

func Serve

func Serve(impl ProviderPlugin)

Serve is a helper function for plugin implementers to serve their plugins This should be called from the plugin's main() function

Types

type Client

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

Client wraps a plugin client and manages its lifecycle

func Discover

func Discover(pluginDirs []string) ([]*Client, error)

Discover discovers plugins from the given directories

func NewClient

func NewClient(pluginPath string) (*Client, error)

NewClient creates a new plugin client

func (*Client) ExecuteProvider

func (c *Client) ExecuteProvider(ctx context.Context, providerName string, input map[string]any) (*provider.Output, error)

ExecuteProvider executes a provider with the given input

func (*Client) GetProviderDescriptor

func (c *Client) GetProviderDescriptor(ctx context.Context, providerName string) (*provider.Descriptor, error)

GetProviderDescriptor returns metadata for a specific provider

func (*Client) GetProviders

func (c *Client) GetProviders(ctx context.Context) ([]string, error)

GetProviders returns all provider names exposed by this plugin

func (*Client) Kill

func (c *Client) Kill()

Kill terminates the plugin process

func (*Client) Name

func (c *Client) Name() string

Name returns the plugin name

func (*Client) Path

func (c *Client) Path() string

Path returns the plugin path

type GRPCClient

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

GRPCClient implements the gRPC client for the plugin

func (*GRPCClient) ExecuteProvider

func (c *GRPCClient) ExecuteProvider(ctx context.Context, providerName string, input map[string]any) (*provider.Output, error)

ExecuteProvider implements ProviderPlugin.ExecuteProvider

func (*GRPCClient) GetProviderDescriptor

func (c *GRPCClient) GetProviderDescriptor(ctx context.Context, providerName string) (*provider.Descriptor, error)

GetProviderDescriptor implements ProviderPlugin.GetProviderDescriptor

func (*GRPCClient) GetProviders

func (c *GRPCClient) GetProviders(ctx context.Context) ([]string, error)

GetProviders implements ProviderPlugin.GetProviders

type GRPCPlugin

type GRPCPlugin struct {
	plugin.Plugin
	Impl ProviderPlugin
}

GRPCPlugin implements plugin.GRPCPlugin from hashicorp/go-plugin

func (*GRPCPlugin) GRPCClient

func (p *GRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (any, error)

GRPCClient returns the gRPC client

func (*GRPCPlugin) GRPCServer

func (p *GRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error

GRPCServer registers the gRPC server

type GRPCServer

type GRPCServer struct {
	proto.UnimplementedPluginServiceServer
	Impl ProviderPlugin
}

GRPCServer implements the gRPC server for the plugin

func (*GRPCServer) ExecuteProvider

ExecuteProvider implements the ExecuteProvider RPC

func (*GRPCServer) GetProviderDescriptor

GetProviderDescriptor implements the GetProviderDescriptor RPC

func (*GRPCServer) GetProviders

GetProviders implements the GetProviders RPC

type HandshakeConfigData

type HandshakeConfigData struct {
	ProtocolVersion  uint
	MagicCookieKey   string
	MagicCookieValue string
}

HandshakeConfigData contains the handshake configuration

type ProviderPlugin

type ProviderPlugin interface {
	// GetProviders returns all provider names exposed by this plugin
	GetProviders(ctx context.Context) ([]string, error)

	// GetProviderDescriptor returns metadata for a specific provider
	GetProviderDescriptor(ctx context.Context, providerName string) (*provider.Descriptor, error)

	// ExecuteProvider executes a provider with the given input
	ExecuteProvider(ctx context.Context, providerName string, input map[string]any) (*provider.Output, error)
}

ProviderPlugin is the interface that plugins must implement This wraps the provider.Provider interface for plugin communication

type ProviderWrapper

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

ProviderWrapper wraps a plugin provider to implement the provider.Provider interface

func NewProviderWrapper

func NewProviderWrapper(client *Client, providerName string) (*ProviderWrapper, error)

NewProviderWrapper creates a new provider wrapper for a plugin provider

func (*ProviderWrapper) Client

func (w *ProviderWrapper) Client() *Client

Client returns the underlying plugin client

func (*ProviderWrapper) Descriptor

func (w *ProviderWrapper) Descriptor() *provider.Descriptor

Descriptor returns the provider descriptor

func (*ProviderWrapper) Execute

func (w *ProviderWrapper) Execute(ctx context.Context, input any) (*provider.Output, error)

Execute executes the provider

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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