def make_response(): listener = get_listener(name='stream') app_iter = format( listener.generator(events=events, action_refs=action_refs, execution_ids=execution_ids)) res = Response(content_type='text/event-stream', app_iter=app_iter) return res
def make_response(): listener = get_listener(name='stream') app_iter = format(listener.generator(events=events, action_refs=action_refs, end_event=end_event, end_statuses=action_constants.LIVEACTION_COMPLETED_STATES, end_execution_id=end_execution_id, execution_ids=execution_ids)) res = Response(headerlist=[("X-Accel-Buffering", "no"), ('Cache-Control', 'no-cache'), ("Content-Type", "text/event-stream; charset=UTF-8")], app_iter=app_iter) return res
def new_output_iter(): def noop_gen(): yield six.binary_type(NO_MORE_DATA_EVENT.encode("utf-8")) # Bail out if execution has already completed / been paused if execution_db.status in self.CLOSE_STREAM_LIVEACTION_STATES: return noop_gen() # Wait for and return any new line which may come in execution_ids = [execution_id] listener = get_listener( name="execution_output" ) # pylint: disable=no-member gen = listener.generator(execution_ids=execution_ids) def format(gen): for pack in gen: if not pack: continue else: (_, model_api) = pack # Note: gunicorn wsgi handler expect bytes, not unicode # pylint: disable=no-member if isinstance(model_api, ActionExecutionOutputAPI): if ( output_type and output_type != "all" and model_api.output_type != output_type ): continue output = format_output_object(model_api).encode("utf-8") yield six.binary_type(output) elif isinstance(model_api, ActionExecutionAPI): if model_api.status in self.CLOSE_STREAM_LIVEACTION_STATES: yield six.binary_type( NO_MORE_DATA_EVENT.encode("utf-8") ) break else: LOG.debug("Unrecognized message type: %s" % (model_api)) gen = format(gen) return gen
def new_output_iter(): def noop_gen(): yield six.binary_type(NO_MORE_DATA_EVENT.encode('utf-8')) # Bail out if execution has already completed / been paused if execution_db.status in self.CLOSE_STREAM_LIVEACTION_STATES: return noop_gen() # Wait for and return any new line which may come in execution_ids = [execution_id] listener = get_listener(name='execution_output') # pylint: disable=no-member gen = listener.generator(execution_ids=execution_ids) def format(gen): for pack in gen: if not pack: continue else: (_, model_api) = pack # Note: gunicorn wsgi handler expect bytes, not unicode # pylint: disable=no-member if isinstance(model_api, ActionExecutionOutputAPI): if output_type and output_type != 'all' and \ model_api.output_type != output_type: continue output = format_output_object(model_api).encode('utf-8') yield six.binary_type(output) elif isinstance(model_api, ActionExecutionAPI): if model_api.status in self.CLOSE_STREAM_LIVEACTION_STATES: yield six.binary_type(NO_MORE_DATA_EVENT.encode('utf-8')) break else: LOG.debug('Unrecognized message type: %s' % (model_api)) gen = format(gen) return gen
def test_get_output_running_execution(self): # Retrieve lister instance to avoid race with listener connection not being established # early enough for tests to pass. # NOTE: This only affects tests where listeners are not pre-initialized. listener = get_listener(name='execution_output') eventlet.sleep(1.0) # Test the execution output API endpoint for execution which is running (blocking) status = action_constants.LIVEACTION_STATUS_RUNNING timestamp = date_utils.get_datetime_utc_now() action_execution_db = ActionExecutionDB( start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) action_execution_db = ActionExecution.add_or_update( action_execution_db) output_params = dict(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout before start\n') # Insert mock output object output_db = ActionExecutionOutputDB(**output_params) ActionExecutionOutput.add_or_update(output_db, publish=False) def insert_mock_data(): output_params['data'] = 'stdout mid 1\n' output_db = ActionExecutionOutputDB(**output_params) ActionExecutionOutput.add_or_update(output_db) # Since the API endpoint is blocking (connection is kept open until action finishes), we # spawn an eventlet which eventually finishes the action. def publish_action_finished(action_execution_db): # Insert mock output object output_params['data'] = 'stdout pre finish 1\n' output_db = ActionExecutionOutputDB(**output_params) ActionExecutionOutput.add_or_update(output_db) eventlet.sleep(1.0) # Transition execution to completed state so the connection closes action_execution_db.status = action_constants.LIVEACTION_STATUS_SUCCEEDED action_execution_db = ActionExecution.add_or_update( action_execution_db) eventlet.spawn_after(0.2, insert_mock_data) eventlet.spawn_after(1.5, publish_action_finished, action_execution_db) # Retrieve data while execution is running - endpoint return new data once it's available # and block until the execution finishes resp = self.app.get('/v1/executions/%s/output' % (str(action_execution_db.id)), expect_errors=False) self.assertEqual(resp.status_int, 200) events = self._parse_response(resp.text) self.assertEqual(len(events), 4) self.assertEqual(events[0][1]['data'], 'stdout before start\n') self.assertEqual(events[1][1]['data'], 'stdout mid 1\n') self.assertEqual(events[2][1]['data'], 'stdout pre finish 1\n') self.assertEqual(events[3][0], 'EOF') # Once the execution is in completed state, existing output should be returned immediately resp = self.app.get('/v1/executions/%s/output' % (str(action_execution_db.id)), expect_errors=False) self.assertEqual(resp.status_int, 200) events = self._parse_response(resp.text) self.assertEqual(len(events), 4) self.assertEqual(events[0][1]['data'], 'stdout before start\n') self.assertEqual(events[1][1]['data'], 'stdout mid 1\n') self.assertEqual(events[2][1]['data'], 'stdout pre finish 1\n') self.assertEqual(events[3][0], 'EOF') listener.shutdown()
def make_response(): listener = get_listener(name='stream') app_iter = format(listener.generator(events=events, action_refs=action_refs, execution_ids=execution_ids)) res = Response(content_type='text/event-stream', app_iter=app_iter) return res
def test_get_output_running_execution(self): # Retrieve lister instance to avoid race with listener connection not being established # early enough for tests to pass. # NOTE: This only affects tests where listeners are not pre-initialized. listener = get_listener(name='execution_output') eventlet.sleep(1.0) # Test the execution output API endpoint for execution which is running (blocking) status = action_constants.LIVEACTION_STATUS_RUNNING timestamp = date_utils.get_datetime_utc_now() action_execution_db = ActionExecutionDB(start_timestamp=timestamp, end_timestamp=timestamp, status=status, action={'ref': 'core.local'}, runner={'name': 'local-shell-cmd'}, liveaction={'ref': 'foo'}) action_execution_db = ActionExecution.add_or_update(action_execution_db) output_params = dict(execution_id=str(action_execution_db.id), action_ref='core.local', runner_ref='dummy', timestamp=timestamp, output_type='stdout', data='stdout before start\n') # Insert mock output object output_db = ActionExecutionOutputDB(**output_params) ActionExecutionOutput.add_or_update(output_db, publish=False) def insert_mock_data(): output_params['data'] = 'stdout mid 1\n' output_db = ActionExecutionOutputDB(**output_params) ActionExecutionOutput.add_or_update(output_db) # Since the API endpoint is blocking (connection is kept open until action finishes), we # spawn an eventlet which eventually finishes the action. def publish_action_finished(action_execution_db): # Insert mock output object output_params['data'] = 'stdout pre finish 1\n' output_db = ActionExecutionOutputDB(**output_params) ActionExecutionOutput.add_or_update(output_db) eventlet.sleep(1.0) # Transition execution to completed state so the connection closes action_execution_db.status = action_constants.LIVEACTION_STATUS_SUCCEEDED action_execution_db = ActionExecution.add_or_update(action_execution_db) eventlet.spawn_after(0.2, insert_mock_data) eventlet.spawn_after(1.5, publish_action_finished, action_execution_db) # Retrieve data while execution is running - endpoint return new data once it's available # and block until the execution finishes resp = self.app.get('/v1/executions/%s/output' % (str(action_execution_db.id)), expect_errors=False) self.assertEqual(resp.status_int, 200) events = self._parse_response(resp.text) self.assertEqual(len(events), 4) self.assertEqual(events[0][1]['data'], 'stdout before start\n') self.assertEqual(events[1][1]['data'], 'stdout mid 1\n') self.assertEqual(events[2][1]['data'], 'stdout pre finish 1\n') self.assertEqual(events[3][0], 'EOF') # Once the execution is in completed state, existing output should be returned immediately resp = self.app.get('/v1/executions/%s/output' % (str(action_execution_db.id)), expect_errors=False) self.assertEqual(resp.status_int, 200) events = self._parse_response(resp.text) self.assertEqual(len(events), 4) self.assertEqual(events[0][1]['data'], 'stdout before start\n') self.assertEqual(events[1][1]['data'], 'stdout mid 1\n') self.assertEqual(events[2][1]['data'], 'stdout pre finish 1\n') self.assertEqual(events[3][0], 'EOF') listener.shutdown()