Example #1
0
    def poll(self, task_list=None, identity=None, **kwargs):
        """
        Polls a decision task and returns the token and the full history of the
        workflow's events.

        :param task_list: task list to poll for decision tasks from.
        :type task_list: str

        :param identity: Identity of the decider making the request,
        which is recorded in the DecisionTaskStarted event in the
        workflow history.
        :type identity: str

        :returns: a Response object with history, token, and execution set
        :rtype: swf.responses.Response

        """
        logging_context.reset()
        task_list = task_list or self.task_list

        task = self.connection.poll_for_decision_task(
            self.domain.name,
            task_list=task_list,
            identity=format.identity(identity),
            **kwargs)
        token = task.get('taskToken')
        if token is None:
            raise PollTimeout("Decider poll timed out")

        events = task['events']
        logging_context.set("workflow_id",
                            task["workflowExecution"]["workflowId"])
        logging_context.set("task_type", "decision")
        logging_context.set("event_id", task["startedEventId"])

        next_page = task.get('nextPageToken')
        while next_page:
            try:
                task = self.connection.poll_for_decision_task(
                    self.domain.name,
                    task_list=task_list,
                    identity=format.identity(identity),
                    next_page_token=next_page,
                    **kwargs)
            except boto.exception.SWFResponseError as e:
                message = self.get_error_message(e)
                if e.error_code == 'UnknownResourceFault':
                    raise DoesNotExistError(
                        "Unable to poll decision task",
                        message,
                    )

                raise ResponseError(message)

            token = task.get('taskToken')
            if token is None:
                raise PollTimeout("Decider poll timed out")

            events.extend(task['events'])
            next_page = task.get('nextPageToken')

        history = History.from_event_list(events)

        workflow_type = WorkflowType(
            domain=self.domain,
            name=task['workflowType']['name'],
            version=task['workflowType']['version'],
        )
        execution = WorkflowExecution(
            domain=self.domain,
            workflow_id=task['workflowExecution']['workflowId'],
            run_id=task['workflowExecution']['runId'],
            workflow_type=workflow_type,
        )

        # TODO: move history into execution (needs refactoring on WorkflowExecution.history())
        return Response(token=token, history=history, execution=execution)
Example #2
0
    def get(self, name, version, *args, **kwargs):
        """Fetches the Workflow Type with `name` and `version`

        :param  name: name of the workflow type
        :type   name: String

        :param  version: workflow type version
        :type   version: String

        :returns: matched workflow type instance
        :rtype: swf.core.model.workflow.WorkflowType

        A typical Amazon response looks like:

        .. code-block:: json

            {
                "configuration": {
                    "defaultExecutionStartToCloseTimeout": "300",
                    "defaultTaskStartToCloseTimeout": "300",
                    "defaultTaskList": {
                        "name": "None"
                    },
                    "defaultChildPolicy": "TERMINATE"
                },
                "typeInfo": {
                    "status": "REGISTERED",
                    "creationDate": 1364492094.968,
                    "workflowType": {
                        "version": "1",
                        "name": "testW"
                    }
                }
            }
        """
        try:
            response = self.connection.describe_workflow_type(
                self.domain.name, name, version)
        except SWFResponseError as e:
            if e.error_code == 'UnknownResourceFault':
                raise DoesNotExistError(e.body['message'])

            raise ResponseError(e.body['message'])

        wt_info = response[self._infos]
        wt_config = response['configuration']

        task_list = kwargs.get('task_list')
        if task_list is None:
            task_list = get_subkey(wt_config, ['defaultTaskList', 'name'])

        child_policy = kwargs.get('child_policy')
        if child_policy is None:
            child_policy = wt_config.get('defaultChildPolicy')

        decision_task_timeout = kwargs.get('decision_task_timeout')
        if decision_task_timeout is None:
            decision_task_timeout = wt_config.get(
                'defaultTaskStartToCloseTimeout')

        execution_timeout = kwargs.get('execution_timeout')
        if execution_timeout is None:
            execution_timeout = wt_config.get(
                'defaultExecutionStartToCloseTimeout')

        decision_tasks_timeout = kwargs.get('decision_tasks_timeout')
        if decision_tasks_timeout is None:
            decision_tasks_timeout = wt_config.get(
                'defaultTaskStartToCloseTimeout')

        return self.to_WorkflowType(
            self.domain,
            wt_info,
            task_list=task_list,
            child_policy=child_policy,
            execution_timeout=execution_timeout,
            decision_tasks_timeout=decision_tasks_timeout,
        )
