Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions cmd/ateom-gvisor/runsc.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,23 @@ import (
"github.com/agent-substrate/substrate/internal/ateompath"
)

// runsc drives one actor's gVisor sandbox with the exact runtime binary chosen
// by atelet. SandboxConfig pins each architecture's runsc asset by URL and
// SHA-256. Atelet verifies the digest, caches the binary at a content-addressed
// path, and passes that path to ateom. Checkpoint manifests retain the same
// asset pin so a restore uses the runsc version that created the checkpoint.
//
// All containers for an actor share a runsc root directory. The "pause"
// container is the sandbox root; application containers join that sandbox.
type runsc struct {
path string
actorUID string
}

// cmdCreate creates a stopped container from the OCI bundle prepared by
// atelet. Creating "pause" creates the sandbox; subsequent application
// containers join it. additionalArgs carries restore-specific create flags,
// such as the data-only filesystem image.
func (r *runsc) cmdCreate(ctx context.Context, out io.Writer, containerName string, additionalArgs []string) error {
reapLock.RLock()
defer reapLock.RUnlock()
Expand Down Expand Up @@ -70,6 +82,8 @@ func (r *runsc) cmdCreate(ctx context.Context, out io.Writer, containerName stri
return nil
}

// cmdStart starts a container previously created by cmdCreate. Connected
// sockets are allowed to survive a later full checkpoint.
func (r *runsc) cmdStart(ctx context.Context, out io.Writer, containerName string) error {
reapLock.RLock()
defer reapLock.RUnlock()
Expand Down Expand Up @@ -102,6 +116,9 @@ func (r *runsc) cmdStart(ctx context.Context, out io.Writer, containerName strin
return nil
}

// cmdCheckpoint writes a full process, sentry, and filesystem checkpoint for
// the shared sandbox. Callers invoke it only for the root "pause" container;
// that captures every container in the sandbox.
func (r *runsc) cmdCheckpoint(ctx context.Context, containerName, checkpointPath string) error {
reapLock.RLock()
defer reapLock.RUnlock()
Expand Down Expand Up @@ -132,6 +149,9 @@ func (r *runsc) cmdCheckpoint(ctx context.Context, containerName, checkpointPath
return nil
}

// cmdFsCheckpoint writes a data-only checkpoint of the listed durable
// directories. It deliberately excludes process state and other rootfs
// changes, so restore will cold-start the containers around the saved data.
func (r *runsc) cmdFsCheckpoint(ctx context.Context, containerName, checkpointPath string, durableDirMounts []string) error {
reapLock.RLock()
defer reapLock.RUnlock()
Expand Down Expand Up @@ -171,8 +191,10 @@ func (r *runsc) cmdFsCheckpoint(ctx context.Context, containerName, checkpointPa
return nil
}

// We take a checkpoint only of the root container of the sandbox, but we need
// to call restore on each container, using the same checkpoint.
// cmdRestore restores one container from a full sandbox checkpoint and leaves
// it running in the background. Although cmdCheckpoint runs only against the
// root container, restore must be called for the root and every application
// container using the same checkpoint.
func (r *runsc) cmdRestore(ctx context.Context, out io.Writer, containerName, checkpointPath string) error {
reapLock.RLock()
defer reapLock.RUnlock()
Expand Down Expand Up @@ -207,6 +229,8 @@ func (r *runsc) cmdRestore(ctx context.Context, out io.Writer, containerName, ch
return nil
}

// cmdDelete forcibly removes a container after its sandbox has been
// checkpointed. Ateom deletes application containers before the root.
func (r *runsc) cmdDelete(ctx context.Context, containerName string) error {
reapLock.RLock()
defer reapLock.RUnlock()
Expand Down Expand Up @@ -236,6 +260,9 @@ func (r *runsc) cmdDelete(ctx context.Context, containerName string) error {
return nil
}

// cmdState asks runsc to reconcile and report container state before deletion.
// This mirrors containerd's cleanup sequence and avoids intermittent failures
// from deleting immediately after checkpoint.
func (r *runsc) cmdState(ctx context.Context, containerName string) error {
reapLock.RLock()
defer reapLock.RUnlock()
Expand Down
41 changes: 24 additions & 17 deletions internal/proto/ateompb/ateom.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,39 @@ package ateom;

option go_package = "github.com/agent-substrate/substrate/internal/proto/ateompb";

// Ateom is the interface to control a single gVisor (or, in the future microVM)
// guest inside a worker pod.
// Ateom is the interface used by atelet to control one sandbox guest inside a
// worker pod. Ateom serves gRPC on a per-worker Unix domain socket in the
// filesystem shared with atelet. Before calling an RPC, atelet prepares the OCI
// bundles, checkpoint files, and content-addressed runtime assets on that
// shared filesystem; the request carries their local paths. Calls for a given
// ateom must not overlap.
//
// Each ateom server has two main states, "available" and "executing".
//
// When the ateom is "available", the substrate control plane is free to either
// boot a new workload (using RunWorkload), or restore an existing workload from
// a checkpoint (using RestoreWorkload). These calls move the ateom into
// "executing" state.
// When the ateom is "available", atelet can either boot a new workload (using
// RunWorkload), or restore an existing workload from a checkpoint (using
// RestoreWorkload). These calls move the ateom into "executing" state.
//
// When the ateom is "executing", the substrate control plane can checkpoint the
// running workload (with CheckpointWorkload). This moves the ateom back to
// "free" state.
// When the ateom is "executing", atelet can checkpoint the running workload
// (with CheckpointWorkload). This moves the ateom back to "available" state.
service Ateom {
// RunWorkload tells ateom to begin running a new workload (one or more
// containers, potentially with shared filesystems).
// RunWorkload requires an available ateom. It creates and starts the sandbox
// root plus all application containers from atelet-prepared OCI bundles,
// waits for configured readiness checks, and leaves the ateom executing.
rpc RunWorkload(RunWorkloadRequest) returns (RunWorkloadResponse) {}

// CheckpointWorkload tells ateom to save the current state of the running
// workload to object storage, and then completely reset itself to a blank
// state (back to "available" state.)
// CheckpointWorkload requires an executing ateom. It writes either the full
// sandbox state or the requested durable-directory data to the shared
// checkpoint directory, tears down the sandbox, and returns the relative
// names of the files for atelet to persist. On success the ateom is
// available again.
rpc CheckpointWorkload(CheckpointWorkloadRequest) returns (CheckpointWorkloadResponse) {}

// RestoreWorkload restores a workload from checkpoint that was previously
// written by CheckpointWorkload. Ateom will handle downloading the correct
// gVisor / runsc version to match the checkpoint.
// RestoreWorkload requires an available ateom. It restores from checkpoint
// files and matching runtime assets that atelet placed on the shared
// filesystem, waits for configured readiness checks, and leaves the ateom
// executing. A full snapshot restores process state; a data-only snapshot
// starts new processes with the durable-directory data restored.
rpc RestoreWorkload(RestoreWorkloadRequest) returns (RestoreWorkloadResponse) {}
}

Expand Down
82 changes: 48 additions & 34 deletions internal/proto/ateompb/ateom_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 31 additions & 7 deletions pkg/proto/ateapipb/ateapi.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading