382 lines
16 KiB
Python
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())
|