Exemplo n.º 1
0
 async def test():
     tmpl = TEMPLATES['task_timeout']
     wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
     wflow.run({'initial': 'data'})
     # The workflow is OK
     await wflow
     self.assertEqual(FutureState.get(wflow), FutureState.finished)
     # The task has timed out
     task = wflow._tasks_by_id.get('1')
     with self.assertRaises(asyncio.CancelledError):
         task.exception()
     self.assertEqual(FutureState.get(task), FutureState.timeout)
Exemplo n.º 2
0
 async def test():
     tmpl = TEMPLATES['task_timeout']
     wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
     wflow.run({'initial': 'data'})
     # The workflow is OK
     await wflow
     self.assertEqual(FutureState.get(wflow), FutureState.finished)
     # The task has timed out
     task = wflow._tasks_by_id.get('1')
     with self.assertRaises(asyncio.CancelledError):
         task.exception()
     self.assertEqual(FutureState.get(task), FutureState.timeout)
Exemplo n.º 3
0
 async def test():
     tmpl = TEMPLATES['ok']
     wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
     wflow.run({'initial': 'data'})
     await wflow
     # These tasks have finished
     for tid in tmpl['graph'].keys():
         task = wflow._tasks_by_id.get(tid)
         self.assertTrue(task.done())
         self.assertEqual(FutureState.get(task), FutureState.finished)
     # The workflow finished properly
     self.assertTrue(wflow.done())
     self.assertEqual(FutureState.get(wflow), FutureState.finished)
Exemplo n.º 4
0
 async def test():
     tmpl = TEMPLATES['ok']
     wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
     wflow.run({'initial': 'data'})
     await wflow
     # These tasks have finished
     for tid in tmpl['graph'].keys():
         task = wflow._tasks_by_id.get(tid)
         self.assertTrue(task.done())
         self.assertEqual(FutureState.get(task), FutureState.finished)
     # The workflow finished properly
     self.assertTrue(wflow.done())
     self.assertEqual(FutureState.get(wflow), FutureState.finished)
Exemplo n.º 5
0
 async def test():
     tmpl = TEMPLATES['workflow_cancel']
     wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
     wflow.run({'initial': 'data'})
     # Workflow is cancelled
     with self.assertRaises(asyncio.CancelledError):
         await wflow
     self.assertEqual(FutureState.get(wflow), FutureState.cancelled)
     # This task was cancelled
     task = wflow._tasks_by_id.get('cancel')
     with self.assertRaises(asyncio.CancelledError):
         task.exception()
     self.assertEqual(FutureState.get(task), FutureState.cancelled)
     # These tasks were never started
     for tid in ('2', '3', '4'):
         task = wflow._tasks_by_id.get(tid)
         self.assertIs(task, None)
Exemplo n.º 6
0
 async def test():
     tmpl = TEMPLATES['workflow_cancel']
     wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
     wflow.run({'initial': 'data'})
     # Workflow is cancelled
     with self.assertRaises(asyncio.CancelledError):
         await wflow
     self.assertEqual(FutureState.get(wflow), FutureState.cancelled)
     # This task was cancelled
     task = wflow._tasks_by_id.get('cancel')
     with self.assertRaises(asyncio.CancelledError):
         task.exception()
     self.assertEqual(FutureState.get(task), FutureState.cancelled)
     # These tasks were never started
     for tid in ('2', '3', '4'):
         task = wflow._tasks_by_id.get(tid)
         self.assertIs(task, None)
