Example #1
0
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)
Example #2
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)
Example #3
0
    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)
Example #4
0
    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
            })