mirror of
https://github.com/Such-Software/adaptive-multi-protocol-gateway.git
synced 2026-10-17 15:41:41 +02:00
Add apply preflight gate
This commit is contained in:
+107
-1
@@ -6,7 +6,11 @@ import shlex
|
||||
|
||||
from .addresses import AddressRecord, address_registry_path, effective_address_records
|
||||
from .config import GatewayConfig, ProtocolConfig, SiteConfig
|
||||
from .install_plan import write_install_artifacts
|
||||
from .install_plan import (
|
||||
InstallStateCopy,
|
||||
InstallSupervisorAction,
|
||||
write_install_artifacts,
|
||||
)
|
||||
from .plan import plan_artifacts, write_plan_artifacts
|
||||
from .platforms import PlatformProvider
|
||||
from .status import (
|
||||
@@ -29,6 +33,30 @@ class ActivationStep:
|
||||
command: str = "-"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ApplyPreflightItem:
|
||||
phase: str
|
||||
site_id: str
|
||||
protocol: str
|
||||
status: str
|
||||
action: str
|
||||
target: str
|
||||
command: str
|
||||
message: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ApplyPreflight:
|
||||
status: str
|
||||
items: tuple[ApplyPreflightItem, ...]
|
||||
ready: int
|
||||
review: int
|
||||
planned: int
|
||||
blocked: int
|
||||
skipped: int
|
||||
message: str
|
||||
|
||||
|
||||
def activation_steps(
|
||||
config: GatewayConfig,
|
||||
*,
|
||||
@@ -93,6 +121,45 @@ def blocked_steps(steps: list[ActivationStep]) -> list[ActivationStep]:
|
||||
return [step for step in steps if step.status == "blocked"]
|
||||
|
||||
|
||||
def apply_preflight(
|
||||
*,
|
||||
activation: list[ActivationStep],
|
||||
state_copies: list[InstallStateCopy],
|
||||
supervisor_actions: list[InstallSupervisorAction],
|
||||
) -> ApplyPreflight:
|
||||
items = tuple(
|
||||
[
|
||||
*(_activation_preflight_item(step) for step in activation),
|
||||
*(_state_copy_preflight_item(copy) for copy in state_copies),
|
||||
*(_supervisor_preflight_item(action) for action in supervisor_actions),
|
||||
]
|
||||
)
|
||||
ready = sum(1 for item in items if item.status == "ready")
|
||||
review = sum(1 for item in items if item.status == "review")
|
||||
planned = sum(1 for item in items if item.status == "planned")
|
||||
blocked = sum(1 for item in items if item.status == "blocked")
|
||||
skipped = sum(1 for item in items if item.status == "skipped")
|
||||
if blocked:
|
||||
status = "blocked"
|
||||
message = "live apply is blocked until reported items are fixed"
|
||||
elif review:
|
||||
status = "review"
|
||||
message = "live apply requires operator review before mutation"
|
||||
else:
|
||||
status = "ready"
|
||||
message = "live apply preflight is clean"
|
||||
return ApplyPreflight(
|
||||
status=status,
|
||||
items=items,
|
||||
ready=ready,
|
||||
review=review,
|
||||
planned=planned,
|
||||
blocked=blocked,
|
||||
skipped=skipped,
|
||||
message=message,
|
||||
)
|
||||
|
||||
|
||||
def _protocol_steps(
|
||||
config: GatewayConfig,
|
||||
site: SiteConfig,
|
||||
@@ -258,6 +325,45 @@ def _transport_activation_status(status: TransportStatus) -> str:
|
||||
return "ready"
|
||||
|
||||
|
||||
def _activation_preflight_item(step: ActivationStep) -> ApplyPreflightItem:
|
||||
return ApplyPreflightItem(
|
||||
phase=f"activation:{step.stage}",
|
||||
site_id=step.site_id,
|
||||
protocol=step.protocol,
|
||||
status=step.status,
|
||||
action=step.action,
|
||||
target=step.target,
|
||||
command=step.command,
|
||||
message=step.message,
|
||||
)
|
||||
|
||||
|
||||
def _state_copy_preflight_item(copy: InstallStateCopy) -> ApplyPreflightItem:
|
||||
return ApplyPreflightItem(
|
||||
phase="state-copy",
|
||||
site_id=copy.site_id,
|
||||
protocol=copy.protocol,
|
||||
status=copy.status,
|
||||
action=copy.kind,
|
||||
target=str(copy.target),
|
||||
command=copy.command,
|
||||
message=copy.message,
|
||||
)
|
||||
|
||||
|
||||
def _supervisor_preflight_item(action: InstallSupervisorAction) -> ApplyPreflightItem:
|
||||
return ApplyPreflightItem(
|
||||
phase="supervisor",
|
||||
site_id=action.site_id,
|
||||
protocol=action.protocol,
|
||||
status=action.status,
|
||||
action=action.kind,
|
||||
target=action.service,
|
||||
command=action.command,
|
||||
message=action.message,
|
||||
)
|
||||
|
||||
|
||||
def write_activation_artifacts(
|
||||
config: GatewayConfig,
|
||||
*,
|
||||
|
||||
+51
-6
@@ -5,8 +5,11 @@ from pathlib import Path
|
||||
import sys
|
||||
|
||||
from .activation import (
|
||||
ApplyPreflight,
|
||||
ApplyPreflightItem,
|
||||
ActivationStep,
|
||||
activation_steps,
|
||||
apply_preflight,
|
||||
blocked_steps,
|
||||
write_activation_artifacts,
|
||||
)
|
||||
@@ -302,19 +305,33 @@ def _cmd_apply(config, args) -> int:
|
||||
return 1
|
||||
|
||||
platform_provider = _platform_override(config, args)
|
||||
if _write_artifacts_enabled(config, args):
|
||||
write_artifacts = _write_artifacts_enabled(config, args)
|
||||
if write_artifacts:
|
||||
for path in write_activation_artifacts(config, platform_provider=platform_provider):
|
||||
print(f"AMPG_APPLY_ARTIFACT path={path} status=written")
|
||||
for copy in install_state_copies(config, platform_provider=platform_provider):
|
||||
|
||||
state_copies = install_state_copies(config, platform_provider=platform_provider)
|
||||
supervisor_actions = install_supervisor_actions(config, platform_provider=platform_provider)
|
||||
if write_artifacts:
|
||||
for copy in state_copies:
|
||||
_print_install_state_copy(copy)
|
||||
for action in install_supervisor_actions(config, platform_provider=platform_provider):
|
||||
for action in supervisor_actions:
|
||||
_print_supervisor_action(action)
|
||||
|
||||
steps = activation_steps(config, platform_provider=platform_provider)
|
||||
for step in steps:
|
||||
_print_activation_step(step)
|
||||
|
||||
blocked = blocked_steps(steps)
|
||||
preflight = apply_preflight(
|
||||
activation=steps,
|
||||
state_copies=state_copies,
|
||||
supervisor_actions=supervisor_actions,
|
||||
)
|
||||
for item in preflight.items:
|
||||
if item.status in {"blocked", "review"}:
|
||||
_print_apply_preflight_item(item)
|
||||
_print_apply_preflight(preflight)
|
||||
|
||||
print(
|
||||
"AMPG_APPLY_SUMMARY "
|
||||
"mode=dry-run "
|
||||
@@ -323,9 +340,9 @@ def _cmd_apply(config, args) -> int:
|
||||
f"ready={sum(1 for step in steps if step.status == 'ready')} "
|
||||
f"review={sum(1 for step in steps if step.status == 'review')} "
|
||||
f"planned={sum(1 for step in steps if step.status == 'planned')} "
|
||||
f"blocked={len(blocked)}"
|
||||
f"blocked={len(blocked_steps(steps))}"
|
||||
)
|
||||
return 1 if blocked else 0
|
||||
return 1 if preflight.status == "blocked" else 0
|
||||
|
||||
|
||||
def _cmd_install_plan(config, args) -> int:
|
||||
@@ -562,6 +579,34 @@ def _print_supervisor_action(action: InstallSupervisorAction) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _print_apply_preflight(preflight: ApplyPreflight) -> None:
|
||||
print(
|
||||
"AMPG_APPLY_PREFLIGHT "
|
||||
f"status={preflight.status} "
|
||||
f"items={len(preflight.items)} "
|
||||
f"ready={preflight.ready} "
|
||||
f"review={preflight.review} "
|
||||
f"planned={preflight.planned} "
|
||||
f"skipped={preflight.skipped} "
|
||||
f"blocked={preflight.blocked} "
|
||||
f"message=\"{_quote(preflight.message)}\""
|
||||
)
|
||||
|
||||
|
||||
def _print_apply_preflight_item(item: ApplyPreflightItem) -> None:
|
||||
print(
|
||||
"AMPG_APPLY_PREFLIGHT_ITEM "
|
||||
f"phase={item.phase} "
|
||||
f"site={item.site_id} "
|
||||
f"protocol={item.protocol} "
|
||||
f"status={item.status} "
|
||||
f"action={item.action} "
|
||||
f"target=\"{_quote(item.target)}\" "
|
||||
f"command=\"{_quote(item.command)}\" "
|
||||
f"message=\"{_quote(item.message)}\""
|
||||
)
|
||||
|
||||
|
||||
def _print_activation_step(step: ActivationStep) -> None:
|
||||
print(
|
||||
"AMPG_APPLY_STEP "
|
||||
|
||||
Reference in New Issue
Block a user