container

package
v0.0.0-...-89e6720 Latest Latest
Warning

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

Go to latest
Published: Jun 15, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package container provides the Backend seam — the interface the engine's Dispatcher uses to run commands inside long-lived containers (a single digest-pinned image or a compose project, per awf-workflow(5), CONTAINERS).

Phase 2 ships only the interface and an in-memory fake (fake.go); Phase 4 adds the Docker impl behind the same interface. Cross-impl conformance is the backendtest sub-package's job (RunBasicContract).

Per runtime-design §3, container depends on no other AWF package. CaptureFiles therefore returns content bytes (CapturedFile{Path, Content}), NOT CAS refs (which would require Backend to Put to Blobs, violating the CLAUDE.md invariant "the interpreter is the only writer to state"). The slice 2.4 dispatcher passes the bytes up; the interpreter Puts at the commit boundary.

Index

Constants

View Source
const AWFOutputDir = "/tmp/awf"

AWFOutputDir is the base directory the engine dispatcher derives AWF_OUTPUT tempfile paths under — AWFOutputDir/<sanitized-step>.json (see engine/awf_output.go) — and that the native Backend pre-creates at New() so the author's `> $AWF_OUTPUT` redirect can write the file. Docker containers get a fresh /tmp and don't need it. One source so the engine's derivation and the native bootstrap cannot drift.

View Source
const TeardownGrace = 30 * time.Second

TeardownGrace is how long Backend.Destroy / Backend.Down gets after the run's ctx has been cancelled (Ctrl-C / SIGTERM). The signal cancels in-flight work via ctx, but the deferred teardown needs a non-cancelled ctx so containers actually come down. 30s is generous for the instant Fake and for Docker (`docker stop --time=10s` + cleanup typically <30s). One source so every teardown site shares one deadline.

Variables

View Source
var ErrSnapshotTooLarge = errors.New("snapshot diff exceeds the backend size limit")

