export_archive.py #1

  • //
  • guest/
  • russell_jackson/
  • sdp/
  • test/
  • containers/
  • export_archive.py
  • View
  • Commits
  • Open Download .zip Download (3 KB)
#!/usr/bin/env python3
"""Export an operator-identified deployment archive without changing its bytes.

Windows members are recorded as excluded, never extracted or executed. Only
regular files/directories inside the single sdp/ tree are accepted.
"""
import argparse
import hashlib
import io
import json
from pathlib import Path, PurePosixPath
import tarfile


def export(archive, destination, confirmed=False):
    if destination.exists():
        raise ValueError("Baseline destination must be new")
    data = archive.read_bytes()
    digest = hashlib.sha256(data).hexdigest()
    manifest, excluded, names, contents = [], [], set(), []
    with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as source:
        for member in source:
            path = PurePosixPath(member.name)
            if path.is_absolute() or ".." in path.parts or not path.parts or path.parts[0] != "sdp":
                raise ValueError("Unsafe archive path: " + member.name)
            if not (member.isfile() or member.isdir()):
                raise ValueError("Archive links/devices are unsupported: " + member.name)
            if member.isdir():
                continue
            relative = PurePosixPath(*path.parts[1:]).as_posix()
            if relative in names or relative in (".", ".export-manifest.json", ".baseline-info.json"):
                raise ValueError("Duplicate or reserved archive path: " + member.name)
            names.add(relative)
            if relative.startswith("Server/Windows/"):
                excluded.append(relative)
                continue
            contents.append((relative, source.extractfile(member).read(), member.mode))
    if not contents:
        raise ValueError("Archive contains no testable files")
    destination.mkdir(parents=True)
    for relative, content, mode in contents:
        target = destination / relative
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_bytes(content)
        target.chmod(0o755 if mode & 0o111 else 0o644)
        manifest.append({"path": relative, "source_kind": "deployment_archive", "action": "",
                         "archive_sha256": digest, "sha256": hashlib.sha256(content).hexdigest(), "symlink": None})
    manifest.sort(key=lambda row: row["path"])
    (destination / ".export-manifest.json").write_text(json.dumps(manifest, indent=2) + "\n")
    (destination / ".baseline-info.json").write_text(json.dumps({
        "source_kind": "deployment_archive", "archive": str(archive.resolve()), "archive_sha256": digest,
        "production_baseline_confirmed": confirmed,
        "production_equivalence": "Operator identified this exact archive; individual host drift is not measured" if confirmed else "Unconfirmed archive",
        "excluded_windows_files": sorted(excluded), "files": len(manifest)}, indent=2) + "\n")
    return manifest


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("archive", type=Path)
    parser.add_argument("destination", type=Path)
    parser.add_argument("--operator-confirmed", action="store_true", help="Use only after the operator identifies this exact deployed archive")
    args = parser.parse_args()
    try:
        rows = export(args.archive, args.destination, args.operator_confirmed)
    except (OSError, ValueError, tarfile.TarError) as error:
        parser.exit(1, str(error) + "\n")
    print("Exported %d deployment archive files" % len(rows))


if __name__ == "__main__":
    main()
# Change User Description Committed
#1 34004 Russell C. Jackson (Rusty) Improve SDP backup/recovery, Maintenance, logging and cloud operation.

Add owned checkpoint locks, recovery-aware retention, preserved database switches,
bounded cleanup and coordinated AWS/Azure snapshots. Fix service-user password
initialization, edge staging, standby promotion state parsing, proxy cache setup,
typed depot verification and historical-server configuration/error handling.
Remove unused OTP programs; keep Windows removed, server.locks cleanup and the
journal-count condition unchanged. Add no global shell strict modes.

Update operational documentation and add reproducible mkdirs.sh/mkdirs.cfg
container assurance. Validate Ubuntu 26.04 and UBI 8.10/9.8/10.2 across both Python
runtimes: 44 stages; 428 offline tests per runtime/OS, zero offline skips;
19 deployed-archive comparisons per combination. Three six-node topology runs
pass all 30 recovery/failure scenarios. P4D 2023.1 through 2026.1 on UBI 8/9
pass all 14 cells and 98 scenarios. Static, documentation and provenance checks
pass. Containers do not certify every fleet kernel, patch build or integration.

Tested source manifest SHA-256:
e1a23e17b8ced8c1148d9a250589977c6f6e98606bf2a81dc3ff5d7c909e3695
Deployed comparison archive SHA-256:
1bd23217356ee3808e040d39aea5be9519bda7c8f2b4924656a70aff8ad7293a