def delete(self, id): """Delete the specified function.""" LOG.info("Delete function %s.", id) with db_api.transaction(): func_db = db_api.get_function(id) if len(func_db.jobs) > 0: raise exc.NotAllowedException( 'The function is still associated with running job(s).') if func_db.webhook: raise exc.NotAllowedException( 'The function is still associated with webhook.') # Even admin user can not delete other project's function because # the trust associated can only be removed by function owner. if func_db.project_id != context.get_ctx().projectid: raise exc.NotAllowedException( 'Function can only be deleted by its owner.') source = func_db.code['source'] if source == constants.PACKAGE_FUNCTION: self.storage_provider.delete(func_db.project_id, id) # Delete all resources created by orchestrator asynchronously. self.engine_client.delete_function(id) # Delete trust if needed if func_db.trust_id: keystone_util.delete_trust(func_db.trust_id) # Delete etcd keys etcd_util.delete_function(id) # This will also delete function service mapping as well. db_api.delete_function(id)
def delete(self, function_id, version): """Delete a specific function version. - The version should not being used by any job - The version should not being used by any webhook - Admin user can not delete normal user's version """ ctx = context.get_ctx() acl.enforce('function_version:delete', ctx) LOG.info("Deleting version %s of function %s.", version, function_id) with db_api.transaction(): version_db = db_api.get_function_version(function_id, version, insecure=False) latest_version = version_db.function.latest_version version_jobs = db_api.get_jobs( function_id=version_db.function_id, function_version=version_db.version_number, status={'nin': ['done', 'cancelled']}) if len(version_jobs) > 0: raise exc.NotAllowedException( 'The function version is still associated with running ' 'job(s).') version_webhook = db_api.get_webhooks( function_id=version_db.function_id, function_version=version_db.version_number, ) if len(version_webhook) > 0: raise exc.NotAllowedException( 'The function version is still associated with webhook.') filters = rest_utils.get_filters( function_id=version_db.function_id, function_version=version_db.version_number) version_aliases = db_api.get_function_aliases(**filters) if len(version_aliases) > 0: raise exc.NotAllowedException( 'The function version is still associated with alias.') # Delete resources for function version self.engine_client.delete_function(function_id, version=version) etcd_util.delete_function(function_id, version=version) self.storage_provider.delete(ctx.projectid, function_id, None, version=version) db_api.delete_function_version(function_id, version) if latest_version == version: version_db.function.latest_version = latest_version - 1 LOG.info("Version %s of function %s deleted.", version, function_id)
def delete(self, id): """Delete the specified function. Delete function will also delete all its versions. """ LOG.info("Delete function %s.", id) with db_api.transaction(): func_db = db_api.get_function(id) if len(func_db.jobs) > 0: raise exc.NotAllowedException( 'The function is still associated with running job(s).') if len(func_db.webhooks) > 0: raise exc.NotAllowedException( 'The function is still associated with webhook(s).') if len(func_db.aliases) > 0: raise exc.NotAllowedException( 'The function is still associated with function alias(es).' ) # Even admin user can not delete other project's function because # the trust associated can only be removed by function owner. if func_db.project_id != context.get_ctx().projectid: raise exc.NotAllowedException( 'Function can only be deleted by its owner.') # Delete trust if needed if func_db.trust_id: keystone_util.delete_trust(func_db.trust_id) for version_db in func_db.versions: # Delete all resources created by orchestrator asynchronously. self.engine_client.delete_function( id, version=version_db.version_number) # Delete etcd keys etcd_util.delete_function(id, version=version_db.version_number) # Delete function version packages. Versions is only supported # for package type function. self.storage_provider.delete(func_db.project_id, id, None, version=version_db.version_number) # Delete resources for function version 0(func_db.versions==[]) self.engine_client.delete_function(id) etcd_util.delete_function(id) source = func_db.code['source'] if source == constants.PACKAGE_FUNCTION: self.storage_provider.delete(func_db.project_id, id, func_db.code['md5sum']) # This will also delete function service mapping and function # versions as well. db_api.delete_function(id)
def _create_function_version(self, project_id, function_id, **kwargs): with etcd_util.get_function_version_lock(function_id) as lock: if not lock.is_acquired(): return False with db_api.transaction(): # Get latest function package md5 and version number func_db = db_api.get_function(function_id, insecure=False) if func_db.code['source'] != constants.PACKAGE_FUNCTION: raise exc.NotAllowedException( "Function versioning only allowed for %s type " "function." % constants.PACKAGE_FUNCTION ) l_md5 = func_db.code['md5sum'] l_version = func_db.latest_version if len(func_db.versions) >= constants.MAX_VERSION_NUMBER: raise exc.NotAllowedException( 'Can not exceed maximum number(%s) of versions' % constants.MAX_VERSION_NUMBER ) # Check if the latest package changed since last version changed = self.storage_provider.changed_since(project_id, function_id, l_md5, l_version) if not changed: raise exc.NotAllowedException( 'Function package not changed since the latest ' 'version %s.' % l_version ) LOG.info("Creating %s, function_id: %s, old_version: %d", self.type, function_id, l_version) # Create new version and copy package. self.storage_provider.copy(project_id, function_id, l_md5, l_version) version = db_api.increase_function_version(function_id, l_version, **kwargs) func_db.latest_version = l_version + 1 LOG.info("New version %d for function %s created.", l_version + 1, function_id) return version
def put(self, id, **kwargs): """Update function. - Function can not being used by job. - Function can not being executed. - (TODO)Function status should be changed so no execution will create when function is updating. """ values = {} for key in UPDATE_ALLOWED: if kwargs.get(key) is not None: values.update({key: kwargs[key]}) LOG.info('Update resource, params: %s', values, resource={ 'type': self.type, 'id': id }) ctx = context.get_ctx() if set(values.keys()).issubset(set(['name', 'description'])): func_db = db_api.update_function(id, values) else: source = values.get('code', {}).get('source') with db_api.transaction(): pre_func = db_api.get_function(id) if len(pre_func.jobs) > 0: raise exc.NotAllowedException( 'The function is still associated with running job(s).' ) pre_source = pre_func.code['source'] if source and source != pre_source: raise exc.InputException( "The function code type can not be changed.") if source == constants.IMAGE_FUNCTION: raise exc.InputException( "The image type function code can not be changed.") if (pre_source == constants.PACKAGE_FUNCTION and values.get('package') is not None): # Update the package data. data = values['package'].file.read() self.storage_provider.store(ctx.projectid, id, data) values.pop('package') if pre_source == constants.SWIFT_FUNCTION: swift_info = values['code'].get('swift', {}) self._check_swift(swift_info.get('container'), swift_info.get('object')) # Delete allocated resources in orchestrator. db_api.delete_function_service_mapping(id) self.engine_client.delete_function(id) func_db = db_api.update_function(id, values) pecan.response.status = 200 return resources.Function.from_dict(func_db.to_dict()).to_dict()
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 put(self, id, runtime): """Update runtime. Currently, we only support update name, description, image. When updating image, send message to engine for asynchronous handling. """ 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 }) with db_api.transaction(): if 'image' in values: 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 != values['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] mappings = db_api.get_function_service_mappings( insecure=True, function_id={'in': func_ids}) if mappings: raise exc.NotAllowedException( 'Runtime %s is still in use by functions.' % id) values['status'] = status.UPGRADING self.engine_client.update_runtime(id, image=values['image'], pre_image=pre_image) else: values.pop('image') runtime_db = db_api.update_runtime(id, values) return resources.Runtime.from_dict(runtime_db.to_dict())
def delete(self, id): LOG.info("Delete resource.", resource={'type': self.type, 'id': id}) with db_api.transaction(): runtime_db = db_api.get_runtime(id) # Runtime can not be deleted if still associate with functions. funcs = db_api.get_functions(runtime_id={'eq': id}) if len(funcs): raise exc.NotAllowedException('Runtime %s is still in use.' % id) runtime_db.status = status.DELETING # Clean related resources asynchronously self.engine_client.delete_runtime(id)
def delete(self, id): """Delete the specified function.""" LOG.info("Delete resource.", resource={'type': self.type, 'id': id}) with db_api.transaction(): func_db = db_api.get_function(id) if len(func_db.jobs) > 0: raise exc.NotAllowedException( 'The function is still associated with running job(s).') source = func_db.code['source'] if source == 'package': self.storage_provider.delete(context.get_ctx().projectid, id) # Delete all resources created by orchestrator asynchronously. self.engine_client.delete_function(id) # This will also delete function service mapping as well. db_api.delete_function(id)
def delete(self, id): """Delete runtime.""" LOG.info("Delete runtime [id=%s]", id) with db_api.transaction(): runtime_db = db_api.get_runtime(id) # Runtime can not be deleted if still associate with functions. funcs = db_api.get_functions(runtime_id={'eq': id}) if len(funcs): raise exc.NotAllowedException( 'Runtime %s is still in use.' % id ) runtime_db.status = 'deleting' # Clean related resources asynchronously self.engine_client.delete_runtime(id)
def put(self, id, **kwargs): """Update function. - Function can not being used by job. - Function can not being executed. - (TODO)Function status should be changed so no execution will create when function is updating. """ values = {} try: for key in UPDATE_ALLOWED: if kwargs.get(key) is not None: if key == "code": kwargs[key] = json.loads(kwargs[key]) values.update({key: kwargs[key]}) except Exception as e: raise exc.InputException("Invalid input, %s" % str(e)) LOG.info('Update function %s, params: %s', id, values) ctx = context.get_ctx() if values.get('timeout'): common.validate_int_in_range('timeout', values['timeout'], CONF.resource_limits.min_timeout, CONF.resource_limits.max_timeout) db_update_only = set(['name', 'description', 'timeout']) if set(values.keys()).issubset(db_update_only): func_db = db_api.update_function(id, values) else: source = values.get('code', {}).get('source') md5sum = values.get('code', {}).get('md5sum') cpu = values.get('cpu') memory_size = values.get('memory_size') # Check cpu and memory_size values when updating. if cpu is not None: common.validate_int_in_range('cpu', values['cpu'], CONF.resource_limits.min_cpu, CONF.resource_limits.max_cpu) if memory_size is not None: common.validate_int_in_range('memory', values['memory_size'], CONF.resource_limits.min_memory, CONF.resource_limits.max_memory) with db_api.transaction(): pre_func = db_api.get_function(id) if len(pre_func.jobs) > 0: raise exc.NotAllowedException( 'The function is still associated with running job(s).' ) pre_source = pre_func.code['source'] pre_md5sum = pre_func.code.get('md5sum') if source and source != pre_source: raise exc.InputException( "The function code type can not be changed.") if pre_source == constants.IMAGE_FUNCTION: raise exc.InputException( "The image type function code can not be changed.") # Package type function. 'code' and 'entry' make sense only if # 'package' is provided package_updated = False if (pre_source == constants.PACKAGE_FUNCTION and values.get('package') is not None): if md5sum and md5sum == pre_md5sum: raise exc.InputException( "The function code checksum is not changed.") # Update the package data. data = values['package'].file.read() package_updated, md5sum = self.storage_provider.store( ctx.projectid, id, data, md5sum=md5sum) values.setdefault('code', {}).update({ "md5sum": md5sum, "source": pre_source }) values.pop('package') # Swift type function if (pre_source == constants.SWIFT_FUNCTION and "swift" in values.get('code', {})): swift_info = values['code']["swift"] if not (swift_info.get('container') or swift_info.get('object')): raise exc.InputException( "Either container or object must be provided for " "swift type function update.") new_swift_info = pre_func.code['swift'] new_swift_info.update(swift_info) self._check_swift(new_swift_info.get('container'), new_swift_info.get('object')) values['code'] = { "source": pre_source, "swift": new_swift_info } # Delete allocated resources in orchestrator and etcd. self.engine_client.delete_function(id) etcd_util.delete_function(id) func_db = db_api.update_function(id, values) # Delete the old function package if needed if package_updated: self.storage_provider.delete(ctx.projectid, id, pre_md5sum) pecan.response.status = 200 return resources.Function.from_db_obj(func_db).to_dict()