Add certificate replacement lifecycle

This commit is contained in:
Lyncoln
2026-09-20 01:44:56 -04:00
parent 4b094820b2
commit 2668651a98
7 changed files with 582 additions and 21 deletions
+53
View File
@@ -13,6 +13,7 @@ from .adapters import (
ObjectReference,
PlanAction,
PlanOperation,
RetireRequest,
)
from .dependencies import order_plan_actions
from .persistence import SQLiteStateStore
@@ -26,6 +27,8 @@ class ExecutionAdapter(Protocol):
def verify(self, object: NormalizedObject) -> bool: ...
def retire(self, request: RetireRequest) -> None: ...
@dataclass(frozen=True)
class ExecutedAction:
@@ -271,6 +274,30 @@ def _validated_apply_result(
"desired references"
)
if (
action.operation != PlanOperation.REPLACE
and result.retirements
):
raise RuntimeError(
"Only REPLACE actions may request deferred retirement"
)
for retirement in result.retirements:
retired = retirement.object
if retired.local_key is None:
raise RuntimeError(
"Deferred retirement requires endpoint-local identity"
)
if (
retired.object_type == applied.object_type
and retired.local_key == applied.local_key
):
raise RuntimeError(
"Deferred retirement cannot target applied object"
)
return result
@@ -330,6 +357,7 @@ def _preflight(
if action.operation not in {
PlanOperation.CREATE,
PlanOperation.UPDATE,
PlanOperation.REPLACE,
}:
raise RuntimeError(
"Unsupported executable plan operation: "
@@ -376,6 +404,10 @@ def execute_endpoint_plans(
plans,
)
applied: list[ExecutedAction] = []
pending_retirements: list[
tuple[str, RetireRequest]
] = []
local_references: dict[
str,
_LocalReferenceIndex,
@@ -451,6 +483,14 @@ def execute_endpoint_plans(
authoritative=True,
)
for retirement in result.retirements:
pending_retirements.append(
(
action.endpoint_id,
retirement,
)
)
applied.append(
ExecutedAction(
endpoint_id=action.endpoint_id,
@@ -460,6 +500,19 @@ def execute_endpoint_plans(
)
)
for endpoint_id, retirement in pending_retirements:
adapter = adapters_by_endpoint[endpoint_id]
retire = getattr(adapter, "retire", None)
if retire is None:
raise RuntimeError(
f"{endpoint_id} adapter does not support "
"deferred retirement"
)
retire(retirement)
return ExecutionResult(
applied=tuple(applied),
skipped=tuple(skipped),