Example #3
0
class ActivityType(BaseModel):
    """ActivityType wrapper

    :param  domain: Domain the workflow type should be registered in
    :type   domain: swf.models.Domain

    :param  name: name of the ActivityType
    :type   name: str

    :param  version: version of the ActivityType
    :type   version: str

    :param  status: ActivityType status
    :type   status: swf.constants.{REGISTERED, DEPRECATED}

    :param  description: ActivityType description
    :type   description: str | None

    :param   creation_date: creation date of the current ActivityType
    :type    creation_date: float (timestamp)

    :param   deprecation_date: deprecation date of ActivityType
    :type    deprecation_date: float (timestamp)

    :param  task_list: specifies the default task list to use for scheduling
                       tasks of this activity type.
    :type   task_list: str

    :param  task_heartbeat_timeout: default maximum time before which a worker
                                    processing a task of this type must report
                                    progress by calling RecordActivityTaskHeartbeat.
    :type   task_heartbeat_timeout: int

    :param  task_schedule_to_close_timeout: default maximum duration for a task
                                            of this activity type.
    :type   task_schedule_to_close_timeout: int

    :param  task_schedule_to_start_timeout: default maximum duration that a
                                            task of this activity type can wait
                                            before being assigned to a worker.
    :type   task_schedule_to_start_timeout: int

    :param   task_start_to_close_timeout: default maximum duration that a
                                          worker can take to process tasks of
                                          this activity type.
    :type    task_start_to_close_timeout: int
    """
    kind = 'type'

    __slots__ = [
        'domain',
        'name',
        'version',
        'status',
        'description',
        'creation_date',
        'deprecation_date',
        'task_list',
        'task_heartbeat_timeout',
        'task_schedule_to_close_timeout',
        'task_schedule_to_start_timeout',
        'task_start_to_close_timeout',
    ]

    def __init__(self, domain, name, version,
                 status=REGISTERED,
                 description=None,
                 creation_date=0.0,
                 deprecation_date=0.0,
                 task_list=None,
                 task_heartbeat_timeout=0,
                 task_schedule_to_close_timeout=0,
                 task_schedule_to_start_timeout=0,
                 task_start_to_close_timeout=0,
                 *args, **kwargs):

        self.domain = domain
        self.name = name
        self.version = version
        self.status = status
        self.description = description

        self.creation_date = creation_date
        self.deprecation_date = deprecation_date

        self.task_list = task_list
        self.task_heartbeat_timeout = task_heartbeat_timeout
        self.task_schedule_to_close_timeout = task_schedule_to_close_timeout
        self.task_schedule_to_start_timeout = task_schedule_to_start_timeout
        self.task_start_to_close_timeout = task_start_to_close_timeout

        # immutable decorator rebinds class name,
        # so have to use generice self.__class__
        super(self.__class__, self).__init__(*args, **kwargs)

    def _diff(self):
        """Checks for differences between ActivityType instance
        and upstream version

        :returns: A list of swf.models.base.ModelDiff namedtuple describing
                  differences
        :rtype: list
        """
        try:
            description = self.connection.describe_activity_type(
                self.name,
                self.name,
                self.version
            )
        except SWFResponseError as err:
            if err.error_code == 'UnknownResourceFault':
                raise DoesNotExistError("Remote ActivityType does not exist")

            raise ResponseError(err.body['message'])

        info = description['typeInfo']
        config = description['configuration']

        return ModelDiff(
            ('name', self.name, info['activityType']['name']),
            ('version', self.version, info['activityType']['version']),
            ('status', self.status, info['status']),
            ('description', self.description, info['description']),
            ('creation_date', self.creation_date, info['creationDate']),
            ('deprecation_date', self.deprecation_date, info['deprecationDate']),
            ('task_list', self.task_list, config['defaultTaskList']['name']),
            ('task_heartbeat_timeout', self.task_heartbeat_timeout, config['defaultTaskHeartbeatTimeout']),
            ('task_schedule_to_close_timeout', self.task_schedule_to_close_timeout, config['defaultTaskScheduleToCloseTimeout']),
            ('task_schedule_to_start_timeout', self.task_schedule_to_start_timeout, config['defaultTaskScheduleToStartTimeout']),
            ('task_start_to_close_timeout', self.task_start_to_close_timeout, config['defaultTaskStartToCloseTimeout']),
        )

    @property
    @exceptions.is_not(ActivityTypeDoesNotExist)
    @exceptions.when(SWFResponseError,
                     raises(ActivityTypeDoesNotExist,
                            when=exceptions.is_unknown('ActivityType'),
                            extract=exceptions.extract_resource))
    def exists(self):
        """Checks if the ActivityType exists amazon-side

        :rtype: bool
        """
        self.connection.describe_activity_type(
            self.domain.name,
            self.name,
            self.version
        )
        return True

    def save(self):
        """Creates the activity type amazon side"""
        try:
            self.connection.register_activity_type(
                self.domain.name,
                self.name,
                self.version,
                task_list=str(self.task_list),
                default_task_heartbeat_timeout=str(self.task_heartbeat_timeout),
                default_task_schedule_to_close_timeout=str(self.task_schedule_to_close_timeout),
                default_task_schedule_to_start_timeout=str(self.task_schedule_to_start_timeout),
                default_task_start_to_close_timeout=str(self.task_start_to_close_timeout),
                description=self.description)
        except SWFTypeAlreadyExistsError, err:
            raise AlreadyExistsError('{} already exists'.format(self))
        except SWFResponseError as err:
            if err.error_code in ['UnknownResourceFault', 'TypeDeprecatedFault']:
                raise DoesNotExistError(err.body['message'])
            raise
