Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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()