Resolve endpoint-local object dependencies
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
from collections.abc import Iterable
|
||||
|
||||
from .adapters import PlanAction
|
||||
|
||||
|
||||
def _action_identity(
|
||||
action: PlanAction,
|
||||
) -> tuple[str, str, str]:
|
||||
desired = action.desired
|
||||
if desired is None:
|
||||
raise RuntimeError(
|
||||
"Dependency-ordered plan action requires desired state"
|
||||
)
|
||||
|
||||
return (
|
||||
action.endpoint_id,
|
||||
desired.object_type,
|
||||
desired.logical_key,
|
||||
)
|
||||
|
||||
|
||||
def order_plan_actions(
|
||||
actions: Iterable[PlanAction],
|
||||
) -> tuple[PlanAction, ...]:
|
||||
action_list = list(actions)
|
||||
by_identity: dict[
|
||||
tuple[str, str, str],
|
||||
PlanAction,
|
||||
] = {}
|
||||
positions: dict[
|
||||
tuple[str, str, str],
|
||||
int,
|
||||
] = {}
|
||||
|
||||
for position, action in enumerate(action_list):
|
||||
identity = _action_identity(action)
|
||||
if identity in by_identity:
|
||||
raise RuntimeError(
|
||||
"Duplicate plan action identity: "
|
||||
f"{identity[0]} {identity[1]} {identity[2]!r}"
|
||||
)
|
||||
by_identity[identity] = action
|
||||
positions[identity] = position
|
||||
|
||||
dependencies: dict[
|
||||
tuple[str, str, str],
|
||||
set[tuple[str, str, str]],
|
||||
] = {
|
||||
identity: set()
|
||||
for identity in by_identity
|
||||
}
|
||||
|
||||
for identity, action in by_identity.items():
|
||||
desired = action.desired
|
||||
for reference in desired.references:
|
||||
if reference.logical_key is None:
|
||||
continue
|
||||
|
||||
dependency = (
|
||||
action.endpoint_id,
|
||||
reference.object_type,
|
||||
reference.logical_key,
|
||||
)
|
||||
if dependency in by_identity:
|
||||
dependencies[identity].add(dependency)
|
||||
|
||||
remaining = set(by_identity)
|
||||
ordered: list[PlanAction] = []
|
||||
|
||||
while remaining:
|
||||
ready = [
|
||||
identity
|
||||
for identity in remaining
|
||||
if not dependencies[identity].intersection(remaining)
|
||||
]
|
||||
|
||||
if not ready:
|
||||
cycle = ", ".join(
|
||||
f"{identity[1]} {identity[2]!r}"
|
||||
for identity in sorted(
|
||||
remaining,
|
||||
key=lambda item: positions[item],
|
||||
)
|
||||
)
|
||||
raise RuntimeError(
|
||||
"Plan action dependency cycle: "
|
||||
f"{cycle}"
|
||||
)
|
||||
|
||||
ready.sort(
|
||||
key=lambda identity: positions[identity]
|
||||
)
|
||||
|
||||
for identity in ready:
|
||||
ordered.append(by_identity[identity])
|
||||
remaining.remove(identity)
|
||||
|
||||
return tuple(ordered)
|
||||
Reference in New Issue
Block a user