mirror of
https://github.com/Such-Software/adaptive-multi-protocol-gateway.git
synced 2026-11-13 11:44:06 +01:00
Add supervisor stage deploy apply
This commit is contained in:
+149
-23
@@ -4,7 +4,11 @@ import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from ampg.apply import apply_state, apply_supervisor
|
||||
from ampg.cli import main
|
||||
from ampg.config import load_config
|
||||
from ampg.platforms import PlatformProvider
|
||||
from ampg.status import DaemonProbe
|
||||
|
||||
|
||||
def _source(root: Path) -> Path:
|
||||
@@ -47,35 +51,67 @@ def _init_i2p_config(root: Path) -> Path:
|
||||
return config_path
|
||||
|
||||
|
||||
def _write_and_approve_artifacts(config_path: Path) -> None:
|
||||
status, _, error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"apply",
|
||||
"--dry-run",
|
||||
"--write-artifacts",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
]
|
||||
)
|
||||
def _write_and_approve_artifacts(config_path: Path, *, platform: str | None = None) -> None:
|
||||
apply_args = [
|
||||
"--config",
|
||||
str(config_path),
|
||||
"apply",
|
||||
"--dry-run",
|
||||
"--write-artifacts",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
]
|
||||
approve_args = [
|
||||
"--config",
|
||||
str(config_path),
|
||||
"approvals",
|
||||
"approve",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
"--all",
|
||||
]
|
||||
if platform is not None:
|
||||
apply_args.extend(("--platform", platform))
|
||||
approve_args.extend(("--platform", platform))
|
||||
status, _, error = _run_cli(apply_args)
|
||||
if status != 0:
|
||||
raise AssertionError(error)
|
||||
status, _, error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"approvals",
|
||||
"approve",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
"--all",
|
||||
]
|
||||
)
|
||||
status, _, error = _run_cli(approve_args)
|
||||
if status != 0:
|
||||
raise AssertionError(error)
|
||||
|
||||
|
||||
def _missing_probe(adapter):
|
||||
return DaemonProbe(
|
||||
installed=False,
|
||||
running=False,
|
||||
executable_path=None,
|
||||
)
|
||||
|
||||
|
||||
def _linux_user_provider(root: Path) -> PlatformProvider:
|
||||
return PlatformProvider(
|
||||
name="linux-user",
|
||||
process_supervisor="user-process",
|
||||
can_manage_daemons=True,
|
||||
can_write_system_config=False,
|
||||
state_root=root / "supervisor-root",
|
||||
)
|
||||
|
||||
|
||||
def _run_state_apply(config_path: Path, root: Path) -> None:
|
||||
config = load_config(config_path)
|
||||
results = apply_state(
|
||||
config,
|
||||
dry_run=False,
|
||||
platform_provider=_linux_user_provider(root),
|
||||
daemon_probe=_missing_probe,
|
||||
)
|
||||
blocked = [result for result in results if result.status == "blocked"]
|
||||
if blocked:
|
||||
raise AssertionError(blocked[0].message)
|
||||
|
||||
|
||||
class DeployApplyTest(unittest.TestCase):
|
||||
def test_deploy_apply_state_blocks_unapproved_artifacts(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
@@ -212,6 +248,96 @@ class DeployApplyTest(unittest.TestCase):
|
||||
self.assertTrue(daemon_config_exists)
|
||||
self.assertTrue(loopback_config_exists)
|
||||
|
||||
def test_deploy_apply_supervisor_blocks_until_state_is_copied(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp).resolve()
|
||||
config_path = _init_i2p_config(root)
|
||||
_write_and_approve_artifacts(config_path, platform="linux-user")
|
||||
config = load_config(config_path)
|
||||
|
||||
results = apply_supervisor(
|
||||
config,
|
||||
dry_run=True,
|
||||
platform_provider=_linux_user_provider(root),
|
||||
daemon_probe=_missing_probe,
|
||||
)
|
||||
|
||||
self.assertTrue(any(result.status == "blocked" for result in results))
|
||||
self.assertTrue(
|
||||
any("deploy apply --stage state first" in result.message for result in results)
|
||||
)
|
||||
|
||||
def test_deploy_apply_supervisor_dry_run_after_state_apply(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp).resolve()
|
||||
config_path = _init_i2p_config(root)
|
||||
_write_and_approve_artifacts(config_path)
|
||||
|
||||
status, _, error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"deploy",
|
||||
"apply",
|
||||
"--stage",
|
||||
"state",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
"--yes",
|
||||
]
|
||||
)
|
||||
if status != 0:
|
||||
raise AssertionError(error)
|
||||
|
||||
status, output, error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"deploy",
|
||||
"apply",
|
||||
"--stage",
|
||||
"supervisor",
|
||||
"--dry-run",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(0, status, error)
|
||||
self.assertIn("AMPG_DEPLOY_SUPERVISOR", output)
|
||||
self.assertIn("status=planned", output)
|
||||
self.assertIn("AMPG_DEPLOY_APPLY_SUMMARY stage=supervisor mode=dry-run", output)
|
||||
|
||||
def test_deploy_apply_supervisor_live_writes_user_service_files(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp).resolve()
|
||||
config_path = _init_i2p_config(root)
|
||||
_write_and_approve_artifacts(config_path, platform="linux-user")
|
||||
_run_state_apply(config_path, root)
|
||||
config = load_config(config_path)
|
||||
|
||||
results = apply_supervisor(
|
||||
config,
|
||||
dry_run=False,
|
||||
platform_provider=_linux_user_provider(root),
|
||||
daemon_probe=_missing_probe,
|
||||
)
|
||||
daemon_run = (
|
||||
root / "supervisor-root/services/ampg-example-i2p-i2pd/i2pd.run"
|
||||
)
|
||||
loopback_run = (
|
||||
root
|
||||
/ "supervisor-root/services/ampg-example-i2p-nginx-loopback/nginx-loopback.run"
|
||||
)
|
||||
daemon_run_exists = daemon_run.exists()
|
||||
loopback_run_exists = loopback_run.exists()
|
||||
daemon_run_executable = bool(daemon_run.stat().st_mode & 0o111)
|
||||
|
||||
self.assertTrue(all(result.status == "written" for result in results))
|
||||
self.assertTrue(daemon_run_exists)
|
||||
self.assertTrue(loopback_run_exists)
|
||||
self.assertTrue(daemon_run_executable)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -137,6 +137,95 @@ class DeployPlanTest(unittest.TestCase):
|
||||
self.assertIn("AMPG_DEPLOY_STEP stage=build status=ready", output)
|
||||
self.assertIn("AMPG_DEPLOY_STEP stage=artifacts status=todo", output)
|
||||
|
||||
def test_deploy_plan_guides_staged_apply_after_approval(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp).resolve()
|
||||
config_path = root / "gateway.toml"
|
||||
init_status, _, init_error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"init",
|
||||
"site",
|
||||
"example",
|
||||
"--domain",
|
||||
"example.test",
|
||||
"--source",
|
||||
str(_source(root)),
|
||||
"--protocol",
|
||||
"i2p",
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, init_status, init_error)
|
||||
build_status, _, build_error = _run_cli(["--config", str(config_path), "build"])
|
||||
self.assertEqual(0, build_status, build_error)
|
||||
apply_status, _, apply_error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"apply",
|
||||
"--dry-run",
|
||||
"--write-artifacts",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, apply_status, apply_error)
|
||||
approve_status, _, approve_error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"approvals",
|
||||
"approve",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
"--all",
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, approve_status, approve_error)
|
||||
|
||||
status, output, error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"deploy",
|
||||
"plan",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, status, error)
|
||||
|
||||
state_status, _, state_error = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"deploy",
|
||||
"apply",
|
||||
"--stage",
|
||||
"state",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
"--yes",
|
||||
]
|
||||
)
|
||||
self.assertEqual(0, state_status, state_error)
|
||||
status_after_state, output_after_state, error_after_state = _run_cli(
|
||||
[
|
||||
"--config",
|
||||
str(config_path),
|
||||
"deploy",
|
||||
"plan",
|
||||
"--profile",
|
||||
"mobile-i2p",
|
||||
]
|
||||
)
|
||||
|
||||
self.assertIn("AMPG_DEPLOY_STEP stage=deploy-apply status=todo", output)
|
||||
self.assertIn("deploy apply --stage state --dry-run", output)
|
||||
self.assertEqual(0, status_after_state, error_after_state)
|
||||
self.assertIn("deploy apply --stage supervisor --dry-run", output_after_state)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user