def create_execution(engine_client, params): function_id = params['function_id'] is_sync = params.get('sync', True) with db_api.transaction(): func_db = db_api.get_function(function_id) runtime_db = func_db.runtime if runtime_db and runtime_db.status != status.AVAILABLE: raise exc.RuntimeNotAvailableException( 'Runtime %s is not available.' % func_db.runtime_id) # Increase function invoke count, the updated_at field will be also # updated. func_db.count = func_db.count + 1 params.update({'status': status.RUNNING}) db_model = db_api.create_execution(params) engine_client.create_execution(db_model.id, function_id, func_db.runtime_id, input=params.get('input'), is_sync=is_sync) if is_sync: # The execution should already be updated by engine service for sync # execution. db_model = db_api.get_execution(db_model.id) return db_model
def create_execution(engine_client, params): function_id = params['function_id'] is_sync = params.get('sync', True) input = params.get('input') # input in params should be a string. if input: try: params['input'] = json.loads(input) except ValueError: params['input'] = {'__function_input': input} runtime_id = _update_function_db(function_id) params.update({'status': status.RUNNING}) db_model = db_api.create_execution(params) engine_client.create_execution(db_model.id, function_id, runtime_id, input=params.get('input'), is_sync=is_sync) if is_sync: # The execution should already be updated by engine service for sync # execution. db_model = db_api.get_execution(db_model.id) return db_model
def test_create_execution_prepare_execution_exception( self, etcd_util_get_service_url_mock): """test_create_execution_prepare_execution_exception Create execution for image type function, prepare_execution method raises exception. """ function = self.create_function() function_id = function.id runtime_id = function.runtime_id db_api.update_function( function_id, { 'code': { 'source': constants.IMAGE_FUNCTION, 'image': self.rand_name('image', prefix=self.prefix) } }) function = db_api.get_function(function_id) execution = self.create_execution(function_id=function_id) execution_id = execution.id prepare_execution = self.orchestrator.prepare_execution prepare_execution.side_effect = exc.OrchestratorException( 'Exception in prepare_execution') etcd_util_get_service_url_mock.return_value = None self.default_engine.create_execution(mock.Mock(), execution_id, function_id, 0, runtime_id) execution = db_api.get_execution(execution_id) self.assertEqual(execution.status, status.ERROR) self.assertEqual(execution.logs, '') self.assertEqual(execution.result, {'error': 'Function execution failed.'})
def create_execution(self, ctx, execution_id, function_id, runtime_id, input=None): LOG.info( 'Creating execution. execution_id=%s, function_id=%s, ' 'runtime_id=%s', execution_id, function_id, runtime_id) with db_api.transaction(): execution = db_api.get_execution(execution_id) runtime = db_api.get_runtime(runtime_id) identifier = '%s-%s' % (runtime_id, runtime.name) labels = {'runtime_name': runtime.name, 'runtime_id': runtime_id} service_url = self.orchestrator.prepare_execution( function_id, identifier=identifier, labels=labels) output = self.orchestrator.run_execution(function_id, input=input, service_url=service_url) LOG.debug('Finished execution. execution_id=%s, output=%s', execution_id, output) execution.output = output execution.status = 'success' mapping = {'function_id': function_id, 'service_url': service_url} db_api.create_function_service_mapping(mapping)
def test_create_execution_package_type_function( self, etcd_util_get_service_url_mock): function = self.create_function() function_id = function.id runtime_id = function.runtime_id execution = self.create_execution(function_id=function_id) execution_id = execution.id self.default_engine.function_load_check = mock.Mock(return_value='') etcd_util_get_service_url_mock.return_value = None self.orchestrator.prepare_execution.return_value = (mock.Mock(), 'svc_url') self.orchestrator.run_execution.return_value = (True, { 'success': True, 'logs': 'execution log', 'output': 'success output' }) self.default_engine.create_execution(mock.Mock(), execution_id, function_id, 0, runtime_id) self.default_engine.function_load_check.assert_called_once_with( function_id, 0, runtime_id) etcd_util_get_service_url_mock.assert_called_once_with(function_id, 0) self.orchestrator.prepare_execution.assert_called_once_with( function_id, 0, rlimit=self.rlimit, image=None, identifier=runtime_id, labels={'runtime_id': runtime_id}, input=None) self.orchestrator.run_execution.assert_called_once_with( execution_id, function_id, 0, rlimit=self.rlimit, input=None, identifier=runtime_id, service_url='svc_url', entry=function.entry, trust_id=function.trust_id) execution = db_api.get_execution(execution_id) self.assertEqual(execution.status, status.SUCCESS) self.assertEqual(execution.logs, 'execution log') self.assertEqual(execution.result, {'output': 'success output'})
def test_create_execution_loadcheck_exception(self): function = self.create_function() function_id = function.id runtime_id = function.runtime_id execution = self.create_execution(function_id=function_id) execution_id = execution.id self.default_engine.function_load_check = mock.Mock( side_effect=exc.OrchestratorException( 'Exception in scaleup_function')) self.default_engine.create_execution(mock.Mock(), execution_id, function_id, 0, runtime_id) execution = db_api.get_execution(execution_id) self.assertEqual(execution.status, status.ERROR) self.assertEqual(execution.logs, '') self.assertEqual(execution.result, {'error': 'Function execution failed.'})
def post(self, execution): params = execution.to_dict() LOG.info("Creating execution. [execution=%s]", params) function_id = params['function_id'] # Check if the service url is existing. try: mapping = db_api.get_function_service_mapping(function_id) LOG.debug('Found Service url for function: %s', function_id) func_url = '%s/execute' % mapping.service_url LOG.info('Invoke function %s, url: %s', function_id, func_url) r = requests.post(func_url, data=params.get('input')) params.update({ 'status': 'success', 'output': { 'result': r.json() } }) db_model = db_api.create_execution(params) return resources.Execution.from_dict(db_model.to_dict()) except exc.DBEntityNotFoundError: pass func = db_api.get_function(function_id) runtime_id = func.runtime_id params.update({'status': 'running'}) db_model = db_api.create_execution(params) self.engine_client.create_execution(db_model.id, function_id, runtime_id, input=params.get('input')) updated_db = db_api.get_execution(db_model.id) return resources.Execution.from_dict(updated_db.to_dict())
def test_create_execution_found_service_url( self, etcd_util_get_service_url_mock, engine_utils_url_request_mock, engine_utils_get_request_data_mock): function = self.create_function() function_id = function.id runtime_id = function.runtime_id execution = self.create_execution(function_id=function_id) execution_id = execution.id self.default_engine.function_load_check = mock.Mock(return_value='') etcd_util_get_service_url_mock.return_value = 'svc_url' engine_utils_get_request_data_mock.return_value = 'data' engine_utils_url_request_mock.return_value = (False, { 'success': False, 'logs': 'execution log', 'output': 'failed output' }) self.default_engine.create_execution(mock.Mock(), execution_id, function_id, 0, runtime_id, input='input') self.default_engine.function_load_check.assert_called_once_with( function_id, 0, runtime_id) etcd_util_get_service_url_mock.assert_called_once_with(function_id, 0) engine_utils_get_request_data_mock.assert_called_once_with( mock.ANY, function_id, 0, execution_id, self.rlimit, 'input', function.entry, function.trust_id, self.qinling_endpoint, function.timeout) engine_utils_url_request_mock.assert_called_once_with( self.default_engine.session, 'svc_url/execute', body='data') execution = db_api.get_execution(execution_id) self.assertEqual(execution.status, status.FAILED) self.assertEqual(execution.logs, 'execution log') self.assertEqual(execution.result, { 'success': False, 'output': 'failed output' })
def create_execution(self, ctx, execution_id, function_id, runtime_id, input=None): LOG.info( 'Creating execution. execution_id=%s, function_id=%s, ' 'runtime_id=%s, input=%s', execution_id, function_id, runtime_id, input) # FIXME(kong): Make the transaction range smaller. with db_api.transaction(): execution = db_api.get_execution(execution_id) function = db_api.get_function(function_id) if function.service: func_url = '%s/execute' % function.service.service_url LOG.debug('Found service url for function: %s, url: %s', function_id, func_url) data = {'input': input, 'execution_id': execution_id} r = self.session.post(func_url, json=data) res = r.json() LOG.debug('Finished execution %s', execution_id) success = res.pop('success') execution.status = status.SUCCESS if success else status.FAILED execution.logs = res.pop('logs', '') execution.output = res return source = function.code['source'] image = None identifier = None labels = None if source == constants.IMAGE_FUNCTION: image = function.code['image'] identifier = ('%s-%s' % (common.generate_unicode_uuid(dashed=False), function_id))[:63] labels = {'function_id': function_id} else: identifier = runtime_id labels = {'runtime_id': runtime_id} worker_name, service_url = self.orchestrator.prepare_execution( function_id, image=image, identifier=identifier, labels=labels, input=input, entry=function.entry, trust_id=function.trust_id) output = self.orchestrator.run_execution( execution_id, function_id, input=input, identifier=identifier, service_url=service_url, ) logs = '' # Execution log is only available for non-image source execution. if service_url: logs = output.pop('logs', '') success = output.pop('success') else: # If the function is created from docker image, the output is # direct output, here we convert to a dict to fit into the db # schema. output = {'output': output} success = True LOG.debug('Finished execution. execution_id=%s, output=%s', execution_id, output) execution.output = output execution.logs = logs execution.status = status.SUCCESS if success else status.FAILED # No service is created in orchestrator for single container. if not image: mapping = { 'function_id': function_id, 'service_url': service_url, } db_api.create_function_service_mapping(mapping) worker = { 'function_id': function_id, 'worker_name': worker_name } db_api.create_function_worker(worker)
def get(self, id): LOG.info("Fetch resource.", resource={'type': self.type, 'id': id}) execution_db = db_api.get_execution(id) return resources.Execution.from_dict(execution_db.to_dict())
def get_all(self, execution_id): LOG.info("Get logs for execution %s.", execution_id) execution_db = db_api.get_execution(execution_id) return execution_db.logs
def create_execution(engine_client, params): function_id = params['function_id'] is_sync = params.get('sync', True) input = params.get('input') version = params.get('function_version', 0) func_db = db_api.get_function(function_id) runtime_id = func_db.runtime_id # Image type function does not need runtime if runtime_id: runtime_db = db_api.get_runtime(runtime_id) if runtime_db and runtime_db.status != status.AVAILABLE: raise exc.RuntimeNotAvailableException( 'Runtime %s is not available.' % func_db.runtime_id) if version > 0: if func_db.code['source'] != constants.PACKAGE_FUNCTION: raise exc.InputException( "Can not specify version for %s type function." % constants.PACKAGE_FUNCTION) # update version count version_db = db_api.get_function_version(function_id, version) pre_version_count = version_db.count _update_function_version_db(version_db.id, pre_version_count) else: pre_count = func_db.count _update_function_db(function_id, pre_count) # input in params should be a string. if input: try: params['input'] = json.loads(input) except ValueError: params['input'] = {'__function_input': input} params.update({'status': status.RUNNING}) db_model = db_api.create_execution(params) try: engine_client.create_execution(db_model.id, function_id, version, runtime_id, input=params.get('input'), is_sync=is_sync) except exc.QinlingException: # Catch RPC errors for executions: # - for RemoteError in an RPC call, the execution status would be # handled in the engine side; # - for other exceptions in an RPC call or cast, the execution status # would remain RUNNING so we should update it. db_model = db_api.get_execution(db_model.id) if db_model.status == status.RUNNING: db_model = db_api.update_execution(db_model.id, {'status': status.ERROR}) return db_model if is_sync: # The execution should already be updated by engine service for sync # execution. db_model = db_api.get_execution(db_model.id) return db_model
def get(self, id): LOG.info("Fetch execution [id=%s]", id) execution_db = db_api.get_execution(id) return resources.Execution.from_dict(execution_db.to_dict())
def test_create_execution_image_type_function(self, mock_svc_url): """Create 2 executions for an image type function.""" function = self.create_function() function_id = function.id runtime_id = function.runtime_id db_api.update_function( function_id, { 'code': { 'source': constants.IMAGE_FUNCTION, 'image': self.rand_name('image', prefix=self.prefix) } }) function = db_api.get_function(function_id) execution_1 = self.create_execution(function_id=function_id) execution_1_id = execution_1.id execution_2 = self.create_execution(function_id=function_id) execution_2_id = execution_2.id mock_svc_url.return_value = None self.orchestrator.prepare_execution.return_value = (mock.Mock(), None) self.orchestrator.run_execution.side_effect = [(True, { 'duration': 5, 'logs': 'fake log' }), (False, { 'duration': 0, 'error': 'Function execution failed.' })] # Create two executions, with different results self.default_engine.create_execution(mock.Mock(), execution_1_id, function_id, 0, runtime_id) self.default_engine.create_execution(mock.Mock(), execution_2_id, function_id, 0, runtime_id, input='input') get_service_url_calls = [ mock.call(function_id, 0), mock.call(function_id, 0) ] mock_svc_url.assert_has_calls(get_service_url_calls) prepare_calls = [ mock.call(function_id, 0, rlimit=self.rlimit, image=function.code['image'], identifier=mock.ANY, labels=None, input=None), mock.call(function_id, 0, rlimit=self.rlimit, image=function.code['image'], identifier=mock.ANY, labels=None, input='input') ] self.orchestrator.prepare_execution.assert_has_calls(prepare_calls) run_calls = [ mock.call(execution_1_id, function_id, 0, rlimit=None, input=None, identifier=mock.ANY, service_url=None, entry=function.entry, trust_id=function.trust_id), mock.call(execution_2_id, function_id, 0, rlimit=None, input='input', identifier=mock.ANY, service_url=None, entry=function.entry, trust_id=function.trust_id) ] self.orchestrator.run_execution.assert_has_calls(run_calls) execution_1 = db_api.get_execution(execution_1_id) execution_2 = db_api.get_execution(execution_2_id) self.assertEqual(status.SUCCESS, execution_1.status) self.assertEqual('fake log', execution_1.logs) self.assertEqual({"duration": 5}, execution_1.result) self.assertEqual(status.FAILED, execution_2.status) self.assertEqual('', execution_2.logs) self.assertEqual({ 'duration': 0, 'error': 'Function execution failed.' }, execution_2.result)
def get(self, id): LOG.info("Get resource.", resource={'type': self.type, 'id': id}) execution_db = db_api.get_execution(id) return resources.Execution.from_db_obj(execution_db)