fivenines
18/40

Guided Problem

MiniDock Build 14: Enforce Legal Container Transitions

Time
25m
Level
intermediate
Artifacts
not specified
Progress0%
Lesson 18 · Runtime

The Container Lifecycle

Design claim: Container state is a set of explicit promises, and commands are legal transitions rather than arbitrary mutations.

Starting model

  • You can reclaim unreachable image objects without breaking tagged or running releases.
  • The useful vocabulary at this point is deliberately small: Created, Running, pause transition, Exited.

States are promises about resources

Operators and automation must know what resources exist before a process starts, after it exits, and during cleanup. Runtime behavior unfolds over time, so state and ownership must remain valid after the initiating request has returned.

The tempting shortcut is straightforward: represent a container with one running boolean. The shortcut confuses one command or connection with the longer-lived process and resources it happens to touch. Created, paused, exited, and partially removed states retain different resources and permit different operations. Asynchronous exit, retry, disconnect, or timeout exposes that mismatch immediately.

Model transitions, not flags

Each state is a promise about what exists. Created: filesystem mounted, config written, network allocated — but no process; you can inspect it, copy files in, but it consumes no CPU. Running: the process lives. Paused: the process exists but its cgroup is frozen — mid-instruction, consuming memory but zero CPU. Exited: the process is gone but the writable layer and logs remain — debuggable, restartable. Dead: removal failed partway; a tombstone. Removal is only legal from a stopped state — run is not a state but a composite: create + start.

A state machine records legal transitions and accepts both user intent and process events as sources of change. The mechanism records the durable fact separately from transient control and lets events update the model when reality changes.

Read the static view as custody and the dynamic view as evidence crossing that custody boundary.

Architecture — the full state machine
  stateDiagram-v2
    [*] --> Created: create (fs + config ready, no process)
    Created --> Running: start
    Running --> Paused: pause (cgroup freeze)
    Paused --> Running: unpause
    Running --> Exited: process exits or stop/kill
    Exited --> Running: restart
    Created --> [*]: rm
    Exited --> [*]: rm
    Running --> [*]: rm -f (force)
    Exited --> Dead: rm failed midway
    Dead --> [*]: rm (retry cleanup)
      

It enumerates created, running, paused, exited, and cleanup transitions. Its central claim is that recorded state follows observable reality and permits only transitions whose prerequisites hold; the labels therefore describe authority rather than decorative grouping.

Intent and reality both move the machine

Transitions have two sources: user commands (start, pause, stop) and reality — the process can exit on its own at any moment, and the state machine must follow the world, not fight it:

Create prepares durable resources, start launches the process, pause freezes it, exit records reality, restart launches again, and remove releases retained state. The sequence matters because lifecycle decisions made from stale evidence become illegal or destructive operations.

The second figure tests the same model in motion: it shows an asynchronous process exit updating state before a later command is checked.

Sequence — a crash moves the state, not the user
  sequenceDiagram
    participant U as User
    participant E as Engine
    participant CS as Container store
    participant S as Shim
    U->>E: start c-42
    E->>CS: state: Created → Running
    note over S: hours later, the app segfaults
    S->>E: exit event (code 139)
    E->>CS: state: Running → Exited (139)
    U->>E: pause c-42
    E-->>U: error: container not running
      

It shows an asynchronous process exit updating state before a later command is checked. The ordering is valid only when it continues to preserve the stated invariant under retries and interruption.

An exit event outranks stale intent

The process crashes without a user command and a later pause request arrives, so state must already reflect the exit and refuse the illegal transition. The failure case is authoritative input to the state model, not an exception to be hidden behind a successful command response.

Recorded state follows observable reality and permits only transitions whose prerequisites hold. The reusable rule keeps process reality, operator intent, and retained resources from collapsing into one status flag.

Give MiniDock a legal lifecycle

MiniDock can make lifecycle operations predictable by attaching promises to states rather than commands to ad hoc code paths. MiniDock must preserve the lifecycle guarantee while leaving the current integration move to the learner.

What carries forward

  • Recorded state follows observable reality and permits only transitions whose prerequisites hold.
  • Serialize lifecycle commands and events into one legal container state.
  • The rejected shortcut remains a diagnostic: if the design starts depending on it again, the original constraint has probably been lost.
Next step

See what actually stuck.

Take the practice scenarios now.