Example #1
0
 def _amqp_client(self):
     # initialize an amqp client only when needed, ie. if the task is
     # not local
     with_amqp = bool(self.ctx.task_target)
     if with_amqp:
         amqp_client_utils.init_events_publisher()
     try:
         yield
     finally:
         if with_amqp:
             amqp_client_utils.close_amqp_client()
Example #2
0
 def _amqp_client(self):
     # initialize an amqp client only when needed, ie. if the task is
     # not local
     with_amqp = bool(self.ctx.task_target)
     if with_amqp:
         try:
             amqp_client_utils.init_events_publisher()
         except Exception:
             _, ex, tb = sys.exc_info()
             # This one should never (!) raise an exception.
             amqp_client_utils.close_amqp_client()
             raise exceptions.RecoverableError(
                 'Failed initializing AMQP connection',
                 causes=[utils.exception_to_error_cause(ex, tb)])
     try:
         yield
     finally:
         if with_amqp:
             amqp_client_utils.close_amqp_client()
Example #3
0
    def _handle_remote_workflow(self):
        tenant = self.ctx._context['tenant'].get('original_name',
                                                 self.ctx.tenant_name)
        rest = get_rest_client(tenant=tenant)
        execution = rest.executions.get(self.ctx.execution_id,
                                        _include=['status'])
        if execution.status == Execution.STARTED:
            self.ctx.resume = True

        try:
            amqp_client_utils.init_events_publisher()
            try:
                self._workflow_started()
            except InvalidExecutionUpdateStatus:
                self._workflow_cancelled()
                return api.EXECUTION_CANCELLED_RESULT

            queue = Queue.Queue()
            t = AMQPWrappedThread(target=self._remote_workflow_child_thread,
                                  args=(queue, ),
                                  name='Workflow-Child')
            t.start()

            # while the child thread is executing the workflow, the parent
            # thread is polling for 'cancel' requests while also waiting for
            # messages from the child thread
            result = None
            while True:
                # check if child thread sent a message
                try:
                    data = queue.get(timeout=5)
                    if 'result' in data:
                        # child thread has terminated
                        result = data['result']
                        break
                    else:
                        # error occurred in child thread
                        error = data['error']
                        raise exceptions.ProcessExecutionError(
                            error['message'], error['type'],
                            error['traceback'])
                except Queue.Empty:
                    pass

                # A very hacky way to solve an edge case when trying to poll
                # for the execution status while the DB is downgraded during
                # a snapshot restore
                if self.cloudify_context['workflow_id'] == 'restore_snapshot':
                    continue

                # check for 'cancel' requests
                execution = rest.executions.get(self.ctx.execution_id,
                                                _include=['status'])
                if execution.status in [
                        Execution.CANCELLING, Execution.FORCE_CANCELLING,
                        Execution.KILL_CANCELLING
                ]:
                    # send a 'cancel' message to the child thread. It is up to
                    # the workflow implementation to check for this message
                    # and act accordingly (by stopping and raising an
                    # api.ExecutionCancelled error, or by returning the
                    # deprecated api.EXECUTION_CANCELLED_RESULT as result).
                    # parent thread then goes back to polling for messages from
                    # child thread or possibly 'force-cancelling' requests
                    api.cancel_request = True

                if execution.status == Execution.KILL_CANCELLING:
                    # if a custom workflow function must attempt some cleanup,
                    # it might attempt to catch SIGTERM, and confirm using this
                    # flag that it is being kill-cancelled
                    api.kill_request = True

                if execution.status in [
                        Execution.FORCE_CANCELLING, Execution.KILL_CANCELLING
                ]:
                    # force-cancel additionally stops this loop immediately
                    result = api.EXECUTION_CANCELLED_RESULT
                    break

            if result == api.EXECUTION_CANCELLED_RESULT:
                self._workflow_cancelled()
            else:
                self._workflow_succeeded()
            return result
        except exceptions.ProcessExecutionError as e:
            self._workflow_failed(e, e.traceback)
            raise
        except BaseException as e:
            self._workflow_failed(e, traceback.format_exc())
            raise
        finally:
            amqp_client_utils.close_amqp_client()