Example #4
0
    def poll(self, task_list=None,
             identity=None,
             **kwargs):
        """
        Polls a decision task and returns the token and the full history of the
        workflow's events.

        :param task_list: task list to poll for decision tasks from.
        :type task_list: string

        :param identity: Identity of the decider making the request,
        which is recorded in the DecisionTaskStarted event in the
        workflow history.
        :type identity: string

        :returns: (token, history)
        :type: swf.models.History

        """
        task_list = task_list or self.task_list

        task = self.connection.poll_for_decision_task(
            self.domain.name,
            task_list=task_list,
            identity=identity,
            **kwargs
        )
        token = task.get('taskToken')
        if token is None:
            raise PollTimeout("Decider poll timed out")

        events = task['events']

        next_page = task.get('nextPageToken')
        while next_page:
            try:
                task = self.connection.poll_for_decision_task(
                    self.domain.name,
                    task_list=task_list,
                    identity=identity,
                    next_page_token=next_page,
                    **kwargs
                )
            except boto.exception.SWFResponseError as e:
                if e.error_code == 'UnknownResourceFault':
                    raise DoesNotExistError(
                        "Unable to poll decision task.\n",
                        e.body['message'],
                    )

                raise ResponseError(e.body['message'])

            token = task.get('taskToken')
            if token is None:
                raise PollTimeout("Decider poll timed out")

            events.extend(task['events'])
            next_page = task.get('nextPageToken')

        history = History.from_event_list(events)

        return token, history
