containers::tools::run

Tag 1: a client asks the server to run a tool container from an image with limits and mounts; the server obtains the image, deploys, injects the proxy, connects to it and begins it with the arguments, mints the id and has the client register the container — value the secrets it takes — feeds the values to the container, makes every FUSE mount, sends exactly one id, relays the container’s activity, and relays and serves every channel until the run ends.

A client opens the scope with a request whose payload is the tag byte 1 followed by the request as JSON. The server runs the container the request describes for as long as the scope lives, sends the container’s id on the scope and, after it, the proxy’s word whenever the container goes active and inactive, or an error, and ends the scope by the response finish. This section states what the server does, in the order it does it. It states nothing about the client, which may open any channel at any time.

  • The request. Request states its form and every constraint the server enforces on it.
  • The response. Response states the one response and the sequence of the main stream.
  • The channels. The client opens the channels that Client Channels defines; the server opens the channels that Server Channels defines. Each channel is its own section.

The order#

  1. The server reads the request. A request whose payload does not decode is answered by a response finish that no response precedes, as Endpoints provides. A request the server refuses is answered by exactly one response, the error, and the finish. The server refuses a request that names a volume not in the client’s volumes::list listing, a mount whose path is the root of the container, two mounts with one path, a mount inside another mount, a mount path with a component that is empty, . or .., two FUSE mounts with one id, or an image it will not supply. Then, for each distinct volume the request names in volume_mounts, in the order first named, the server decides as Capabilities states, the first rule that applies deciding: a volume not in the mode its mount names is answered by the byte 5 followed by the name and the mode the volume is in; a volume of a mode the server does not mount by the byte 4 followed by the name; a volume under a volumes::stat, volumes::read, volumes::write, volumes::filetree, volumes::edit or volumes::delete at the time of the request by the byte 1 followed by the name; a volume a running container holds, where the server mounts a volume of its mode once, by the byte 2 followed by the name; and a volume a FUSE serve holds, where the server does not mount a volume of its mode beside one, by the byte 3 followed by the name — each as exactly one response, and the finish; the server gives back every volume it held for the request, fetches nothing and deploys nothing for it. A request may name one volume more than once, at different paths.
  2. The server obtains the image. The server obtains the image the digest names: from what it holds — among which every image the client built on it by images::build, and no image another client built — from a referenced registry its policy lists, or from the client; this specification does not prescribe which. Where it would obtain the image from the client, the server asks the client whether it holds the image on an oci-has channel, and a client that holds it answers the repository path the server fetches it under; a manifest or a blob the server requires it asks for on an oci-manifest or oci-blob channel. The server runs only the image the digest names: bytes that are not those the digest names are the run’s error, and so is an image the server obtains from nowhere. How the server holds, verifies or streams what it obtains is not prescribed.
  3. The server deploys the container. The server starts the image with memory and disk as limits; every volume mount resolved by volume_name against the identity under which the connection was authorized, descended by volume_relative_path, at container_path; the container proxy injected into the container and started; and TCP port 14979 of the container reachable to the server. The deploy is complete when the proxy accepts a WebSocket connection at that port. The server sets no environment from the request. A deploy that does not complete is the run’s error.
  4. The server connects to the proxy. The server opens exactly one WebSocket to port 14979 of the container, the connection of the Container Proxy layer, and on it, before anything else, the begin scope of the container’s family — tools::begin, carrying the arguments and the template of the request verbatim and no secret. A proxy that does not accept the connection, or that answers the begin with an error, is a container that did not come up: the server stops the container, and the failure is the run’s error. The server opens no second connection to a container.
  5. The server registers the container. The Begun the proxy answers carries the secrets the container’s program takes, each by name to its mode, and no dependencies: a tool container declares none. The server mints the id of step 8 first and enters the container among those it runs, so that a containers::fuse_serve naming the id is answered from now on. When the declaration is not empty, the server opens exactly one registration channel on the scope carrying the id and the declaration verbatim, and reads the answer to its finish. A deploy, which carries the secrets’ first values, has the server open exactly one secrets channel on the begin scope carrying the values verbatim and read its answer to its finish; the proxy’s Fed lets the run go on. An error on either channel, a channel response finish that no channel response precedes on either, and a client that is gone are the run’s error, and the server stops the container. When the declaration is empty the server opens no registration channel and feeds nothing.
  6. The server makes every FUSE mount. For each entry of fuse_file_mounts, and after the last of them for each entry of fuse_directory_mounts, in the order of the request, the server opens one mount scope on the proxy’s connection, naming the path and the kind, and waits for its answer before opening the next. A mount the proxy does not make is the run’s error, and the server stops the container. The server opens no tree scope and sends no response before the last mount is complete. The mounts and the registration, with its feed, proceed beside each other, and the server acts on the failure of either only when both have ended. A FUSE mount the client asks for after the id, on the fuse-mount channel, is made as that channel states, and is a FUSE mount of the container from the moment it is made.
  7. The server holds what the client opened. A channel the client opens before the server sends the id is served after the id, in the order opened, and is neither refused nor read before the last FUSE mount is complete and the registration channel, when one was opened, has ended and its feed has been answered Fed.
  8. The server sends the id. The id is a string the server chose in step 5, unique among the containers it is running, and not derivable from the request, from the identity of the connection, or from any other id. The server sends it as exactly one response. From that moment the container is running for the client, and a containers::fuse_serve request of the client’s naming the id finds it. After the id, the server sends Active and Inactive as the proxy sends them on the begin scope, the latter carrying the rotating secrets the program handed back, each exactly as the proxy sent it, and no other response on the scope but the one the response states.
  9. The server serves the scope. Concurrently, for as long as the scope lives: every channel the proxy opens on the begin scope, and every ask a mount makes on its own scope, is relayed to the client as a channel the server opens; every channel the client opens is served as its section defines, among them every secrets channel, which the server relays to the begin scope as one secrets channel, the values verbatim, and answers as the proxy answers it.
  10. The server ends the run. When it receives the client’s stop, when the proxy’s connection ends, or when the client’s connection ends, the server stops the container, ends every channel task, and sends the response finish. After the id, the server sends no error of its own on the main stream; the one error it sends after the id is the proxy’s, with which the proxy ends the begin scope when it cannot collect the container’s rotating secrets, relayed as the response states, and the server ends the run after it. The finish that ends a running container carries none.