Exemplo n.º 7
0
        async def test():
            tmpl = TEMPLATES['crash_test']
            # Test crash at task __init__
            wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
            wflow.run({'initial': 'data'})
            await wflow
            # These tasks have finished
            for tid in ('1', '2'):
                task = wflow._tasks_by_id.get(tid)
                self.assertTrue(task.done())
                self.assertEqual(FutureState.get(task), FutureState.finished)
            # These tasks were never started
            for tid in ('crash', 'wont_run'):
                task = wflow._tasks_by_id.get(tid)
                self.assertIs(task, None)
            # The workflow finished properly
            self.assertTrue(wflow.done())
            self.assertEqual(FutureState.get(wflow), FutureState.finished)

            # Test crash inside a task
            tmpl['tasks'][0]['config'] = {'init_ok': None}
            wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
            wflow.run({'initial': 'data'})
            await wflow
            # These tasks have finished
            for tid in ('1', '2'):
                task = wflow._tasks_by_id.get(tid)
                self.assertTrue(task.done())
                self.assertEqual(FutureState.get(task), FutureState.finished)
            # This task crashed during execution
            task = wflow._tasks_by_id.get('crash')
            self.assertTrue(task.done())
            self.assertEqual(FutureState.get(task), FutureState.exception)
            # This task was never started
            task = wflow._tasks_by_id.get('wont_run')
            self.assertIs(task, None)
            # The workflow finished properly
            self.assertTrue(wflow.done())
            self.assertEqual(FutureState.get(wflow), FutureState.finished)
Exemplo n.º 8
0
 async def run_once(self, template, data):
     """
     Starts a new execution of the workflow template regardless of the
     overrun policy and already running workflows.
     Note: it does NOT load the workflow template in the engine.
     """
     if self._must_stop:
         log.debug("The engine is stopping, cannot run a new workflow from"
                   "template id %s", template.uid)
         return None
     with await self._lock:
         wflow = Workflow(template, loop=self._loop)
         self._do_run(wflow, Event(data))
     return wflow
Exemplo n.º 9
0
        async def test():
            tmpl = TEMPLATES['crash_test']
            # Test crash at task __init__
            wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
            wflow.run({'initial': 'data'})
            await wflow
            # These tasks have finished
            for tid in ('1', '2'):
                task = wflow._tasks_by_id.get(tid)
                self.assertTrue(task.done())
                self.assertEqual(FutureState.get(task), FutureState.finished)
            # These tasks were never started
            for tid in ('crash', 'wont_run'):
                task = wflow._tasks_by_id.get(tid)
                self.assertIs(task, None)
            # The workflow finished properly
            self.assertTrue(wflow.done())
            self.assertEqual(FutureState.get(wflow), FutureState.finished)

            # Test crash inside a task
            tmpl['tasks'][0]['config'] = {'init_ok': None}
            wflow = Workflow(WorkflowTemplate.from_dict(tmpl))
            wflow.run({'initial': 'data'})
            await wflow
            # These tasks have finished
            for tid in ('1', '2'):
                task = wflow._tasks_by_id.get(tid)
                self.assertTrue(task.done())
                self.assertEqual(FutureState.get(task), FutureState.finished)
            # This task crashed during execution
            task = wflow._tasks_by_id.get('crash')
            self.assertTrue(task.done())
            self.assertEqual(FutureState.get(task), FutureState.exception)
            # This task was never started
            task = wflow._tasks_by_id.get('wont_run')
            self.assertIs(task, None)
            # The workflow finished properly
            self.assertTrue(wflow.done())
            self.assertEqual(FutureState.get(wflow), FutureState.finished)
Exemplo n.º 10
0
    async def rescue(self, template, report):
        """
        Attaches a dangling workflow instance using its last known report.
        """
        if self._must_stop:
            log.debug("The engine is stopping, cannot rescue the workflow from"
                      "its report (execution id %s)", report['exec']['id'])
            return None

        with await self._lock:
            wflow = Workflow(template, loop=self._loop)
            self._add_wflow(wflow)
            wflow.add_done_callback(self._remove_wflow)
            wflow.fast_forward(report)
        return wflow
