cloud
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""Task dispatcher: composes TaskRunner / WorkflowRunner to execute assignments.
|
||||
|
||||
Capability: ``task-scheduler`` (closing the loop from ``assigned`` to ``executed``).
|
||||
|
||||
Composes the existing single-process execution entry points (``runtime.task.TaskRunner``,
|
||||
``workflow.runner.WorkflowRunner``) without editing them, strictly through their
|
||||
public ``run(task) -> Task`` / ``run(definition, device_id) -> WorkflowRun`` contracts.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from core.models import Task
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cloud.store import CloudStore
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Assignment:
|
||||
"""A scheduler-produced binding of a queued task to a specific device+host."""
|
||||
|
||||
task_id: str
|
||||
device_id: str
|
||||
host_id: str
|
||||
goal: str | None
|
||||
workflow_definition_id: str | None
|
||||
|
||||
|
||||
class RemoteDispatchNotSupportedError(RuntimeError):
|
||||
"""Raised when an assignment targets a host other than the local process."""
|
||||
|
||||
|
||||
class UnknownWorkflowDefinitionError(RuntimeError):
|
||||
"""Raised when a workflow-based assignment references an unknown definition id."""
|
||||
|
||||
|
||||
# Callable type aliases; kept lazy so importing this module never imports runtime/workflow.
|
||||
TaskRunnerFactory = Callable[[], "object"]
|
||||
WorkflowRunnerFactory = Callable[[], "object"]
|
||||
|
||||
|
||||
class TaskDispatcher:
|
||||
"""Executes assignments via the existing task / workflow runners.
|
||||
|
||||
Local-only in this change: an assignment whose ``host_id`` does not match
|
||||
the dispatcher's own ``local_host_id`` raises ``RemoteDispatchNotSupportedError``
|
||||
rather than attempting any execution over the network.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
local_host_id: str,
|
||||
task_runner_factory: TaskRunnerFactory,
|
||||
workflow_runner_factory: WorkflowRunnerFactory,
|
||||
store: "CloudStore",
|
||||
) -> None:
|
||||
self.local_host_id = local_host_id
|
||||
self.task_runner_factory = task_runner_factory
|
||||
self.workflow_runner_factory = workflow_runner_factory
|
||||
self.store = store
|
||||
|
||||
def dispatch(self, assignment: Assignment) -> None:
|
||||
if assignment.host_id != self.local_host_id:
|
||||
raise RemoteDispatchNotSupportedError(
|
||||
f"assignment {assignment.task_id} targets host "
|
||||
f"{assignment.host_id!r}, but this dispatcher owns {self.local_host_id!r}"
|
||||
)
|
||||
|
||||
if assignment.workflow_definition_id:
|
||||
status = self._dispatch_workflow(assignment)
|
||||
else:
|
||||
status = self._dispatch_goal(assignment)
|
||||
|
||||
self.store.update_task(assignment.task_id, status=status)
|
||||
|
||||
def _dispatch_goal(self, assignment: Assignment) -> str:
|
||||
task = Task(
|
||||
goal=assignment.goal or "",
|
||||
device_id=assignment.device_id,
|
||||
)
|
||||
result = self.task_runner_factory().run(task) # type: ignore[attr-defined]
|
||||
result_status = getattr(result, "status", None)
|
||||
return "done" if result_status == "completed" else "failed"
|
||||
|
||||
def _dispatch_workflow(self, assignment: Assignment) -> str:
|
||||
runner = self.workflow_runner_factory()
|
||||
store = getattr(runner, "store", None)
|
||||
if store is None:
|
||||
raise UnknownWorkflowDefinitionError(
|
||||
"workflow runner has no store to load definitions from"
|
||||
)
|
||||
definition = store.get_definition(assignment.workflow_definition_id)
|
||||
if definition is None:
|
||||
raise UnknownWorkflowDefinitionError(
|
||||
f"unknown workflow definition {assignment.workflow_definition_id!r}"
|
||||
)
|
||||
run = runner.run(definition, device_id=assignment.device_id) # type: ignore[attr-defined]
|
||||
run_status = getattr(run, "status", None)
|
||||
return "done" if run_status == "completed" else "failed"
|
||||
Reference in New Issue
Block a user