Пример #1
0
        def __init__(
            self,
            db_url=None,
            repo_base_url=None,
            s3_endpoint_url=None,
            default_namespace=None,
        ):
            cfg = config('yatai_service')
            repo_base_url = repo_base_url or cfg.get('repository_base_url')
            db_url = db_url or cfg.get('db_url')
            s3_endpoint_url = s3_endpoint_url or cfg.get('s3_endpoint_url') or None
            default_namespace = default_namespace or cfg.get('default_namespace')

            self.default_namespace = default_namespace
            self.repo = Repository(repo_base_url, s3_endpoint_url)

            self.db = DB(db_url)
Пример #2
0
def start_yatai_service_grpc_server(
    db_url,
    grpc_port,
    ui_port,
    with_ui,
    base_url,
    repository_type,
    file_system_directory,
    s3_url,
    s3_endpoint_url,
    gcs_url,
    web_ui_log_path: str = Provide[BentoMLContainer.yatai_logging_path],
):
    # Lazily import grpcio for YataiSerivce gRPC related actions
    import grpc
    from bentoml.yatai.db import DB
    from bentoml.yatai.repository import create_repository
    from bentoml.yatai.yatai_service_impl import get_yatai_service_impl
    from bentoml.yatai.proto.yatai_service_pb2_grpc import add_YataiServicer_to_server
    from bentoml.yatai.proto.yatai_service_pb2_grpc import YataiServicer

    YataiServicerImpl = get_yatai_service_impl(YataiServicer)
    yatai_service = YataiServicerImpl(
        repository=create_repository(repository_type, file_system_directory,
                                     s3_url, s3_endpoint_url, gcs_url),
        database=DB(db_url),
    )

    # Define interceptors here
    grpc_interceptors = [PromServerInterceptor(), ServiceLatencyInterceptor()]
    server = grpc.server(
        futures.ThreadPoolExecutor(max_workers=10),
        interceptors=grpc_interceptors,
    )
    add_YataiServicer_to_server(yatai_service, server)
    debug_mode = get_debug_mode()
    if debug_mode:
        try:
            logger.debug("Enabling gRPC server reflection for debugging")
            from bentoml.yatai.proto import yatai_service_pb2
            from grpc_reflection.v1alpha import reflection

            SERVICE_NAMES = (
                yatai_service_pb2.DESCRIPTOR.services_by_name["Yatai"].
                full_name,
                reflection.SERVICE_NAME,
            )
            reflection.enable_server_reflection(SERVICE_NAMES, server)
        except ImportError:
            logger.debug(
                "Failed to enable gRPC server reflection, missing required package: "
                '"pip install grpcio-reflection"')
    server.add_insecure_port(f"[::]:{grpc_port}")

    # NOTE: the current implementation sets prometheus_port to
    # 50052 to accomodate with Makefile setups. Currently there
    # isn't a way to find the reserve_free_port dynamically inside
    # Makefile to find the free ports for prometheus_port without
    # the help of a shell scripts.
    prometheus_port = 50052
    with reserve_free_port() as port:
        prometheus_port = port
    # prevents wsgi to see prometheus_port as used
    start_http_server(prometheus_port)
    server.start()
    if with_ui:
        ensure_node_available_or_raise()
        yatai_grpc_server_address = f"localhost:{grpc_port}"
        prometheus_address = f"http://localhost:{prometheus_port}"
        async_start_yatai_service_web_ui(
            yatai_grpc_server_address,
            prometheus_address,
            ui_port,
            web_ui_log_path,
            debug_mode,
            base_url,
        )

    # We don't import _echo function from click_utils because of circular dep
    if with_ui:
        if debug_mode is True:
            ui_port = 8080
        web_ui_link = f"http://127.0.0.1:{ui_port}"
        if base_url != ".":
            web_ui_link += f"/{base_url}"
        web_ui_message = f"running on {web_ui_link}"
    else:
        web_ui_message = "off"
    if debug_mode:
        prom_ui_message = "off"
    else:
        prom_ui_message = f"running on http://127.0.0.1:{ui_port}/metrics\n"

    click.echo(
        f"* Starting BentoML YataiService gRPC Server\n"
        f'* Debug mode: { "on" if debug_mode else "off"}\n'
        f"* Web UI: {web_ui_message}\n"
        f"* Running on 127.0.0.1:{grpc_port} (Press CTRL+C to quit)\n"
        f"* Prometheus: {prom_ui_message}\n"
        f"* Help and instructions: "
        f"https://docs.bentoml.org/en/latest/guides/yatai_service.html\n"
        f'{f"* Web server log can be found here: {web_ui_log_path}" if with_ui else ""}'
        f"\n-----\n"
        f"* Usage in Python:\n"
        f'*  bento_svc.save(yatai_url="127.0.0.1:{grpc_port}")\n'
        f"*  from bentoml.yatai.client import get_yatai_client\n"
        f'*  get_yatai_client("127.0.0.1:{grpc_port}").repository.list()\n'
        f"* Usage in CLI:\n"
        f"*  bentoml list --yatai-url=127.0.0.1:{grpc_port}\n"
        f"*  bentoml containerize IrisClassifier:latest --yatai-url=127.0.0.1:"
        f"{grpc_port}\n"
        f"*  bentoml push IrisClassifier:20200918001645_CD2886 --yatai-url=127.0.0.1:"
        f"{grpc_port}\n"
        f"*  bentoml pull IrisClassifier:20200918001645_CD2886 --yatai-url=127.0.0.1:"
        f"{grpc_port}\n"
        f"*  bentoml retrieve IrisClassifier:20200918001645_CD2886 "
        f'--yatai-url=127.0.0.1:{grpc_port} --target_dir="/tmp/foo/bar"\n'
        f"*  bentoml delete IrisClassifier:20200918001645_CD2886 "
        f"--yatai-url=127.0.0.1:{grpc_port}\n"
        # TODO: simplify the example usage here once related documentation is ready
    )

    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        logger.info("Terminating YataiService gRPC server..")
        server.stop(grace=None)
