Esempio n. 1
0
    def scale_down(self, function_id, version, scale):
        """Scale down the workers for function version execution.

        This is admin only operation. The load monitoring of execution
        depends on the monitoring solution of underlying orchestrator.
        """
        acl.enforce('function_version:scale_down', context.get_ctx())

        db_api.get_function(function_id)
        params = scale.to_dict()

        # If version=0, it's equivalent to /functions/<funcion-id>/scale_down
        if version > 0:
            db_api.get_function_version(function_id, version)

        workers = etcd_util.get_workers(function_id, version=version)
        if len(workers) <= 1:
            LOG.info('No need to scale down function %s(version %s)',
                     function_id, version)
            return

        LOG.info('Starting to scale down function %s(version %s), params: %s',
                 function_id, version, params)
        self.engine_client.scaledown_function(function_id, version=version,
                                              count=params['count'])
Esempio n. 2
0
    def get_all(self, function_id=None, all_projects=False, project_id=None,
                status=None, description=None):
        """Return a list of executions.

        :param function_id: Optional. Filtering executions by function_id.
        :param project_id: Optional. Admin user can query other projects
            resources, the param is ignored for normal user.
        :param all_projects: Optional. Get resources of all projects.
        :param status: Optional. Filter by execution status.
        :param description: Optional. Filter by description.
        """
        ctx = context.get_ctx()
        if project_id and not ctx.is_admin:
            project_id = context.ctx().projectid
        if project_id and ctx.is_admin:
            all_projects = True

        if all_projects:
            acl.enforce('execution:get_all:all_projects', ctx)

        filters = rest_utils.get_filters(
            function_id=function_id,
            project_id=project_id,
            status=status,
            description=description
        )
        LOG.info("Get all %ss. filters=%s", self.type, filters)

        db_execs = db_api.get_executions(insecure=all_projects, **filters)
        executions = [resources.Execution.from_dict(db_model.to_dict())
                      for db_model in db_execs]

        return resources.Executions(executions=executions)
Esempio n. 3
0
    def scale_up(self, function_id, version, scale):
        """Scale up the workers for function version execution.

        This is admin only operation. The load monitoring of execution
        depends on the monitoring solution of underlying orchestrator.
        """
        acl.enforce('function_version:scale_up', context.get_ctx())

        func_db = db_api.get_function(function_id)

        # If version=0, it's equivalent to /functions/<funcion-id>/scale_up
        if version > 0:
            db_api.get_function_version(function_id, version)

        params = scale.to_dict()

        LOG.info('Starting to scale up function %s(version %s), params: %s',
                 function_id, version, params)

        self.engine_client.scaleup_function(
            function_id,
            runtime_id=func_db.runtime_id,
            version=version,
            count=params['count']
        )
Esempio n. 4
0
    def get(self, alias_name):
        acl.enforce('function_alias:get', context.get_ctx())
        LOG.info("Getting function alias  %s.", alias_name)

        alias = db_api.get_function_alias(alias_name)

        return resources.FunctionAlias.from_db_obj(alias)
Esempio n. 5
0
    def get_all(self, all_projects=False, project_id=None):
        """Get all the function aliases.

        :param project_id: Optional. Admin user can query other projects
            resources, the param is ignored for normal user.
        :param all_projects: Optional. Get resources of all projects.
        """
        ctx = context.get_ctx()
        project_id, all_projects = rest_utils.get_project_params(
            project_id, all_projects)
        if all_projects:
            acl.enforce('function_version:get_all:all_projects', ctx)

        filters = rest_utils.get_filters(project_id=project_id)

        LOG.info("Get all function aliases. filters=%s", filters)

        db_aliases = db_api.get_function_aliases(insecure=all_projects,
                                                 **filters)
        aliases = [
            resources.FunctionAlias.from_db_obj(db_model)
            for db_model in db_aliases
        ]

        return resources.FunctionAliases(function_aliases=aliases)
Esempio n. 6
0
    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)
Esempio n. 7
0
    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))
Esempio n. 8
0
    def get_all(self, all_projects=False, project_id=None):
        """Return a list of functions.

        :param project_id: Optional. Admin user can query other projects
            resources, the param is ignored for normal user.
        :param all_projects: Optional. Get resources of all projects.
        """
        ctx = context.get_ctx()
        if project_id and not ctx.is_admin:
            project_id = context.ctx().projectid
        if project_id and ctx.is_admin:
            all_projects = True

        if all_projects:
            acl.enforce('function:get_all:all_projects', ctx)

        filters = rest_utils.get_filters(project_id=project_id, )
        LOG.info("Get all functions. filters=%s", filters)
        db_functions = db_api.get_functions(insecure=all_projects, **filters)
        functions = [
            resources.Function.from_dict(db_model.to_dict())
            for db_model in db_functions
        ]

        return resources.Functions(functions=functions)
