Documentation
¶
Overview ¶
Package plugin implements infrastructure adapters for the plugin system.
The plugin package provides concrete implementations for plugin discovery, loading, validation, registration, and lifecycle management. It enables AWF workflows to extend functionality through external RPC-based plugins (operations, filters, transformers) without modifying core code. The package handles manifest parsing, version compatibility checking, state persistence, and operation registry integration.
Architecture Role ¶
In the hexagonal architecture:
- Implements plugin loading and lifecycle management (infrastructure adapters)
- Provides OperationRegistry for runtime operation lookup and registration
- Integrates with domain/ports.CommandExecutor for plugin discovery
- Application layer orchestrates workflow execution via registered plugin operations
- Domain layer defines operation contracts without plugin coupling
All plugin components use atomic file operations and thread-safe registries to support concurrent plugin loading during workflow initialization. The manifest parser validates semver constraints and capability declarations before plugin activation.
Plugin Management ¶
## RPCPluginManager (rpc_manager.go)
Plugin lifecycle orchestration:
- Discover: Scan plugins directory for valid manifests
- Load: Initialize plugin RPC client and register operations
- Init: Call plugin initialization hook with configuration
- Shutdown: Gracefully stop a running plugin
- ShutdownAll: Cleanup all active plugins on process termination
- Get: Retrieve loaded plugin by name
- List: Enumerate active plugin names
## Loader (loader.go)
Plugin discovery and validation:
- DiscoverPlugins: Find plugin.awf manifests in plugins directory
- LoadPlugin: Parse manifest, validate constraints, prepare RPC client
- ValidatePlugin: Check manifest schema, version compatibility, capability declarations
Registry and Discovery ¶
## OperationRegistry (registry.go)
Runtime operation registration and lookup:
- RegisterOperation: Add plugin-provided operation (thread-safe)
- UnregisterOperation: Remove operation by name
- GetOperation: Retrieve operation implementation by name
- GetPluginOperations: List operations provided by a plugin
- UnregisterPluginOperations: Remove all operations from a plugin
- Count: Total registered operations
- Clear: Reset registry state
Manifest and Metadata ¶
## ManifestParser (manifest_parser.go)
Plugin metadata parsing:
- ParseManifest: Read plugin.awf YAML manifest
- Validates: name, version, awf_version (semver constraints), capabilities
- Supports metadata: author, description, license, homepage
Capabilities: operation, filter, transform (plugin feature declarations)
State Persistence ¶
## JSONPluginStateStore (state_store.go)
Plugin state persistence:
- Save: Write plugin state to JSON file (atomic via temp file + rename)
- Load: Read plugin state from JSON file
- SetEnabled: Enable/disable plugin by name
- IsEnabled: Check if plugin is enabled
- GetConfig: Retrieve plugin-specific configuration
- SetConfig: Update plugin-specific configuration
- GetState: Access full plugin state
- ListDisabled: Enumerate disabled plugins
Uses file locking to prevent concurrent modification during workflow execution.
Version Handling ¶
## Version (version.go)
Semantic versioning support:
- ParseVersion: Parse semver string (e.g., "1.2.3")
- ParseConstraint: Parse version constraint (e.g., ">=1.0.0", "~1.2", "^2.0")
- CheckVersionConstraint: Validate version against constraint
- IsCompatible: Check plugin compatibility with AWF version
- Compare: Semver comparison (major.minor.patch)
Operators: =, !=, >, >=, <, <=, ~ (tilde range), ^ (caret range)
Index ¶
- Constants
- Variables
- func CheckVersionConstraint(constraintStr, versionStr string) (bool, error)
- func IsCompatible(awfVersionConstraint, currentAWFVersion string) (bool, error)
- type CompositeOperationProvider
- func (c *CompositeOperationProvider) Execute(ctx context.Context, name string, inputs map[string]any) (*pluginmodel.OperationResult, error)
- func (c *CompositeOperationProvider) GetOperation(name string) (*pluginmodel.OperationSchema, bool)
- func (c *CompositeOperationProvider) ListOperations() []*pluginmodel.OperationSchema
- type Constraint
- type Constraints
- type FileSystemLoader
- func (l *FileSystemLoader) DiscoverPlugins(ctx context.Context, pluginsDir string) ([]*pluginmodel.PluginInfo, error)
- func (l *FileSystemLoader) LoadPlugin(ctx context.Context, pluginDir string) (*pluginmodel.PluginInfo, error)
- func (l *FileSystemLoader) ValidatePlugin(info *pluginmodel.PluginInfo) error
- type JSONPluginStateStore
- func (s *JSONPluginStateStore) BasePath() string
- func (s *JSONPluginStateStore) GetConfig(name string) map[string]any
- func (s *JSONPluginStateStore) GetState(name string) *pluginmodel.PluginState
- func (s *JSONPluginStateStore) IsEnabled(name string) bool
- func (s *JSONPluginStateStore) ListDisabled() []string
- func (s *JSONPluginStateStore) Load(ctx context.Context) error
- func (s *JSONPluginStateStore) Save(ctx context.Context) error
- func (s *JSONPluginStateStore) SetConfig(ctx context.Context, name string, config map[string]any) error
- func (s *JSONPluginStateStore) SetEnabled(ctx context.Context, name string, enabled bool) error
- type LoaderError
- type ManifestParseError
- type ManifestParser
- type OperationRegistry
- func (r *OperationRegistry) Clear()
- func (r *OperationRegistry) Count() int
- func (r *OperationRegistry) GetOperation(name string) (*pluginmodel.OperationSchema, bool)
- func (r *OperationRegistry) GetOperationSource(operationName string) (string, bool)
- func (r *OperationRegistry) GetPluginOperations(pluginName string) []*pluginmodel.OperationSchema
- func (r *OperationRegistry) Operations() []*pluginmodel.OperationSchema
- func (r *OperationRegistry) RegisterOperation(op *pluginmodel.OperationSchema) error
- func (r *OperationRegistry) UnregisterOperation(name string) error
- func (r *OperationRegistry) UnregisterPluginOperations(pluginName string) error
- type RPCManagerError
- type RPCPluginManager
- func (m *RPCPluginManager) Discover(ctx context.Context) ([]*pluginmodel.PluginInfo, error)
- func (m *RPCPluginManager) Execute(ctx context.Context, name string, inputs map[string]any) (*pluginmodel.OperationResult, error)
- func (m *RPCPluginManager) Get(name string) (*pluginmodel.PluginInfo, bool)
- func (m *RPCPluginManager) GetOperation(name string) (*pluginmodel.OperationSchema, bool)
- func (m *RPCPluginManager) Init(ctx context.Context, name string, config map[string]any) error
- func (m *RPCPluginManager) List() []*pluginmodel.PluginInfo
- func (m *RPCPluginManager) ListOperations() []*pluginmodel.OperationSchema
- func (m *RPCPluginManager) Load(ctx context.Context, name string) error
- func (m *RPCPluginManager) SetPluginsDir(dir string)
- func (m *RPCPluginManager) Shutdown(ctx context.Context, name string) error
- func (m *RPCPluginManager) ShutdownAll(ctx context.Context) error
- type Version
Constants ¶
const ( OpEqual = "==" // Exact match OpNotEqual = "!=" // Not equal OpGreater = ">" // Greater than OpGreaterOrEqual = ">=" // Greater than or equal OpLess = "<" // Less than OpLessOrEqual = "<=" // Less than or equal OpTilde = "~" // Compatible with (allows patch updates) OpCaret = "^" // Compatible with (allows minor updates) )
Version comparison operators supported by the constraint parser.
const DefaultPluginsDir = "plugins"
Default plugins directory relative to config.
const ManifestFileName = "plugin.yaml"
ManifestFileName is the expected filename for plugin manifests.
Variables ¶
var ( ErrOperationAlreadyRegistered = errors.New("operation already registered") ErrOperationNotFound = errors.New("operation not found") ErrInvalidOperation = errors.New("invalid operation schema") )
Registry errors.
var ErrNoPluginsConfigured = errors.New("rpc_manager: no plugins configured")
ErrNoPluginsConfigured indicates no plugin loader or directory is configured.
Functions ¶
func CheckVersionConstraint ¶
CheckVersionConstraint checks if a version string satisfies a constraint string. This is the main entry point for version compatibility checking.
func IsCompatible ¶
IsCompatible checks if the current AWF version is compatible with a plugin's AWF version constraint. This is a convenience function.
Types ¶
type CompositeOperationProvider ¶
type CompositeOperationProvider struct {
// contains filtered or unexported fields
}
CompositeOperationProvider wraps multiple OperationProvider instances into a single provider, delegating GetOperation/ListOperations/Execute by operation name. Enables coexistence of multiple built-in providers (e.g., github and notify).
func NewCompositeOperationProvider ¶
func NewCompositeOperationProvider(providers ...ports.OperationProvider) *CompositeOperationProvider
NewCompositeOperationProvider creates a new composite operation provider that delegates to the given providers.
func (*CompositeOperationProvider) Execute ¶
func (c *CompositeOperationProvider) Execute(ctx context.Context, name string, inputs map[string]any) (*pluginmodel.OperationResult, error)
Execute runs a plugin operation by delegating to the appropriate provider.
func (*CompositeOperationProvider) GetOperation ¶
func (c *CompositeOperationProvider) GetOperation(name string) (*pluginmodel.OperationSchema, bool)
GetOperation returns an operation by name from the first provider that has it.
func (*CompositeOperationProvider) ListOperations ¶
func (c *CompositeOperationProvider) ListOperations() []*pluginmodel.OperationSchema
ListOperations returns all available operations from all providers.
type Constraint ¶
type Constraint struct {
Operator string // One of the Op* constants
Version Version // The version to compare against
}
Constraint represents a single version constraint (e.g., ">=0.4.0").
func ParseConstraint ¶
func ParseConstraint(s string) (Constraint, error)
ParseConstraint parses a constraint string into a Constraint struct. Accepts formats: ">=0.4.0", "~1.2.0", "^2.0.0", "1.0.0" (implies ==)
func (Constraint) Check ¶
func (c Constraint) Check(v Version) bool
Check tests if a version satisfies this constraint.
type Constraints ¶
type Constraints []Constraint
Constraints represents multiple version constraints that all must be satisfied.
func ParseConstraints ¶
func ParseConstraints(s string) (Constraints, error)
ParseConstraints parses a constraint string that may contain multiple constraints. Constraints are separated by spaces or commas. Examples: ">=0.4.0 <1.0.0", ">=0.4.0, <1.0.0"
func (Constraints) Check ¶
func (cs Constraints) Check(v Version) bool
Check tests if a version satisfies all constraints.
func (Constraints) String ¶
func (cs Constraints) String() string
String returns the string representation of the constraints.
type FileSystemLoader ¶
type FileSystemLoader struct {
// contains filtered or unexported fields
}
FileSystemLoader implements PluginLoader for filesystem-based plugin discovery.
func NewFileSystemLoader ¶
func NewFileSystemLoader(parser *ManifestParser) *FileSystemLoader
NewFileSystemLoader creates a new FileSystemLoader with the given manifest parser.
func (*FileSystemLoader) DiscoverPlugins ¶
func (l *FileSystemLoader) DiscoverPlugins(ctx context.Context, pluginsDir string) ([]*pluginmodel.PluginInfo, error)
DiscoverPlugins scans a directory for plugins and returns their info. Each subdirectory containing a plugin.yaml is considered a plugin.
func (*FileSystemLoader) LoadPlugin ¶
func (l *FileSystemLoader) LoadPlugin(ctx context.Context, pluginDir string) (*pluginmodel.PluginInfo, error)
LoadPlugin loads a single plugin from a directory path. Reads the plugin.yaml manifest and creates PluginInfo with status=StatusLoaded.
func (*FileSystemLoader) ValidatePlugin ¶
func (l *FileSystemLoader) ValidatePlugin(info *pluginmodel.PluginInfo) error
ValidatePlugin checks if a discovered plugin is valid and compatible. Validates manifest fields, capabilities, and AWF version constraint.
type JSONPluginStateStore ¶
type JSONPluginStateStore struct {
// contains filtered or unexported fields
}
JSONPluginStateStore persists plugin states to a JSON file. Implements ports.PluginStateStore interface.
func NewJSONPluginStateStore ¶
func NewJSONPluginStateStore(basePath string) *JSONPluginStateStore
NewJSONPluginStateStore creates a new JSONPluginStateStore.
func (*JSONPluginStateStore) BasePath ¶
func (s *JSONPluginStateStore) BasePath() string
BasePath returns the storage directory path.
func (*JSONPluginStateStore) GetConfig ¶
func (s *JSONPluginStateStore) GetConfig(name string) map[string]any
GetConfig returns the stored configuration for a plugin. Returns nil if plugin has no stored configuration.
func (*JSONPluginStateStore) GetState ¶
func (s *JSONPluginStateStore) GetState(name string) *pluginmodel.PluginState
GetState returns the full state for a plugin, or nil if not found.
func (*JSONPluginStateStore) IsEnabled ¶
func (s *JSONPluginStateStore) IsEnabled(name string) bool
IsEnabled returns whether a plugin is enabled. Returns true for unknown plugins (enabled by default).
func (*JSONPluginStateStore) ListDisabled ¶
func (s *JSONPluginStateStore) ListDisabled() []string
ListDisabled returns names of all explicitly disabled plugins.
func (*JSONPluginStateStore) Load ¶
func (s *JSONPluginStateStore) Load(ctx context.Context) error
Load reads plugin states from storage.
func (*JSONPluginStateStore) Save ¶
func (s *JSONPluginStateStore) Save(ctx context.Context) error
Save persists all plugin states to storage with atomic write.
func (*JSONPluginStateStore) SetConfig ¶
func (s *JSONPluginStateStore) SetConfig(ctx context.Context, name string, config map[string]any) error
SetConfig stores configuration for a plugin.
func (*JSONPluginStateStore) SetEnabled ¶
SetEnabled enables or disables a plugin by name.
type LoaderError ¶
type LoaderError struct {
Path string // plugin directory path
Op string // operation (discover, load, validate)
Message string // error message
Cause error // underlying error
}
LoaderError represents an error during plugin loading operations.
func NewLoaderError ¶
func NewLoaderError(op, path, message string) *LoaderError
NewLoaderError creates a new LoaderError.
func WrapLoaderError ¶
func WrapLoaderError(op, path string, cause error) *LoaderError
WrapLoaderError wraps an existing error as a LoaderError.
func (*LoaderError) Error ¶
func (e *LoaderError) Error() string
Error implements the error interface.
func (*LoaderError) Unwrap ¶
func (e *LoaderError) Unwrap() error
Unwrap returns the underlying error.
type ManifestParseError ¶
type ManifestParseError struct {
File string // file path
Field string // field path (e.g., "config.webhook_url")
Message string // error message
Cause error // underlying error
}
ManifestParseError represents an error during plugin manifest parsing.
func NewManifestParseError ¶
func NewManifestParseError(file, field, message string) *ManifestParseError
NewManifestParseError creates a new ManifestParseError with field and message.
func WrapManifestParseError ¶
func WrapManifestParseError(file string, cause error) *ManifestParseError
WrapManifestParseError wraps an existing error as a ManifestParseError.
func (*ManifestParseError) Error ¶
func (e *ManifestParseError) Error() string
Error implements the error interface.
func (*ManifestParseError) Unwrap ¶
func (e *ManifestParseError) Unwrap() error
Unwrap returns the underlying error.
type ManifestParser ¶
type ManifestParser struct{}
ManifestParser parses plugin manifests from YAML files.
func NewManifestParser ¶
func NewManifestParser() *ManifestParser
NewManifestParser creates a new ManifestParser.
func (*ManifestParser) Parse ¶
func (p *ManifestParser) Parse(r io.Reader) (*pluginmodel.Manifest, error)
Parse reads and parses a plugin manifest from an io.Reader.
func (*ManifestParser) ParseFile ¶
func (p *ManifestParser) ParseFile(path string) (*pluginmodel.Manifest, error)
ParseFile reads and parses a plugin manifest from a file path.
type OperationRegistry ¶
type OperationRegistry struct {
// contains filtered or unexported fields
}
OperationRegistry manages registration of plugin-provided operations. Thread-safe for concurrent access.
func NewOperationRegistry ¶
func NewOperationRegistry() *OperationRegistry
NewOperationRegistry creates a new empty operation registry.
func (*OperationRegistry) Clear ¶
func (r *OperationRegistry) Clear()
Clear removes all registered operations. Useful for testing.
func (*OperationRegistry) Count ¶
func (r *OperationRegistry) Count() int
Count returns the total number of registered operations.
func (*OperationRegistry) GetOperation ¶
func (r *OperationRegistry) GetOperation(name string) (*pluginmodel.OperationSchema, bool)
GetOperation returns an operation by name. Returns nil and false if the operation is not found.
func (*OperationRegistry) GetOperationSource ¶
func (r *OperationRegistry) GetOperationSource(operationName string) (string, bool)
GetOperationSource returns the plugin name that registered an operation. Returns empty string and false if the operation is not found.
func (*OperationRegistry) GetPluginOperations ¶
func (r *OperationRegistry) GetPluginOperations(pluginName string) []*pluginmodel.OperationSchema
GetPluginOperations returns all operations registered by a specific plugin.
func (*OperationRegistry) Operations ¶
func (r *OperationRegistry) Operations() []*pluginmodel.OperationSchema
Operations returns all registered operations as a slice. Returns an empty slice if no operations are registered.
func (*OperationRegistry) RegisterOperation ¶
func (r *OperationRegistry) RegisterOperation(op *pluginmodel.OperationSchema) error
RegisterOperation adds a plugin operation to the registry. Returns ErrOperationAlreadyRegistered if an operation with the same name exists. Returns ErrInvalidOperation if the operation schema is nil or has no name.
func (*OperationRegistry) UnregisterOperation ¶
func (r *OperationRegistry) UnregisterOperation(name string) error
UnregisterOperation removes a plugin operation from the registry. Returns ErrOperationNotFound if the operation is not registered.
func (*OperationRegistry) UnregisterPluginOperations ¶
func (r *OperationRegistry) UnregisterPluginOperations(pluginName string) error
UnregisterPluginOperations removes all operations provided by a specific plugin. Useful when unloading or disabling a plugin.
type RPCManagerError ¶
type RPCManagerError struct {
Op string // operation (load, init, shutdown)
Plugin string // plugin name
Message string // error message
Cause error // underlying error
}
RPCManagerError represents an error during plugin lifecycle operations.
func NewRPCManagerError ¶
func NewRPCManagerError(op, pluginName, message string) *RPCManagerError
NewRPCManagerError creates a new RPCManagerError.
func WrapRPCManagerError ¶
func WrapRPCManagerError(op, pluginName string, cause error) *RPCManagerError
WrapRPCManagerError wraps an existing error as an RPCManagerError.
func (*RPCManagerError) Error ¶
func (e *RPCManagerError) Error() string
Error implements the error interface.
func (*RPCManagerError) Unwrap ¶
func (e *RPCManagerError) Unwrap() error
Unwrap returns the underlying error.
type RPCPluginManager ¶
type RPCPluginManager struct {
// contains filtered or unexported fields
}
RPCPluginManager implements PluginManager using HashiCorp go-plugin for RPC. It manages plugin lifecycle: discovery, loading, initialization, and shutdown.
func NewRPCPluginManager ¶
func NewRPCPluginManager(loader *FileSystemLoader) *RPCPluginManager
NewRPCPluginManager creates a new RPCPluginManager.
func (*RPCPluginManager) Discover ¶
func (m *RPCPluginManager) Discover(ctx context.Context) ([]*pluginmodel.PluginInfo, error)
Discover finds plugins in the plugins directory. Returns ErrNoPluginsConfigured if no loader or plugins directory is configured.
func (*RPCPluginManager) Execute ¶
func (m *RPCPluginManager) Execute(ctx context.Context, name string, inputs map[string]any) (*pluginmodel.OperationResult, error)
Execute delegates an operation call to the correct connected plugin via gRPC. Name format is "pluginName.operationName" (consistent with built-in providers). If unprefixed, iterates all connections as fallback.
func (*RPCPluginManager) Get ¶
func (m *RPCPluginManager) Get(name string) (*pluginmodel.PluginInfo, bool)
Get returns plugin info by name. Returns (nil, false) if plugin not found.
func (*RPCPluginManager) GetOperation ¶
func (m *RPCPluginManager) GetOperation(name string) (*pluginmodel.OperationSchema, bool)
GetOperation returns an operation schema by name, searching all connected plugins. Name format is "pluginName.operationName" (consistent with built-in providers). Uses an internal 5s timeout per call because the port interface does not accept ctx.
func (*RPCPluginManager) List ¶
func (m *RPCPluginManager) List() []*pluginmodel.PluginInfo
List returns all known plugins.
func (*RPCPluginManager) ListOperations ¶
func (m *RPCPluginManager) ListOperations() []*pluginmodel.OperationSchema
ListOperations returns all operation schemas from all connected plugins. Calls gRPC ListOperations on each connection; skips plugins that fail. Uses an internal 5s timeout per call because the port interface does not accept ctx.
func (*RPCPluginManager) Load ¶
func (m *RPCPluginManager) Load(ctx context.Context, name string) error
Load loads a plugin by name. The plugin must have been discovered first, or a pluginsDir must be configured.
func (*RPCPluginManager) SetPluginsDir ¶
func (m *RPCPluginManager) SetPluginsDir(dir string)
SetPluginsDir sets the directory to discover plugins from.
func (*RPCPluginManager) Shutdown ¶
func (m *RPCPluginManager) Shutdown(ctx context.Context, name string) error
Shutdown stops a running plugin gracefully. Full implementation: gRPC Shutdown call, client.Kill(), connection cleanup from m.connections.
func (*RPCPluginManager) ShutdownAll ¶
func (m *RPCPluginManager) ShutdownAll(ctx context.Context) error
ShutdownAll stops all running plugins with a 5s per-plugin deadline. Errors are accumulated via errors.Join() so all plugins are attempted even on partial failure.
type Version ¶
type Version struct {
Major int // Major version number
Minor int // Minor version number
Patch int // Patch version number
Prerelease string // Prerelease identifier (e.g., "alpha.1")
}
Version represents a parsed semantic version.
func ParseVersion ¶
ParseVersion parses a version string into a Version struct. Accepts formats: "1.0.0", "1.0.0-alpha.1"