Files
projectbrain-Public/scripts/legacy-deployment-migration/plan.py
T

104 lines
4.7 KiB
Python

"""Pure, fail-closed migration planning; this module cannot mutate Docker or files.
Accept only the sanitized inspect projection documented in README.md. A plan is
not deployment authorization, a snapshot, a receipt, or proof of successful backup.
"""
import re
from pathlib import PurePosixPath
TARGETS = {
"vacatureradar": ("VacatureRadar", "/mnt/pool/appdata/VacatureRadar"),
"lumaops": ("lumaops", "/mnt/pool/appdata/lumaops"),
}
SHA = re.compile(r"[0-9a-f]{40}")
ID = re.compile(r"[0-9a-f]{64}")
ATTEMPT = re.compile(r"[a-z0-9][a-z0-9-]{7,47}")
def _under(path, parent):
value = PurePosixPath(path)
return (
value.is_absolute()
and ".." not in value.parts
and value != PurePosixPath(parent)
and value.is_relative_to(parent)
)
def build_plan(slug, revision, attempt, observed):
"""Bind one target to retained runtime identity and isolated candidate data."""
if slug not in TARGETS or not SHA.fullmatch(revision) or not ATTEMPT.fullmatch(attempt):
raise ValueError("Invalid fixed target, full revision, or attempt identifier")
expected_name, appdata = TARGETS[slug]
keys = {"name", "containerId", "imageId", "running", "health", "composeProject", "mounts"}
if set(observed) != keys:
raise ValueError("Expected only the sanitized runtime projection")
if (
observed["name"] != expected_name
or observed["composeProject"] != slug
or not ID.fullmatch(observed["containerId"])
or not re.fullmatch(r"sha256:[0-9a-f]{64}", observed["imageId"])
or observed["running"] is not True
or observed["health"] != "healthy"
):
raise ValueError("Legacy runtime identity or health does not match")
mounts = observed["mounts"]
if not mounts or len(mounts) > 16:
raise ValueError("Expected bounded persistent mounts")
destinations = set()
sources = set()
for mount in mounts:
if set(mount) != {"type", "source", "destination", "rw"}:
raise ValueError("Unexpected mount fields")
destination = mount["destination"]
if not _under(destination, "/") or destination in destinations:
raise ValueError("Invalid or duplicate mount destination")
destinations.add(destination)
source = mount["source"]
if mount["type"] == "bind":
valid = _under(source, appdata)
elif mount["type"] == "volume":
valid = bool(re.fullmatch(r"/var/lib/docker/volumes/[0-9a-f]{64}/_data", source))
else:
valid = False
if not valid or mount["rw"] is not True:
raise ValueError("Unreviewed source, mount type, or access mode")
sources.add(source)
# Clone a parent only once; preserve nested bind destinations against the
# corresponding cloned subdirectory, not unrelated independent snapshots.
roots = sorted(source for source in sources if not any(
other != source and _under(source, other) for other in sources
))
candidate_root = f"/mnt/pool/appdata/gitea-deploy-runners/legacy-migrations/{slug}/{attempt}"
clones = {source: f"{candidate_root}/data/{index}" for index, source in enumerate(roots)}
candidate_mounts = []
for mount in mounts:
root = next(source for source in roots if mount["source"] == source or _under(mount["source"], source))
relative = PurePosixPath(mount["source"]).relative_to(root)
candidate_mounts.append({
"source": str(PurePosixPath(clones[root]) / relative),
"destination": mount["destination"],
"type": "bind", "rw": True,
})
return {
"schema": "legacy-migration-plan.v1", "executable": False,
"repository": f"jens/{slug}", "candidateRevision": revision,
"originalProvenance": "unknown-legacy", "originalRevision": None,
"originalContainerId": observed["containerId"], "originalImageId": observed["imageId"],
"originalName": expected_name, "retainedName": f"{expected_name}-legacy-{attempt}",
"candidateComposeProject": f"{slug}-managed",
"workspace": candidate_root,
"coldCopyRoots": [{"original": root, "candidate": clones[root]} for root in roots],
"candidateMounts": candidate_mounts,
"requiredGates": [
"exact-commit-and-tree-validated", "candidate-built-before-cutover",
"immutable-oci-provenance-verified", "project-and-global-locks-held",
"original-identity-revalidated", "legacy-stopped-before-data-copy",
"cold-copy-checksums-verified", "original-data-never-mounted-by-candidate",
"candidate-container-ids-recorded", "docker-and-http-health-verified",
"real-broker-receipt-verified", "broker-config-and-state-committed",
],
}