Пример #3
0
def get_yatai_service(
    channel_address: str = Provide[BentoMLContainer.config.yatai.remote.url],
    access_token: str = Provide[
        BentoMLContainer.config.yatai.remote.access_token],
    access_token_header: str = Provide[
        BentoMLContainer.config.yatai.remote.access_token_header],
    tls_root_ca_cert: str = Provide[BentoMLContainer.yatai_tls_root_ca_cert],
    tls_client_key: str = Provide[
        BentoMLContainer.config.yatai.remote.tls.client_key],
    tls_client_cert: str = Provide[
        BentoMLContainer.config.yatai.remote.tls.client_cert],
    db_url: str = Provide[BentoMLContainer.yatai_database_url],
    default_namespace: str = Provide[BentoMLContainer.config.yatai.namespace],
    repository_type: str = Provide[
        BentoMLContainer.config.yatai.repository.type],
    file_system_directory: str = Provide[
        BentoMLContainer.yatai_file_system_directory],
    s3_url: str = Provide[BentoMLContainer.config.yatai.repository.s3.url],
    gcs_url: str = Provide[BentoMLContainer.config.yatai.repository.gcs.url],
):
    if channel_address:
        # Lazily import grpcio for YataiSerivce gRPC related actions
        import grpc
        from bentoml.yatai.client.interceptor import header_client_interceptor
        from bentoml.yatai.proto.yatai_service_pb2_grpc import YataiStub

        channel_address = channel_address.strip()
        logger.debug("Connecting YataiService gRPC server at: %s",
                     channel_address)
        scheme, addr = parse_grpc_url(channel_address)
        header_adder_interceptor = header_client_interceptor.header_adder_interceptor(
            access_token_header, access_token)
        if scheme in ("grpcs", "https"):
            tls_root_ca_cert = (tls_root_ca_cert
                                or certifi.where())  # default: Mozilla ca cert
            with open(tls_root_ca_cert, "rb") as fb:
                ca_cert = fb.read()
            if tls_client_key:
                with open(tls_client_key, "rb") as fb:
                    tls_client_key = fb.read()
            if tls_client_cert:
                with open(tls_client_cert, "rb") as fb:
                    tls_client_cert = fb.read()
            credentials = grpc.ssl_channel_credentials(ca_cert, tls_client_key,
                                                       tls_client_cert)
            channel = grpc.intercept_channel(
                grpc.secure_channel(addr, credentials),
                header_adder_interceptor)
        else:
            channel = grpc.intercept_channel(grpc.insecure_channel(addr),
                                             header_adder_interceptor)
        return YataiStub(channel)
    else:
        from bentoml.yatai.db import DB
        from bentoml.yatai.repository import create_repository
        from bentoml.yatai.yatai_service_impl import get_yatai_service_impl

        LocalYataiService = get_yatai_service_impl()

        logger.debug("Creating local YataiService instance")
        return LocalYataiService(
            repository=create_repository(repository_type,
                                         file_system_directory, s3_url,
                                         gcs_url),
            database=DB(db_url),
            default_namespace=default_namespace,
        )
