teleo-infrastructure/scripts/run_gcp_leoclean_nosend_oci_smoke.py

382 lines
16 KiB
Python

#!/usr/bin/env python3
"""Build and exercise one disposable synthetic leoclean no-send OCI image."""
from __future__ import annotations
import argparse
import json
import platform as host_platform
import shutil
import subprocess
import sys
import tempfile
import time
import uuid
from pathlib import Path
from typing import Any
if __package__ in {None, ""}:
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from ops import gcp_leoclean_nosend_package as package
SCHEMA = "livingip.leocleanNoSendOciSmoke.v1"
SUCCESS_CLAIM_CEILING = (
"One disposable image built from a synthetic noncanonical identity started, reached its built-in no-send health "
"contract, rejected an unsupported send-message entrypoint command, and was removed. This does not prove a real "
"Hermes tool-call denial, general network-egress denial, canonical identity, Cloud SQL permissions, Artifact "
"Registry publication, GCP/systemd behavior, model parity, Telegram isolation in a live service, or production "
"readiness. Only the candidate container, tag, and visible image ID are required absent; digest-pinned base images "
"and build cache may remain."
)
FAILURE_CLAIM_CEILING = (
"This failed receipt makes no affirmative image-build, startup, health, command-rejection, or cleanup claim. "
"Cleanup is passing only when Docker inventory queries succeed and show the candidate container, tag, and visible "
"image ID absent; digest-pinned base images and build cache may remain."
)
ENTRYPOINT = "/opt/livingip/leoclean-nosend/entrypoint.py"
RUNTIME_PYTHON = "/opt/livingip/leoclean-nosend/venv/bin/python"
class SmokeError(RuntimeError):
"""The disposable image did not satisfy its bounded smoke contract."""
def _require(condition: bool, message: str) -> None:
if not condition:
raise SmokeError(message)
def _run(
arguments: list[str],
*,
check: bool = True,
timeout: float = 120.0,
) -> subprocess.CompletedProcess[str]:
try:
result = subprocess.run(
arguments,
check=False,
capture_output=True,
text=True,
timeout=timeout,
)
except (OSError, subprocess.TimeoutExpired) as exc:
raise SmokeError(f"command unavailable or timed out: {arguments[0]}") from exc
if check and result.returncode != 0:
raise SmokeError(f"command failed: {arguments[0]} {arguments[1] if len(arguments) > 1 else ''}".strip())
return result
def _docker_json(arguments: list[str], *, timeout: float = 120.0) -> Any:
result = _run(["docker", *arguments], timeout=timeout)
try:
return json.loads(result.stdout)
except json.JSONDecodeError as exc:
raise SmokeError("Docker did not return JSON") from exc
def _write_receipt(path: Path, value: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
temporary.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n", encoding="utf-8")
temporary.replace(path)
def _build_command(*, context: Path, tag: str, smoke_label: str, build_args: dict[str, str]) -> list[str]:
command = [
"docker",
"build",
"--platform",
package.PLATFORM,
"--pull=false",
"--tag",
tag,
"--label",
smoke_label,
]
for name, value in sorted(build_args.items()):
command.extend(["--build-arg", f"{name}={value}"])
command.append(str(context))
return command
def _run_command(*, tag: str, name: str, smoke_label: str) -> list[str]:
return [
"docker",
"run",
"--detach",
"--rm",
f"--name={name}",
"--pull=never",
"--read-only",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=SETGID",
"--cap-add=SETUID",
"--cap-add=SETPCAP",
"--security-opt=no-new-privileges:true",
"--pids-limit=512",
"--network=bridge",
"--stop-timeout=30",
f"--tmpfs={package.PROFILE_ROOT}:rw,noexec,nosuid,nodev,size=64m,uid=0,gid=0,mode=0700",
f"--tmpfs=/tmp:rw,noexec,nosuid,nodev,size=64m,uid={package.RUNTIME_UID},gid={package.RUNTIME_GID},mode=0700",
"--tmpfs=/var/lib/postgresql/data:ro,noexec,nosuid,nodev,size=64k,uid=0,gid=0,mode=0000",
"--label",
smoke_label,
tag,
"service",
]
def _wait_healthy(name: str, *, expected_image_id: str, timeout: float = 180.0) -> dict[str, Any]:
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
result = _run(["docker", "container", "inspect", name], check=False, timeout=15)
if result.returncode != 0:
raise SmokeError("smoke container disappeared before health verification")
try:
inspected = json.loads(result.stdout)[0]
except (IndexError, KeyError, json.JSONDecodeError) as exc:
raise SmokeError("smoke container inspection is invalid") from exc
state = inspected.get("State") or {}
_require(inspected.get("Image") == expected_image_id, "running container image ID differs from the built image")
status = state.get("Status")
health = (state.get("Health") or {}).get("Status")
if health == "healthy":
return {"container_status": status, "health_status": health, "image_id": inspected["Image"]}
if status in {"dead", "exited", "removing"} or health == "unhealthy":
raise SmokeError(f"smoke container failed before health: {status}/{health}")
time.sleep(1)
raise SmokeError("smoke container did not become healthy before the deadline")
def _verify_health(name: str) -> dict[str, Any]:
result = _run(
[
"docker",
"exec",
"--user",
f"{package.RUNTIME_UID}:{package.RUNTIME_GID}",
name,
RUNTIME_PYTHON,
ENTRYPOINT,
"health",
],
timeout=30,
)
try:
value = json.loads(result.stdout)
except json.JSONDecodeError as exc:
raise SmokeError("container health command did not return JSON") from exc
_require(
value
== {
"schema": "livingip.leocleanNoSendContainerHealth.v1",
"status": "pass",
"process_id": 1,
"gateway_adapter_count": 0,
"send_message_present": False,
},
"container health response drifted",
)
return value
def _verify_unsupported_send(name: str) -> dict[str, Any]:
result = _run(
[
"docker",
"exec",
"--user",
f"{package.RUNTIME_UID}:{package.RUNTIME_GID}",
name,
RUNTIME_PYTHON,
ENTRYPOINT,
"send-message",
],
check=False,
timeout=30,
)
_require(result.returncode == 65, "unsupported send-message command did not fail closed")
_require(result.stdout == "", "unsupported send-message command emitted stdout")
try:
error = json.loads(result.stderr)
except json.JSONDecodeError as exc:
raise SmokeError("unsupported send-message command did not return redacted JSON") from exc
_require(
error == {"status": "fail", "error": "container_contract_failed"},
"unsupported send-message failure was not the exact redacted contract error",
)
return {"exit_code": result.returncode, "stdout_empty": True, "redacted_error": error}
def _cleanup(*, name: str, tag: str, image_id: str | None, smoke_label: str) -> dict[str, Any]:
_run(["docker", "rm", "--force", name], check=False, timeout=45)
_run(["docker", "image", "rm", "--force", tag], check=False, timeout=90)
labeled = _run(
["docker", "ps", "-aq", "--filter", f"label={smoke_label}"],
timeout=15,
).stdout.split()
named = _run(["docker", "ps", "-aq", "--filter", f"name=^{name}$"], timeout=15).stdout.split()
tagged = _run(
["docker", "image", "ls", "--quiet", "--filter", f"reference={tag}"], timeout=15
).stdout.split()
labeled_images = _run(
["docker", "image", "ls", "--quiet", "--filter", f"label={smoke_label}"], timeout=15
).stdout.split()
visible_image_ids = _run(["docker", "image", "ls", "--no-trunc", "--quiet"], timeout=15).stdout.split()
result = {
"labeled_containers_absent": labeled == [],
"named_container_absent": named == [],
"image_tag_absent": tagged == [],
"labeled_images_absent": labeled_images == [],
"candidate_image_id_absent": image_id is None or image_id not in visible_image_ids,
"docker_inventory_queries_succeeded": True,
}
result["status"] = "pass" if all(result.values()) else "fail"
return result
def run_smoke(*, repo_root: Path, artifact: Path, runtime_receipt: Path, output: Path) -> tuple[dict[str, Any], int]:
smoke_id = uuid.uuid4().hex
tag = f"livingip-leoclean-nosend-smoke:{smoke_id}"
name = f"livingip-leoclean-nosend-smoke-{smoke_id[:12]}"
smoke_label = f"livingip.leoclean-nosend-smoke={smoke_id}"
image_id: str | None = None
report: dict[str, Any] = {
"schema": SCHEMA,
"status": "fail",
"claim_ceiling": FAILURE_CLAIM_CEILING,
"smoke_id": smoke_id,
"platform": package.PLATFORM,
"synthetic_identity": True,
"canonical_authority_granted": False,
"build_count": 1,
"general_network_egress_denial_tested": False,
"real_hermes_send_tool_invocation_tested": False,
}
exit_code = 65
try:
_require(shutil.which("docker") is not None, "Docker is unavailable")
docker_server_arch = _docker_json(["version", "--format", "{{json .Server.Arch}}"], timeout=30)
_require(isinstance(docker_server_arch, str) and bool(docker_server_arch), "Docker server architecture is invalid")
execution_mode = "native" if docker_server_arch in {"amd64", "x86_64"} else "emulated_or_cross_platform"
_require(repo_root.is_dir() and not repo_root.is_symlink(), "repository root is missing or unsafe")
revision = package._git_head(repo_root)
runtime = package.validate_artifact(artifact, runtime_receipt, revision)
contract = package.build_expected_identity_contract(
artifact=artifact,
receipt_path=runtime_receipt,
runtime=runtime,
source_revision=revision,
source_root=repo_root,
)
fixture = repo_root / "fixtures" / "working-leo" / "leo-identity-v1"
manifest = package.strict_identity_manifest.build_identity_manifest(
identity_runtime_contract=contract,
database_fingerprint_path=fixture / "leo-database-fingerprint-v1.json",
constitution_path=fixture / "leo-constitution-v1.json",
database_identity_path=fixture / "leo-database-identity-v1.json",
source_root=repo_root,
)
with tempfile.TemporaryDirectory(prefix="livingip-leoclean-oci-smoke-") as temporary:
temporary_root = Path(temporary)
identity = temporary_root / "identity"
package.compile_identity_bundle(
manifest=manifest,
expected_identity_contract=contract,
source_root=repo_root,
output=identity,
)
context = temporary_root / "context"
image_input = package.prepare_context(
repo_root=repo_root,
artifact=artifact,
receipt=runtime_receipt,
identity=identity,
identity_source_root=repo_root,
output=context,
source_revision=revision,
)
build_args = package.load_json(context / "build-args.json", "build arguments")
_run(_build_command(context=context, tag=tag, smoke_label=smoke_label, build_args=build_args), timeout=1200)
inspected = _docker_json(["image", "inspect", tag], timeout=30)
_require(isinstance(inspected, list) and len(inspected) == 1, "smoke image inspection is not exact")
image = inspected[0]
image_id = image.get("Id")
_require(isinstance(image_id, str) and image_id.startswith("sha256:"), "smoke image ID is invalid")
_require(image.get("Os") == "linux" and image.get("Architecture") == "amd64", "smoke image platform drifted")
labels = (image.get("Config") or {}).get("Labels") or {}
for key, value in package.expected_image_labels(image_input).items():
_require(labels.get(key) == value, f"smoke image label drifted: {key}")
_require(labels.get("livingip.leoclean-nosend-smoke") == smoke_id, "smoke image label is missing")
_run(_run_command(tag=tag, name=name, smoke_label=smoke_label), timeout=60)
health_wait = _wait_healthy(name, expected_image_id=image_id)
health = _verify_health(name)
unsupported = _verify_unsupported_send(name)
_run(["docker", "stop", "--time=30", name], timeout=60)
deadline = time.monotonic() + 60
while time.monotonic() < deadline:
if _run(["docker", "container", "inspect", name], check=False, timeout=15).returncode != 0:
break
time.sleep(0.5)
else:
raise SmokeError("smoke container was not removed after stop")
report.update(
{
"teleo_git_head": revision,
"artifact_sha256": runtime["artifact_sha256"],
"identity_sha256": image_input["identity"]["bundle_sha256"],
"input_sha256": image_input["input_sha256"],
"image_id": image_id,
"docker_server_architecture": docker_server_arch,
"host_machine": host_platform.machine(),
"target_execution_mode": execution_mode,
"health_wait": health_wait,
"health": health,
"unsupported_send_message": unsupported,
}
)
exit_code = 0
except (SmokeError, package.PackageError, OSError, ValueError) as exc:
report["error"] = str(exc)
finally:
try:
cleanup = _cleanup(name=name, tag=tag, image_id=image_id, smoke_label=smoke_label)
except SmokeError as exc:
cleanup = {"status": "fail", "error": str(exc)}
report["cleanup"] = cleanup
if cleanup.get("status") != "pass":
exit_code = 65
report["error"] = "disposable OCI cleanup did not pass"
report["status"] = "pass" if exit_code == 0 else "fail"
report["claim_ceiling"] = SUCCESS_CLAIM_CEILING if exit_code == 0 else FAILURE_CLAIM_CEILING
_write_receipt(output, report)
return report, exit_code
def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--artifact", type=Path, required=True)
parser.add_argument("--runtime-receipt", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
args = _parse_args(argv)
report, exit_code = run_smoke(
repo_root=args.repo_root.resolve(),
artifact=args.artifact.resolve(),
runtime_receipt=args.runtime_receipt.resolve(),
output=args.output.absolute(),
)
stream = sys.stdout if exit_code == 0 else sys.stderr
print(json.dumps({"schema": SCHEMA, "status": report["status"]}, sort_keys=True), file=stream)
return exit_code
if __name__ == "__main__":
raise SystemExit(main())