def post(self, **kwargs): LOG.info("Creating function, params=%s", kwargs) if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s not available.' % kwargs['runtime_id']) values = { 'name': kwargs['name'], 'description': kwargs.get('description', None), 'runtime_id': kwargs['runtime_id'], 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main'), } if values['code'].get('package', False): data = kwargs['package'].file.read() ctx = context.get_ctx() with db_api.transaction(): func_db = db_api.create_function(values) self.storage_provider.store(ctx.projectid, func_db.id, data) pecan.response.status = 201 return resources.Function.from_dict(func_db.to_dict()).to_dict()
def store(self, project_id, function, data, md5sum=None): """Store the function package data to local file system. :param project_id: Project ID. :param function: Function ID. :param data: Package file content. :param md5sum: The MD5 provided by the user. :return: MD5 value of the package. """ LOG.debug('Store package, function: %s, project: %s', function, project_id) project_path = os.path.join(self.base_path, project_id) fileutils.ensure_tree(project_path) # Check md5 md5_actual = common.md5(content=data) if md5sum and md5_actual != md5sum: raise exc.InputException("Package md5 mismatch.") # The md5 is contained in the package path. new_func_zip = os.path.join(project_path, '%s.zip.new' % function) func_zip = os.path.join(project_path, PACKAGE_NAME_TEMPLATE % (function, md5_actual)) # Store package with open(new_func_zip, 'wb') as fd: fd.write(data) if not zipfile.is_zipfile(new_func_zip): fileutils.delete_if_exists(new_func_zip) raise exc.InputException("Package is not a valid ZIP package.") os.rename(new_func_zip, func_zip) return md5_actual
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 validate_job(params): first_time = params.get('first_execution_time') pattern = params.get('pattern') count = params.get('count') start_time = timeutils.utcnow() if not (first_time or pattern): raise exc.InputException( 'Pattern or first_execution_time must be specified.') if first_time: first_time = validate_next_time(first_time) if not pattern and count and count > 1: raise exc.InputException( 'Pattern must be provided if count is greater than 1.') next_time = first_time if not (pattern or count): count = 1 if pattern: validate_pattern(pattern) if not first_time: next_time = croniter.croniter(pattern, start_time).get_next( datetime.datetime) return first_time, next_time, count
def post(self, **kwargs): LOG.info("Creating %s, params: %s", self.type, kwargs) # When using image to create function, runtime_id is not a required # param. if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) values = { 'name': kwargs.get('name'), 'description': kwargs.get('description'), 'runtime_id': kwargs.get('runtime_id'), 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main.main'), } source = values['code'].get('source') if not source or source not in CODE_SOURCE: raise exc.InputException( 'Invalid code source specified, available sources: %s' % ', '.join(CODE_SOURCE)) if source != constants.IMAGE_FUNCTION: if not kwargs.get('runtime_id'): raise exc.InputException('"runtime_id" must be specified.') runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s is not available.' % kwargs['runtime_id']) store = False if values['code']['source'] == constants.PACKAGE_FUNCTION: store = True data = kwargs['package'].file.read() elif values['code']['source'] == constants.SWIFT_FUNCTION: swift_info = values['code'].get('swift', {}) self._check_swift(swift_info.get('container'), swift_info.get('object')) if cfg.CONF.pecan.auth_enable: try: values['trust_id'] = keystone_util.create_trust().id LOG.debug('Trust %s created', values['trust_id']) except Exception: raise exc.TrustFailedException( 'Trust creation failed for function.') with db_api.transaction(): func_db = db_api.create_function(values) if store: ctx = context.get_ctx() self.storage_provider.store(ctx.projectid, func_db.id, data) pecan.response.status = 201 return resources.Function.from_dict(func_db.to_dict()).to_dict()
def _check_swift(self, container, object): # Auth needs to be enabled because qinling needs to check swift # object using user's credential. if not CONF.pecan.auth_enable: raise exc.InputException('Swift object not supported.') if not swift_util.check_object(container, object): raise exc.InputException('Object does not exist in Swift.')
def put(self, id, job): """Update job definition. 1. Can not update a finished job. 2. Can not change job type. 3. Allow to pause a one-shot job and resume before its first execution time. """ values = {} for key in UPDATE_ALLOWED: if job.to_dict().get(key) is not None: values.update({key: job.to_dict()[key]}) LOG.info('Update resource, params: %s', values, resource={ 'type': self.type, 'id': id }) new_status = values.get('status') pattern = values.get('pattern') next_execution_time = values.get('next_execution_time') job_db = db_api.get_job(id) if job_db.status in [status.DONE, status.CANCELLED]: raise exc.InputException('Can not update a finished job.') if pattern: if not job_db.pattern: raise exc.InputException('Can not change job type.') jobs.validate_pattern(pattern) elif pattern == '' and job_db.pattern: raise exc.InputException('Can not change job type.') valid_states = [status.RUNNING, status.CANCELLED, status.PAUSED] if new_status and new_status not in valid_states: raise exc.InputException('Invalid status.') if next_execution_time: values['next_execution_time'] = jobs.validate_next_time( next_execution_time) elif (job_db.status == status.PAUSED and new_status == status.RUNNING): p = job_db.pattern or pattern if not p: # Check if the next execution time for one-shot job is still # valid. jobs.validate_next_time(job_db.next_execution_time) else: # Update next_execution_time for recurring job. values['next_execution_time'] = croniter.croniter( p, timeutils.utcnow()).get_next(datetime.datetime) updated_job = db_api.update_job(id, values) return resources.Job.from_dict(updated_job.to_dict())
def validate_int_in_range(name, value, min_allowed, max_allowed): try: value_int = int(value) except ValueError: raise exc.InputException( 'Invalid %s resource specified. An integer is required.' % name) if (value_int < min_allowed or value_int > max_allowed): raise exc.InputException( '%s resource limitation not within the allowable range: ' '%s ~ %s(%s).' % (name, min_allowed, max_allowed, 'millicpu' if name == 'cpu' else 'bytes'))
def validate_int_in_range(name, value, min_allowed, max_allowed): unit_mapping = {"cpu": "millicpu", "memory": "bytes", "timeout": "seconds"} try: value_int = int(value) except ValueError: raise exc.InputException( 'Invalid %s resource specified. An integer is required.' % name) if (value_int < min_allowed or value_int > max_allowed): raise exc.InputException( '%s resource limitation not within the allowable range: ' '%s ~ %s(%s).' % (name, min_allowed, max_allowed, unit_mapping[name]))
def validate_next_time(next_execution_time): next_time = next_execution_time if isinstance(next_execution_time, six.string_types): try: # We need naive datetime object. next_time = parser.parse(next_execution_time, ignoretz=True) except ValueError as e: raise exc.InputException(str(e)) valid_min_time = timeutils.utcnow() + datetime.timedelta(0, 60) if valid_min_time > next_time: raise exc.InputException( 'Execution time must be at least 1 minute in the future.') return next_time
def post(self, webhook): acl.enforce('webhook:create', context.get_ctx()) params = webhook.to_dict() if not (params.get("function_id") or params.get("function_alias")): raise exc.InputException( 'Either function_alias or function_id must be provided.') # if function_alias provided function_alias = params.get('function_alias') if function_alias: alias_db = db_api.get_function_alias(function_alias) function_id = alias_db.function_id version = alias_db.function_version params.update({ 'function_id': function_id, 'function_version': version }) LOG.info("Creating %s, params: %s", self.type, params) # Even admin user can not expose normal user's function db_api.get_function(params['function_id'], insecure=False) version = params.get('function_version', 0) if version > 0: db_api.get_function_version(params['function_id'], version) webhook_d = db_api.create_webhook(params).to_dict() return resources.Webhook.from_dict( self._add_webhook_url(webhook_d['id'], webhook_d))
def validate_pattern(pattern): try: croniter.croniter(pattern) except (ValueError, KeyError): raise exc.InputException( 'The specified pattern is not valid: {}'.format(pattern) )
def post(self, body): """Create a new alias for the specified function. The supported body params: - function_id: Required. Function id the alias points to. - name: Required. Alias name, must be unique within the project. - function_version: Optional. Version number the alias points to. - description: Optional. The description of the new alias. """ ctx = context.get_ctx() acl.enforce('function_alias:create', ctx) params = body.to_dict() if not POST_REQUIRED.issubset(set(params.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED ) LOG.info("Creating Alias, params: %s", params) values = { 'function_id': params.get('function_id'), 'name': params.get('name'), 'function_version': params.get('function_version'), 'description': params.get('description'), } alias = db_api.create_function_alias(**values) LOG.info("New alias created.") return resources.FunctionAlias.from_db_obj(alias)
def post(self, job): """Creates a new job.""" params = job.to_dict() if not POST_REQUIRED.issubset(set(params.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) # Check the input params. first_time, next_time, count = jobs.validate_job(params) LOG.info("Creating %s, params: %s", self.type, params) with db_api.transaction(): db_api.get_function(params['function_id']) values = { 'name': params.get('name'), 'pattern': params.get('pattern'), 'first_execution_time': first_time, 'next_execution_time': next_time, 'count': count, 'function_id': params['function_id'], 'function_input': params.get('function_input') or {}, 'status': status.RUNNING } db_job = db_api.create_job(values) return resources.Job.from_dict(db_job.to_dict())
def validate(self, value): if not value: return {} if not isinstance(value, dict): raise exc.InputException( 'JsonType field value must be a dictionary [actual=%s]' % value) return value
def post(self, **kwargs): LOG.info("Creating %s, params: %s", self.type, kwargs) # When using image to create function, runtime_id is not a required # param. if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) values = { 'name': kwargs['name'], 'description': kwargs.get('description'), 'runtime_id': kwargs.get('runtime_id'), 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main.main'), } source = values['code'].get('source') if not source or source not in CODE_SOURCE: raise exc.InputException( 'Invalid code source specified, available sources: %s' % ', '.join(CODE_SOURCE)) if source != 'image': if not kwargs.get('runtime_id'): raise exc.InputException('"runtime_id" must be specified.') runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s is not available.' % kwargs['runtime_id']) store = False if values['code']['source'] == 'package': store = True data = kwargs['package'].file.read() elif values['code']['source'] == 'swift': # Auth needs to be enabled because qinling needs to check swift # object using user's credential. if not CONF.pecan.auth_enable: raise exc.InputException('Swift object not supported.') container = values['code']['swift'].get('container') object = values['code']['swift'].get('object') if not swift_util.check_object(container, object): raise exc.InputException('Object does not exist in Swift.') with db_api.transaction(): func_db = db_api.create_function(values) if store: ctx = context.get_ctx() self.storage_provider.store(ctx.projectid, func_db.id, data) pecan.response.status = 201 return resources.Function.from_dict(func_db.to_dict()).to_dict()
def post(self, body): ctx = context.get_ctx() acl.enforce('execution:create', ctx) params = body.to_dict() if not (params.get("function_id") or params.get("function_alias")): raise exc.InputException( 'Either function_alias or function_id must be provided.') LOG.info("Creating %s. [params=%s]", self.type, params) db_model = executions.create_execution(self.engine_client, params) return resources.Execution.from_db_obj(db_model)
def post(self, runtime): params = runtime.to_dict() if not POST_REQUIRED.issubset(set(params.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) LOG.info("Creating %s, params: %s", self.type, params) params.update({'status': status.CREATING}) db_model = db_api.create_runtime(params) self.engine_client.create_runtime(db_model.id) return resources.Runtime.from_dict(db_model.to_dict())
def store(self, project_id, function, data): LOG.info('Store package, function: %s, project: %s', function, project_id) project_path = os.path.join(CONF.storage.file_system_dir, project_id) fileutils.ensure_tree(project_path) func_zip = os.path.join(project_path, '%s.zip' % function) with open(func_zip, 'wb') as fd: fd.write(data) if not zipfile.is_zipfile(func_zip): fileutils.delete_if_exists(func_zip) raise exc.InputException("Package is not a valid ZIP package.")
def post(self, webhook): acl.enforce('webhook:create', context.get_ctx()) params = webhook.to_dict() if not POST_REQUIRED.issubset(set(params.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) LOG.info("Creating %s, params: %s", self.type, params) # Even admin user can not expose normal user's function db_api.get_function(params['function_id'], insecure=False) webhook_d = db_api.create_webhook(params).to_dict() return resources.Webhook.from_dict( self._add_webhook_url(webhook_d['id'], webhook_d))
def post(self, runtime): acl.enforce('runtime:create', context.get_ctx()) params = runtime.to_dict() if 'trusted' not in params: params['trusted'] = True if not POST_REQUIRED.issubset(set(params.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) LOG.info("Creating %s, params: %s", self.type, params) params.update({'status': status.CREATING}) db_model = db_api.create_runtime(params) self.engine_client.create_runtime(db_model.id) return resources.Runtime.from_db_obj(db_model)
def post(self, job): """Creates a new job.""" params = job.to_dict() if not (params.get("function_id") or params.get("function_alias")): raise exc.InputException( 'Either function_alias or function_id must be provided.') LOG.info("Creating %s, params: %s", self.type, params) # Check the input params. first_time, next_time, count = jobs.validate_job(params) version = params.get('function_version', 0) function_alias = params.get('function_alias') if function_alias: # Check if the alias exists. db_api.get_function_alias(function_alias) else: # Check the function(version) exists. db_api.get_function(params['function_id']) if version > 0: # Check if the version exists. db_api.get_function_version(params['function_id'], version) values = { 'name': params.get('name'), 'pattern': params.get('pattern'), 'first_execution_time': first_time, 'next_execution_time': next_time, 'count': count, 'function_alias': function_alias, 'function_id': params.get("function_id"), 'function_version': version, 'function_input': params.get('function_input'), 'status': status.RUNNING } db_job = db_api.create_job(values) return resources.Job.from_db_obj(db_job)
def store(self, project_id, function, data): """Store the function package data to local file system. :param project_id: Project ID. :param function: Function ID. :param data: Package data. """ LOG.debug('Store package, function: %s, project: %s', function, project_id) project_path = os.path.join(CONF.storage.file_system_dir, project_id) fileutils.ensure_tree(project_path) new_func_zip = os.path.join(project_path, '%s.zip.new' % function) func_zip = os.path.join(project_path, '%s.zip' % function) with open(new_func_zip, 'wb') as fd: fd.write(data) if not zipfile.is_zipfile(new_func_zip): fileutils.delete_if_exists(new_func_zip) raise exc.InputException("Package is not a valid ZIP package.") os.rename(new_func_zip, func_zip)
def post(self, job): """Creates a new job.""" params = job.to_dict() if not POST_REQUIRED.issubset(set(params.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) # Check the input params. first_time, next_time, count = jobs.validate_job(params) LOG.info("Creating %s, params: %s", self.type, params) with db_api.transaction(): db_api.get_function(params['function_id']) values = { 'name': params.get('name'), 'pattern': params.get('pattern'), 'first_execution_time': first_time, 'next_execution_time': next_time, 'count': count, 'function_id': params['function_id'], 'function_input': params.get('function_input') or {}, 'status': status.RUNNING } if cfg.CONF.pecan.auth_enable: values['trust_id'] = keystone_util.create_trust().id try: db_job = db_api.create_job(values) except Exception: # Delete trust before raising exception. keystone_util.delete_trust(values.get('trust_id')) raise return resources.Job.from_dict(db_job.to_dict())
def validate(value): if not uuidutils.is_uuid_like(value): raise exc.InputException("Expected a uuid but received %s." % value) return value
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 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()
def post(self, **kwargs): # When using image to create function, runtime_id is not a required # param. if not POST_REQUIRED.issubset(set(kwargs.keys())): raise exc.InputException( 'Required param is missing. Required: %s' % POST_REQUIRED) LOG.info("Creating function, params: %s", kwargs) values = { 'name': kwargs.get('name'), 'description': kwargs.get('description'), 'runtime_id': kwargs.get('runtime_id'), 'code': json.loads(kwargs['code']), 'entry': kwargs.get('entry', 'main.main'), 'cpu': kwargs.get('cpu', CONF.resource_limits.default_cpu), 'memory_size': kwargs.get('memory_size', CONF.resource_limits.default_memory), 'timeout': kwargs.get('timeout', CONF.resource_limits.default_timeout), } common.validate_int_in_range('timeout', values['timeout'], CONF.resource_limits.min_timeout, CONF.resource_limits.max_timeout) common.validate_int_in_range('cpu', values['cpu'], CONF.resource_limits.min_cpu, CONF.resource_limits.max_cpu) common.validate_int_in_range('memory', values['memory_size'], CONF.resource_limits.min_memory, CONF.resource_limits.max_memory) source = values['code'].get('source') if not source or source not in CODE_SOURCE: raise exc.InputException( 'Invalid code source specified, available sources: %s' % ', '.join(CODE_SOURCE)) if source != constants.IMAGE_FUNCTION: if not kwargs.get('runtime_id'): raise exc.InputException('"runtime_id" must be specified.') runtime = db_api.get_runtime(kwargs['runtime_id']) if runtime.status != 'available': raise exc.InputException('Runtime %s is not available.' % kwargs['runtime_id']) store = False create_trust = True if source == constants.PACKAGE_FUNCTION: store = True md5sum = values['code'].get('md5sum') data = kwargs['package'].file.read() elif source == constants.SWIFT_FUNCTION: swift_info = values['code'].get('swift', {}) if not (swift_info.get('container') and swift_info.get('object')): raise exc.InputException("Both container and object must be " "provided for swift type function.") self._check_swift(swift_info.get('container'), swift_info.get('object')) else: create_trust = False values['entry'] = None if cfg.CONF.pecan.auth_enable and create_trust: try: values['trust_id'] = keystone_util.create_trust().id LOG.debug('Trust %s created', values['trust_id']) except Exception: raise exc.TrustFailedException( 'Trust creation failed for function.') # Create function and store the package data inside a db transaction so # that the function won't be created if any error happened during # package store. with db_api.transaction(): func_db = db_api.create_function(values) if store: try: ctx = context.get_ctx() _, actual_md5 = self.storage_provider.store(ctx.projectid, func_db.id, data, md5sum=md5sum) values['code'].update({"md5sum": actual_md5}) func_db = db_api.update_function(func_db.id, values) except Exception as e: LOG.exception("Failed to store function package.") keystone_util.delete_trust(values['trust_id']) raise e pecan.response.status = 201 return resources.Function.from_db_obj(func_db).to_dict()