Пример #4
0
def fixture_db():
    db_url = 'sqlite:///bentoml/storage.db'
    return DB(db_url)
Пример #5
0
    class YataiServiceImpl(base):

        # pylint: disable=unused-argument
        # pylint: disable=broad-except

        def __init__(
            self,
            db_url=None,
            repo_base_url=None,
            s3_endpoint_url=None,
            default_namespace=None,
        ):
            cfg = config('yatai_service')
            repo_base_url = repo_base_url or cfg.get('repository_base_url')
            db_url = db_url or cfg.get('db_url')
            s3_endpoint_url = s3_endpoint_url or cfg.get('s3_endpoint_url') or None
            default_namespace = default_namespace or cfg.get('default_namespace')

            self.default_namespace = default_namespace
            self.repo = Repository(repo_base_url, s3_endpoint_url)

            self.db = DB(db_url)

        def HealthCheck(self, request, context=None):
            return HealthCheckResponse(status=Status.OK())

        def GetYataiServiceVersion(self, request, context=None):
            return GetYataiServiceVersionResponse(
                status=Status.OK, version=BENTOML_VERSION
            )

        def ApplyDeployment(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    # apply default namespace if not set
                    request.deployment.namespace = (
                        request.deployment.namespace or self.default_namespace
                    )

                    validation_errors = validate_deployment_pb(request.deployment)
                    if validation_errors:
                        raise InvalidArgument(
                            'Failed to validate deployment. {errors}'.format(
                                errors=validation_errors
                            )
                        )

                    previous_deployment = self.db.deployment_store.get(
                        sess, request.deployment.name, request.deployment.namespace
                    )
                    if not previous_deployment:
                        request.deployment.created_at.GetCurrentTime()
                    request.deployment.last_updated_at.GetCurrentTime()

                    self.db.deployment_store.insert_or_update(sess, request.deployment)
                    # find deployment operator based on deployment spec
                    operator = get_deployment_operator(self, request.deployment)

                    # deploying to target platform
                    if previous_deployment:
                        response = operator.update(
                            request.deployment, previous_deployment
                        )
                    else:
                        response = operator.add(request.deployment)

                    if response.status.status_code == status_pb2.Status.OK:
                        # update deployment state
                        if response and response.deployment:
                            self.db.deployment_store.insert_or_update(
                                sess, response.deployment
                            )
                        else:
                            raise BentoMLException(
                                "DeploymentOperator Internal Error: failed to add or "
                                "update deployment metadata to database"
                            )
                        logger.info(
                            "ApplyDeployment (%s, namespace %s) succeeded",
                            request.deployment.name,
                            request.deployment.namespace,
                        )
                    else:
                        if not previous_deployment:
                            # When failed to create the deployment,
                            # delete it from active deployments records
                            self.db.deployment_store.delete(
                                sess,
                                request.deployment.name,
                                request.deployment.namespace,
                            )
                        logger.debug(
                            "ApplyDeployment (%s, namespace %s) failed: %s",
                            request.deployment.name,
                            request.deployment.namespace,
                            response.status.error_message,
                        )

                    return response

                except BentoMLException as e:
                    logger.error("RPC ERROR ApplyDeployment: %s", e)
                    return ApplyDeploymentResponse(status=e.status_proto)
                except Exception as e:
                    logger.error("URPC ERROR ApplyDeployment: %s", e)
                    return ApplyDeploymentResponse(status=Status.INTERNAL(str(e)))

        def DeleteDeployment(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    request.namespace = request.namespace or self.default_namespace
                    deployment_pb = self.db.deployment_store.get(
                        sess, request.deployment_name, request.namespace
                    )

                    if deployment_pb:
                        # find deployment operator based on deployment spec
                        operator = get_deployment_operator(self, deployment_pb)

                        # executing deployment deletion
                        response = operator.delete(deployment_pb)

                        # if delete successful, remove it from active deployment records
                        # table
                        if response.status.status_code == status_pb2.Status.OK:
                            track_deployment_delete(
                                deployment_pb.spec.operator, deployment_pb.created_at
                            )
                            self.db.deployment_store.delete(
                                sess, request.deployment_name, request.namespace
                            )
                            return response

                        # If force delete flag is True, we will remove the record
                        # from yatai database.
                        if request.force_delete:
                            # Track deployment delete before it
                            # vanishes from deployment store
                            track_deployment_delete(
                                deployment_pb.spec.operator,
                                deployment_pb.created_at,
                                True,
                            )
                            self.db.deployment_store.delete(
                                sess, request.deployment_name, request.namespace
                            )
                            return DeleteDeploymentResponse(status=Status.OK())

                        if response.status.status_code == status_pb2.Status.NOT_FOUND:
                            modified_message = (
                                'Cloud resources not found, error: {} - it '
                                'may have been deleted manually. Try delete '
                                'deployment with "--force" option to ignore this '
                                'error and force deleting the deployment record'.format(
                                    response.status.error_message
                                )
                            )
                            response.status.error_message = modified_message

                        return response
                    else:
                        return DeleteDeploymentResponse(
                            status=Status.NOT_FOUND(
                                'Deployment "{}" in namespace "{}" not found'.format(
                                    request.deployment_name, request.namespace
                                )
                            )
                        )

                except BentoMLException as e:
                    logger.error("RPC ERROR DeleteDeployment: %s", e)
                    return DeleteDeploymentResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR DeleteDeployment: %s", e)
                    return DeleteDeploymentResponse(status=Status.INTERNAL(str(e)))

        def GetDeployment(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    request.namespace = request.namespace or self.default_namespace
                    deployment_pb = self.db.deployment_store.get(
                        sess, request.deployment_name, request.namespace
                    )
                    if deployment_pb:
                        return GetDeploymentResponse(
                            status=Status.OK(), deployment=deployment_pb
                        )
                    else:
                        return GetDeploymentResponse(
                            status=Status.NOT_FOUND(
                                'Deployment "{}" in namespace "{}" not found'.format(
                                    request.deployment_name, request.namespace
                                )
                            )
                        )
                except BentoMLException as e:
                    logger.error("RPC ERROR GetDeployment: %s", e)
                    return GetDeploymentResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR GetDeployment: %s", e)
                    return GetDeploymentResponse(status=Status.INTERNAL())

        def DescribeDeployment(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    request.namespace = request.namespace or self.default_namespace
                    deployment_pb = self.db.deployment_store.get(
                        sess, request.deployment_name, request.namespace
                    )

                    if deployment_pb:
                        operator = get_deployment_operator(self, deployment_pb)

                        response = operator.describe(deployment_pb)

                        if response.status.status_code == status_pb2.Status.OK:
                            with self.db.deployment_store.update_deployment(
                                sess, request.deployment_name, request.namespace
                            ) as deployment:
                                deployment.state = ProtoMessageToDict(response.state)

                        return response
                    else:
                        return DescribeDeploymentResponse(
                            status=Status.NOT_FOUND(
                                'Deployment "{}" in namespace "{}" not found'.format(
                                    request.deployment_name, request.namespace
                                )
                            )
                        )
                except BentoMLException as e:
                    logger.error("RPC ERROR DescribeDeployment: %s", e)
                    return DeleteDeploymentResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR DescribeDeployment: %s", e)
                    return DeleteDeploymentResponse(status=Status.INTERNAL())

        def ListDeployments(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    namespace = request.namespace or self.default_namespace
                    deployment_pb_list = self.db.deployment_store.list(
                        sess,
                        namespace=namespace,
                        label_selectors=request.label_selectors,
                        offset=request.offset,
                        limit=request.limit,
                        operator=request.operator,
                        order_by=request.order_by,
                        ascending_order=request.ascending_order,
                    )

                    return ListDeploymentsResponse(
                        status=Status.OK(), deployments=deployment_pb_list
                    )
                except BentoMLException as e:
                    logger.error("RPC ERROR ListDeployments: %s", e)
                    return ListDeploymentsResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR ListDeployments: %s", e)
                    return ListDeploymentsResponse(status=Status.INTERNAL())

        def AddBento(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    # TODO: validate request
                    bento_pb = self.db.metadata_store.get(
                        sess, request.bento_name, request.bento_version
                    )
                    if bento_pb:
                        error_message = (
                            "BentoService bundle: "
                            "{}:{} already exists".format(
                                request.bento_name, request.bento_version
                            )
                        )
                        logger.error(error_message)
                        return AddBentoResponse(status=Status.ABORTED(error_message))
                    new_bento_uri = self.repo.add(
                        request.bento_name, request.bento_version
                    )
                    self.db.metadata_store.add(
                        sess,
                        bento_name=request.bento_name,
                        bento_version=request.bento_version,
                        uri=new_bento_uri.uri,
                        uri_type=new_bento_uri.type,
                    )
                    return AddBentoResponse(status=Status.OK(), uri=new_bento_uri)
                except BentoMLException as e:
                    logger.error("RPC ERROR AddBento: %s", e)
                    return DeleteDeploymentResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("URPC ERROR AddBento: %s", e)
                    return DeleteDeploymentResponse(status=Status.INTERNAL())

        def UpdateBento(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    # TODO: validate request
                    if request.upload_status:
                        self.db.metadata_store.update_upload_status(
                            sess,
                            request.bento_name,
                            request.bento_version,
                            request.upload_status,
                        )
                    if request.service_metadata:
                        self.db.metadata_store.update(
                            sess,
                            request.bento_name,
                            request.bento_version,
                            request.service_metadata,
                        )
                    return UpdateBentoResponse(status=Status.OK())
                except BentoMLException as e:
                    logger.error("RPC ERROR UpdateBento: %s", e)
                    return UpdateBentoResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR UpdateBento: %s", e)
                    return UpdateBentoResponse(status=Status.INTERNAL())

        def DangerouslyDeleteBento(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    # TODO: validate request
                    bento_pb = self.db.metadata_store.get(
                        sess, request.bento_name, request.bento_version
                    )
                    if not bento_pb:
                        msg = (
                            f"BentoService "
                            f"{request.bento_name}:{request.bento_version} "
                            f"has already been deleted"
                        )
                        return DangerouslyDeleteBentoResponse(
                            status=Status.ABORTED(msg)
                        )

                    logger.debug(
                        'Deleting BentoService %s:%s',
                        request.bento_name,
                        request.bento_version,
                    )
                    self.db.metadata_store.dangerously_delete(
                        sess, request.bento_name, request.bento_version
                    )
                    self.repo.dangerously_delete(
                        request.bento_name, request.bento_version
                    )
                    return DangerouslyDeleteBentoResponse(status=Status.OK())
                except BentoMLException as e:
                    logger.error("RPC ERROR DangerouslyDeleteBento: %s", e)
                    return DangerouslyDeleteBentoResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR DangerouslyDeleteBento: %s", e)
                    return DangerouslyDeleteBentoResponse(status=Status.INTERNAL())

        def GetBento(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    # TODO: validate request
                    bento_pb = self.db.metadata_store.get(
                        sess, request.bento_name, request.bento_version
                    )
                    if bento_pb:
                        if request.bento_version.lower() == 'latest':
                            logger.info(
                                'Getting latest version %s:%s',
                                request.bento_name,
                                bento_pb.version,
                            )
                        if bento_pb.uri.type == BentoUri.S3:
                            bento_pb.uri.s3_presigned_url = self.repo.get(
                                bento_pb.name, bento_pb.version
                            )
                        elif bento_pb.uri.type == BentoUri.GCS:
                            bento_pb.uri.gcs_presigned_url = self.repo.get(
                                bento_pb.name, bento_pb.version
                            )
                        return GetBentoResponse(status=Status.OK(), bento=bento_pb)
                    else:
                        return GetBentoResponse(
                            status=Status.NOT_FOUND(
                                "BentoService `{}:{}` is not found".format(
                                    request.bento_name, request.bento_version
                                )
                            )
                        )
                except BentoMLException as e:
                    logger.error("RPC ERROR GetBento: %s", e)
                    return GetBentoResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR GetBento: %s", e)
                    return GetBentoResponse(status=Status.INTERNAL())

        def ListBento(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    # TODO: validate request
                    bento_metadata_pb_list = self.db.metadata_store.list(
                        sess,
                        bento_name=request.bento_name,
                        offset=request.offset,
                        limit=request.limit,
                        order_by=request.order_by,
                        label_selectors=request.label_selectors,
                        ascending_order=request.ascending_order,
                    )

                    return ListBentoResponse(
                        status=Status.OK(), bentos=bento_metadata_pb_list
                    )
                except BentoMLException as e:
                    logger.error("RPC ERROR ListBento: %s", e)
                    return ListBentoResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error("RPC ERROR ListBento: %s", e)
                    return ListBentoResponse(status=Status.INTERNAL())

        def ContainerizeBento(self, request, context=None):
            with self.db.create_session() as sess:
                try:
                    ensure_docker_available_or_raise()
                    tag = request.tag
                    if tag is None or len(tag) == 0:
                        name = to_valid_docker_image_name(request.bento_name)
                        version = to_valid_docker_image_version(request.bento_version)
                        tag = f"{name}:{version}"
                    if ":" not in tag:
                        version = to_valid_docker_image_version(request.bento_version)
                        tag = f"{tag}:{version}"
                    import docker

                    docker_client = docker.from_env()
                    bento_pb = self.db.metadata_store.get(
                        sess, request.bento_name, request.bento_version
                    )
                    if not bento_pb:
                        raise YataiRepositoryException(
                            f'BentoService '
                            f'{request.bento_name}:{request.bento_version} '
                            f'does not exist'
                        )

                    with TempDirectory() as temp_dir:
                        temp_bundle_path = f'{temp_dir}/{bento_pb.name}'
                        bento_service_bundle_path = bento_pb.uri.uri
                        if bento_pb.uri.type == BentoUri.S3:
                            bento_service_bundle_path = self.repo.get(
                                bento_pb.name, bento_pb.version
                            )
                        elif bento_pb.uri.type == BentoUri.GCS:
                            bento_service_bundle_path = self.repo.get(
                                bento_pb.name, bento_pb.version
                            )
                        safe_retrieve(bento_service_bundle_path, temp_bundle_path)
                        try:
                            docker_client.images.build(
                                path=temp_bundle_path,
                                tag=tag,
                                buildargs=dict(request.build_args),
                            )
                        except (
                            docker.errors.APIError,
                            docker.errors.BuildError,
                        ) as error:
                            logger.error(f'Encounter container building issue: {error}')
                            raise YataiRepositoryException(error)
                        if request.push is True:
                            try:
                                docker_client.images.push(
                                    repository=request.repository, tag=tag
                                )
                            except docker.errors.APIError as error:
                                raise YataiRepositoryException(error)

                        return ContainerizeBentoResponse(status=Status.OK(), tag=tag)
                except BentoMLException as e:
                    logger.error(f"RPC ERROR ContainerizeBento: {e}")
                    return ContainerizeBentoResponse(status=e.status_proto)
                except Exception as e:  # pylint: disable=broad-except
                    logger.error(f"RPC ERROR ContainerizeBento: {e}")
                    return ContainerizeBentoResponse(status=Status.INTERNAL(e))