Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions scripts/ci/test-deploy-recovery.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
"""Run actual workflow Bash against offline CLI/HTTP boundaries only."""
import hashlib
import json
from pathlib import Path
import re
Expand All @@ -19,6 +20,10 @@
PRE_N0 = (ROOT / "ops/rollback/pre-n0-compose.yaml").read_bytes()
STOP_LOGS = (ROOT / "ops/rollback/pre-n0-stop-local-logs.yaml").read_bytes()
CURRENT = (ROOT / "docker-compose.dstack-postgres.yaml").read_bytes()
# Ordinary (pre-N0) template from main before #309: the captured rollback compose with its digest pin
# reverted to the :dstack tag. The workflow accepts these bytes by raw hash.
PRE_N0_TEMPLATE = PRE_N0.replace(b"@" + DIGEST.encode(), b":dstack")
assert hashlib.sha256(PRE_N0_TEMPLATE).hexdigest() == "28302d6761d841b7cc5c11314aed2d377735e6bbdcaf85972a752e03bc9fdc93"
CANARIES = []


Expand Down Expand Up @@ -247,7 +252,7 @@ def run_deploy(self, compose=CURRENT, changes=None, *, fail_at="", artifact_erro
"compose": compose_path.read_bytes(), "summary": report, "logs": logs}

def test_recovery_current_pre_n0_and_reviewed_n0(self):
for compose in (CURRENT, N0):
for compose in (PRE_N0_TEMPLATE, N0):
with self.subTest(n0=compose == N0):
result = self.run_deploy(compose)
self.assertTrue(result["success"], "Authorized same-image recovery must work")
Expand All @@ -257,7 +262,7 @@ def test_recovery_current_pre_n0_and_reviewed_n0(self):
self.assertNotIn("1.20.7", result["logs"] + result["summary"])

def test_future_digest_gets_preflight_and_general_quota_restore(self):
result = self.run_deploy(changes={"deploy_image_digest": "sha256:" + "a" * 64,
result = self.run_deploy(PRE_N0_TEMPLATE, {"deploy_image_digest": "sha256:" + "a" * 64,
"image_version": "1.20.8", "rollback_image_version": "1.20.8", "rollback_image_revision": "a" * 40})
self.assertTrue(result["success"])
self.assertIn("sealed-preflight", result["events"])
Expand All @@ -274,9 +279,9 @@ def test_explicit_rollback_privacy_variants(self):
self.assertIn(mode, result["summary"])

def test_missing_authorization_never_reaches_any_mutation(self):
for compose, changes, mode in ((CURRENT, {}, ""), (N0, {}, ""),
for compose, changes, mode in ((PRE_N0_TEMPLATE, {}, ""), (N0, {}, ""),
(N0, {"rollback_pre_n0": "true", "rollback_privacy_mode": "keep-json-file"}, "keep-json-file"),
(CURRENT, {"deploy_image_digest": "sha256:" + "a" * 64, "image_version": "1.20.8",
(PRE_N0_TEMPLATE, {"deploy_image_digest": "sha256:" + "a" * 64, "image_version": "1.20.8",
"rollback_image_version": "1.20.8", "rollback_image_revision": "a" * 40}, "")):
result = self.run_deploy(compose, {**changes, "authorize_quota_values": "false"}, approved_privacy=mode)
self.assertFalse(result["success"])
Expand Down Expand Up @@ -327,7 +332,7 @@ def test_wrong_oci_labels_refuse_before_compose_or_cvm_mutation(self):
self.assertEqual(result["compose"], N0)

def test_normal_build_deployment_keeps_preflight_and_general_quota(self):
result = self.run_deploy(changes={"deploy_image_digest": "", "image_version": "1.20.7",
result = self.run_deploy(PRE_N0_TEMPLATE, {"deploy_image_digest": "", "image_version": "1.20.7",
"rollback_image_version": "", "rollback_image_revision": ""},
build_digest="sha256:" + "f" * 64)
self.assertTrue(result["success"])
Expand All @@ -343,6 +348,12 @@ def test_forward_n0_lock_and_captured_rollback_are_not_substitutable(self):
self.assertFalse(result["success"])
self.assertNotIn("cvm-deploy", result["events"])
self.assertEqual(result["compose"], N0)
result = self.run_deploy(N0, {"deploy_image_digest": "", "image_version": "1.20.7",
"rollback_image_version": "", "rollback_image_revision": ""},
build_digest="sha256:" + "f" * 64)
self.assertFalse(result["success"])
self.assertNotIn("cvm-deploy", result["events"])
self.assertEqual(result["compose"], N0)

def test_last_restart_precedes_health_inventory_and_quota(self):
result = self.run_deploy(N0, {"rollback_pre_n0": "true", "rollback_privacy_mode": "keep-json-file"}, approved_privacy="keep-json-file")
Expand Down
Loading