Esempio n. 9
0
    def put(self, id, webhook):
        """Update webhook.

        Currently, only function_id and function_version are allowed to update.
        """
        acl.enforce('webhook:update', context.get_ctx())

        values = {}
        for key in UPDATE_ALLOWED:
            if webhook.to_dict().get(key) is not None:
                values.update({key: webhook.to_dict()[key]})

        LOG.info('Update %s %s, params: %s', self.type, id, values)

        # Even admin user can not expose normal user's function
        webhook_db = db_api.get_webhook(id, insecure=False)
        pre_function_id = webhook_db.function_id
        pre_version = webhook_db.function_version

        new_function_id = values.get("function_id", pre_function_id)
        new_version = values.get("function_version", pre_version)

        db_api.get_function(new_function_id, insecure=False)
        if new_version > 0:
            db_api.get_function_version(new_function_id, new_version)

        webhook = db_api.update_webhook(id, values).to_dict()
        return resources.Webhook.from_dict(self._add_webhook_url(id, webhook))
Esempio n. 10
0
    def post(self, function_id, body):
        """Create a new version for the function.

        Only allow to create version for package type function.

        The supported boy params:
            - description: Optional. The description of the new version.
        """
        ctx = context.get_ctx()
        acl.enforce('function_version:create', ctx)

        params = body.to_dict()
        values = {
            'description': params.get('description'),
        }

        # Try to create a new function version within lock and db transaction
        try:
            version = self._create_function_version(ctx.project_id,
                                                    function_id, **values)
        except exc.EtcdLockException as e:
            LOG.exception(str(e))
            # Reraise a generic exception as the end users should not know
            # the underlying details.
            raise exc.QinlingException('Internal server error.')

        return resources.FunctionVersion.from_db_obj(version)
Esempio n. 11
0
    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)
Esempio n. 12
0
    def post(self, body):
        ctx = context.get_ctx()
        acl.enforce('execution:create', ctx)

        params = body.to_dict()
        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)
Esempio n. 13
0
    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)
Esempio n. 14
0
    def delete(self, alias_name):
        """Delete a specific alias.

        """
        ctx = context.get_ctx()
        acl.enforce('function_alias:delete', ctx)
        LOG.info("Deleting alias %s.", alias_name)

        db_api.delete_function_alias(alias_name)

        LOG.info("Alias %s deleted.", alias_name)
Esempio n. 15
0
    def get_all(self, all_projects=False, project_id=None):
        project_id, all_projects = rest_utils.get_project_params(
            project_id, all_projects)
        if all_projects:
            acl.enforce('job:get_all:all_projects', context.get_ctx())

        filters = rest_utils.get_filters(project_id=project_id, )
        LOG.info("Get all %ss. filters=%s", self.type, filters)
        db_jobs = db_api.get_jobs(insecure=all_projects, **filters)
        jobs = [resources.Job.from_db_obj(db_model) for db_model in db_jobs]

        return resources.Jobs(jobs=jobs)
Esempio n. 16
0
    def get_all(self, function_id):
        acl.enforce('function_worker:get_all', context.get_ctx())
        LOG.info("Get workers for function %s.", function_id)

        workers = etcd_util.get_workers(function_id, CONF)
        workers = [
            resources.FunctionWorker.from_dict({
                'function_id': function_id,
                'worker_name': w
            }) for w in workers
        ]

        return resources.FunctionWorkers(workers=workers)
Esempio n. 17
0
    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)
Esempio n. 18
0
    def detach(self, id):
        """Detach the function from its underlying workers.

        This is admin only operation, which gives admin user a safe way to
        clean up the underlying resources allocated for the function.
        """
        acl.enforce('function:detach', context.get_ctx())

        db_api.get_function(id)
        LOG.info('Starting to detach function %s', id)

        # Delete allocated resources in orchestrator and etcd keys.
        self.engine_client.delete_function(id)
        etcd_util.delete_function(id)
Esempio n. 19
0
    def get_all(self, function_id, function_version=0):
        acl.enforce('function_worker:get_all', context.get_ctx())

        LOG.info("Getting workers for function %s(version %s).", function_id,
                 function_version)

        workers = etcd_util.get_workers(function_id, version=function_version)
        workers = [
            resources.FunctionWorker.from_dict({
                'function_id': function_id,
                'function_version': function_version,
                'worker_name': w
            }) for w in workers
        ]

        return resources.FunctionWorkers(workers=workers)
Esempio n. 20
0
    def scale_up(self, id, scale):
        """Scale up the containers for function execution.

        This is admin only operation. The load monitoring of function execution
        depends on the monitoring solution of underlying orchestrator.
        """
        acl.enforce('function:scale_up', context.get_ctx())

        func_db = db_api.get_function(id)
        params = scale.to_dict()

        LOG.info('Starting to scale up function %s, params: %s', id, params)

        self.engine_client.scaleup_function(id,
                                            runtime_id=func_db.runtime_id,
                                            count=params['count'])
Esempio n. 21
0
    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))
Esempio n. 22
0
    def get_all(self, all_projects=False, project_id=None):
        project_id, all_projects = rest_utils.get_project_params(
            project_id, all_projects)
        if all_projects:
            acl.enforce('webhook:get_all:all_projects', context.get_ctx())

        filters = rest_utils.get_filters(project_id=project_id, )

        LOG.info("Get all %ss. filters=%s", self.type, filters)
        webhooks = []
        for i in db_api.get_webhooks(insecure=all_projects, **filters):
            webhooks.append(
                resources.Webhook.from_dict(
                    self._add_webhook_url(i.id, i.to_dict())))

        return resources.Webhooks(webhooks=webhooks)