Example #5
0
    def get(self, name, version, *args, **kwargs):
        """Fetches the activity type with provided ``name`` and ``version``

        :param      name: activity type name to fetch
        :type       name: String

        :param      version: activity version to fetch
        :type       version: String

        :returns: Matched activity type instance
        :rtype: swf.models.activity.ActivityType

        A typical Amazon response looks like:

        .. code-block:: json

            {
                "configuration": {
                    "defaultTaskHeartbeatTimeout": "string",
                    "defaultTaskList": {
                        "name": "string"
                    },
                    "defaultTaskScheduleToCloseTimeout": "string",
                    "defaultTaskScheduleToStartTimeout": "string",
                    "defaultTaskStartToCloseTimeout": "string"
                },
                "typeInfo": {
                    "activityType": {
                        "name": "string",
                        "version": "string"
                    },
                    "creationDate": "number",
                    "deprecationDate": "number",
                    "description": "string",
                    "status": "string"
                }
            }
        """
        try:
            response = self.connection.describe_activity_type(
                self.domain.name, name, version
            )
        except SWFResponseError as e:
            if e.error_code == "UnknownResourceFault":
                raise DoesNotExistError(e.error_message)

            raise ResponseError(e.error_message)

        activity_info = response[self._infos]
        activity_config = response["configuration"]

        task_list = kwargs.get("task_list")
        if task_list is None:
            task_list = get_subkey(activity_config, ["defaultTaskList", "name"])

        task_heartbeat_timeout = kwargs.get("task_heartbeat_timeout")
        if task_heartbeat_timeout is None:
            task_heartbeat_timeout = activity_config.get("defaultTaskHeartbeatTimeout")

        task_schedule_to_close_timeout = kwargs.get("task_schedule_to_close_timeout")
        if task_schedule_to_close_timeout is None:
            task_schedule_to_close_timeout = activity_config.get(
                "defaultTaskScheduleToCloseTimeout"
            )

        task_schedule_to_start_timeout = kwargs.get("task_schedule_to_start_timeout")
        if task_schedule_to_start_timeout is None:
            task_schedule_to_start_timeout = activity_config.get(
                "defaultTaskScheduleToStartTimeout"
            )

        task_start_to_close_timeout = kwargs.get("task_start_to_close_timeout")
        if task_start_to_close_timeout is None:
            task_start_to_close_timeout = activity_config.get(
                "defaultTaskStartToCloseTimeout"
            )

        return self.to_ActivityType(
            self.domain,
            activity_info,
            task_list=task_list,
            task_heartbeat_timeout=task_heartbeat_timeout,
            task_schedule_to_close_timeout=task_schedule_to_close_timeout,
            task_schedule_to_start_timeout=task_schedule_to_start_timeout,
            task_start_to_close_timeout=task_start_to_close_timeout,
        )
Example #6
0
    def poll(self, task_list=None, identity=None):
        """Polls for an activity task to process from current
        actor's instance defined ``task_list``

        if no activity task was polled, raises a PollTimeout
        exception.

        :param  task_list: task list the Actor should watch for tasks on
        :type   task_list: string

        :param  identity: Identity of the worker making the request,
                          which is recorded in the ActivityTaskStarted
                          event in the workflow history. This enables
                          diagnostic tracing when problems arise.
                          The form of this identity is user defined.
        :type   identity: string

        :raises: PollTimeout

        :returns: task token, polled activity task
        :rtype: (str, ActivityTask)
        """
        logging_context.reset()
        task_list = task_list or self.task_list
        identity = identity or self._identity

        try:
            task = self.connection.poll_for_activity_task(
                self.domain.name,
                task_list,
                identity=format.identity(identity),
            )
        except boto.exception.SWFResponseError as e:
            message = self.get_error_message(e)
            if e.error_code == 'UnknownResourceFault':
                raise DoesNotExistError(
                    "Unable to poll activity task",
                    message,
                )

            raise ResponseError(message)

        if 'taskToken' not in task:
            raise PollTimeout("Activity Worker poll timed out")

        logging_context.set("workflow_id",
                            task["workflowExecution"]["workflowId"])
        logging_context.set("task_type", "activity")
        logging_context.set("event_id", task["startedEventId"])
        logging_context.set("activity_id", task["activityId"])

        activity_task = ActivityTask.from_poll(
            self.domain,
            self.task_list,
            task,
        )

        return Response(
            task_token=activity_task.task_token,
            activity_task=activity_task,
            raw_response=task,
        )