Documentation
¶
Overview ¶
Package gplugin is the plugin mechanism shared by the framework and the protocol libraries.
It answers two different questions with two small layers, because they are two different problems:
- Assembly (Set / Select / Assembly): an application declares the capabilities it wants ("log", "cache", "web"), the set resolves the dependency closure, orders it topologically and produces a manifest. Capabilities register values into a container of the caller's type C.
- Host extensions (Plugin / Registry): a library owns a host object H (a broker, a server) and lets named plugins attach to it. A plugin starts against the host, stops in reverse order, and reports a state — registered, enabled or stopped — that an Observer can turn into whatever the application needs: a log line, a status light, a metric.
Neither layer knows any protocol, framework or container: C and H are always types the caller declares. That is what lets an application framework and an MQTT broker share one mechanism without sharing any semantics.
Deliberately not supported: dynamic plugins (no .so, no download, no service discovery). A plugin is compiled into the binary; the mechanism decides what is wired, in which order, and whether it is currently switched on — not what exists at run time.
Index ¶
- Constants
- Variables
- type Assembly
- type Container
- type Dependent
- type Entry
- type Event
- type Module
- type Observer
- type Option
- type Plugin
- type Registry
- func (r *Registry[H]) Add(plugin Plugin[H]) error
- func (r *Registry[H]) Enabled() []string
- func (r *Registry[H]) Names() []string
- func (r *Registry[H]) Snapshot() ([]Status, error)
- func (r *Registry[H]) Start(names ...string) error
- func (r *Registry[H]) StartAll() error
- func (r *Registry[H]) State(name string) (State, bool)
- func (r *Registry[H]) Stop(names ...string) error
- func (r *Registry[H]) StopAll() error
- type Set
- type Spec
- type Starter
- type State
- type Status
- type Stopper
- type Versioned
Constants ¶
const Version = "0.0.0-dev"
Version is the semantic version of this library.
Variables ¶
var ErrAlreadyRegistered = errors.New("gplugin: already registered")
ErrAlreadyRegistered means an id or name was registered twice.
var ErrPermanent = errors.New("gplugin: permanent error")
ErrPermanent marks wiring mistakes: retrying cannot help.
Functions ¶
This section is empty.
Types ¶
type Assembly ¶
type Assembly[C any] struct { // contains filtered or unexported fields }
Assembly is a selected, ordered set of capabilities.
type Container ¶
Container carries values between capabilities during Register.
It is the smallest untyped store a capability needs. A framework with its own container satisfies it without adapters, and an application without one can use a map.
type Dependent ¶
type Dependent interface {
DependsOn() []string
}
Dependent declares the ids that must come first, for both layers of this package.
type Event ¶
Event is one observed state transition.
A failed transition reports From == To with Err set, so an observer can log the failure from the event alone instead of querying the registry again.
type Module ¶
Module is an assembled capability: it registers its values into the container and may have a lifecycle.
C is the container type of the caller. An application framework passes its own container type; a test passes a map; this package never assumes either.
func From ¶
From turns a Spec into a Module.
No description is inferred: everything the manifest shows comes from the Spec, so an adapted module's version and dependencies must be stated rather than guessed. A spec without hooks becomes a wiring-only module — it does not satisfy Starter, so selection never lists it as started and a rollback has nothing to stop for it.
type Observer ¶
type Observer interface {
StateChanged(Event)
}
Observer receives every state transition.
It is how an application learns what happened without polling: printing what started, turning a status light grey, green or red, counting failures. Observers are called synchronously, after the transition is already recorded and with no lock held; an implementation must not block and must not call back into the registry's Start or Stop.
type Option ¶
type Option func(*options)
Option configures a Registry.
func WithObserver ¶
WithObserver registers an observer. Passing it several times registers several.
type Plugin ¶
type Plugin[H any] interface { // Name identifies the plugin. It is unique within a Registry and is what status // output and error messages report. Name() string // Start attaches the plugin to the host. Returning an error leaves the plugin // stopped, records the reason and rolls back the plugins this call started. Start(H) error // Stop detaches the plugin. It is called in reverse start order and must tolerate // being called when Start never succeeded. Stop() error }
Plugin is a named extension of a host object.
H is the host the plugin attaches to — a broker, a server, anything the owning library decides. Start receives the live host, so a plugin can register callbacks, start goroutines and publish through the host's own API; Stop must undo that.
type Registry ¶
type Registry[H any] struct { // contains filtered or unexported fields }
Registry is the ordered set of plugins a library offers and controls at run time.
Order comes from the optional Dependent interface and breaks ties on name, so a process start is deterministic. A duplicate or empty name is a wiring error rather than a silent replacement.
func NewRegistry ¶
NewRegistry returns a registry bound to host.
The host is fixed for the registry's lifetime: it is what every plugin's Start receives, whether the plugin starts at boot or later from a control call.
func (*Registry[H]) Add ¶
Add registers a plugin without starting it. Its state becomes StateRegistered.
func (*Registry[H]) Snapshot ¶
Snapshot reports every registered plugin in start order, with its state and the reason for its last failure.
It has no side effects: nothing is started and no event is emitted, so it is safe to call at any point — which is what makes "print what the process enabled" a one-liner for the application.
func (*Registry[H]) Start ¶
Start starts the named plugins and everything they depend on, in dependency order.
An already running plugin is left alone and produces no event: the operation is idempotent so a control call can be retried. A dependency that is not registered is an error, never a silently smaller start. When a plugin fails to start, the plugins this call started are stopped again in reverse order, the failure is recorded and observed, and the error names the plugin that failed.
func (*Registry[H]) State ¶
State reports where one plugin stands. The second result is false when the name is not registered.
func (*Registry[H]) Stop ¶
Stop stops the named plugins and every running plugin that depends on them, newest first.
Stopping something that is not running is a no-op. A failure does not abort the operation — the remaining plugins are still stopped and the failures are joined — because a half-stopped dependency graph is more dangerous than a logged error. A plugin whose Stop failed stays enabled with the reason recorded: it did not confirm that it stopped, and pretending otherwise would hide a half-detached plugin.
type Set ¶
type Set[C any] struct { // contains filtered or unexported fields }
Set is the registry of capabilities available to this binary.
It is explicit on purpose: no package-level registry, no panic on duplicates. Wiring must be testable in isolation, so a duplicate id is an error the caller sees.
func (*Set[C]) Add ¶
Add registers a capability.
A duplicate or empty id is a wiring error: silently replacing a capability would make the running binary differ from its manifest.
func (*Set[C]) Select ¶
Select resolves the requested ids, pulls in their transitive dependencies and orders the result topologically.
Every failure is a startup error rather than a silently smaller assembly: an unknown capability, a dependency that was not selected, or a cycle all mean the binary does not match the declaration.
type Spec ¶
type Spec[C any] struct { // ID is the capability name used for selection. ID string // Version is reported in the manifest; empty means the module does not declare one. Version string // DependsOn lists the capabilities that must start first. DependsOn []string // Register wires the capability. The container is this package's interface; a module // whose Register takes its own concrete type adapts it inside this closure. Register func(C) error // Start and Stop are optional; nil means the capability has no lifecycle. Start func(context.Context) error Stop func(context.Context) error }
Spec describes a capability by closures instead of a type.
It exists for modules that already have their own shape — a module whose Register takes a concrete container type, or that has its own lifecycle interface, cannot satisfy this package's interfaces by signature. Describing such a module with closures lets it join selection and manifests without renaming anything in its own repository.
type State ¶
type State int
State is the lifecycle position of one entry.
The three values are what an operator needs to tell apart: known but never started, running, and stopped again. An application renders them however it likes — a colour, a word, a metric — because presentation is the application's decision, not this package's.
const ( // StateRegistered means the entry is known and has not started yet. StateRegistered State = iota // StateEnabled means the entry is running. StateEnabled // StateStopped means the entry ran and was stopped. It is deliberately distinct from // StateRegistered: "switched off" and "never switched on" are different facts. StateStopped )
type Status ¶
type Status struct {
ID string
Version string
DependsOn []string
// State is the current position.
State State
// Err is the last start or stop failure, nil after a clean transition. It never
// changes State: a plugin whose start failed is still not running, and pretending
// otherwise would hide the reason.
Err error
}
Status is one row of a snapshot: what is registered, and where it stands.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
lifecycle
command
Command lifecycle shows what an application does with gplugin's lifecycle hooks: start a few plugins, observe every transition, and print a status table whose light is grey (registered), green (enabled) or red (stopped).
|
Command lifecycle shows what an application does with gplugin's lifecycle hooks: start a few plugins, observe every transition, and print a status table whose light is grey (registered), green (enabled) or red (stopped). |