Exemplo n.º 11
0
    async def execute(self, event):
        """
        Entrypoint execution method.
        """
        data = event.data
        self._task = asyncio.Task.current_task()

        # Send the HTTP request
        log.info('Request %s to process template %s', self.nyuki_api,
                 self.template)
        log.debug('Request details: url=%s, draft=%s, data=%s', self.url,
                  self.draft, data)

        # Setup headers (set requester and exec-track to avoid workflow loops)
        workflow = Workflow.current_workflow()
        wf_instance = runtime.workflows[workflow.uid]
        parent = wf_instance.exec.get('requester')
        track = list(wf_instance.exec.get('track', []))
        if parent:
            track.append(parent)

        headers = {
            'Content-Type': 'application/json',
            'Referer': URI.instance(workflow),
            'X-Surycat-Exec-Track': ','.join(track)
        }

        # Handle blocking trigger_workflow using mqtt
        if self.blocking:
            topic = '{}/async/{}'.format(runtime.bus.name, str(uuid4())[:8])
            headers['X-Surycat-Async-Topic'] = topic
            self.async_future = asyncio.Future()
            await runtime.bus.subscribe(topic, self.async_exec)

            def _unsub(f):
                asyncio.ensure_future(runtime.bus.unsubscribe(topic))

            self._task.add_done_callback(_unsub)

        async with ClientSession() as session:
            params = {
                'url':
                self.url,
                'headers':
                headers,
                'data':
                json.dumps({
                    'id': self.template,
                    'draft': self.draft,
                    'inputs': data
                })
            }
            async with session.put(**params) as response:
                # Response validity
                if response.status != 200:
                    msg = "Can't process workflow template {} on {}".format(
                        self.template, self.nyuki_api)
                    if response.status % 400 < 100:
                        reason = await response.json()
                        msg = "{}, reason: {}".format(msg, reason['error'])
                    raise RuntimeError(msg)
                resp_body = await response.json()

        instance = resp_body['exec']['id']
        self._triggered['workflow_exec_id'] = instance
        log.debug('Request sent successfully to {}', self.nyuki_api)

        if not self.blocking:
            self._triggered['status'] = Status.DONE.value
            self._task.dispatch_progress(self.report())

        # Block until task completed
        if self.blocking:
            self._triggered['status'] = Status.RUNNING.value
            self._task.dispatch_progress(self.report())
            log.info('Waiting for %s@%s to complete', instance, self.nyuki_api)
            try:
                await asyncio.wait_for(self.async_future, self.timeout)
                self._triggered['status'] = Status.DONE.value
                log.info('Instance %s@%s is done', instance, self.nyuki_api)
            except asyncio.TimeoutError:
                self._triggered['status'] = Status.TIMEOUT.value
                log.info('Instance %s@%s has timeouted', instance,
                         self.nyuki_api)
            self._task.dispatch_progress({'status': self._triggered['status']})

        return data
Exemplo n.º 12
0
    async def execute(self, event):
        """
        Entrypoint execution method.
        """
        data = event.data
        self._task = asyncio.Task.current_task()

        # Send the HTTP request
        log.info('Request %s to process template %s', self.nyuki_api, self.template)
        log.debug(
            'Request details: url=%s, draft=%s, data=%s',
            self.url, self.draft, data
        )

        # Setup headers (set requester and exec-track to avoid workflow loops)
        workflow = Workflow.current_workflow()
        wf_instance = runtime.workflows[workflow.uid]
        parent = wf_instance.exec.get('requester')
        track = list(wf_instance.exec.get('track', []))
        if parent:
            track.append(parent)

        headers = {
            'Content-Type': 'application/json',
            'Referer': URI.instance(workflow),
            'X-Surycat-Exec-Track': ','.join(track)
        }

        # Handle blocking trigger_workflow using mqtt
        if self.blocking:
            topic = '{}/async/{}'.format(runtime.bus.name, str(uuid4())[:8])
            headers['X-Surycat-Async-Topic'] = topic
            self.async_future = asyncio.Future()
            await runtime.bus.subscribe(topic, self.async_exec)
            asyncio.get_event_loop().call_later(
                self.timeout,
                asyncio.ensure_future,
                runtime.bus.unsubscribe(topic)
            )

        async with ClientSession() as session:
            params = {
                'url': self.url,
                'headers': headers,
                'data': json.dumps({
                    'id': self.template,
                    'draft': self.draft,
                    'inputs': data
                })
            }
            async with session.put(**params) as response:
                # Response validity
                if response.status != 200:
                    msg = "Can't process workflow template {} on {}".format(
                        self.template, self.nyuki_api
                    )
                    if response.status % 400 < 100:
                        reason = await response.json()
                        msg = "{}, reason: {}".format(msg, reason['error'])
                    raise RuntimeError(msg)
                resp_body = await response.json()

        instance = resp_body['exec']['id']
        self._triggered['workflow_exec_id'] = instance
        log.debug('Request sent successfully to {}', self.nyuki_api)

        if not self.blocking:
            self._triggered['status'] = Status.DONE.value
            self._task.dispatch_progress(self.report())

        # Block until task completed
        if self.blocking:
            self._triggered['status'] = Status.RUNNING.value
            self._task.dispatch_progress(self.report())
            log.info('Waiting for %s@%s to complete', instance, self.nyuki_api)
            try:
                await asyncio.wait_for(self.async_future, self.timeout)
                self._triggered['status'] = Status.DONE.value
                log.info('Instance %s@%s is done', instance, self.nyuki_api)
            except asyncio.TimeoutError:
                self._triggered['status'] = Status.TIMEOUT.value
                log.info('Instance %s@%s has timeouted', instance, self.nyuki_api)
            self._task.dispatch_progress({'status': self._triggered['status']})

        return data