ErrSnapshotTooLarge is the sentinel for a deterministic snapshot-capacity failure (the workspace diff exceeds the backend's size cap). A backend's concrete too-large error wraps this so callers can errors.Is without importing the concrete backend package. Phase-4 decision 11: this is a permanent_failure (retrying re-runs the step to fail identically).

View Source
var ErrUnsupported = errors.New("container: operation not supported by this backend")

ErrUnsupported is the sentinel returned by Backend methods this backend doesn't implement. Phase 2 fake returns it from Snapshot / Restore. Callers route with errors.Is(err, container.ErrUnsupported).

Functions

This section is empty.

Types

type Backend

type Backend interface {
	// Capabilities reports what this backend supports. Must be callable on a
	// zero-state backend (no Create needed); the engine queries it at startup
	// to decide whether snapshot:workspace containers can be served.
	Capabilities() Caps

	// Create brings a container to readiness per the spec. The returned Handle
	// is opaque to the caller and must be passed back to Exec / CaptureFiles /
	// Snapshot / Destroy. Phase 2 fake: allocates an in-mem fs map keyed by a
	// monotonic ID. Phase 4 Docker: image-pinned create or compose `up --wait`.
	Create(ctx context.Context, spec ContainerSpec) (Handle, error)

	// Exec runs a command synchronously inside the handle. Returns two
	// channels and an error.
	//
	// chunks: STREAMING channel of IOChunks. Emits each stdout/stderr chunk
	//   as it arrives from the underlying pipe — callers ranging over it
	//   observe the process's output in real time. Closed by the Backend
	//   implementation when both stdout and stderr pipes drain (process
	//   either exited or ctx-cancelled). MUST be drained by the caller; an
	//   unread chunk back-pressures the reader goroutines (chan buffer is
	//   unspecified but bounded).
	//
	// result: SINGLE-VALUE channel of ExecResult. Receives exactly one
	//   value AFTER the chunks channel closes AND the process has been
	//   waited-on (ExitCode + accumulated Stdout + Err available). Then
	//   closes. Receiving from it before chunks closes is permitted (blocks
	//   until the close) but unusual — the natural order is
	//   `for range chunks { ... }; r := <-result`. ExecResult.Err surfaces
	//   transport-class errors (stdcopy mid-stream failure on docker;
	//   ctx.Canceled on native pipe close mid-read) WITHOUT swallowing them
	//   silently.
	//
	// On non-nil error return, BOTH channels are nil; callers must check
	// err before ranging or receiving. The "launch / transport" error
	// class — couldn't start the command at all — surfaces here. If the
	// command ran and merely exited nonzero, err is nil and result's
	// ExecResult.ExitCode carries the code.
	//
	// ctx cancellation: aborts the command; chunks channel closes; result
	// emits an ExecResult with Err set to ctx.Err() (and likely
	// process-killed ExitCode). The function's error return remains nil —
	// the cancellation surfaces via the result value's Err field, NOT via
	// err. Callers that used `errors.Is(err, context.Canceled)` must
	// update to check `result.Err` instead.
	//
	// Slice 5.3: STREAMING refactor of the original Phase 2
	// "(ExecResult, <-chan IOChunk, error)" contract. The Claude Code
	// adapter (agent/claude.Launch) needs progressive event arrival;
	// refactoring Backend.Exec across all three backends is the
	// load-bearing prerequisite.
	Exec(ctx context.Context, h Handle, cmd Cmd) (<-chan IOChunk, <-chan ExecResult, error)

	// CaptureFiles reads the named in-container paths and returns their content,
	// one CapturedFile per path in the input order. Missing-path is a hard error
	// (no partial returns) — output_files are declared by the author and absence
	// at commit time is a step bug, not graceful degradation. The slice 2.4
	// dispatcher passes the bytes up; the interpreter Puts each into Blobs at
	// the commit boundary (this method never touches state.Blobs; see pkg doc).
	CaptureFiles(ctx context.Context, h Handle, paths []string) ([]CapturedFile, error)

	// CopyTo writes each InputFile's bytes to its in-container path BEFORE the
	// step's Exec/Launch — the symmetric inverse of CaptureFiles. Overwrites an
	// existing path. len(files)==0 is a no-op returning nil. Unknown handle is a
	// hard error. Never touches state.Blobs.
	CopyTo(ctx context.Context, h Handle, files []InputFile) error

	// Snapshot captures the handle's filesystem as a CoW diff. Only meaningful
	// for snapshot:workspace containers (spec §3). Phase 2 fake: returns
	// ("", ErrUnsupported). Phase 4 Docker (slice 4.4): streaming gzip-tar
	// diff via ContainerDiff + per-path CopyFromContainer through
	// state.Blobs.Put. Returns a 3-segment SnapshotRef:
	//     <state-blobs-ref>@<image-ref>@<base64-json-of-cmd-entrypoint>
	// so Restore can ContainerCreate against the source image AND faithfully
	// re-apply the effective Cmd/Entrypoint (a Restore→Snapshot loop
	// preserves runtime config across the round-trip).
	//
	// Peak memory: bounded by snapshotMaxBlobBytes at state.Blobs.Put time
	// (the compressed blob lives in memory for the Put call; the streaming
	// gzip+tar intermediate buffers stay at ~64 KiB throughout the build).
	//
	// If the gzip-compressed diff exceeds the configured cap (default 256
	// MiB), returns *ErrSnapshotTooLarge — the engine routes this as
	// permanent_failure.
	Snapshot(ctx context.Context, h Handle) (SnapshotRef, error)

	// Restore rebuilds a handle from a prior Snapshot. The name argument is the
	// IR-declared container name (spec §3); the returned Handle.Name is set to
	// it so the dispatcher's d.Handles[name] keys consistently across
	// pre-snapshot Create and post-resume Restore.
	//
	// Phase 2 fake: returns ErrUnsupported.
	//
	// Phase 4 Docker (slice 4.4): parses the 3-segment SnapshotRef, Blobs.Get
	// the diff-tar, ContainerCreate against the embedded image + Cmd +
	// Entrypoint, stream-CopyToContainer the data entries via io.Pipe (peak
	// memory ~64 KiB + the diff blob bytes already in RAM from Get),
	// Exec "rm -rf -- '<path>'" for each .awf-deletes entry. waitReady runs
	// on the restored container so the spec §3 readiness contract holds.
	// The embedded image is NOT auto-pulled; callers responsible for prior
	// ImagePull (same as Backend.Create's image-mode path).
	Restore(ctx context.Context, ref SnapshotRef, name string) (Handle, error)

	// Destroy releases the handle's resources. The caller must call Destroy
	// exactly once per Create; a second call returns an error (the handle is
	// gone). Matches os.File.Close and Docker's ContainerRemove behavior;
	// io.Closer itself leaves post-first-call behavior undefined, but the
	// engine relies on the stricter "error on double" guarantee to surface
	// dispatcher bugs.
	Destroy(ctx context.Context, h Handle) error
}

Backend executes commands inside long-lived containers. One concrete impl per phase: in-memory Fake (Phase 2, fake.go); Docker (Phase 4, docker.go).

Lifecycle: Create on first reference for a declared container name, Exec / CaptureFiles many times across the run, Snapshot / Restore only for snapshot:workspace containers (Phase 4+; Phase 2 fake errors with ErrUnsupported), Destroy at run end or cancellation. The engine treats Handles as opaque; only the Backend that produced one may consume it.

Concurrency: single-writer per instance in Phase 2 (matches state.InMemoryLog/InMemoryBlobs precedent; the interpreter is single- threaded for commits — runtime-design §5). Phase 3's `parallel` will need per-method synchronization on the fake — added when that slice lands, not pre-emptively here (CLAUDE.md rule 2: simplest solution first).

type Caps

type Caps struct {
	Snapshot SnapshotMode

	// RuntimeImage reports whether the backend can honor a map's runtime-
	// resolved per-element image: (P6a) — i.e. actually boot the rendered
	// image and report its content digest. Zero-value false FAILS CLOSED:
	// a backend that ignores image: (native) advertises false, and the CLI
	// guard rejects a runtime-image workflow on it rather than silently
	// running bodies on the host.
	RuntimeImage bool

	// RuntimeCompose reports whether the backend can promote compose bytes
	// generated during a workflow run into a managed project with normal
	// lifecycle ownership. Zero-value false FAILS CLOSED: native cannot honor
	// compose projects and must be rejected before execution.
	RuntimeCompose bool
}

Caps reports a backend's capabilities. Backends MUST explicitly construct (e.g. Caps{Snapshot: SnapshotNone}) — zero-value Caps has Snapshot == "" so a missing assignment is observable in tests rather than silently advertising "no snapshot."

type CapturedFile

type CapturedFile struct {
	Path    string
	Content []byte
}

CapturedFile is one path + its contents, as returned by CaptureFiles. The slice 2.4 dispatcher passes them up; the interpreter Puts each Content into Blobs at the commit boundary and records Path → ref in engine.NodeCompletedData.Files.

type Cmd

type Cmd struct {
	Run string
	Env map[string]string
}

Cmd describes a command to Exec. Run is the shell command (from CodeStep.Run after template substitution). Env carries the dispatcher-injected env vars (slice 2.4: AWF_IDEMPOTENCY_KEY; slice 4.2: AWF_OUTPUT when output_schema is declared); the Phase 2 fake accepts Env but does not use it (its scripted result table is keyed on Run alone — slice 2.4 verifies dispatcher env injection by inspecting what it passes to Backend.Exec, not by what the fake does with the env it receives).

Shell-quoting note: Run is interpreted as a single string passed to `sh -c` (POSIX baseline) — slice 4.2 implementation in container/docker/exec.go. Authors needing bash-specific features (`<(...)`, `[[ ]]`, double-bracket regex) ship bash in their image and write `bash -c '...'` as the inner script. An author who templates an untrusted agent-controlled value into Run MUST quote the substitution: `./scan.sh "{{ step.x.url }}"` — an unquoted `$(...)` / backtick / `;` in the typed value becomes an unintended command. Phase 4 may add a parallel `Argv []string` field for shell-free exec if a workload requires it; Phase 2 takes the simpler path and documents the contract.

type ContainerResources

type ContainerResources struct {
	CPU string
	Mem string
}

ContainerResources mirrors the IR Container.Resources fields (spec §3) for transport to the Backend. Image-mode only — for compose-mode, per-service resources are declared in the compose file.

type ContainerSpec

type ContainerSpec struct {
	Name string

	// Image-mode fields.
	Image     string
	Resources *ContainerResources
	// Cmd is an optional override for the image's CMD instruction. When
	// nil or empty, the image's default Cmd applies. Slice 4.4 adds this
	// field so test fixtures can inject a long-running entrypoint into
	// short-CMD images (e.g., alpine's /bin/sh → sleep infinity) without
	// bypassing Backend.Create. Today's engine.ContainerSpecFor never
	// populates Cmd; a future IR slice adding `cmd: [...]` to Container
	// declarations would.
	Cmd []string

	// PullIfAbsent requests that Create ensure Image is present in the local
	// cache before starting it — image-mode only (P6a). The engine sets it
	// solely for a map's runtime-resolved per-element image: (engine/map.go),
	// where the image is learned at dispatch and is generally not pre-pulled.
	// Statically-declared containers leave it false: they are pre-provisioned
	// from a validator-pinned (digest) image, so the run-start pre-pull suffices.
	//
	// Because a runtime-resolved image cannot be validator-pinned, a backend
	// honoring this flag MUST require Image to be digest-pinned (name@sha256:…)
	// so the booted bytes are content-addressed and resume-reproducible, and
	// MUST report the booted digest on Handle.ResolvedImageDigest. The Docker
	// backend implements this; backends that ignore image: (native) advertise
	// Caps.RuntimeImage=false and are rejected by the CLI guard before dispatch.
	PullIfAbsent bool

	// Compose-mode fields (slice 4.3).
	Compose     []byte
	ComposePath string
	Service     string
}

ContainerSpec describes a container the engine wants Created. The Backend dispatches on which mode-specific fields are populated:

  • Image-mode (slice 4.1): Image required, Resources optional, Cmd optional, Compose nil.
  • Compose-mode (slice 4.3): Compose+ComposePath+Service required, Image empty, Resources/Cmd nil (per-service config lives in the compose file).

The Phase 2 fake ignores every field except Name.

type ExecResult

type ExecResult struct {
	ExitCode  int
	AWFOutput []byte
	Stdout    []byte
	// Err (slice 5.3) surfaces transport-class errors that occurred AFTER
	// Backend.Exec successfully launched the process — stdcopy mid-stream
	// failure (docker), pipe-read failure (native), ctx-cancel during
	// in-flight read. nil on the happy path. Pre-slice-5.3 these came back
	// via Backend.Exec's err return; the streaming refactor moved them
	// here so the result channel can carry them without the function
	// returning early before chunks drain.
	Err error
}

ExecResult is the streamed result of an Exec call (slice 5.3: delivered via Backend.Exec's result channel). Slice 2.4's outcome classifier reads ExitCode (per §6 — nonzero outside non_retryable_exit_codes is retryable_failure; in the list is permanent_failure); the dispatcher parses AWFOutput against the step's output_schema for typed outputs; Stdout is exposed via {{ step.<id>.stdout }} substitution.

Stderr is intentionally NOT a field — awf-workflow(5) (Code step) lists only exit_code and stdout as implicit outputs; the templating section never references stderr. The live tap (slice 2.5) and node.failed events (slice 2.4) get stderr via IOChunk{Stream: "stderr"} on the chunk channel.

type Fake

type Fake struct {

	// Calls is the defensive-copied history of every Cmd this fake's Exec
	// received. Slice 2.4 (and later) tests inspect this to verify the
	// dispatcher's env-injection contract (AWF_IDEMPOTENCY_KEY per AWF §10).
	// The fake's doc-comment anticipated this need at slice-2.2 time but
	// shipped no mechanism; this is the recording slot.
	Calls []Cmd

	// CreateSpecs records every ContainerSpec passed to Create, in order
	// (test assertion aid — mirrors Calls for Exec). The P6a wiring test reads
	// PullIfAbsent / Image off it to verify the engine flags a map's
	// runtime-resolved image spec for the backend to pull. Shallow copies: the
	// recorded scalars (Name/Image/PullIfAbsent) are what tests assert on.
	CreateSpecs []ContainerSpec

	// ExecHandles records the Handle value passed to each Exec call. Tests use
	// this to assert compose service overrides (`container: lab:api`) reached
	// the backend as a handle-level Service override.
	ExecHandles []Handle

	// DestroyCalls records every Handle passed to Destroy, in order.
	DestroyCalls []Handle

	// RestoreCalls records every Restore invocation, in order (test assertion
	// aid — mirrors Calls for Exec).
	RestoreCalls []RestoreCall
	// contains filtered or unexported fields
}

Fake is the in-memory Backend used by Phase 2 engine tests and the conformance suite (slice 2.6). Deterministic: monotonic-counter handle IDs, no time.Now, no OS-level process spawning, no goroutines.

Thread-safe (Phase 3 slice 3.2): per-method mutex protects handles / execTable / streamTable / Calls / counters so parallel branch goroutines dispatching to distinct containers (all backed by the same *Fake via the harness's Handles map) can race-cleanly.

Phase 4 Docker impl will live alongside this in container/docker.go; backendtest.RunBasicContract runs against both unchanged.

func NewFake

func NewFake() *Fake

NewFake mints an empty Fake. Returns *Fake (not Backend) so test callers can reach helpers like ProgramExec / WriteFile / FailExecAfterN that the Backend interface deliberately doesn't expose.

func (*Fake) BlockExec

func (f *Fake) BlockExec() chan struct{}

BlockExec arms the block gate: the NEXT Exec call (and all subsequent calls until ReleaseBlockedExec is called) will block inside Exec until released. Returns the gate channel — callers may also select on it directly if needed. Used by signal conformance tests (Bucket 8) to hold the engine at its first step dispatch so the pollControls goroutine can detect a pre-written pause/cancel file before any step completes.

Call ReleaseBlockedExec to unblock all waiting Exec calls. If the engine's ctx is cancelled while blocked (e.g. by the poller detecting pause/cancel), Exec returns ctx.Err — the caller does NOT need to release the gate.

func (*Fake) Capabilities

func (f *Fake) Capabilities() Caps

func (*Fake) CaptureFiles

func (f *Fake) CaptureFiles(ctx context.Context, h Handle, paths []string) ([]CapturedFile, error)

CaptureFiles reads each path from the handle's in-mem fs and returns the content in input order. Missing-path errors the whole call; unknown handle is a hard error. Defensive-copies content (matches state.InMemoryBlobs.Get discipline — callers mutating their slice must not corrupt our store).

func (*Fake) ClearFault

func (f *Fake) ClearFault()

ClearFault resets BOTH fault hooks (Exec + CaptureFiles). The conformance harness uses this between bucket runs that share a fake instance — though the standard pattern is to factory() a fresh fake per run/resume (matching the "infra rebuilt from recipe" spec §8 semantic), ClearFault is the in-place reset used when the same fake survives a crash-resume boundary in the conformance harness's atomic-commit bucket.

func (*Fake) CopyTo

func (f *Fake) CopyTo(ctx context.Context, h Handle, files []InputFile) error

CopyTo writes each InputFile into the handle's in-mem fs, defensive-copying content. Unknown handle is a hard error; len==0 is a no-op. Thread-safe.

func (*Fake) Create

func (f *Fake) Create(_ context.Context, spec ContainerSpec) (Handle, error)

func (*Fake) Destroy

func (f *Fake) Destroy(_ context.Context, h Handle) error

func (*Fake) Exec

func (f *Fake) Exec(ctx context.Context, h Handle, cmd Cmd) (<-chan IOChunk, <-chan ExecResult, error)

Exec returns the ExecResult programmed for cmd.Run (via ProgramExec). The streaming contract (slice 5.3): returns two channels. chunks is buffered with every programmed IOChunk and pre-closed; result is 1-buffered with the programmed ExecResult and pre-closed. chunks closes BEFORE result emits (the Fake's deterministic-burst semantic — every chunk is materialized before the result is observable).

Unprogrammed cmd.Run is a hard error — silent zero-value would mask dispatcher bugs in slice 2.4. Unknown handle is a hard error. Cmd.Env is accepted but not used (slice 2.4 verifies dispatcher env injection by inspecting what it passes to Exec, not by what the fake does with it).

On any error return, both channels are nil — the new Backend contract requires callers to check err before ranging or receiving.

func (*Fake) FailCaptureAfterN

func (f *Fake) FailCaptureAfterN(n int)

FailCaptureAfterN is the CaptureFiles analogue — same one-shot semantic, same guarantees. Crashes BEFORE any blob is written (CaptureFiles runs before Blobs.Put), so resume simply re-runs the step. Useful in slice 2.6 bucket-2 (replay) to simulate a crash mid-execution before state is committed. Bucket-3 (atomic commit) is the job of FailAppendAfterN on state.InMemoryLog, which crashes between Blobs.Put and Log.Append(node.completed).

func (*Fake) FailCreateConfigForImage

func (f *Fake) FailCreateConfigForImage(image string)

FailCreateConfigForImage makes Create return a PLAIN (non-ImageUnavailable) error for a spec.Image — models a deterministic DEFINITION fault (e.g. a malformed resources: the backend rejects) that the engine must surface by failing the WHOLE map as permanent_failure, NOT tolerate under min_success (contrast FailCreateForImage, the tolerated availability failure). Lazily allocates the set.

func (*Fake) FailCreateForImage

func (f *Fake) FailCreateForImage(image string)

FailCreateForImage makes Create return a *container.ImageUnavailableError for a spec.Image — models a valid runtime image that can't be pulled/booted (the SOLE tolerated per-element Create failure, P6a). Lazily allocates the set.

func (*Fake) FailExecAfterN

func (f *Fake) FailExecAfterN(n int)

FailExecAfterN configures the fake so the first n Exec calls succeed and the (n+1)-th fails with an "induced fault" error. FailExecAfterN(0) fails the very first call. One-shot: call #(n+1) and beyond succeed normally — matches slice 2.6's bucket-3 use (a single crash per test process; the process exits and resumes fresh).

The check sits in Exec itself, so calling this method has no retroactive effect on already-completed calls — the count is the live counter at the next call.

func (*Fake) ProgramExec

func (f *Fake) ProgramExec(run string, result ExecResult, chunks []IOChunk)

ProgramExec queues a result + optional stream chunks for a Cmd.Run. Calling Exec with that Cmd.Run returns the queued result and a closed channel pre-filled with the chunks. Test helper, NOT on the Backend interface.

Defensive-copies every byte slice (result.AWFOutput, result.Stdout, each chunk's Data) so a caller mutating its slices after ProgramExec returns cannot corrupt the fake. Matches state.InMemoryBlobs.Put's discipline.

func (*Fake) ProgramExecAny

func (f *Fake) ProgramExecAny(result ExecResult, chunks []IOChunk)

ProgramExecAny is the "match any Cmd.Run" variant of ProgramExec, used by tests where the Cmd.Run is built by the caller and the test only cares about the response (e.g., agent/claude.Launch_test, where the assembled command line is the test SUBJECT, not the lookup key).

The fake keeps a single "any" programmed entry; subsequent calls overwrite. Use ProgramExec when you want exact-match lookup. The fall-through in Exec consults anyExec only AFTER execTable[cmd.Run] misses, so ProgramExec entries still win when both are programmed.

Defensive-copies bytes per the same discipline as ProgramExec.

func (*Fake) ProgramExecWithFiles

func (f *Fake) ProgramExecWithFiles(run string, result ExecResult, chunks []IOChunk, files map[string][]byte)

ProgramExecWithFiles is ProgramExec plus the files the programmed command WRITES into the executing handle's fs when it runs — simulating output_files production on the scripted fake (SP1 Task 8a). The conformance harness creates handles internally with no seed hook, so a producer step makes its own artifact via this affordance (the written files are then captured by the dispatcher's post-Exec CaptureFiles). Defensive-copies the map and every byte slice. Existing ProgramExec callers are unaffected (this is a new method).

func (*Fake) ProgramImageDigest

func (f *Fake) ProgramImageDigest(image, digest string)

ProgramImageDigest maps a spec.Image value to the resolved content digest the next Create against it returns on Handle.ResolvedImageDigest (P6a test helper; not on the Backend interface). Lazily allocates the table.

func (*Fake) ReleaseBlockedExec

func (f *Fake) ReleaseBlockedExec()

ReleaseBlockedExec unblocks all Exec calls currently waiting on the gate channel and disarms the block hook (future Exec calls proceed normally). Idempotent — safe to call even if BlockExec was not called or was already released.

func (*Fake) Restore

func (f *Fake) Restore(_ context.Context, ref SnapshotRef, name string) (Handle, error)

Restore reads the serialized files from the injected Blobs store and creates a fresh handle preloaded with them; records the call in RestoreCalls. Without an injected store returns ErrUnsupported — the Phase 2 default. The restored handle is built exactly as Create builds one (monotonic-ID key into handles), so CaptureFiles works on it unchanged.

func (*Fake) Snapshot

func (f *Fake) Snapshot(_ context.Context, h Handle) (SnapshotRef, error)

Snapshot serializes the handle's in-mem files to JSON and Puts them to the injected Blobs store, returning the CAS ref. Without an injected store (WithBlobs not called) returns ErrUnsupported — the Phase 2 default.

The real Docker backend captures a CoW *diff*; the fake serializes the whole file map. This exercises the capture→CAS→ref WIRING (slice 7.1's snapshot:workspace round-trip), not Docker's diff fidelity, which the Docker tests own.

func (*Fake) WithBlobs

func (f *Fake) WithBlobs(b blobStore) *Fake

WithBlobs wires a CAS store so the fake can serialize a container's in-mem filesystem into Blobs on Snapshot and read it back on Restore — the durable path that survives the run→resume fake recreation (the conformance harness mints a fresh Fake for run and another for resume, but the same state.Blobs survives). Without it the fake advertises SnapshotNone and Snapshot/Restore return ErrUnsupported (preserving the Phase 2 default). Returns f for chaining.

func (*Fake) WriteFile

func (f *Fake) WriteFile(h Handle, path string, content []byte) error

WriteFile preloads a file into the handle's in-mem fs. Test helper for setting up CaptureFiles scenarios; not on the Backend interface (Phase 4 Docker would docker cp instead). Defensive-copies content on the way in.

type Handle

type Handle struct {
	Name    string
	ID      string
	Service string

	// ResolvedImageDigest is the content digest of the image that actually
	// booted (P6a) — set by Create when the image was runtime-resolved (a map's
	// per-element image:). Empty for statically-pinned containers and backends
	// that don't resolve a digest (native). The engine records it on the
	// element's map.item commit so resume has a durable record of what booted.
	ResolvedImageDigest string
}

Handle identifies a Created container. Treated as opaque by the engine; only the Backend that produced it may consume it.

Service is non-empty iff the handle is compose-shaped — it's the service the next Exec call will route to. The engine dispatcher (engine/local_dispatcher.go runCode) MAY override Service by cloning the Handle value before passing to Exec — this is the spec §3 `container: lab:db` cross-service form.

type IOChunk

type IOChunk struct {
	Stream string
	Data   []byte
}

IOChunk is one stream slice produced during Exec. Phase 2 emits the chunks queued by the fake's ProgramExec; Phase 4 Docker forwards real stream frames. The Stream field is "stdout" or "stderr" — typed loosely so future streams (e.g. agent.event) can reuse the type without churn.

type ImageUnavailableError

type ImageUnavailableError struct {
	Image string
	Err   error
}

ImageUnavailableError, returned by Backend.Create, marks the SOLE Create failure a map TOLERATES per element (P6a): a valid, non-empty image reference that could not be made runnable — its pull failed, or its container would not start / become ready — plus a rendered reference that is not a `@sha256:` content digest (untrusted worklist run-data the format contract rejects per element, not the author's pinned definition). The engine routes it to a tolerated item_failed + ReasonImageUnavailable, counted against min_success.

Every OTHER Create error is a deterministic DEFINITION error — an invalid per-element spec the author wrote: a malformed `resources:` (e.g. mem: 4Gi the daemon/parse rejects), or a host config the daemon refuses — and fails the WHOLE map as permanent_failure. Backends MUST wrap ONLY genuine image- availability / boot faults in this type; an UNWRAPPED Create error is treated as a hard, map-failing config error. That fail-loud default is deliberate: it stops a broken definition from being silently laundered into a tolerated per-item skip (the bug that hid `mem: 4Gi` behind image_unavailable). Callers route with errors.As.

func (*ImageUnavailableError) Error

func (e *ImageUnavailableError) Error() string

func (*ImageUnavailableError) Unwrap

func (e *ImageUnavailableError) Unwrap() error

type InputFile

type InputFile struct {
	Path    string
	Content []byte
}

InputFile is one in-container destination path + the bytes to write there, the symmetric input to CopyTo. The engine (the only Blobs reader) resolves an input_files ref to a CAS blob, Blobs.Get's the bytes, and passes them here. This method never touches state.Blobs (see pkg doc).

type RestoreCall

type RestoreCall struct {
	Name string
	Ref  SnapshotRef
}

RestoreCall records one Restore invocation (test assertion aid).

type SnapshotMode

type SnapshotMode string

SnapshotMode is the snapshot capability a backend supports.

const (
	// SnapshotNone — the backend has no snapshot facility. Phase 2 fake; any
	// non-snapshot:workspace Docker container in Phase 4.
	SnapshotNone SnapshotMode = "none"
	// SnapshotFSCoW — the backend can capture and restore a CoW filesystem
	// diff. Phase 4 Docker for snapshot:workspace containers.
	SnapshotFSCoW SnapshotMode = "fs-cow"
)

type SnapshotRef

type SnapshotRef string

SnapshotRef is the opaque reference returned by Snapshot and consumed by Restore. Phase 2 fake never produces one (Snapshot returns ErrUnsupported); Phase 4 sets it to the CAS ref of the CoW diff.

Directories

Path Synopsis
Package backendtest is the parameterized interface-conformance test for container.Backend.
Package backendtest is the parameterized interface-conformance test for container.Backend.
Package docker implements container.Backend against the Docker Engine SDK.
Package docker implements container.Backend against the Docker Engine SDK.
Package native implements container.Backend by running commands directly on the host via os/exec.
Package native implements container.Backend by running commands directly on the host via os/exec.

Jump to

Keyboard shortcuts

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