Esempio n. 23
0
    def get_all(self, function_id):
        """Get all the versions of the given function.

        Admin user can get all versions for the normal user's function.
        """
        acl.enforce('function_version:get_all', context.get_ctx())
        LOG.info("Getting versions for function %s.", function_id)

        # Getting function and versions needs to happen in a db transaction
        with db_api.transaction():
            func_db = db_api.get_function(function_id)
            db_versions = func_db.versions

        versions = [resources.FunctionVersion.from_db_obj(v)
                    for v in db_versions]

        return resources.FunctionVersions(function_versions=versions)
Esempio n. 24
0
    def scale_down(self, id, scale):
        """Scale down the containers for function execution.

        This is admin only operation. The load monitoring of function execution
        depends on the monitoring solution of underlying orchestrator.
        """
        acl.enforce('function:scale_down', context.get_ctx())

        db_api.get_function(id)
        workers = etcd_util.get_workers(id)
        params = scale.to_dict()
        if len(workers) <= 1:
            LOG.info('No need to scale down function %s', id)
            return

        LOG.info('Starting to scale down function %s, params: %s', id, params)
        self.engine_client.scaledown_function(id, count=params['count'])
Esempio n. 25
0
    def post(self, runtime):
        acl.enforce('runtime:create', context.get_ctx())

        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_db_obj(db_model)
Esempio n. 26
0
    def delete(self, id):
        acl.enforce('runtime:delete', context.get_ctx())

        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(insecure=True, 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)
Esempio n. 27
0
    def get(self, id):
        """Get function information or download function package.

        This method can support HTTP request using either
        'Accept:application/json' or no 'Accept' header.
        """
        ctx = context.get_ctx()
        acl.enforce('function:get', ctx)

        download = strutils.bool_from_string(
            pecan.request.GET.get('download', False)
        )
        func_db = db_api.get_function(id)

        if not download:
            LOG.info("Getting function %s.", id)
            pecan.override_template('json')
            return resources.Function.from_db_obj(func_db).to_dict()

        LOG.info("Downloading function %s", id)
        source = func_db.code['source']

        if source == constants.PACKAGE_FUNCTION:
            f = self.storage_provider.retrieve(func_db.project_id, id,
                                               func_db.code['md5sum'])
        elif source == constants.SWIFT_FUNCTION:
            container = func_db.code['swift']['container']
            obj = func_db.code['swift']['object']
            f = swift_util.download_object(container, obj)
        else:
            msg = 'Download image function is not allowed.'
            pecan.abort(
                status_code=405,
                detail=msg,
                headers={'Server-Error-Message': msg}
            )

        pecan.response.app_iter = (f if isinstance(f, collections.Iterable)
                                   else FileIter(f))
        pecan.response.headers['Content-Type'] = 'application/zip'
        pecan.response.headers['Content-Disposition'] = (
            'attachment; filename="%s"' % id
        )
Esempio n. 28
0
    def detach(self, function_id, version):
        """Detach the function version from its underlying workers.

        This is admin only operation, which gives admin user a safe way to
        clean up the underlying resources allocated for the function version.
        """
        acl.enforce('function_version:detach', context.get_ctx())

        db_api.get_function(function_id)
        # If version=0, it's equivalent to /functions/<funcion-id>/detach
        if version > 0:
            db_api.get_function_version(function_id, version)

        LOG.info('Starting to detach function %s(version %s)', function_id,
                 version)

        # Delete allocated resources in orchestrator and etcd keys.
        self.engine_client.delete_function(function_id, version=version)
        etcd_util.delete_function(function_id, version=version)
Esempio n. 29
0
    def pool(self, id):
        """Get the pool information for the runtime.

        This operation should be admin only.

        We don't check the runtime existence, because this function
        also helps us to check the underlying pool even after the runtime
        is already deleted.
        """
        acl.enforce('runtime_pool:get_all', context.get_ctx())

        LOG.info("Getting pool information for runtime %s.", id)
        capacity = self.engine_client.get_runtime_pool(id)
        pool_capacity = resources.RuntimePoolCapacity.from_dict(capacity)

        return resources.RuntimePool.from_dict({
            "name": id,
            "capacity": pool_capacity
        })
Esempio n. 30
0
    def put(self, id, webhook):
        """Update webhook.

        Currently, we only support update function_id.
        """
        acl.enforce('webhook:update', context.get_ctx())

        values = {}
        for key in UPDATE_ALLOWED:
            if webhook.to_dict().get(key) is not None:
                values.update({key: webhook.to_dict()[key]})

        LOG.info('Update %s %s, params: %s', self.type, id, values)

        if 'function_id' in values:
            # Even admin user can not expose normal user's function
            db_api.get_function(values['function_id'], insecure=False)

        webhook = db_api.update_webhook(id, values).to_dict()
        return resources.Webhook.from_dict(self._add_webhook_url(id, webhook))