feat(2w): W0.5 WC3 snapshot/restore helper (warmsnap.py)
runner/harness/warmsnap.py: raw per-volume tar of an app's stack volumes while UNDEPLOYED, under /var/lib/ci-warm/<recipe>/ (meta.json + volumes/<vol>.tar); one last-good, atomic dir swap; restore clears+untars each volume back. Asserts undeployed (consistency). Reused by WC1.1 (pre-upgrade keycloak snapshot) + WC5. +5 unit tests (48 unit pass). LIVE round-trip PROVEN on warm keycloak: create marker realm -> undeploy -> snapshot (mariadb+providers vols) -> deploy -> delete marker (mutate DB) -> undeploy -> restore -> deploy -> marker realm BACK; keycloak healthy. WC3 core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
59
tests/unit/test_warmsnap.py
Normal file
59
tests/unit/test_warmsnap.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""Unit tests for the WC3 snapshot helper's pure path/meta logic (runner/harness/warmsnap.py).
|
||||
|
||||
The docker/tar snapshot+restore round-trip is integration (proven live on cc-ci against a real
|
||||
keycloak volume, W0.5). Here we cover the layout, meta read, and the has_snapshot completeness check.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
|
||||
from harness import warmsnap # noqa: E402
|
||||
|
||||
|
||||
def test_warm_root_env_override(monkeypatch):
|
||||
monkeypatch.setenv("CCCI_WARM_ROOT", "/tmp/ci-warm-test")
|
||||
assert warmsnap.warm_root() == "/tmp/ci-warm-test"
|
||||
assert warmsnap.app_dir("keycloak") == "/tmp/ci-warm-test/keycloak"
|
||||
assert warmsnap.meta_path("keycloak") == "/tmp/ci-warm-test/keycloak/meta.json"
|
||||
assert warmsnap.volumes_dir("keycloak") == "/tmp/ci-warm-test/keycloak/volumes"
|
||||
|
||||
|
||||
def test_warm_root_default(monkeypatch):
|
||||
monkeypatch.delenv("CCCI_WARM_ROOT", raising=False)
|
||||
assert warmsnap.warm_root() == "/var/lib/ci-warm"
|
||||
|
||||
|
||||
def test_read_meta_absent(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("CCCI_WARM_ROOT", str(tmp_path))
|
||||
assert warmsnap.read_meta("keycloak") is None
|
||||
assert warmsnap.has_snapshot("keycloak") is False
|
||||
|
||||
|
||||
def _write_snapshot(tmp_path, recipe, volumes):
|
||||
appdir = tmp_path / recipe
|
||||
(appdir / "volumes").mkdir(parents=True)
|
||||
(appdir / "meta.json").write_text(json.dumps({"recipe": recipe, "volumes": volumes}))
|
||||
for v in volumes:
|
||||
(appdir / "volumes" / f"{v}.tar").write_bytes(b"fake")
|
||||
|
||||
|
||||
def test_has_snapshot_complete(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("CCCI_WARM_ROOT", str(tmp_path))
|
||||
_write_snapshot(tmp_path, "keycloak", ["a_mariadb", "a_providers"])
|
||||
assert warmsnap.has_snapshot("keycloak") is True
|
||||
meta = warmsnap.read_meta("keycloak")
|
||||
assert meta["volumes"] == ["a_mariadb", "a_providers"]
|
||||
|
||||
|
||||
def test_has_snapshot_incomplete_missing_tar(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("CCCI_WARM_ROOT", str(tmp_path))
|
||||
# meta lists two volumes but only one tar present -> incomplete -> not usable.
|
||||
appdir = tmp_path / "keycloak"
|
||||
(appdir / "volumes").mkdir(parents=True)
|
||||
(appdir / "meta.json").write_text(json.dumps({"recipe": "keycloak", "volumes": ["a", "b"]}))
|
||||
(appdir / "volumes" / "a.tar").write_bytes(b"fake")
|
||||
assert warmsnap.has_snapshot("keycloak") is False
|
||||
Reference in New Issue
Block a user