Documentation
¶
Overview ¶
Package containers is the VM side of the dynamic container set. loom-server (or weft-agent reconciler) publishes a pod.ContainerSet on the per-VM subject `weft.containers.<vmID>` — the subscriber here diffs it against the running set + drives crun to converge : pull missing images via ncl, start newly-listed containers, stop containers that vanished.
State is pushed whole and applied idempotently (replace-by-name) — same model pkg/mounts uses for shares. A missed message self-heals on the next publish.
Same Subscriber+ApplyFunc shape as pkg/mounts so any test that drove mounts can be cargo-culted to drive containers ; the real crun apply lives in Runtime below behind an interface so the reconciler is testable without root + cgroups.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HandleMessage ¶
func HandleMessage(ctx context.Context, data []byte, r *Reconciler) error
HandleMessage decodes a published ContainerSet + applies it. Pure aside from the injected runtime — same testable shape as pkg/mounts.
Types ¶
type CrunRuntime ¶
type CrunRuntime struct {
BundleRoot string // e.g. "/run/weft/containers"
NCLImagePath string // e.g. "/run/weft/images"
Logger *log.Logger
}
CrunRuntime drives crun. BundleRoot is where per-container bundle dirs land (<bundleRoot>/<name>/{config.json,rootfs}) ; NCLImagePath is the ncl cache the pre-baked rootfs lives under when an image has already been pre-staged at build time (the dev path).
func (*CrunRuntime) Pull ¶
func (r *CrunRuntime) Pull(ctx context.Context, image string) error
Pull defers to ncl to fetch+unpack the image. When ncl isn't on PATH (dev rootfs without ncl pre-baked) the pull is treated as a no-op : Start will look for a pre-staged rootfs at NCLImagePath keyed by image ref and use that. Lets the dev path work today without the full V0.6 ncl pipeline in place.
func (*CrunRuntime) Running ¶
func (r *CrunRuntime) Running(ctx context.Context) ([]string, error)
Running asks crun for the list of containers it knows about.
func (*CrunRuntime) Start ¶
func (r *CrunRuntime) Start(ctx context.Context, c pod.WorkloadContainer) error
Start materialises an OCI bundle from the pulled image + asks crun to run it. The bundle lives at <BundleRoot>/<name>/ with config.json rendered from c (Command/Args/Env/Mounts/WorkingDir/Net) and rootfs symlinked / bind-mounted from the ncl image cache.
Restart="always" is honoured by the agent's reconciler layer (it re-invokes Start on exit) ; this method is fire-and-forget once crun start returns.
type Reconciler ¶
type Reconciler struct {
// contains filtered or unexported fields
}
Reconciler holds the last-applied set so diffs are quick + the crun runtime stays the source of truth for "is it actually running". Concurrent Apply-safe (one in flight at a time ; later publishes during an apply wait).
func NewReconciler ¶
func NewReconciler(runtime Runtime, logger *log.Logger) *Reconciler
NewReconciler builds a Reconciler over runtime. logger may be nil.
func (*Reconciler) Apply ¶
func (r *Reconciler) Apply(ctx context.Context, s pod.ContainerSet) error
Apply converges the runtime to s. Steps :
- validate the payload (rejection on schema bugs)
- compute add / update / drop relative to the previously-applied set (replace-by-name)
- for each add : pull + start for each update: stop + start (the simplest semantics that covers env / image / command changes) for each drop : stop
Returns the first error from a step ; subsequent ones still run so a single broken container doesn't gate the rest of the set.
type Runtime ¶
type Runtime interface {
// Pull ensures the image's bundle exists locally. Idempotent on
// the digest — pulling the same ref twice is a no-op.
Pull(ctx context.Context, image string) error
// Start launches the container. Name is the stable handle the
// diff uses to find this container later.
Start(ctx context.Context, c pod.WorkloadContainer) error
// Stop terminates + cleans up the container's bundle.
Stop(ctx context.Context, name string) error
// Running returns the names of every container the runtime
// currently considers alive ; used to seed the diff after a
// JetStream replay.
Running(ctx context.Context) ([]string, error)
}
Runtime is the crun-driving side the reconciler talks to. Stubbed in tests + in dev mode (where containers are simulated as plain processes), real impl lives next to the agent's crun bundle path.
type Subscriber ¶
type Subscriber struct {
// contains filtered or unexported fields
}
Subscriber listens for this VM's container-set updates + drives the reconciler. Direct mirror of mounts.Subscriber down to the JetStream last-per-subject replay trick — that's what makes boot-time replay of the previously-desired set work, even if loom-server published it before the VM was alive.
func NewSubscriber ¶
NewSubscriber wires a Subscriber for vmID against runtime + logger.
func (*Subscriber) Start ¶
func (s *Subscriber) Start(ctx context.Context) (*nats.Subscription, error)
Start subscribes to the VM's container-set subject. The returned subscription is live until unsubscribed or the connection drops. JetStream replay : a stream named `weft-containers` with subjects `weft.containers.*` + LastPerSubject retention is the boot-time guarantee that the agent picks up the most recent desired set even if it was published before the VM existed.