The rules that hold throughout#

  • Who may mount a volume. A volume whose mode is ephemeral or read_only may be mounted in any number of containers of its client at once, as may a volume whose mode is persistent, each container naming it at several paths if it likes, every container mounting the one volume; a persistent volume is held by at most one volumes::fuse_serve scope at a time, and never by a FUSE serve beside a running container. That is the most a mode allows; what this server allows for each mode is the mode object it holds out — mount, mount_many, mount_and_fuse_serve — and the server refuses a request naming a volume that it does not mount, that a running container holds beyond what mount_many allows, or that a FUSE serve holds beyond what mount_and_fuse_serve allows, as Capabilities and the response state. The server holds every volume a request names, shared, from the moment it accepts the request until the run ends: a stat, a read, a write, a filetree, an edit or a delete of a volume so held is refused for as long as any run holds it, and a run naming a volume under one of those six is refused for as long as that is in flight.
  • The means of deployment. This revision prescribes no runtime, no operating system, no virtualization, no network topology and no vendor. The server may deploy the container on hardware it operates, on infrastructure a third party supplies, or through an intermediary, and every requirement of this section holds however it deploys.
  • No timeout. The server times nothing out: not a fetch, not a deploy, not a channel, not the scope. A container whose entrypoint never listens is served as a container whose exchanges the proxy cannot serve.
  • The conduit. The server relays the bytes of every relayed exchange verbatim. It does not read them, does not alter them, does not reorder frames within one channel, and does not send an ask a second time. A channel the client finishes with no frame before the finish is relayed to the proxy as a channel response finish that no channel response precedes.
  • What the server never does. The server never refuses or alters a FUSE ask on its own account: whether a change to a FUSE mount is allowed is the client’s answer to that ask. The server never writes to a volume or a mount on its own account. The server never sends a second id.