Documentation
¶
Overview ¶
Package controller implements the stack lifecycle management for gridctl. It extracts orchestration logic from cmd/gridctl/deploy.go into testable, interface-backed components.
Index ¶
- func BuildWorkloadSummaries(stack *config.Stack, result *runtime.UpResult) []output.WorkloadSummary
- type ClientBuilder
- type Config
- type ContainerSpawner
- type ContainerSpawnerOptions
- type DaemonManager
- type GatewayBuilder
- func (b *GatewayBuilder) Build(verbose bool) (*GatewayInstance, error)
- func (b *GatewayBuilder) BuildAndRun(ctx context.Context, verbose bool) error
- func (b *GatewayBuilder) Run(ctx context.Context, inst *GatewayInstance, verbose bool) error
- func (b *GatewayBuilder) SetExistingLogInfra(buffer *logging.LogBuffer, handler slog.Handler)
- func (b *GatewayBuilder) SetPinStore(ps *pins.PinStore)
- func (b *GatewayBuilder) SetVaultStore(v *vault.Store)
- func (b *GatewayBuilder) SetVersion(v string)
- func (b *GatewayBuilder) SetWebFS(fn WebFSFunc)
- type GatewayInstance
- type PortAllocator
- type ProcessSpawner
- type ReplicaRuntime
- type SSHSpawner
- type ServerRegistrar
- func (r *ServerRegistrar) RegisterAll(ctx context.Context, result *runtime.UpResult, stack *config.Stack, ...)
- func (r *ServerRegistrar) RegisterOne(ctx context.Context, server config.MCPServer, replicas []ReplicaRuntime, ...) error
- func (r *ServerRegistrar) SetBasePort(p int)
- func (r *ServerRegistrar) SetLogger(logger *slog.Logger)
- func (r *ServerRegistrar) SetRuntime(rt runtime.WorkloadRuntime)
- type StackController
- type WebFSFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildWorkloadSummaries ¶
BuildWorkloadSummaries creates summary data for the status table.
Types ¶
type ClientBuilder ¶
type ClientBuilder interface {
BuildAgentClient(ctx context.Context, cfg mcp.MCPServerConfig) (mcp.AgentClient, error)
}
ClientBuilder abstracts the Gateway helper that constructs and initialises an AgentClient from an MCPServerConfig. Kept as an interface so unit tests can supply fakes without spinning up a full gateway.
type Config ¶
type Config struct {
StackPath string
Port int
BasePort int
Verbose bool
Quiet bool
NoCache bool
NoExpand bool
Foreground bool
Watch bool
DaemonChild bool
CodeMode bool // Enable code mode via CLI flag
Runtime string // Explicit runtime selection (docker, podman)
Replace bool // Stop a running stack before deploying (used by plan apply)
LogFile string // Path to log file (overrides stack.yaml logging.file)
}
Config holds all deploy configuration, replacing package-level variables.
type ContainerSpawner ¶
type ContainerSpawner struct {
// contains filtered or unexported fields
}
ContainerSpawner spawns container-backed MCP replicas (stdio or HTTP). Each Spawn starts a new container named `<stack>-<server>-replica-<id>`, waits for it via the same BuildAgentClient path the static bring-up uses, and tracks the workload id so Reap can stop + remove the container.
func NewContainerSpawner ¶
func NewContainerSpawner(opts ContainerSpawnerOptions) *ContainerSpawner
NewContainerSpawner constructs a ContainerSpawner from explicit options.
func (*ContainerSpawner) Reap ¶
Reap closes the client and removes the backing container. Callers pass the full Replica (not the bare client) so the scaler can log per-replica id.
func (*ContainerSpawner) Spawn ¶
func (c *ContainerSpawner) Spawn(ctx context.Context) (mcp.AgentClient, error)
Spawn starts a new container and returns a ready-to-serve AgentClient. On any failure after container start but before client initialisation, the spawned container is stopped and removed so no orphan containers linger. Matches the "defer cleanup()" requirement in the spec.
type ContainerSpawnerOptions ¶
type ContainerSpawnerOptions struct {
Builder ClientBuilder
Runtime runtime.WorkloadRuntime
Stack string // stack.Name
Server config.MCPServer // full server config (command, env, port, etc.)
Network string // resolved network name
Image string // pre-built image (source-based workloads pre-tag as gridctl-<stack>-<server>:latest)
Transport string // "http" | "stdio" | "sse"
Ports PortAllocator
Logger *slog.Logger
InitialID int // next replica id to assign (typically set.Size() at register time)
}
ContainerSpawnerOptions bundles everything a ContainerSpawner needs. Kept as a struct so callers don't accumulate positional arguments.
type DaemonManager ¶
type DaemonManager struct {
// contains filtered or unexported fields
}
DaemonManager handles daemon lifecycle: forking child processes and waiting for readiness.
func NewDaemonManager ¶
func NewDaemonManager(cfg Config) *DaemonManager
NewDaemonManager creates a DaemonManager.
func (*DaemonManager) Fork ¶
func (d *DaemonManager) Fork(stack *config.Stack) (int, error)
Fork starts a daemon child process that runs the MCP gateway in the background. Returns the child PID.
func (*DaemonManager) ForkStackless ¶
func (d *DaemonManager) ForkStackless() (int, error)
ForkStackless starts a stackless daemon child that runs only the HTTP API and web UI (no stack, no containers). Returns the child PID.
func (*DaemonManager) WaitForHealth ¶
func (d *DaemonManager) WaitForHealth(port int, timeout time.Duration) error
WaitForHealth polls the /health endpoint until it returns 200 or timeout. Used for stackless mode where /ready always returns 503.
func (*DaemonManager) WaitForReady ¶
func (d *DaemonManager) WaitForReady(port int, timeout time.Duration) error
WaitForReady polls the /ready endpoint until it returns 200 or timeout. The /ready endpoint only succeeds when all MCP servers are initialized, unlike /health which succeeds immediately when the HTTP server starts.
type GatewayBuilder ¶
type GatewayBuilder struct {
// contains filtered or unexported fields
}
GatewayBuilder constructs and runs the MCP gateway from a stack config.
func NewGatewayBuilder ¶
func NewGatewayBuilder(cfg Config, stack *config.Stack, stackPath string, rt *runtime.Orchestrator, result *runtime.UpResult) *GatewayBuilder
NewGatewayBuilder creates a GatewayBuilder.
func (*GatewayBuilder) Build ¶
func (b *GatewayBuilder) Build(verbose bool) (*GatewayInstance, error)
Build constructs all gateway components without starting the HTTP server.
func (*GatewayBuilder) BuildAndRun ¶
func (b *GatewayBuilder) BuildAndRun(ctx context.Context, verbose bool) error
BuildAndRun constructs the gateway and runs it until shutdown. This is the main blocking call that replaces the old runGateway() function.
func (*GatewayBuilder) Run ¶
func (b *GatewayBuilder) Run(ctx context.Context, inst *GatewayInstance, verbose bool) error
Run starts the HTTP server, registers MCP servers, and blocks until shutdown.
func (*GatewayBuilder) SetExistingLogInfra ¶
func (b *GatewayBuilder) SetExistingLogInfra(buffer *logging.LogBuffer, handler slog.Handler)
SetExistingLogInfra allows reusing a log buffer/handler created earlier (e.g., in foreground mode where orchestrator events should also be captured).
func (*GatewayBuilder) SetPinStore ¶
func (b *GatewayBuilder) SetPinStore(ps *pins.PinStore)
SetPinStore sets the pin store for API server injection.
func (*GatewayBuilder) SetVaultStore ¶
func (b *GatewayBuilder) SetVaultStore(v *vault.Store)
SetVaultStore sets the vault store for API server injection and log redaction.
func (*GatewayBuilder) SetVersion ¶
func (b *GatewayBuilder) SetVersion(v string)
SetVersion sets the gateway version string.
func (*GatewayBuilder) SetWebFS ¶
func (b *GatewayBuilder) SetWebFS(fn WebFSFunc)
SetWebFS sets the function for getting embedded web files.
type GatewayInstance ¶
type GatewayInstance struct {
Gateway *mcp.Gateway
APIServer *api.Server
HTTPServer *http.Server
LogBuffer *logging.LogBuffer
Handler slog.Handler
RegistryServer *registry.Server // Internal registry MCP server (nil if empty)
}
GatewayInstance holds all components of a running gateway.
type PortAllocator ¶
type PortAllocator interface {
Allocate() int
}
PortAllocator hands out unique host ports for new container replicas. Implementations must be safe to call from the autoscaler's goroutine.
func NewAtomicPortAllocator ¶
func NewAtomicPortAllocator(base int) PortAllocator
NewAtomicPortAllocator returns a PortAllocator starting at base+1 on the first call. base is typically the basePort handed to Orchestrator.Up plus the number of ports already assigned during the initial bring-up.
type ProcessSpawner ¶
type ProcessSpawner struct {
// contains filtered or unexported fields
}
ProcessSpawner spawns local-process MCP replicas. Each Spawn clones the template MCPServerConfig and delegates construction to the gateway's BuildAgentClient so the same transport plumbing the static path uses also services autoscaled spawns.
func NewProcessSpawner ¶
func NewProcessSpawner(builder ClientBuilder, template mcp.MCPServerConfig) *ProcessSpawner
NewProcessSpawner returns a Spawner for LocalProcess (and SSH; see below) replicas. template.Name is the logical server name; it's reused for every replica and does not need per-replica uniqueness at this layer because the ReplicaSet assigns monotonic ids on AddReplica.
func (*ProcessSpawner) Spawn ¶
func (s *ProcessSpawner) Spawn(ctx context.Context) (mcp.AgentClient, error)
Spawn constructs a new replica's AgentClient, initialises MCP, and returns the ready-to-serve client. Callers (the Autoscaler) add the result to the ReplicaSet via AddReplica.
type ReplicaRuntime ¶
ReplicaRuntime carries the runtime handles for one replica that the reload handler has already provisioned. For non-container replicas, ContainerID is "" and HostPort may be 0.
type SSHSpawner ¶
type SSHSpawner struct {
// contains filtered or unexported fields
}
SSHSpawner spawns SSH-transport MCP replicas. Each Spawn opens a fresh SSH session via the gateway's transport switch (BuildAgentClient handles the ssh command construction). The template carries SSHHost / SSHUser / SSHPort / SSHIdentityFile etc. and is reused verbatim per replica: gridctl already tolerates many concurrent ssh sessions to the same host.
func NewSSHSpawner ¶
func NewSSHSpawner(builder ClientBuilder, template mcp.MCPServerConfig) *SSHSpawner
NewSSHSpawner returns a Spawner for SSH-transport replicas.
func (*SSHSpawner) Spawn ¶
func (s *SSHSpawner) Spawn(ctx context.Context) (mcp.AgentClient, error)
Spawn constructs and initialises a new SSH-backed AgentClient.
type ServerRegistrar ¶
type ServerRegistrar struct {
// contains filtered or unexported fields
}
ServerRegistrar handles MCP server registration with the gateway. It provides a unified registration path for both bulk and single-server registration, eliminating the duplication between the former registerMCPServers() and registerSingleMCPServer() functions.
func NewServerRegistrar ¶
func NewServerRegistrar(gateway *mcp.Gateway, noExpand bool) *ServerRegistrar
NewServerRegistrar creates a ServerRegistrar.
func (*ServerRegistrar) RegisterAll ¶
func (r *ServerRegistrar) RegisterAll(ctx context.Context, result *runtime.UpResult, stack *config.Stack, stackPath string)
RegisterAll registers all MCP servers from the UpResult with the gateway. For each server it builds one MCPServerConfig per replica and registers them as a single ReplicaSet under the server's logical name. Autoscaled servers are routed through registerAutoscaled instead so the Spawner owns replica provisioning.
func (*ServerRegistrar) RegisterOne ¶
func (r *ServerRegistrar) RegisterOne(ctx context.Context, server config.MCPServer, replicas []ReplicaRuntime, stackPath string) error
RegisterOne registers a single MCP server (with one or more replicas) with the gateway. Used by the reload handler to register newly added servers. replicas carries the runtime handles the caller has already provisioned — one entry per replica, in replica-id order. For External / LocalProcess / SSH / OpenAPI servers that were not container-provisioned, pass a slice of zero-valued ReplicaRuntime entries of the desired length (or a single-entry slice for the single-replica case).
func (*ServerRegistrar) SetBasePort ¶
func (r *ServerRegistrar) SetBasePort(p int)
SetBasePort sets the starting host port for dynamic container spawns by the autoscaler. Picks up where the initial bring-up left off.
func (*ServerRegistrar) SetLogger ¶
func (r *ServerRegistrar) SetLogger(logger *slog.Logger)
SetLogger sets the logger.
func (*ServerRegistrar) SetRuntime ¶
func (r *ServerRegistrar) SetRuntime(rt runtime.WorkloadRuntime)
SetRuntime wires the workload runtime used to clean up orphan containers when an HTTP/SSE MCP server fails its readiness check. Without a runtime, readiness failures still surface as errors but the container is left running.
type StackController ¶
type StackController struct {
// contains filtered or unexported fields
}
StackController orchestrates the full deploy lifecycle.
func (*StackController) Deploy ¶
func (sc *StackController) Deploy(ctx context.Context) error
Deploy orchestrates the full stack lifecycle.
func (*StackController) Serve ¶
func (sc *StackController) Serve(ctx context.Context) error
Serve starts the API server and web UI in stackless mode. No stack file is required; no container runtime is started. Vault and wizard endpoints are fully functional; stack-dependent endpoints return 503 until a stack is deployed.
func (*StackController) SetVersion ¶
func (sc *StackController) SetVersion(v string)
SetVersion sets the version string for the gateway.
func (*StackController) SetWebFS ¶
func (sc *StackController) SetWebFS(fn WebFSFunc)
SetWebFS sets the function for getting embedded web files.