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()
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()
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()