Documentation
¶
Overview ¶
Package plugin glues the fleet.Backend contract to hashicorp/go-plugin's subprocess+gRPC model. grpc.go is the only translation point between package fleet types and protobuf messages.
Index ¶
- Constants
- Variables
- func Plugins(impl fleet.Backend) map[string]goplugin.Plugin
- type FleetPlugin
- type LaunchConfig
- type Manifest
- type Plugin
- type Supervisor
- func (s *Supervisor) Allocate(ctx context.Context, req fleet.AllocationRequest) (*fleet.Allocation, error)
- func (s *Supervisor) CleanupResolution(ctx context.Context, id string, config map[string]string) error
- func (s *Supervisor) Close() error
- func (s *Supervisor) Deallocate(ctx context.Context, id fleet.AllocationID, ref string) error
- func (s *Supervisor) HealthCheck(ctx context.Context) error
- func (s *Supervisor) Manifest() *Manifest
- func (s *Supervisor) Name() string
- func (s *Supervisor) Pid() int
- func (s *Supervisor) RestartCount() int
- func (s *Supervisor) Status(ctx context.Context, id fleet.AllocationID, ref string) (fleet.Status, error)
- func (s *Supervisor) TotalRestartCount() int
- func (s *Supervisor) Watch(ctx context.Context, id fleet.AllocationID, ref string) (<-chan fleet.StatusUpdate, error)
- type SupervisorConfig
Constants ¶
const PluginName = "fleet_backend"
PluginName is the dispense key shared by host and plugin. A single binary serves exactly one backend, so the map only ever has one entry.
Variables ¶
var Handshake = goplugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "GGSCALE_FLEET_PLUGIN",
MagicCookieValue: "ggscale-v1",
}
Handshake is the magic-cookie/protocol pair both host and plugin must agree on before any RPC is dispatched. ProtocolVersion bumps alongside the proto package version on any breaking change.
Functions ¶
Types ¶
type FleetPlugin ¶
type FleetPlugin struct {
goplugin.Plugin
Impl fleet.Backend // populated only on the plugin (serving) side
}
FleetPlugin embeds goplugin.Plugin so the unused net/rpc stubs are satisfied; the transport is gRPC only.
func (*FleetPlugin) GRPCClient ¶
func (p *FleetPlugin) GRPCClient(ctx context.Context, _ *goplugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error)
GRPCClient returns the host-side fleet.Backend wrapper around the plugin's gRPC client. Called by go-plugin after the subprocess is up.
func (*FleetPlugin) GRPCServer ¶
func (p *FleetPlugin) GRPCServer(_ *goplugin.GRPCBroker, s *grpc.Server) error
GRPCServer registers the plugin's fleet.Backend on s. Called by go-plugin.
type LaunchConfig ¶
type LaunchConfig struct {
Dir string // default /etc/ggscale/plugins
Name string // suffix after ggscale-fleet-; matches FLEET_BACKEND=plugin:<name>
Env []string // extra env appended to os.Environ() for the subprocess
}
LaunchConfig resolves to the binary path "<Dir>/ggscale-fleet-<Name>".
func (LaunchConfig) BinaryPath ¶
func (c LaunchConfig) BinaryPath() string
BinaryPath is the absolute path the host expects for the named plugin.
type Manifest ¶
type Manifest struct {
Name string
Version string
ProtocolVersion int
// SHA256 is the hex-encoded expected hash of the plugin binary. Empty
// means the manifest declines integrity enforcement (warn-only path);
// a non-empty value is verified before launch and a mismatch refuses
// to start the plugin.
SHA256 string
}
Manifest is the optional metadata sidecar a plugin author drops alongside the binary as "<binary>.manifest.toml". Only flat top-level keys are supported — that's all the host needs and avoids a full TOML dependency.
type Plugin ¶
Plugin is the host's handle on a running subprocess. It satisfies fleet.Backend (via the embedded gRPC client) and io.Closer (Kill on shutdown). The optional Manifest carries metadata from the sidecar TOML file if present at launch time.
func Launch ¶
func Launch(cfg LaunchConfig) (*Plugin, error)
Launch starts the named plugin binary and returns a Backend wrapper around the resulting gRPC client. Caller owns the returned *Plugin and must Close it during shutdown.
type Supervisor ¶
type Supervisor struct {
// contains filtered or unexported fields
}
Supervisor wraps Plugin lifecycle: detects subprocess exit, restarts up to MaxRestarts consecutive deaths, and forwards fleet.Backend calls to whichever plugin is currently live. Implements fleet.Backend + io.Closer.
restartCount is "consecutive failures since last healthy probe" — a successful Ping (in pingLoop or the immediate post-restart probe) resets it. Without that reset, a plugin that crashes once a year would eventually be abandoned.
func NewSupervisor ¶
func NewSupervisor(cfg SupervisorConfig) (*Supervisor, error)
NewSupervisor launches the plugin once and starts the watcher + pingLoop goroutines. Returns an error if the initial Launch fails.
func (*Supervisor) Allocate ¶
func (s *Supervisor) Allocate(ctx context.Context, req fleet.AllocationRequest) (*fleet.Allocation, error)
Allocate forwards to the live plugin.
func (*Supervisor) CleanupResolution ¶
func (s *Supervisor) CleanupResolution(ctx context.Context, id string, config map[string]string) error
CleanupResolution forwards durable orphan recovery to capable plugins.
func (*Supervisor) Close ¶
func (s *Supervisor) Close() error
Close stops the watcher + pingLoop and kills the current subprocess. Safe to call concurrently with an in-flight restart: swap() rejects the new plugin and closes it instead.
func (*Supervisor) Deallocate ¶
func (s *Supervisor) Deallocate(ctx context.Context, id fleet.AllocationID, ref string) error
Deallocate forwards to the live plugin.
func (*Supervisor) HealthCheck ¶
func (s *Supervisor) HealthCheck(ctx context.Context) error
HealthCheck forwards to the live plugin.
func (*Supervisor) Manifest ¶
func (s *Supervisor) Manifest() *Manifest
Manifest returns the parsed manifest from the live plugin, or nil if no plugin is live or no sidecar was found.
func (*Supervisor) Name ¶
func (s *Supervisor) Name() string
Name forwards to the live plugin, or returns "plugin" if none.
func (*Supervisor) Pid ¶
func (s *Supervisor) Pid() int
Pid returns the current plugin subprocess PID, or 0 if no plugin is live.
func (*Supervisor) RestartCount ¶
func (s *Supervisor) RestartCount() int
RestartCount is the number of consecutive failed lifecycles since the last healthy Ping; reset on every successful probe.
func (*Supervisor) Status ¶
func (s *Supervisor) Status(ctx context.Context, id fleet.AllocationID, ref string) (fleet.Status, error)
Status forwards to the live plugin.
func (*Supervisor) TotalRestartCount ¶
func (s *Supervisor) TotalRestartCount() int
TotalRestartCount is the lifetime count of subprocess restarts. Never resets — useful for ops monitoring and tests that assert a restart happened without racing the consecutive-count reset.
func (*Supervisor) Watch ¶
func (s *Supervisor) Watch(ctx context.Context, id fleet.AllocationID, ref string) (<-chan fleet.StatusUpdate, error)
Watch forwards to the live plugin.
type SupervisorConfig ¶
type SupervisorConfig struct {
Launch LaunchConfig
MaxRestarts int // default 3
PollInterval time.Duration // default 1s
// MaxBackoff caps the exponential backoff applied between restart
// attempts. Default 30s.
MaxBackoff time.Duration
// PingInterval catches hung-but-alive plugins that Exited() can't see.
PingInterval time.Duration // default 10s
PingFailureBudget int // default 3
// HealthResetThreshold is how many consecutive successful pings reset
// the restart counter. A single healthy probe used to do it, which
// let a flapping plugin restart forever; default 30 (~5 min at 10s
// PingInterval).
HealthResetThreshold int
}
SupervisorConfig wires NewSupervisor. The interval/budget knobs exist primarily for tests; production uses the defaults.