fix(build): close remaining mutable image inputs
This commit is contained in:
@@ -116,7 +116,11 @@ def _layer_nested_containers(
|
||||
)
|
||||
info(f"using cached nested-container image {derived!r}")
|
||||
return derived
|
||||
return nested_containers_mod.build_image(agent_image, container_mod.build_image)
|
||||
return nested_containers_mod.build_image(
|
||||
agent_image,
|
||||
container_mod.build_image,
|
||||
container_mod.pinned_local_image_ref,
|
||||
)
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
||||
@@ -57,6 +57,7 @@ _GUEST_DEVICES = ("/dev/fuse", "/dev/net/tun")
|
||||
def build_image(
|
||||
base_image: str,
|
||||
build: Callable[..., None],
|
||||
pin_local_base: Callable[[str], str],
|
||||
) -> str:
|
||||
"""Layer the nested-container tooling onto an already-built agent image.
|
||||
|
||||
@@ -67,13 +68,15 @@ def build_image(
|
||||
# abstraction once that infrastructure exists.
|
||||
"""
|
||||
image = f"{base_image}{IMAGE_SUFFIX}"
|
||||
pinned_base = pin_local_base(base_image)
|
||||
init_script = Path(__file__).with_name("nested-containers-init.sh")
|
||||
with tempfile.TemporaryDirectory(prefix="bot-bottle-nested-containers.") as tmp:
|
||||
context = Path(tmp)
|
||||
shutil.copy2(init_script, context / "nested-containers-init.sh")
|
||||
(context / "Dockerfile").write_text(
|
||||
"FROM docker:28-cli AS docker_cli\n"
|
||||
f"FROM {base_image}\n"
|
||||
"ARG DOCKER_CLI_BASE_IMAGE\n"
|
||||
"FROM ${DOCKER_CLI_BASE_IMAGE} AS docker_cli\n"
|
||||
f"FROM {pinned_base}\n"
|
||||
"USER root\n"
|
||||
"COPY --from=docker_cli /usr/local/bin/docker /usr/local/bin/docker\n"
|
||||
"COPY --from=docker_cli /usr/local/libexec/docker/cli-plugins/"
|
||||
|
||||
@@ -682,6 +682,40 @@ def image_id(ref: str) -> str:
|
||||
raise AssertionError("unreachable")
|
||||
|
||||
|
||||
def pinned_local_image_ref(ref: str) -> str:
|
||||
"""Tag a local image with its complete content ID for a stable ``FROM``.
|
||||
|
||||
Agent images are immediately used as bases for the optional
|
||||
nested-containers layer. A content-derived tag prevents another concurrent
|
||||
build from moving the provider's ordinary ``:latest`` tag between those
|
||||
two builds.
|
||||
"""
|
||||
image = image_id(ref)
|
||||
digest = image.removeprefix("sha256:")
|
||||
if len(digest) != 64 or any(char not in "0123456789abcdef" for char in digest):
|
||||
die(f"could not derive a local base tag from invalid image ID {image!r}")
|
||||
repository = ref.split("@", 1)[0]
|
||||
last_slash = repository.rfind("/")
|
||||
last_colon = repository.rfind(":")
|
||||
if last_colon > last_slash:
|
||||
repository = repository[:last_colon]
|
||||
pinned_ref = f"{repository}:sha256-{digest}"
|
||||
result = subprocess.run(
|
||||
[_CONTAINER, "image", "tag", image, pinned_ref],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
die(
|
||||
f"could not tag exact local image {image}: "
|
||||
f"{(result.stderr or result.stdout or '').strip() or '<no detail>'}"
|
||||
)
|
||||
if image_id(pinned_ref) != image:
|
||||
die(f"content-derived local tag {pinned_ref!r} did not resolve to {image}")
|
||||
return pinned_ref
|
||||
|
||||
|
||||
def image_created_at(ref: str) -> datetime | None:
|
||||
"""Return the image creation timestamp as an aware UTC datetime, or None
|
||||
when the field is absent or unparseable (e.g. FROM-scratch images, images
|
||||
|
||||
Reference in New Issue
Block a user