sorcery-go/pkg/runtime/runtime.go

234 lines
8.5 KiB
Go
Executable File

// Package runtime provides a unified abstraction over container runtimes.
//
// Sorcery-Go can deploy Essences into different execution environments
// (called "Sanctums"). This package defines the Runtime interface that
// decouples the Warding, Coven, and Reanimation logic from any specific
// container technology.
//
// Supported runtimes:
// - LXC: Traditional system containers (lxc-tools CLI)
// - Podman: OCI containers (podman CLI / REST API)
// - Firecracker: Lightweight microVMs (Firecracker VMM API via Unix socket)
// - BareMetal: Direct filesystem deployment (no container)
//
// The active runtime is selected at startup via SORCERY_GO_RUNTIME env var
// or the Runtime field in the Config struct. Each runtime adapter implements
// the Runtime interface, providing a consistent API for container lifecycle
// management, process execution, freeze/thaw, and status queries.
package runtime
import (
"context"
"fmt"
"time"
)
// Type identifies the container runtime backend.
type Type string
const (
// RuntimeLXC uses the LXC system container tools (lxc-create, lxc-start, etc.)
RuntimeLXC Type = "lxc"
// RuntimePodman uses the Podman OCI container engine.
RuntimePodman Type = "podman"
// RuntimeFirecracker uses Firecracker microVMs.
RuntimeFirecracker Type = "firecracker"
// RuntimeBareMetal deploys directly to the host filesystem (no container).
RuntimeBareMetal Type = "baremetal"
)
// SanctumStatus describes the current state of a container/sanctum.
type SanctumStatus string
const (
StatusRunning SanctumStatus = "running"
StatusStopped SanctumStatus = "stopped"
StatusFrozen SanctumStatus = "frozen"
StatusCreating SanctumStatus = "creating"
StatusDestroyed SanctumStatus = "destroyed"
StatusUnknown SanctumStatus = "unknown"
)
// SanctumInfo provides runtime-specific information about a sanctum.
type SanctumInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Runtime Type `json:"runtime"`
Status SanctumStatus `json:"status"`
Arch string `json:"arch"`
IP string `json:"ip,omitempty"`
PID uint32 `json:"pid,omitempty"`
Cgroup string `json:"cgroup,omitempty"`
Created time.Time `json:"created"`
RootFS string `json:"rootfs,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// CreateOpts holds the parameters for creating a new sanctum.
type CreateOpts struct {
// Name is the human-readable container name.
Name string
// Image is the base image or template (LXC template, OCI image, rootfs path).
Image string
// Arch is the target architecture (x86_64, aarch64).
Arch string
// RootFS is the path to the root filesystem (for baremetal or custom rootfs).
RootFS string
// NetworkConfig specifies the network attachment.
NetworkConfig *NetworkConfig
// BindMounts are host paths to bind-mount into the container.
BindMounts []BindMount
// EnvVars are environment variables to set inside the container.
EnvVars map[string]string
// Caps is the set of Linux capabilities to retain (empty = drop all).
Caps []string
// MemoryMB is the memory limit in megabytes (0 = unlimited).
MemoryMB uint64
// VCPUs is the number of virtual CPUs (for Firecracker).
VCPUs uint32
// KernelPath is the host kernel image path (for Firecracker).
KernelPath string
// RootDrivePath is the root block device path (for Firecracker).
RootDrivePath string
// ExtraConfig passes runtime-specific configuration as key-value pairs.
ExtraConfig map[string]string
}
// NetworkConfig specifies network attachment for a sanctum.
type NetworkConfig struct {
// Type is the network mode (bridge, none, host).
Type string // "bridge", "none", "host"
// Bridge is the host bridge interface name (e.g., "br0").
Bridge string
// MACAddress is the desired MAC address (empty = auto-generate).
MACAddress string
// IP is the desired IP address (empty = DHCP/auto).
IP string
}
// BindMount represents a host-to-container bind mount.
type BindMount struct {
// HostPath is the path on the host.
HostPath string
// ContainerPath is the path inside the container.
ContainerPath string
// ReadOnly makes the mount read-only.
ReadOnly bool
}
// ExecResult captures the output of a command executed inside a sanctum.
type ExecResult struct {
ExitCode int
Stdout []byte
Stderr []byte
}
// Runtime is the interface that all container runtime backends must implement.
//
// It provides a unified API for the full lifecycle of a Sanctum:
// create, start, stop, freeze/thaw, exec, and destroy. The Warding uses
// this interface to manage containers without knowing the underlying runtime.
type Runtime interface {
// Type returns the runtime type identifier (lxc, podman, firecracker, baremetal).
Type() Type
// Name returns a human-readable name for this runtime instance.
Name() string
// Probe checks whether this runtime is available on the host.
// Returns nil if the runtime tools/API are accessible.
Probe() error
// Create provisions a new sanctum with the given options.
// Returns the sanctum ID on success.
Create(ctx context.Context, opts CreateOpts) (string, error)
// Start boots or starts a stopped sanctum.
Start(ctx context.Context, sanctumID string) error
// Stop gracefully stops a running sanctum.
Stop(ctx context.Context, sanctumID string) error
// Freeze suspends all processes in a running sanctum (cgroup freezer).
Freeze(ctx context.Context, sanctumID string) error
// Thaw resumes a frozen sanctum.
Thaw(ctx context.Context, sanctumID string) error
// Destroy removes a sanctum and all its resources.
Destroy(ctx context.Context, sanctumID string) error
// Exec runs a command inside a running sanctum and returns the output.
Exec(ctx context.Context, sanctumID string, command []string, stdin []byte) (*ExecResult, error)
// Status returns the current state and info about a sanctum.
Status(ctx context.Context, sanctumID string) (*SanctumInfo, error)
// List returns all sanctums managed by this runtime.
List(ctx context.Context) ([]*SanctumInfo, error)
// CgroupPath returns the cgroup v2 path for a sanctum.
// This is used by the eBPF enforcer to attach cgroup filters.
// Returns "" if the runtime does not use cgroups (e.g., Firecracker).
CgroupPath(sanctumID string) string
}
// Factory creates a Runtime instance for the given type.
// Returns an error if the runtime type is unknown or unavailable.
func Factory(rtType Type) (Runtime, error) {
switch rtType {
case RuntimeLXC:
r := NewLXCRuntime()
if err := r.Probe(); err != nil {
return nil, err
}
return r, nil
case RuntimePodman:
r := NewPodmanRuntime()
if err := r.Probe(); err != nil {
return nil, err
}
return r, nil
case RuntimeFirecracker:
r := NewFirecrackerRuntime()
if err := r.Probe(); err != nil {
return nil, err
}
return r, nil
case RuntimeBareMetal:
return NewBareMetalRuntime(), nil
default:
return nil, fmt.Errorf("runtime: unknown runtime type %q (supported: lxc, podman, firecracker, baremetal)", rtType)
}
}
// AutoDetect tries to find an available runtime on the host.
// It probes LXC first, then Podman, then Firecracker, and falls back
// to baremetal if none are found.
func AutoDetect() (Runtime, error) {
candidates := []Type{RuntimeLXC, RuntimePodman, RuntimeFirecracker, RuntimeBareMetal}
for _, t := range candidates {
r, err := Factory(t)
if err == nil {
return r, nil
}
}
return NewBareMetalRuntime(), nil
}