mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
sandbox: kitchen-sink images, feature-aware selection (Phase 2)
Phase 1 made the provisioner able to activate allowlisted extensions post-ready but kept the bare upstream images. Phase 2 ships the images that actually carry the extension/module files, and selects them only when a venture requests features — bare sandboxes stay on the light upstream image (no heavier pull, honoring the 'existing opters stay bare' decision). - _PostgresEngine / _RedisEngine gain kitchen_sink_image + image_for(features): bare (no features) -> the light image; features requested -> the kitchen-sink image. The provisioner runs engine.image_for(features), not engine.image, so the bare path is byte-for-byte unchanged. Mongo inherits the base image_for (returns its image regardless — no activatable features). - docker/sandbox-pg.Dockerfile: pgvector/pgvector:pg16 (ships vector) + postgis apt install; contrib (pg_trgm/citext/uuid-ossp) inherited from the official postgres base. Built at deploy via the sandbox-pg-image compose one-shot (mirrors the agent-image builders); the provisioner's _ensure_image finds the local tag and never pulls. Published by release.yml; pulled in registry compose. The verify step fails loudly if an extension's files are missing. - _RedisEngine kitchen-sink image: redis/redis-stack-server:latest (headless; ships search/json/bloom as loadable-but-unloaded modules — no custom build). - Extended the sandbox image-tag ghost-tag guard (the mongo:8-alpine regression test) to also cover kitchen_sink_image: skips locally-built roboco-* images, uses the namespaced Docker Hub endpoint for redis/redis-stack-server. Image-specific package names / module .so paths are verified at the CEO's NAS deploy (the spec's NAS smoke); the unit tests with the fake runner remain the CI bar, and the verify step is the fail-loud safety net for a wrong build.
This commit is contained in:
@@ -5,8 +5,15 @@ a tag that has never existed on Docker Hub (MongoDB ships no Alpine variant).
|
||||
Every unit test mocks the docker CLI, so none of them ever touch a real
|
||||
registry and none caught it — the bug only surfaces the moment a real
|
||||
``docker run`` pulls the image. This test queries the Docker Hub registry API
|
||||
for every ``SANDBOX_ENGINES`` entry's pinned ``image:tag`` and fails if the
|
||||
tag does not actually exist, which is the check that would have caught it.
|
||||
for every upstream image a sandbox engine may run (the bare ``image`` AND any
|
||||
``kitchen_sink_image`` used when extensions/modules are requested) and fails if
|
||||
the tag does not actually exist — the check that would have caught it.
|
||||
|
||||
Locally-built images (the ``roboco-sandbox-pg`` kitchen-sink, built by the
|
||||
``sandbox-pg-image`` compose service) are skipped: they aren't on Docker Hub.
|
||||
Namespaced upstream images (``redis/redis-stack-server``) use the namespaced
|
||||
registry endpoint; bare library images (``postgres``, ``redis``, ``mongo``)
|
||||
use the ``library/`` endpoint.
|
||||
|
||||
Network-dependent by design; skips cleanly when the registry is unreachable
|
||||
rather than failing (mirrors ``test_background_engines.py``'s local-Redis
|
||||
@@ -19,9 +26,12 @@ import httpx
|
||||
import pytest
|
||||
from roboco.models.sandbox import SANDBOX_ENGINES
|
||||
|
||||
_REGISTRY_URL = (
|
||||
# Docker Hub registry endpoints — library images live under ``library/``;
|
||||
# namespaced images (e.g. ``redis/redis-stack-server``) live under their ns.
|
||||
_REGISTRY_URL_LIBRARY = (
|
||||
"https://registry.hub.docker.com/v2/repositories/library/{name}/tags/{tag}"
|
||||
)
|
||||
_REGISTRY_URL_NS = "https://registry.hub.docker.com/v2/repositories/{name}/tags/{tag}"
|
||||
_TIMEOUT_SECONDS = 10.0
|
||||
_HTTP_OK = 200
|
||||
|
||||
@@ -31,11 +41,33 @@ def _split_image(image: str) -> tuple[str, str]:
|
||||
return name, tag or "latest"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("engine_name", sorted(SANDBOX_ENGINES))
|
||||
def test_sandbox_engine_image_tag_exists_on_docker_hub(engine_name: str) -> None:
|
||||
image = SANDBOX_ENGINES[engine_name].image
|
||||
def _engine_images() -> list[tuple[str, str]]:
|
||||
"""(engine_name, image) pairs for every upstream image an engine may run."""
|
||||
pairs: list[tuple[str, str]] = []
|
||||
for name, engine in sorted(SANDBOX_ENGINES.items()):
|
||||
pairs.append((name, engine.image))
|
||||
kitchen = getattr(engine, "kitchen_sink_image", None)
|
||||
if kitchen:
|
||||
pairs.append((name, kitchen))
|
||||
return pairs
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"engine_name,image",
|
||||
_engine_images(),
|
||||
ids=[f"{n}={img}" for n, img in _engine_images()],
|
||||
)
|
||||
def test_sandbox_engine_image_tag_exists_on_docker_hub(
|
||||
engine_name: str, image: str
|
||||
) -> None:
|
||||
name, tag = _split_image(image)
|
||||
url = _REGISTRY_URL.format(name=name, tag=tag)
|
||||
# Locally-built project images (no registry namespace, roboco- prefix) are
|
||||
# built by a compose service, not pulled — skip the Docker Hub check.
|
||||
if "/" not in name and name.startswith("roboco-"):
|
||||
pytest.skip(f"{image!r} is a locally-built image (not on Docker Hub)")
|
||||
url = (_REGISTRY_URL_NS if "/" in name else _REGISTRY_URL_LIBRARY).format(
|
||||
name=name, tag=tag
|
||||
)
|
||||
|
||||
try:
|
||||
resp = httpx.get(url, timeout=_TIMEOUT_SECONDS)
|
||||
@@ -44,5 +76,5 @@ def test_sandbox_engine_image_tag_exists_on_docker_hub(engine_name: str) -> None
|
||||
|
||||
assert resp.status_code == _HTTP_OK, (
|
||||
f"{engine_name}: pinned image {image!r} not found on Docker Hub "
|
||||
f"(library/{name}, tag {tag!r}, status {resp.status_code}) — {url}"
|
||||
f"({name}, tag {tag!r}, status {resp.status_code}) — {url}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user