Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions scripts/ci/activity_acceptance_buildx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Fresh private Buildx tool state; Docker credential authority stays separate."""

from __future__ import annotations

import os
import shutil
import stat
import tempfile
from pathlib import Path


class BuildxState:
def __init__(self, environment, parent=None):
self.path = Path(tempfile.mkdtemp(prefix="published-activity-buildx-", dir=parent))
self.directory = os.open(self.path, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
self.identity = self.shape(os.fstat(self.directory))
self.environment = {**environment, "BUILDX_CONFIG": str(self.path)}

@staticmethod
def shape(info):
return info.st_dev, info.st_ino, info.st_uid, info.st_gid, info.st_mode

def check(self):
current = self.path.lstat()
held = os.fstat(self.directory)
if (
self.shape(current) != self.identity
or self.shape(held) != self.identity
or not stat.S_ISDIR(current.st_mode)
or current.st_uid != os.getuid()
or stat.S_IMODE(current.st_mode) != 0o700
):
raise ValueError("private Buildx state directory changed")

def __enter__(self):
self.check()
return self.environment

def __exit__(self, _error_type, error, _traceback):
try:
self.check()
shutil.rmtree(self.path)
except Exception as state_error:
if error is None:
raise
# Preserve the command's captured streams when both operations fail.
error.buildx_state_failure = type(state_error).__name__
finally:
os.close(self.directory)
return False
8 changes: 8 additions & 0 deletions scripts/ci/published-activity-acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ six-minute execution bound, followed by independent cleanup of each captured
ID and then the network. Label-bound reconciliation covers uncertain create
responses. A foreign replacement is preserved. A reserved result descriptor
retains a terminal failure even if the output path refuses further use.
Failed subprocesses retain up to 64 KiB of stdout and stderr, the full stream
lengths and hashes, and the exit code or timeout. Losing an optional diagnostic
file cannot disable cleanup or turn the terminal failure into a pass.
Buildx receives fresh owner-only tool state for each manifest lookup, checked
against a held directory identity and removed afterward. Its writable state is
separate from Docker's empty credential configuration and never enters a
container or artifact. Other Docker operations and cleanup keep their original
credential and engine authority.

The seven-day artifact contains the selected tuple, complete wheel hashes,
dependency versions, SDK inventory, before/after container and network facts,
Expand Down
41 changes: 39 additions & 2 deletions scripts/ci/published-activity-acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
try:
from scripts.ci import activity_acceptance_output as output_files
from scripts.ci import activity_acceptance_runtime as runtime
from scripts.ci.activity_acceptance_buildx import BuildxState
from scripts.ci.activity_acceptance_session import Session
except ModuleNotFoundError as error:
if error.name != "scripts":
raise
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from scripts.ci import activity_acceptance_output as output_files
from scripts.ci import activity_acceptance_runtime as runtime
from scripts.ci.activity_acceptance_buildx import BuildxState
from scripts.ci.activity_acceptance_session import Session

ROOT = Path(__file__).resolve().parents[2]
Expand All @@ -35,6 +37,7 @@
OWNER_VALUE = "=sdk-python"
LABEL_FILTER = "label="
ID_FORMAT = "{{.ID}}"
DIAGNOSTIC_LIMIT = 64 * 1024
# No inherited context, registry credentials, proxy, SSH or remote Docker endpoint.
# This kernel directory is private to each CLI process and contains no config.json.
DOCKER_ENVIRONMENT = {
Expand All @@ -47,13 +50,48 @@
def call(*arguments, check=True, **kwargs):
if arguments[0] == "docker":
kwargs["env"] = DOCKER_ENVIRONMENT
if arguments[:2] == ("docker", "buildx"):
with BuildxState(DOCKER_ENVIRONMENT) as environment:
kwargs["env"] = environment
return subprocess.run(arguments, check=check, capture_output=True, text=True, timeout=180, **kwargs)
return subprocess.run(arguments, check=check, capture_output=True, text=True, timeout=180, **kwargs)


def cleanup_call(*arguments, check=True):
return subprocess.run(arguments, check=check, capture_output=True, text=True, timeout=10, env=DOCKER_ENVIRONMENT)


def record_failure(files, report, error):
report["failure"] = type(error).__name__ + ": " + str(error)[:1000]
report["complete"] = False
if not isinstance(error, subprocess.CalledProcessError | subprocess.TimeoutExpired):
return
arguments = list(error.cmd) if isinstance(error.cmd, tuple | list) else [error.cmd]
report["command_failure"] = {
"program": arguments[0],
"operation": arguments[1:4],
"argv_sha256": hashlib.sha256(json.dumps(arguments).encode()).hexdigest(),
"exit_code": getattr(error, "returncode", None),
"timeout_seconds": getattr(error, "timeout", None),
"streams": {},
}
if hasattr(error, "buildx_state_failure"):
report["command_failure"]["buildx_state_failure"] = error.buildx_state_failure
for name, value in (("stdout", error.stdout), ("stderr", error.stderr)):
data = value.encode() if isinstance(value, str) else value or b""
retained = data[:DIAGNOSTIC_LIMIT]
report["command_failure"]["streams"][name] = {
"bytes": len(data),
"retained_bytes": len(retained),
"truncated": len(data) > len(retained),
"sha256": hashlib.sha256(data).hexdigest(),
}
try:
files.write("command-" + name + ".txt", retained)
except (OSError, ValueError) as retention_error:
report.setdefault("diagnostic_retention_failures", {})[name] = type(retention_error).__name__


def deadline(_signum, _frame):
raise TimeoutError("published cell reached its six-minute execution bound")

Expand Down Expand Up @@ -463,8 +501,7 @@ def main():
if engine() != report["engine"]:
raise ValueError("Docker engine authority changed")
except Exception as error:
report["failure"] = type(error).__name__ + ": " + str(error)[:1000]
report["complete"] = False
record_failure(files, report, error)
finally:
signal.alarm(0)
owned.cleanup()
Expand Down
133 changes: 133 additions & 0 deletions tests/test_published_activity_acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,3 +541,136 @@ def test_platform_image_binding_refuses_an_ambiguous_or_foreign_local_object(mut
del platform["Descriptor"]
with pytest.raises(ValueError):
runtime.bind_local_image(index, platform, index_digest, child_digest, config_digest)


def test_failed_command_retains_diagnostics_and_cleanup_in_the_terminal_failure(monkeypatch, tmp_path):
output = tmp_path / "result"
monkeypatch.setattr(runner, "OUTPUT", output)
error = runner.subprocess.CalledProcessError(
1, ["docker", "buildx", "imagetools", "inspect"], output="captured stdout\n", stderr="metadata reader failed\n"
)

def fail_source(_report):
raise error

cleaned = []
monkeypatch.setattr(runner, "capture_source", fail_source)
monkeypatch.setattr(runner.OwnedDocker, "cleanup", lambda self: cleaned.append(self.nonce))
with pytest.raises(RuntimeError, match="acceptance or cleanup is incomplete"):
runner.main()
result = json.loads((output / "result.json").read_text())
assert result["complete"] is False
assert result["command_failure"]["exit_code"] == 1
assert (output / "command-stdout.txt").read_text() == "captured stdout\n"
assert (output / "command-stderr.txt").read_text() == "metadata reader failed\n"
assert len(cleaned) == 1


def test_failed_command_streams_are_bounded_and_keep_full_lengths_and_hashes(tmp_path):
files = runner.output_files.Output(tmp_path / "result")
data = b"x" * (runner.DIAGNOSTIC_LIMIT + 123)
error = runner.subprocess.TimeoutExpired(["docker", "buildx"], 5, output=data, stderr=b"timeout detail")
report = {"complete": True}
try:
runner.record_failure(files, report, error)
stream = report["command_failure"]["streams"]["stdout"]
assert stream["bytes"] == len(data)
assert stream["retained_bytes"] == runner.DIAGNOSTIC_LIMIT
assert stream["truncated"] is True
assert stream["sha256"] == runner.hashlib.sha256(data).hexdigest()
assert (files.path / "command-stdout.txt").stat().st_size == runner.DIAGNOSTIC_LIMIT
assert report["command_failure"]["timeout_seconds"] == 5
assert report["complete"] is False
finally:
files.close()


def test_diagnostic_path_refusal_does_not_prevent_cleanup_or_the_held_failure_receipt(monkeypatch, tmp_path):
output = tmp_path / "result"
original = tmp_path / "original"
monkeypatch.setattr(runner, "OUTPUT", output)

def fail_source(_report):
output.rename(original)
output.mkdir()
raise runner.subprocess.CalledProcessError(1, ["docker", "buildx"], stderr="retained failure")

cleaned = []
monkeypatch.setattr(runner, "capture_source", fail_source)
monkeypatch.setattr(runner.OwnedDocker, "cleanup", lambda self: cleaned.append(self.nonce))
with pytest.raises(RuntimeError, match="acceptance or cleanup is incomplete"):
runner.main()
result = json.loads((original / "result.json").read_text())
assert result["complete"] is False
assert result["output_failure"] == "identity_changed"
assert result["diagnostic_retention_failures"] == {"stdout": "ValueError", "stderr": "ValueError"}
assert list(output.iterdir()) == []
assert len(cleaned) == 1


def test_buildx_has_private_tool_state_without_changing_docker_credential_authority(monkeypatch, tmp_path):
original = runner.BuildxState
monkeypatch.setattr(runner, "BuildxState", lambda environment: original(environment, parent=tmp_path))
monkeypatch.setenv("DOCKER_CONFIG", "/foreign/credentials")
observed = []

def run(arguments, **kwargs):
environment = kwargs["env"]
state = Path(environment["BUILDX_CONFIG"])
assert environment["DOCKER_CONFIG"] == "/proc/self/fd"
assert environment["DOCKER_HOST"] == "unix:///var/run/docker.sock"
assert state.is_dir()
assert state.stat().st_mode & 0o777 == 0o700
assert list(state.iterdir()) == []
(state / "current").write_text("synthetic tool state")
observed.append(state)
return SimpleNamespace(stdout="manifest", returncode=0)

monkeypatch.setattr(runner.subprocess, "run", run)
result = runner.call("docker", "buildx", "imagetools", "inspect", "--raw", "public-image")
assert result.stdout == "manifest"
assert len(observed) == 1
assert not observed[0].exists()


def test_regular_docker_and_cleanup_calls_do_not_inherit_buildx_state(monkeypatch):
observed = []
monkeypatch.setattr(runner, "BuildxState", lambda *_args: pytest.fail("unexpected Buildx state"))
monkeypatch.setattr(runner.subprocess, "run", lambda _arguments, **kwargs: observed.append(kwargs["env"]))
runner.call("docker", "image", "inspect", "selected-image")
runner.cleanup_call("docker", "ps", "-a")
assert observed == [runner.DOCKER_ENVIRONMENT, runner.DOCKER_ENVIRONMENT]
assert "BUILDX_CONFIG" not in runner.DOCKER_ENVIRONMENT


def test_buildx_state_path_replacement_preserves_the_foreign_directory(tmp_path):
state = runner.BuildxState(runner.DOCKER_ENVIRONMENT, parent=tmp_path)
original = tmp_path / "original"
state.__enter__()
state.path.rename(original)
state.path.mkdir()
(state.path / "foreign").write_text("preserved")
with pytest.raises(ValueError, match="state directory changed"):
state.__exit__(None, None, None)
assert (state.path / "foreign").read_text() == "preserved"
assert original.is_dir()


def test_buildx_state_refusal_preserves_the_command_error_and_its_stderr(tmp_path):
state = runner.BuildxState(runner.DOCKER_ENVIRONMENT, parent=tmp_path)
original = tmp_path / "original"
error = runner.subprocess.CalledProcessError(1, ["docker", "buildx"], stderr="original metadata failure")

def fail_with_replaced_state():
with state:
state.path.rename(original)
state.path.mkdir()
(state.path / "foreign").write_text("preserved")
raise error

with pytest.raises(runner.subprocess.CalledProcessError) as captured:
fail_with_replaced_state()
assert captured.value is error
assert captured.value.stderr == "original metadata failure"
assert captured.value.buildx_state_failure == "ValueError"
assert (state.path / "foreign").read_text() == "preserved"
Loading