This commit is contained in:
Steve Pulec 2017-02-23 21:37:43 -05:00
commit f37bad0e00
260 changed files with 6363 additions and 3766 deletions

View file

@ -12,15 +12,15 @@ from ..exceptions import (
SWFTypeDeprecatedFault,
SWFValidationException,
)
from .activity_task import ActivityTask
from .activity_type import ActivityType
from .decision_task import DecisionTask
from .domain import Domain
from .generic_type import GenericType
from .history_event import HistoryEvent
from .timeout import Timeout
from .workflow_type import WorkflowType
from .workflow_execution import WorkflowExecution
from .activity_task import ActivityTask # flake8: noqa
from .activity_type import ActivityType # flake8: noqa
from .decision_task import DecisionTask # flake8: noqa
from .domain import Domain # flake8: noqa
from .generic_type import GenericType # flake8: noqa
from .history_event import HistoryEvent # flake8: noqa
from .timeout import Timeout # flake8: noqa
from .workflow_type import WorkflowType # flake8: noqa
from .workflow_execution import WorkflowExecution # flake8: noqa
KNOWN_SWF_TYPES = {
@ -30,6 +30,7 @@ KNOWN_SWF_TYPES = {
class SWFBackend(BaseBackend):
def __init__(self, region_name):
self.region_name = region_name
self.domains = []
@ -246,7 +247,8 @@ class SWFBackend(BaseBackend):
if decision_task.state != "STARTED":
if decision_task.state == "COMPLETED":
raise SWFUnknownResourceFault(
"decision task, scheduledEventId = {0}".format(decision_task.scheduled_event_id)
"decision task, scheduledEventId = {0}".format(
decision_task.scheduled_event_id)
)
else:
raise ValueError(
@ -300,7 +302,8 @@ class SWFBackend(BaseBackend):
count = 0
for _task_list, tasks in domain.activity_task_lists.items():
if _task_list == task_list:
pending = [t for t in tasks if t.state in ["SCHEDULED", "STARTED"]]
pending = [t for t in tasks if t.state in [
"SCHEDULED", "STARTED"]]
count += len(pending)
return count
@ -330,7 +333,8 @@ class SWFBackend(BaseBackend):
if activity_task.state != "STARTED":
if activity_task.state == "COMPLETED":
raise SWFUnknownResourceFault(
"activity, scheduledEventId = {0}".format(activity_task.scheduled_event_id)
"activity, scheduledEventId = {0}".format(
activity_task.scheduled_event_id)
)
else:
raise ValueError(
@ -354,15 +358,18 @@ class SWFBackend(BaseBackend):
self._process_timeouts()
activity_task = self._find_activity_task_from_token(task_token)
wfe = activity_task.workflow_execution
wfe.fail_activity_task(activity_task.task_token, reason=reason, details=details)
wfe.fail_activity_task(activity_task.task_token,
reason=reason, details=details)
def terminate_workflow_execution(self, domain_name, workflow_id, child_policy=None,
details=None, reason=None, run_id=None):
# process timeouts on all objects
self._process_timeouts()
domain = self._get_domain(domain_name)
wfe = domain.get_workflow_execution(workflow_id, run_id=run_id, raise_if_closed=True)
wfe.terminate(child_policy=child_policy, details=details, reason=reason)
wfe = domain.get_workflow_execution(
workflow_id, run_id=run_id, raise_if_closed=True)
wfe.terminate(child_policy=child_policy,
details=details, reason=reason)
def record_activity_task_heartbeat(self, task_token, details=None):
# process timeouts on all objects

View file

@ -9,6 +9,7 @@ from .timeout import Timeout
class ActivityTask(object):
def __init__(self, activity_id, activity_type, scheduled_event_id,
workflow_execution, timeouts, input=None):
self.activity_id = activity_id

View file

@ -2,6 +2,7 @@ from .generic_type import GenericType
class ActivityType(GenericType):
@property
def _configuration_keys(self):
return [

View file

@ -9,6 +9,7 @@ from .timeout import Timeout
class DecisionTask(object):
def __init__(self, workflow_execution, scheduled_event_id):
self.workflow_execution = workflow_execution
self.workflow_type = workflow_execution.workflow_type
@ -60,7 +61,8 @@ class DecisionTask(object):
if not self.started or not self.workflow_execution.open:
return None
# TODO: handle the "NONE" case
start_to_close_at = self.started_timestamp + int(self.start_to_close_timeout)
start_to_close_at = self.started_timestamp + \
int(self.start_to_close_timeout)
_timeout = Timeout(self, start_to_close_at, "START_TO_CLOSE")
if _timeout.reached:
return _timeout

View file

@ -8,6 +8,7 @@ from ..exceptions import (
class Domain(object):
def __init__(self, name, retention, description=None):
self.name = name
self.retention = retention

View file

@ -4,6 +4,7 @@ from moto.core.utils import camelcase_to_underscores
class GenericType(object):
def __init__(self, name, version, **kwargs):
self.name = name
self.version = version

View file

@ -28,10 +28,12 @@ SUPPORTED_HISTORY_EVENT_TYPES = (
class HistoryEvent(object):
def __init__(self, event_id, event_type, event_timestamp=None, **kwargs):
if event_type not in SUPPORTED_HISTORY_EVENT_TYPES:
raise NotImplementedError(
"HistoryEvent does not implement attributes for type '{0}'".format(event_type)
"HistoryEvent does not implement attributes for type '{0}'".format(
event_type)
)
self.event_id = event_id
self.event_type = event_type

View file

@ -2,6 +2,7 @@ from moto.core.utils import unix_time
class Timeout(object):
def __init__(self, obj, timestamp, kind):
self.obj = obj
self.timestamp = timestamp

View file

@ -64,9 +64,12 @@ class WorkflowExecution(object):
# NB: the order follows boto/SWF order of exceptions appearance (if no
# param is set, # SWF will raise DefaultUndefinedFault errors in the
# same order as the few lines that follow)
self._set_from_kwargs_or_workflow_type(kwargs, "execution_start_to_close_timeout")
self._set_from_kwargs_or_workflow_type(kwargs, "task_list", "task_list")
self._set_from_kwargs_or_workflow_type(kwargs, "task_start_to_close_timeout")
self._set_from_kwargs_or_workflow_type(
kwargs, "execution_start_to_close_timeout")
self._set_from_kwargs_or_workflow_type(
kwargs, "task_list", "task_list")
self._set_from_kwargs_or_workflow_type(
kwargs, "task_start_to_close_timeout")
self._set_from_kwargs_or_workflow_type(kwargs, "child_policy")
self.input = kwargs.get("input")
# counters
@ -368,13 +371,16 @@ class WorkflowExecution(object):
# check decision types mandatory attributes
# NB: the real SWF service seems to check attributes even for attributes list
# that are not in line with the decisionType, so we do the same
attrs_to_check = [d for d in dcs.keys() if d.endswith("DecisionAttributes")]
attrs_to_check = [
d for d in dcs.keys() if d.endswith("DecisionAttributes")]
if dcs["decisionType"] in self.KNOWN_DECISION_TYPES:
decision_type = dcs["decisionType"]
decision_attr = "{0}DecisionAttributes".format(decapitalize(decision_type))
decision_attr = "{0}DecisionAttributes".format(
decapitalize(decision_type))
attrs_to_check.append(decision_attr)
for attr in attrs_to_check:
problems += self._check_decision_attributes(attr, dcs.get(attr, {}), decision_number)
problems += self._check_decision_attributes(
attr, dcs.get(attr, {}), decision_number)
# check decision type is correct
if dcs["decisionType"] not in self.KNOWN_DECISION_TYPES:
problems.append({
@ -396,12 +402,14 @@ class WorkflowExecution(object):
# handle each decision separately, in order
for decision in decisions:
decision_type = decision["decisionType"]
attributes_key = "{0}DecisionAttributes".format(decapitalize(decision_type))
attributes_key = "{0}DecisionAttributes".format(
decapitalize(decision_type))
attributes = decision.get(attributes_key, {})
if decision_type == "CompleteWorkflowExecution":
self.complete(event_id, attributes.get("result"))
elif decision_type == "FailWorkflowExecution":
self.fail(event_id, attributes.get("details"), attributes.get("reason"))
self.fail(event_id, attributes.get(
"details"), attributes.get("reason"))
elif decision_type == "ScheduleActivityTask":
self.schedule_activity_task(event_id, attributes)
else:
@ -415,7 +423,8 @@ class WorkflowExecution(object):
# TODO: implement Decision type: SignalExternalWorkflowExecution
# TODO: implement Decision type: StartChildWorkflowExecution
# TODO: implement Decision type: StartTimer
raise NotImplementedError("Cannot handle decision: {0}".format(decision_type))
raise NotImplementedError(
"Cannot handle decision: {0}".format(decision_type))
# finally decrement counter if and only if everything went well
self.open_counts["openDecisionTasks"] -= 1
@ -447,7 +456,8 @@ class WorkflowExecution(object):
def fail_schedule_activity_task(_type, _cause):
# TODO: implement other possible failure mode: OPEN_ACTIVITIES_LIMIT_EXCEEDED
# NB: some failure modes are not implemented and probably won't be implemented in
# the future, such as ACTIVITY_CREATION_RATE_EXCEEDED or OPERATION_NOT_PERMITTED
# the future, such as ACTIVITY_CREATION_RATE_EXCEEDED or
# OPERATION_NOT_PERMITTED
self._add_event(
"ScheduleActivityTaskFailed",
activity_id=attributes["activityId"],
@ -591,13 +601,15 @@ class WorkflowExecution(object):
def first_timeout(self):
if not self.open or not self.start_timestamp:
return None
start_to_close_at = self.start_timestamp + int(self.execution_start_to_close_timeout)
start_to_close_at = self.start_timestamp + \
int(self.execution_start_to_close_timeout)
_timeout = Timeout(self, start_to_close_at, "START_TO_CLOSE")
if _timeout.reached:
return _timeout
def timeout(self, timeout):
# TODO: process child policy on child workflows here or in the triggering function
# TODO: process child policy on child workflows here or in the
# triggering function
self.execution_status = "CLOSED"
self.close_status = "TIMED_OUT"
self.timeout_type = timeout.kind

View file

@ -2,6 +2,7 @@ from .generic_type import GenericType
class WorkflowType(GenericType):
@property
def _configuration_keys(self):
return [