CI / ruff · mypy · pytest (pull_request) Successful in 4m3s
- infra/runner/tgit-runner: deploy/restart/remove/list по SSH forced command; аргументы не исполняются и не глобятся; архив — только файлы и папки, без «..», ≤ 20 МБ, есть main.py; health_url — только localhost - песочница: uid 10001, read-only, tmpfs noexec, cap-drop ALL, no-new-privileges, 128 МБ без swap, 0.25 CPU, 64 PID, --network none (сеть — только по egress-allow админа), лимит 10 сервисов, 2 образа - новая версия стартует рядом (-next) и заменяет старую, только став healthy; иначе удаляется, старая работает, exit 3 - бот: после push git archive изменённых services/<name>/ → runner; автору — «🚀 работает» или «⛔ не развёрнут» с логом, команде — алерт ⛔; удалённая папка → remove; «♻️ Перезапустить» в «Статусе» для админа (S-5) - examples/escape_attempts: все попытки выбраться — BLOCKED на живом Docker - install.sh, authorized_keys.example; CD бота обновляет runner на хосте Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
2.6 KiB
Python
Executable File
84 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""A fake `docker` CLI for tgit-runner tests: records every call, keeps containers in a JSON file.
|
|
|
|
Behaviour knobs (env): FAKE_HEALTH (healthy|unhealthy|none), FAKE_STATUS (running|exited), FAKE_RESTARTS,
|
|
FAKE_BUILD_FAIL=1. State: $FAKE_DOCKER_STATE (json), calls: $FAKE_DOCKER_LOG (one json array per line).
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
state_path = os.environ["FAKE_DOCKER_STATE"]
|
|
args = sys.argv[1:]
|
|
with open(os.environ["FAKE_DOCKER_LOG"], "a") as log:
|
|
log.write(json.dumps(args) + "\n")
|
|
try:
|
|
with open(state_path) as f:
|
|
state = json.load(f)
|
|
except FileNotFoundError:
|
|
state = {"containers": {}, "images": []}
|
|
|
|
|
|
def save() -> None:
|
|
with open(state_path, "w") as f:
|
|
json.dump(state, f)
|
|
|
|
|
|
def done(code: int = 0, out: str = "") -> None:
|
|
if out:
|
|
print(out)
|
|
save()
|
|
sys.exit(code)
|
|
|
|
|
|
cmd = args[0] if args else ""
|
|
if cmd == "container" and args[1] == "inspect":
|
|
done(0 if args[2] in state["containers"] else 1)
|
|
if cmd == "inspect":
|
|
fmt, name = args[2], args[3]
|
|
if name not in state["containers"]:
|
|
done(1)
|
|
if "Health" in fmt:
|
|
done(0, os.environ.get("FAKE_HEALTH", "healthy"))
|
|
if "RestartCount" in fmt:
|
|
done(0, os.environ.get("FAKE_RESTARTS", "0"))
|
|
done(0, os.environ.get("FAKE_STATUS", "running"))
|
|
if cmd == "ps":
|
|
done(0, "\n".join(n for n in state["containers"]))
|
|
if cmd == "build":
|
|
if os.environ.get("FAKE_BUILD_FAIL"):
|
|
done(1, "ERROR: could not install requirements")
|
|
tag = args[args.index("-t") + 1]
|
|
ctx = args[-1]
|
|
with open(os.path.join(ctx, "Dockerfile")) as f:
|
|
state.setdefault("dockerfiles", {})[tag] = f.read()
|
|
state["images"].append(tag)
|
|
done(0, "sha256:feed")
|
|
if cmd == "run":
|
|
name = args[args.index("--name") + 1]
|
|
state["containers"][name] = {"image": args[-1], "args": args}
|
|
done(0, "cid")
|
|
if cmd == "rm":
|
|
for name in args[2:]:
|
|
state["containers"].pop(name, None)
|
|
done(0)
|
|
if cmd == "rename":
|
|
state["containers"][args[2]] = state["containers"].pop(args[1])
|
|
done(0)
|
|
if cmd == "restart":
|
|
done(0)
|
|
if cmd == "logs":
|
|
done(0, "Traceback: boom")
|
|
if cmd == "image" and args[1] == "ls":
|
|
repo = args[2]
|
|
tags = [t.split(":", 1)[1] for t in reversed(state["images"]) if t.split(":", 1)[0] == repo]
|
|
full = "{{.Repository}}" in args[-1]
|
|
done(0, "\n".join(f"{repo}:{t}" if full else t for t in tags))
|
|
if cmd == "image" and args[1] == "rm":
|
|
state["images"] = [t for t in state["images"] if t != args[2]]
|
|
done(0)
|
|
if cmd == "network":
|
|
done(0)
|
|
done(2, f"fake docker: unsupported {args}")
|