plugin

package
v0.9.77 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: GPL-3.0 Imports: 25 Imported by: 0

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

View Source
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

View Source
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

func Plugins

func Plugins(impl fleet.Backend) map[string]goplugin.Plugin

Plugins returns the goplugin.PluginMap. The plugin side passes its real fleet.Backend in impl; the host passes nil and receives a gRPC client wrapper at Dispense time.

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

type Plugin struct {
	fleet.Backend
	// contains filtered or unexported fields
}

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.

func (*Plugin) Close

func (p *Plugin) Close() error

Close is idempotent; hashicorp/go-plugin guards double-kill internally.

func (*Plugin) Manifest

func (p *Plugin) Manifest() *Manifest

Manifest returns the parsed sidecar manifest, or nil if none was present.

func (*Plugin) Pid

func (p *Plugin) Pid() int

Pid is the running subprocess PID, or 0 if the plugin is not (yet) live.

func (*Plugin) Ping

func (p *Plugin) Ping(ctx context.Context) error

Ping invokes the plugin's gRPC Ping RPC. Returns nil if the backend does not implement the probe (e.g. an in-tree backend wrapped for tests).

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

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.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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