Exemplo n.º 13
0
 async def execute(self, event):
     Workflow.current_workflow().cancel()
     await asyncio.sleep(1.0)
Exemplo n.º 14
0
 async def execute(self, event):
     Workflow.current_workflow().cancel()
     await asyncio.sleep(1.0)
Exemplo n.º 15
0
    async def execute(self, event):
        """
        Entrypoint execution method.
        """
        self.data = event.data
        self.task = asyncio.Task.current_task()
        is_draft = self.template.get('draft', False)

        # Send the HTTP request
        log.info('Triggering template %s%s on service %s', self.template['id'],
                 ' (draft)' if is_draft else '', self.template['service'])

        # Setup headers (set requester and exec-track to avoid workflow loops)
        workflow = runtime.workflows[Workflow.current_workflow().uid]
        parent = workflow.exec.get('requester')
        track = list(workflow.exec.get('track', []))
        if parent:
            track.append(parent)

        headers = {
            'Content-Type': 'application/json',
            'Referer': URI.instance(workflow.instance),
            'X-Surycat-Exec-Track': ','.join(track)
        }

        # Handle blocking trigger_workflow using mqtt
        if self.blocking:
            topic = '{}/async/{}'.format(runtime.bus.name, self.uid[:8])
            headers['X-Surycat-Async-Topic'] = topic
            headers['X-Surycat-Async-Events'] = ','.join([
                WorkflowExecState.END.value,
                WorkflowExecState.ERROR.value,
            ])
            self.async_future = asyncio.Future()
            await runtime.bus.subscribe(topic, self.async_exec)

            def _unsub(f):
                asyncio.ensure_future(runtime.bus.unsubscribe(topic))

            self.task.add_done_callback(_unsub)

        async with ClientSession() as session:
            # Compute data to send to sub-workflows
            url = '{}/vars/{}{}'.format(
                self._engine,
                self.template['id'],
                '/draft' if is_draft else '',
            )
            async with session.get(url) as response:
                if response.status != 200:
                    raise RuntimeError("Can't load template info")
                wf_vars = await response.json()
            lightened_data = {
                key: self.data[key]
                for key in wf_vars if key in self.data
            }

            params = {
                'url':
                '{}/instances'.format(self._engine),
                'headers':
                headers,
                'data':
                json.dumps({
                    'id': self.template['id'],
                    'draft': is_draft,
                    'inputs': lightened_data,
                })
            }
            async with session.put(**params) as response:
                if response.status != 200:
                    log.critical(await response.text())
                    msg = "Can't process workflow template {} on {}".format(
                        self.template, self.nyuki_api)
                    if response.status % 400 < 100:
                        reason = await response.json()
                        msg = "{}, reason: {}".format(msg, reason['error'])
                    raise RuntimeError(msg)
                resp_body = await response.json()
                self.triggered_id = resp_body['id']

        wf_id = '@'.join([self.triggered_id[:8], self.template['service']])
        self.status = WorkflowStatus.RUNNING.value
        log.info('Successfully started %s', wf_id)
        self.task.dispatch_progress(self.report())

        # Block until task completed
        if self.blocking:
            log.info('Waiting for workflow %s to complete', wf_id)
            await self.async_future
            self.status = WorkflowStatus.DONE.value
            log.info('Workflow %s is done', wf_id)
            self.task.dispatch_progress({'status': self.status})

        return self.data