Documentation
¶
Overview ¶
Package external provides gRPC-based external plugin support for the workflow engine. External plugins run as separate processes communicating over gRPC via hashicorp/go-plugin.
Index ¶
- Constants
- Variables
- type CallbackServer
- func (s *CallbackServer) GetService(_ context.Context, req *pb.GetServiceRequest) (*pb.GetServiceResponse, error)
- func (s *CallbackServer) Log(_ context.Context, req *pb.LogRequest) (*emptypb.Empty, error)
- func (s *CallbackServer) TriggerWorkflow(_ context.Context, req *pb.TriggerWorkflowRequest) (*pb.ErrorResponse, error)
- type ExternalPluginAdapter
- func (a *ExternalPluginAdapter) Capabilities() []capability.Contract
- func (a *ExternalPluginAdapter) Dependencies() []plugin.PluginDependency
- func (a *ExternalPluginAdapter) Description() string
- func (a *ExternalPluginAdapter) EngineManifest() *plugin.PluginManifest
- func (a *ExternalPluginAdapter) ModuleFactories() map[string]plugin.ModuleFactory
- func (a *ExternalPluginAdapter) ModuleSchemas() []*schema.ModuleSchema
- func (a *ExternalPluginAdapter) Name() string
- func (a *ExternalPluginAdapter) OnDisable(_ plugin.PluginContext) error
- func (a *ExternalPluginAdapter) OnEnable(_ plugin.PluginContext) error
- func (a *ExternalPluginAdapter) RegisterRoutes(_ *http.ServeMux)
- func (a *ExternalPluginAdapter) StepFactories() map[string]plugin.StepFactory
- func (a *ExternalPluginAdapter) TriggerFactories() map[string]plugin.TriggerFactory
- func (a *ExternalPluginAdapter) UIPages() []plugin.UIPageDef
- func (a *ExternalPluginAdapter) Version() string
- func (a *ExternalPluginAdapter) WiringHooks() []plugin.WiringHook
- func (a *ExternalPluginAdapter) WorkflowHandlers() map[string]plugin.WorkflowHandlerFactory
- type ExternalPluginManager
- func (m *ExternalPluginManager) DiscoverPlugins() ([]string, error)
- func (m *ExternalPluginManager) IsLoaded(name string) bool
- func (m *ExternalPluginManager) LoadPlugin(name string) (*ExternalPluginAdapter, error)
- func (m *ExternalPluginManager) LoadedPlugins() []string
- func (m *ExternalPluginManager) ReloadPlugin(name string) (*ExternalPluginAdapter, error)
- func (m *ExternalPluginManager) Shutdown()
- func (m *ExternalPluginManager) UnloadPlugin(name string) error
- type GRPCPlugin
- type PluginClient
- type PluginHandler
- type RemoteModule
- func (m *RemoteModule) Dependencies() []string
- func (m *RemoteModule) Destroy() error
- func (m *RemoteModule) Init(app modular.Application) error
- func (m *RemoteModule) InvokeService(method string, args map[string]any) (map[string]any, error)
- func (m *RemoteModule) Name() string
- func (m *RemoteModule) ProvidesServices() []string
- func (m *RemoteModule) RegisterConfig(app modular.Application) error
- func (m *RemoteModule) RequiresServices() []string
- func (m *RemoteModule) Start(ctx context.Context) error
- func (m *RemoteModule) Stop(ctx context.Context) error
- type RemoteStep
- type ServiceLookupFunc
- type TriggerFunc
- type UIManifest
- type UINavItem
- type UIPluginEntry
- type UIPluginHandler
- type UIPluginInfo
- type UIPluginManager
- func (m *UIPluginManager) AllUIPluginInfos() []UIPluginInfo
- func (m *UIPluginManager) AsNativePlugin(name string) plugin.NativePlugin
- func (m *UIPluginManager) DiscoverPlugins() ([]string, error)
- func (m *UIPluginManager) GetPlugin(name string) (*UIPluginEntry, bool)
- func (m *UIPluginManager) IsLoaded(name string) bool
- func (m *UIPluginManager) LoadPlugin(name string) error
- func (m *UIPluginManager) LoadedPlugins() []string
- func (m *UIPluginManager) ReloadPlugin(name string) error
- func (m *UIPluginManager) ServeAssets(name string) http.Handler
- func (m *UIPluginManager) UIPages(name string) []plugin.UIPageDef
- func (m *UIPluginManager) UnloadPlugin(name string) error
- type UIPluginNativePlugin
- func (p *UIPluginNativePlugin) Dependencies() []plugin.PluginDependency
- func (p *UIPluginNativePlugin) Description() string
- func (p *UIPluginNativePlugin) Name() string
- func (p *UIPluginNativePlugin) OnDisable(_ plugin.PluginContext) error
- func (p *UIPluginNativePlugin) OnEnable(_ plugin.PluginContext) error
- func (p *UIPluginNativePlugin) RegisterRoutes(_ *http.ServeMux)
- func (p *UIPluginNativePlugin) UIPages() []plugin.UIPageDef
- func (p *UIPluginNativePlugin) Version() string
Constants ¶
const ( // ProtocolVersion is the plugin protocol version. // Increment this when making breaking changes to the gRPC interface. ProtocolVersion = 1 // MagicCookieKey is the environment variable used for the handshake. MagicCookieKey = "WORKFLOW_PLUGIN" // MagicCookieValue is the expected value for the handshake cookie. MagicCookieValue = "workflow-external-plugin-v1" )
Variables ¶
var Handshake = goplugin.HandshakeConfig{ ProtocolVersion: ProtocolVersion, MagicCookieKey: MagicCookieKey, MagicCookieValue: MagicCookieValue, }
Handshake is the shared handshake configuration between host and plugins. Both the host (client) and plugin (server) must use identical values.
Functions ¶
This section is empty.
Types ¶
type CallbackServer ¶
type CallbackServer struct {
pb.UnimplementedEngineCallbackServiceServer
// contains filtered or unexported fields
}
CallbackServer implements the EngineCallbackService gRPC server. It runs on the host and is called by plugin processes.
func NewCallbackServer ¶
func NewCallbackServer(onTrigger TriggerFunc, lookup ServiceLookupFunc, logger *log.Logger) *CallbackServer
NewCallbackServer creates a new callback server.
func (*CallbackServer) GetService ¶
func (s *CallbackServer) GetService(_ context.Context, req *pb.GetServiceRequest) (*pb.GetServiceResponse, error)
func (*CallbackServer) Log ¶
func (s *CallbackServer) Log(_ context.Context, req *pb.LogRequest) (*emptypb.Empty, error)
func (*CallbackServer) TriggerWorkflow ¶
func (s *CallbackServer) TriggerWorkflow(_ context.Context, req *pb.TriggerWorkflowRequest) (*pb.ErrorResponse, error)
type ExternalPluginAdapter ¶
type ExternalPluginAdapter struct {
// contains filtered or unexported fields
}
ExternalPluginAdapter wraps a gRPC plugin client to implement plugin.EnginePlugin. The engine sees this as a regular plugin — no changes to engine.go needed.
func NewExternalPluginAdapter ¶
func NewExternalPluginAdapter(name string, client *PluginClient) (*ExternalPluginAdapter, error)
NewExternalPluginAdapter creates an adapter from a connected plugin client.
func (*ExternalPluginAdapter) Capabilities ¶
func (a *ExternalPluginAdapter) Capabilities() []capability.Contract
func (*ExternalPluginAdapter) Dependencies ¶
func (a *ExternalPluginAdapter) Dependencies() []plugin.PluginDependency
func (*ExternalPluginAdapter) Description ¶
func (a *ExternalPluginAdapter) Description() string
func (*ExternalPluginAdapter) EngineManifest ¶
func (a *ExternalPluginAdapter) EngineManifest() *plugin.PluginManifest
func (*ExternalPluginAdapter) ModuleFactories ¶
func (a *ExternalPluginAdapter) ModuleFactories() map[string]plugin.ModuleFactory
func (*ExternalPluginAdapter) ModuleSchemas ¶
func (a *ExternalPluginAdapter) ModuleSchemas() []*schema.ModuleSchema
func (*ExternalPluginAdapter) Name ¶
func (a *ExternalPluginAdapter) Name() string
func (*ExternalPluginAdapter) OnDisable ¶
func (a *ExternalPluginAdapter) OnDisable(_ plugin.PluginContext) error
func (*ExternalPluginAdapter) OnEnable ¶
func (a *ExternalPluginAdapter) OnEnable(_ plugin.PluginContext) error
func (*ExternalPluginAdapter) RegisterRoutes ¶
func (a *ExternalPluginAdapter) RegisterRoutes(_ *http.ServeMux)
func (*ExternalPluginAdapter) StepFactories ¶
func (a *ExternalPluginAdapter) StepFactories() map[string]plugin.StepFactory
func (*ExternalPluginAdapter) TriggerFactories ¶
func (a *ExternalPluginAdapter) TriggerFactories() map[string]plugin.TriggerFactory
func (*ExternalPluginAdapter) UIPages ¶
func (a *ExternalPluginAdapter) UIPages() []plugin.UIPageDef
func (*ExternalPluginAdapter) Version ¶
func (a *ExternalPluginAdapter) Version() string
func (*ExternalPluginAdapter) WiringHooks ¶
func (a *ExternalPluginAdapter) WiringHooks() []plugin.WiringHook
func (*ExternalPluginAdapter) WorkflowHandlers ¶
func (a *ExternalPluginAdapter) WorkflowHandlers() map[string]plugin.WorkflowHandlerFactory
type ExternalPluginManager ¶
type ExternalPluginManager struct {
// contains filtered or unexported fields
}
ExternalPluginManager discovers, loads, and manages external plugin subprocesses. Each plugin lives in its own subdirectory under the plugins directory and communicates with the host via gRPC through the go-plugin framework.
func NewExternalPluginManager ¶
func NewExternalPluginManager(pluginsDir string, logger *log.Logger) *ExternalPluginManager
NewExternalPluginManager creates a new manager that scans the given directory for plugins.
func (*ExternalPluginManager) DiscoverPlugins ¶
func (m *ExternalPluginManager) DiscoverPlugins() ([]string, error)
DiscoverPlugins scans the plugins directory for subdirectories that contain a plugin.json manifest and an executable binary matching the directory name. It returns the list of discovered plugin names.
func (*ExternalPluginManager) IsLoaded ¶
func (m *ExternalPluginManager) IsLoaded(name string) bool
IsLoaded returns true if the named plugin is currently loaded.
func (*ExternalPluginManager) LoadPlugin ¶
func (m *ExternalPluginManager) LoadPlugin(name string) (*ExternalPluginAdapter, error)
LoadPlugin starts the named plugin subprocess, performs the handshake, and creates an ExternalPluginAdapter. The plugin must have been previously discovered via DiscoverPlugins.
func (*ExternalPluginManager) LoadedPlugins ¶
func (m *ExternalPluginManager) LoadedPlugins() []string
LoadedPlugins returns the names of all currently loaded plugins.
func (*ExternalPluginManager) ReloadPlugin ¶
func (m *ExternalPluginManager) ReloadPlugin(name string) (*ExternalPluginAdapter, error)
ReloadPlugin unloads and then loads the named plugin.
func (*ExternalPluginManager) Shutdown ¶
func (m *ExternalPluginManager) Shutdown()
Shutdown kills all loaded plugin subprocesses.
func (*ExternalPluginManager) UnloadPlugin ¶
func (m *ExternalPluginManager) UnloadPlugin(name string) error
UnloadPlugin stops the named plugin subprocess and removes it from the internal map.
type GRPCPlugin ¶
type GRPCPlugin struct {
goplugin.Plugin
// CallbackServer is the host-side callback implementation.
// When non-nil, it will be registered on the broker for plugin access.
CallbackServer *CallbackServer
}
GRPCPlugin implements go-plugin's Plugin and GRPCPlugin interfaces. It bridges between go-plugin's plugin system and our gRPC services.
func (*GRPCPlugin) GRPCClient ¶
func (p *GRPCPlugin) GRPCClient(_ context.Context, broker *goplugin.GRPCBroker, c *grpc.ClientConn) (any, error)
GRPCClient returns the client wrapper (host side). This is called by the host process to get a client that talks to the plugin.
func (*GRPCPlugin) GRPCServer ¶
func (p *GRPCPlugin) GRPCServer(broker *goplugin.GRPCBroker, s *grpc.Server) error
GRPCServer registers the plugin service on the gRPC server (plugin side). This is called by the plugin process.
type PluginClient ¶
type PluginClient struct {
// contains filtered or unexported fields
}
PluginClient wraps the gRPC client for the plugin service.
type PluginHandler ¶
type PluginHandler struct {
// contains filtered or unexported fields
}
PluginHandler provides HTTP API endpoints for managing external plugins.
func NewPluginHandler ¶
func NewPluginHandler(manager *ExternalPluginManager) *PluginHandler
NewPluginHandler creates a new handler backed by the given external plugin manager.
func (*PluginHandler) RegisterRoutes ¶
func (h *PluginHandler) RegisterRoutes(mux *http.ServeMux)
RegisterRoutes registers the external plugin management HTTP routes on the given mux.
type RemoteModule ¶
type RemoteModule struct {
// contains filtered or unexported fields
}
RemoteModule implements modular.Module by delegating to a gRPC plugin.
func NewRemoteModule ¶
func NewRemoteModule(name, handleID string, client pb.PluginServiceClient) *RemoteModule
NewRemoteModule creates a remote module proxy.
func (*RemoteModule) Dependencies ¶
func (m *RemoteModule) Dependencies() []string
func (*RemoteModule) Destroy ¶
func (m *RemoteModule) Destroy() error
Destroy releases the remote module resources.
func (*RemoteModule) Init ¶
func (m *RemoteModule) Init(app modular.Application) error
func (*RemoteModule) InvokeService ¶
InvokeService calls a named method on the remote module's service interface.
func (*RemoteModule) Name ¶
func (m *RemoteModule) Name() string
func (*RemoteModule) ProvidesServices ¶
func (m *RemoteModule) ProvidesServices() []string
func (*RemoteModule) RegisterConfig ¶
func (m *RemoteModule) RegisterConfig(app modular.Application) error
func (*RemoteModule) RequiresServices ¶
func (m *RemoteModule) RequiresServices() []string
type RemoteStep ¶
type RemoteStep struct {
// contains filtered or unexported fields
}
RemoteStep implements module.PipelineStep by delegating to a gRPC plugin.
func NewRemoteStep ¶
func NewRemoteStep(name, handleID string, client pb.PluginServiceClient) *RemoteStep
NewRemoteStep creates a remote step proxy.
func (*RemoteStep) Destroy ¶
func (s *RemoteStep) Destroy() error
Destroy releases the remote step resources.
func (*RemoteStep) Execute ¶
func (s *RemoteStep) Execute(ctx context.Context, pc *module.PipelineContext) (*module.StepResult, error)
func (*RemoteStep) Name ¶
func (s *RemoteStep) Name() string
type ServiceLookupFunc ¶
ServiceLookupFunc checks if a named service exists in the host.
type TriggerFunc ¶
TriggerFunc is called when a plugin fires a workflow trigger.
type UIManifest ¶ added in v0.1.1
type UIManifest struct {
// Name is the plugin identifier. Must match the plugin directory name.
Name string `json:"name"`
// Version is the plugin version string (e.g. "1.0.0").
Version string `json:"version"`
// Description is a short human-readable description of the plugin.
Description string `json:"description,omitempty"`
NavItems []UINavItem `json:"navItems,omitempty"`
// AssetDir is the subdirectory within the plugin directory that holds
// static assets. Defaults to "assets" when empty.
AssetDir string `json:"assetDir,omitempty"`
}
UIManifest describes the UI contribution of a go-plugin UI plugin. Place this as "ui.json" in the plugin directory alongside "plugin.json".
Directory Layout ¶
plugins/
my-ui-plugin/
my-ui-plugin (binary, required for API handlers; optional for asset-only plugins)
plugin.json (gRPC plugin manifest; required when binary is present)
ui.json (UI manifest: nav items and asset location)
assets/ (static files: HTML, CSS, JS, images, etc.)
index.html
main.js
Asset Versioning ¶
Include a version hash in asset filenames (e.g. main.abc123.js) and reference them from index.html so browsers pick up new files after hot-deploy.
Hot-Reload ¶
After updating assets or ui.json, call:
POST /api/v1/plugins/ui/{name}/reload
This re-reads the manifest and serves the new files without restarting the workflow engine.
Hot-Deploy ¶
Replace the plugin binary and/or assets directory with the new version, then call the reload endpoint above.
type UINavItem ¶ added in v0.1.1
type UINavItem struct {
ID string `json:"id"`
Label string `json:"label"`
Icon string `json:"icon,omitempty"`
// "global" – always visible top-level entries
// "workflow" – shown only when a workflow is open
// "plugin" – plugin-contributed entries (default)
// "tools" – administrative tooling entries
Category string `json:"category,omitempty"`
Order int `json:"order,omitempty"`
// (e.g. "viewer", "editor", "admin", "operator").
RequiredRole string `json:"requiredRole,omitempty"`
// (e.g. "plugins.manage").
RequiredPermission string `json:"requiredPermission,omitempty"`
}
UINavItem describes a single entry in the admin UI navigation sidebar.
type UIPluginEntry ¶ added in v0.1.1
type UIPluginEntry struct {
// Manifest is the parsed ui.json for this plugin.
Manifest UIManifest
// AssetsDir is the absolute path to the plugin's static assets directory.
AssetsDir string
}
UIPluginEntry holds the runtime state of a loaded UI plugin.
type UIPluginHandler ¶ added in v0.1.1
type UIPluginHandler struct {
// contains filtered or unexported fields
}
UIPluginHandler provides HTTP API endpoints for managing UI plugins.
Routes registered by RegisterRoutes:
GET /api/v1/plugins/ui – list loaded UI plugins
GET /api/v1/plugins/ui/available – list all discovered UI plugins
GET /api/v1/plugins/ui/{name}/manifest – get a plugin's UI manifest
POST /api/v1/plugins/ui/{name}/load – load a UI plugin
POST /api/v1/plugins/ui/{name}/unload – unload a UI plugin
POST /api/v1/plugins/ui/{name}/reload – hot-reload a UI plugin
GET /api/v1/plugins/ui/{name}/assets/{path…} – serve static assets
func NewUIPluginHandler ¶ added in v0.1.1
func NewUIPluginHandler(manager *UIPluginManager) *UIPluginHandler
NewUIPluginHandler creates a new handler backed by the given UIPluginManager.
func (*UIPluginHandler) RegisterRoutes ¶ added in v0.1.1
func (h *UIPluginHandler) RegisterRoutes(mux *http.ServeMux)
RegisterRoutes registers all UI plugin HTTP routes on mux.
type UIPluginInfo ¶ added in v0.1.1
type UIPluginInfo struct {
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
Loaded bool `json:"loaded"`
}
UIPluginInfo is the JSON representation of a UI plugin for API responses.
type UIPluginManager ¶ added in v0.1.1
type UIPluginManager struct {
// contains filtered or unexported fields
}
UIPluginManager discovers and manages UI plugins under a shared plugins directory. Each UI plugin is a subdirectory that contains a "ui.json" manifest and an optional "assets" subdirectory with static files.
Hot-reload ¶
Calling ReloadPlugin re-reads the manifest and the assets directory from disk without restarting the workflow engine. The static file server for the plugin is updated atomically so in-flight requests are not interrupted.
Integration with PluginManager navigation ¶
Call UIPages to get UIPageDef entries for a loaded UI plugin, then register a UIPluginNativePlugin wrapper with a PluginManager to surface those entries through the standard navigation API.
func NewUIPluginManager ¶ added in v0.1.1
func NewUIPluginManager(pluginsDir string, logger *log.Logger) *UIPluginManager
NewUIPluginManager creates a manager that scans the given directory for UI plugins (subdirectories containing a "ui.json" manifest).
func (*UIPluginManager) AllUIPluginInfos ¶ added in v0.1.1
func (m *UIPluginManager) AllUIPluginInfos() []UIPluginInfo
AllUIPluginInfos returns summary information for every currently loaded UI plugin.
func (*UIPluginManager) AsNativePlugin ¶ added in v0.1.1
func (m *UIPluginManager) AsNativePlugin(name string) plugin.NativePlugin
AsNativePlugin returns a plugin.NativePlugin implementation that surfaces the named UI plugin's navigation entries through the standard PluginManager API. Returns nil if the plugin is not loaded.
Use this to register a UI plugin's nav items with a PluginManager:
if np := uiMgr.AsNativePlugin("my-ui-plugin"); np != nil {
_ = pluginMgr.Register(np)
_ = pluginMgr.Enable("my-ui-plugin")
}
func (*UIPluginManager) DiscoverPlugins ¶ added in v0.1.1
func (m *UIPluginManager) DiscoverPlugins() ([]string, error)
DiscoverPlugins scans the plugins directory and returns names of all subdirectories that contain a "ui.json" manifest file.
func (*UIPluginManager) GetPlugin ¶ added in v0.1.1
func (m *UIPluginManager) GetPlugin(name string) (*UIPluginEntry, bool)
GetPlugin returns the entry for the named UI plugin. The second return value is false if the plugin is not loaded.
func (*UIPluginManager) IsLoaded ¶ added in v0.1.1
func (m *UIPluginManager) IsLoaded(name string) bool
IsLoaded returns true if the named UI plugin is currently loaded.
func (*UIPluginManager) LoadPlugin ¶ added in v0.1.1
func (m *UIPluginManager) LoadPlugin(name string) error
LoadPlugin reads the "ui.json" manifest for the named plugin and registers it. If the plugin is already loaded it is replaced (hot-reload semantics).
func (*UIPluginManager) LoadedPlugins ¶ added in v0.1.1
func (m *UIPluginManager) LoadedPlugins() []string
LoadedPlugins returns the names of all currently loaded UI plugins.
func (*UIPluginManager) ReloadPlugin ¶ added in v0.1.1
func (m *UIPluginManager) ReloadPlugin(name string) error
ReloadPlugin re-reads the manifest and assets directory from disk for the named plugin. This is the primary hot-reload mechanism: deploy updated assets to the plugin directory, then call this method.
func (*UIPluginManager) ServeAssets ¶ added in v0.1.1
func (m *UIPluginManager) ServeAssets(name string) http.Handler
ServeAssets returns an http.Handler that serves the static assets of the named plugin directly from its assets directory. Returns nil if the plugin is not loaded or its assets directory does not exist.
func (*UIPluginManager) UIPages ¶ added in v0.1.1
func (m *UIPluginManager) UIPages(name string) []plugin.UIPageDef
UIPages converts the nav items declared in a loaded UI plugin's manifest into plugin.UIPageDef entries compatible with the PluginManager navigation system.
func (*UIPluginManager) UnloadPlugin ¶ added in v0.1.1
func (m *UIPluginManager) UnloadPlugin(name string) error
UnloadPlugin removes a UI plugin from the manager. Returns an error if the plugin is not currently loaded.
type UIPluginNativePlugin ¶ added in v0.1.1
type UIPluginNativePlugin struct {
// contains filtered or unexported fields
}
UIPluginNativePlugin adapts a loaded UI plugin as a plugin.NativePlugin so its navigation entries are visible through the standard PluginManager API. It implements a read-through to the UIPluginManager so that hot-reloads automatically update the navigation data returned by UIPages.
func (*UIPluginNativePlugin) Dependencies ¶ added in v0.1.1
func (p *UIPluginNativePlugin) Dependencies() []plugin.PluginDependency
func (*UIPluginNativePlugin) Description ¶ added in v0.1.1
func (p *UIPluginNativePlugin) Description() string
func (*UIPluginNativePlugin) Name ¶ added in v0.1.1
func (p *UIPluginNativePlugin) Name() string
func (*UIPluginNativePlugin) OnDisable ¶ added in v0.1.1
func (p *UIPluginNativePlugin) OnDisable(_ plugin.PluginContext) error
func (*UIPluginNativePlugin) OnEnable ¶ added in v0.1.1
func (p *UIPluginNativePlugin) OnEnable(_ plugin.PluginContext) error
func (*UIPluginNativePlugin) RegisterRoutes ¶ added in v0.1.1
func (p *UIPluginNativePlugin) RegisterRoutes(_ *http.ServeMux)
func (*UIPluginNativePlugin) UIPages ¶ added in v0.1.1
func (p *UIPluginNativePlugin) UIPages() []plugin.UIPageDef
UIPages reads the current nav items from the UIPluginManager so that hot-reloads (which call UIPluginManager.ReloadPlugin) are reflected immediately without re-registering the plugin.
func (*UIPluginNativePlugin) Version ¶ added in v0.1.1
func (p *UIPluginNativePlugin) Version() string