Run unmodified task images: guest injected as an OCI image volume, plus second-runtime layers - #70
Open
Tomer Glottmann (tomergee) wants to merge 2 commits into
Open
Tomer Glottmann (tomergee) wants to merge 2 commits into
Tomer Glottmann (tomergee) wants to merge 2 commits into
Conversation
Until now a template's container image had to be the ate-env-guest image itself, so running a task image meant rebaking it with the guest as the entrypoint, once per image. Substrate's image volumes allow the other split: the task image stays unmodified and the guest is mounted into it read-only and started from the mount. Two ways to get such a template: - ate-env manifest template --task-image <repo@sha256:...> writes a template whose container is the task image, with the guest image as a volume at /ate and the command re-rooted to /ate/ko-app/ate-env-guest. --workspace passes -workspace to the guest in either mode. - CreateEnvironment gains an image field. ate-env-api takes the request's template (default-template by default) as the base, derives "<base>-<12 hex digits of the digest>" from it on first use and reuses it for later environments on the same image; the response reports it. A derived name already taken by a template that runs something else is a FailedPrecondition, as is a missing base; an unpinned image, a derived name over 63 characters, or a base command that is not an absolute path to the guest is an InvalidArgument. Concurrent creates racing on one derived template both succeed. The derivation lives in internal/apiservice/imagetemplate.go and accepts either kind of base, guest-as-image or already injected, so a second runtime can reuse it. internal/ate gains Get/CreateActorTemplate and the fake control plane stores templates. The CLI gains create --image, the Python client create(image=), and the full-stack e2e suite a create-from-image test gated on ATE_ENV_TASK_IMAGE that checks the guest comes from the volume, the rootfs is the image's, and a second environment reuses the derived template. docs/task-images/README.md is the how-to (both flows, what an image needs, errors, cleanup) and docs/task-images/DESIGN.md the design (derivation rules, naming and idempotency, failure semantics, security, alternatives, future work). Stubs regenerated: Go with buf and protoc-gen-go v1.36.11, Python with grpcio-tools 1.61.3, keeping the committed license headers.
A task-image template carries the guest as a read-only image volume; an agent framework's own in-sandbox daemon, such as OpenSandbox's execd, is one more volume plus a process that has to start next to the guest. The guest is the container's only process, so it is the one that starts it. ate-env-guest gains -sidecar (repeatable: an absolute binary path and its arguments, run without a shell, output logged with the binary's name as prefix, restarted with a backoff that doubles from one second to thirty and resets after a minute of stable running) and -sidecar-readyz (repeatable: URLs that must answer 2xx once before /readyz reports ready, so Substrate's wakeup probe admits traffic only when every runtime is serving). Bad -sidecar values fail at start rather than loop. ate-env manifest template gains --layer name=image@sha256:...=/mount, --sidecar and --sidecar-readyz, which become image volumes, mounts and guest flags on the template. Template derivation recognizes the guest volume by name rather than as "an image volume", so a base with layers still gets the guest added or kept correctly and environments created on demand with --image inherit the second runtime. Only a command that starts with the guest binary is re-rooted under the mount. docs/task-images/RUNTIMES.md describes the model, the flags, who can reach the second runtime from where (processes inside the environment now; outside callers still only reach port 80 through the router, which is the next piece), suspend and restart behavior, and the limits. examples/runtime-layers is a runnable demo with a static busybox as the stand-in runtime: httpd on 44772 as the sidecar under both a python and a debian task image, the second one created from an image on the layered base. The first attempt with the dynamically linked busybox failed on the debian image with a glibc version error, which is why the docs make the self-contained-layer rule and its failure signature explicit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Run any digest-pinned OCI image as an ate-env environment without rebuilding it. The task image stays as published; the
ate-env-guestis mounted into it as a read-only OCI image volume at/ateand started from there. Two commits:1. Task images (
3f943a8)CreateEnvironmentgains animagefield.ate-env-apitakes the request's template (defaultdefault-template) as the base, derives a template named<base>-<12 hex of the digest>next to it on first use, reuses it afterwards, and reports it in the response. Concurrent first creates on the same image both succeed.ate-env manifest template --task-imagewrites such a template up front;--workspacepasses-workspaceto the guest.ate-env create --image, Pythonclient.create(image=).InvalidArgumentbefore anything is created; missing base or a derived name held by a template running another image areFailedPrecondition.internal/apiservice/imagetemplate.goand accepts either kind of base (guest as image, or already injected).2. A second runtime as a layer (
a76974d)ate-env-guestgains-sidecar(start and supervise an extra runtime from a layer: no shell, output logged with a prefix, restart with backoff 1s→30s) and-sidecar-readyz(URLs the guest's/readyz, Substrate's wakeup probe, waits for once).ate-env manifest templategains--layer name=image@sha256:...=/mount,--sidecar,--sidecar-readyz. Layers survive derivation, socreate --imageon a layered base inherits the second runtime.This is step 1 of the runtime-injection plan: the mechanism every injected runtime needs. The intended first foreign runtime is OpenSandbox's
execd, which is built to be injected this way.Docs
docs/task-images/README.md: how-to, both flows, what an image needs, errors, cleanup.docs/task-images/RUNTIMES.md: second runtime as a layer, OpenSandbox specifics, reachability, limits.docs/task-images/DESIGN.md: derivation rules, naming and idempotency, failure semantics, security, alternatives, future work.examples/runtime-layers: runnable demo with a static busybox as the stand-in runtime.Testing
go test ./...: 11 packages, including derivation from both base kinds, naming limits, every rejection, server create/reuse/race/failure paths against the fake control plane, manifest layers and sidecars, and the guest's sidecar supervisor (restart with backoff, stop on shutdown, readiness gate once per URL).clients/pythonunit tests: 59 pass.tests/e2e/test_full_stack.pygains two cheap negative tests and a create-from-image test gated onATE_ENV_TASK_IMAGEthat checks the guest comes from the volume and not the image, the rootfs is the image's, files round-trip, and a second environment reuses the derived template.ate-env create --imageof an unmodifiedpython:3.12-slim: created in 1 s, derived template present,python3 --versionanswered from the image's own rootfs with the guest under/ate, deleted; 44 s end to end on a cold worker.examples/runtime-layers/demo.sh: layered template, environment serving in 3 s, the second runtime answered from inside, then adebian:bookworm-slimenvironment created on the same base with--imagedid the same; 23 s total.busybox:1.37failed on the debian image with a glibc version error, exactly as the supervisor and fail-fast readiness are meant to surface it. The docs now state the self-contained-layer rule and that signature.hack/verify/boilerplate.shpasses. Go stubs viabuf generatewith protoc-gen-go v1.36.11; Python stubs via grpcio-tools 1.61.3, matching the committed generator and keeping the license headers.Known gaps, by design
ate-env-apiproxies only the guest. An endpoint lookup that names a runtime's port is the next piece and belongs with the OpenSandbox backend.CreateEnvironmentresolves the template in the environment's atespace and ignorestemplate.atespace.Relation to other PRs
spec.imagestraight through instead of itsimage_templatesmap.fakes.pyandtest_full_stack.py) and Enable settings to use warm cache #67 (cmd/ate-env/manifest.go). If those land first I will rebase; the conflicts are mechanical.