controller

package
v0.1.0-beta.8 Latest Latest
Warning

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

Go to latest
Published: May 5, 2026 License: Apache-2.0 Imports: 36 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildWorkloadSummaries

func BuildWorkloadSummaries(stack *config.Stack, result *runtime.UpResult) []output.WorkloadSummary

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

func (c *ContainerSpawner) Reap(ctx context.Context, r *mcp.Replica) error

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

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) Reap

func (s *ProcessSpawner) Reap(_ context.Context, r *mcp.Replica) error

Reap closes the replica's client so its process exits.

func (*ProcessSpawner) Spawn

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

type ReplicaRuntime struct {
	HostPort    int
	ContainerID string
}

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) Reap

func (s *SSHSpawner) Reap(_ context.Context, r *mcp.Replica) error

Reap closes the SSH stream so the remote process exits.

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 New

func New(cfg Config) *StackController

New creates a StackController.

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.

type WebFSFunc

type WebFSFunc func() (fs.FS, error)

WebFSFunc is a function that returns embedded web UI files. This decouples the controller from the build-tag-conditional embed logic.

Jump to

Keyboard shortcuts

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