def handle_function_service_expiration(ctx, engine): """Clean up resources related to expired functions. If it's image function, we will rely on the orchestrator itself to do the image clean up, e.g. image collection feature in kubernetes. """ context.set_ctx(ctx) delta = timedelta(seconds=CONF.engine.function_service_expiration) expiry_time = datetime.utcnow() - delta results = db_api.get_functions( sort_keys=['updated_at'], insecure=True, updated_at={'lte': expiry_time} ) for func_db in results: if not etcd_util.get_service_url(func_db.id, 0): continue LOG.info( 'Deleting service mapping and workers for function ' '%s(version 0)', func_db.id ) # Delete resources related to the function engine.delete_function(ctx, func_db.id, 0) # Delete etcd keys etcd_util.delete_function(func_db.id, 0) versions = db_api.get_function_versions( sort_keys=['updated_at'], insecure=True, updated_at={'lte': expiry_time}, ) for v in versions: if not etcd_util.get_service_url(v.function_id, v.version_number): continue LOG.info( 'Deleting service mapping and workers for function ' '%s(version %s)', v.function_id, v.version_number ) # Delete resources related to the function engine.delete_function(ctx, v.function_id, v.version_number) # Delete etcd keys etcd_util.delete_function(v.function_id, v.version_number)
def put(self, id, runtime): """Update runtime. Currently, we support update name, description, image. When updating image, send message to engine for asynchronous handling. """ acl.enforce('runtime:update', context.get_ctx()) values = {} for key in UPDATE_ALLOWED: if runtime.to_dict().get(key) is not None: values.update({key: runtime.to_dict()[key]}) LOG.info('Update resource, params: %s', values, resource={ 'type': self.type, 'id': id }) image = values.get('image') with db_api.transaction(): if image is not None: pre_runtime = db_api.get_runtime(id) if pre_runtime.status != status.AVAILABLE: raise exc.RuntimeNotAvailableException( 'Runtime %s is not available.' % id) pre_image = pre_runtime.image if pre_image != image: # Ensure there is no function running in the runtime. db_funcs = db_api.get_functions(insecure=True, fields=['id'], runtime_id=id) func_ids = [func.id for func in db_funcs] for id in func_ids: if etcd_util.get_service_url(id): raise exc.NotAllowedException( 'Runtime %s is still in use by functions.' % id) values['status'] = status.UPGRADING self.engine_client.update_runtime( id, image=image, pre_image=pre_image, ) runtime_db = db_api.update_runtime(id, values) return resources.Runtime.from_db_obj(runtime_db)
def create_execution(self, ctx, execution_id, function_id, function_version, runtime_id, input=None): LOG.info( 'Creating execution. execution_id=%s, function_id=%s, ' 'function_version=%s, runtime_id=%s, input=%s', execution_id, function_id, function_version, runtime_id, input) function = db_api.get_function(function_id) source = function.code['source'] rlimit = {'cpu': function.cpu, 'memory_size': function.memory_size} image = None identifier = None labels = None svc_url = None is_image_source = source == constants.IMAGE_FUNCTION # Auto scale workers if needed if not is_image_source: try: svc_url = self.function_load_check(function_id, function_version, runtime_id) except exc.OrchestratorException as e: utils.handle_execution_exception(execution_id, str(e)) return temp_url = etcd_util.get_service_url(function_id, function_version) svc_url = svc_url or temp_url if svc_url: func_url = '%s/execute' % svc_url LOG.debug( 'Found service url for function: %s(version %s), execution: ' '%s, url: %s', function_id, function_version, execution_id, func_url) data = utils.get_request_data(CONF, function_id, function_version, execution_id, rlimit, input, function.entry, function.trust_id, self.qinling_endpoint) success, res = utils.url_request(self.session, func_url, body=data) utils.finish_execution(execution_id, success, res, is_image_source=is_image_source) return if source == constants.IMAGE_FUNCTION: image = function.code['image'] # Be consistent with k8s naming convention identifier = ( '%s-%s' % (common.generate_unicode_uuid(dashed=False), function_id))[:63] else: identifier = runtime_id labels = {'runtime_id': runtime_id} try: # For image function, it will be executed inside this method; for # package type function it only sets up underlying resources and # get a service url. If the service url is already created # beforehand, nothing happens. _, svc_url = self.orchestrator.prepare_execution( function_id, function_version, rlimit=rlimit, image=image, identifier=identifier, labels=labels, input=input, ) except exc.OrchestratorException as e: utils.handle_execution_exception(execution_id, str(e)) return # For image type function, read the worker log; For package type # function, invoke and get log success, res = self.orchestrator.run_execution( execution_id, function_id, function_version, rlimit=rlimit if svc_url else None, input=input, identifier=identifier, service_url=svc_url, entry=function.entry, trust_id=function.trust_id) utils.finish_execution(execution_id, success, res, is_image_source=is_image_source)
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) function = db_api.get_function(function_id) source = function.code['source'] image = None identifier = None labels = None svc_url = None # Auto scale workers if needed if source != constants.IMAGE_FUNCTION: svc_url = self.function_load_check(function_id, runtime_id) temp_url = etcd_util.get_service_url(function_id) svc_url = svc_url or temp_url if svc_url: func_url = '%s/execute' % svc_url LOG.debug( 'Found service url for function: %s, execution: %s, url: %s', function_id, execution_id, func_url) data = utils.get_request_data(CONF, function_id, execution_id, input, function.entry, function.trust_id, self.qinling_endpoint) success, res = utils.url_request(self.session, func_url, body=data) success = success and res.pop('success') LOG.debug('Finished execution %s, success: %s', execution_id, success) db_api.update_execution( execution_id, { 'status': status.SUCCESS if success else status.FAILED, 'logs': res.pop('logs', ''), 'result': res }) return 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} _, svc_url = self.orchestrator.prepare_execution( function_id, image=image, identifier=identifier, labels=labels, input=input, ) success, res = self.orchestrator.run_execution( execution_id, function_id, input=input, identifier=identifier, service_url=svc_url, entry=function.entry, trust_id=function.trust_id) logs = '' # Execution log is only available for non-image source execution. if svc_url: logs = res.pop('logs', '') success = success and res.pop('success') else: # If the function is created from docker image, the result is # direct output, here we convert to a dict to fit into the db # schema. res = {'output': res} LOG.debug('Finished execution %s, success: %s', execution_id, success) db_api.update_execution( execution_id, { 'status': status.SUCCESS if success else status.FAILED, 'logs': logs, 'result': res })