Exemple #1
0
    def DeleteDeployment(self, request, context=None):
        try:
            request.namespace = request.namespace or self.default_namespace
            deployment_pb = self.deployment_store.get(
                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:
                    self.deployment_store.delete(
                        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:
                    self.deployment_store.delete(
                        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)))
Exemple #2
0
    def ListBento(self, request, context=None):
        try:
            # TODO: validate request
            bento_metadata_pb_list = self.bento_metadata_store.list(
                request.bento_name, request.offset, request.limit, request.filter
            )

            return ListBentoResponse(status=Status.OK(), bentos=bento_metadata_pb_list)
        except BentoMLException as e:
            logger.error("INTERNAL ERROR: %s", e)
            return ListBentoResponse(status=Status.INTERNAL(str(e)))
Exemple #3
0
    def ApplyDeployment(self, request, context=None):
        try:
            # apply default namespace if not set
            request.deployment.namespace = (
                request.deployment.namespace or self.default_namespace
            )

            validation_errors = validate_deployment_pb_schema(request.deployment)
            if validation_errors:
                return ApplyDeploymentResponse(
                    status=Status.INVALID_ARGUMENT(
                        'Failed to validate deployment. {errors}'.format(
                            errors=validation_errors
                        )
                    )
                )

            previous_deployment = self.deployment_store.get(
                request.deployment.name, request.deployment.namespace
            )
            if previous_deployment:
                # check deployment platform
                if (
                    previous_deployment.spec.operator
                    != request.deployment.spec.operator
                ):
                    return ApplyDeploymentResponse(
                        status=Status.ABORTED(
                            'Can not change the target deploy platform of existing '
                            'active deployment. Try delete existing deployment and '
                            'deploy to new target platform again'
                        )
                    )
                request.deployment.state.state = DeploymentState.PENDING
            else:
                request.deployment.created_at.GetCurrentTime()

            request.deployment.last_updated_at.GetCurrentTime()

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

            # deploying to target platform
            response = operator.apply(request.deployment, self, previous_deployment)

            # update deployment state
            self.deployment_store.insert_or_update(response.deployment)

            return response

        except BentoMLException as e:
            logger.error("INTERNAL ERROR: %s", e)
            return ApplyDeploymentResponse(status=Status.INTERNAL(str(e)))
Exemple #4
0
    def delete(self, deployment_pb, yatai_service=None):
        try:
            state = self.describe(deployment_pb, yatai_service).state
            if state.state != DeploymentState.RUNNING:
                message = ('Failed to delete, no active deployment {name}. '
                           'The current state is {state}'.format(
                               name=deployment_pb.name,
                               state=DeploymentState.State.Name(state.state),
                           ))
                return DeleteDeploymentResponse(status=Status.ABORTED(message))

            deployment_spec = deployment_pb.spec
            aws_config = deployment_spec.aws_lambda_operator_config

            bento_pb = yatai_service.GetBento(
                GetBentoRequest(
                    bento_name=deployment_spec.bento_name,
                    bento_version=deployment_spec.bento_version,
                ))
            bento_service_metadata = bento_pb.bento.bento_service_metadata
            # We are not validating api_name, because for delete, you don't
            # need them.
            api_names = ([aws_config.api_name] if aws_config.api_name else
                         [api.name for api in bento_service_metadata.apis])

            with TempDirectory() as serverless_project_dir:
                generate_aws_lambda_serverless_config(
                    bento_service_metadata.env.python_version,
                    deployment_pb.name,
                    api_names,
                    serverless_project_dir,
                    aws_config.region,
                    # BentoML deployment namespace is mapping to serverless `stage`
                    # concept
                    stage=deployment_pb.namespace,
                )
                response = call_serverless_command(['remove'],
                                                   serverless_project_dir)
                stack_name = '{name}-{namespace}'.format(
                    name=deployment_pb.name, namespace=deployment_pb.namespace)
                if "Serverless: Stack removal finished..." in response:
                    status = Status.OK()
                elif "Stack '{}' does not exist".format(
                        stack_name) in response:
                    status = Status.NOT_FOUND(
                        'Deployment {} not found'.format(stack_name))
                else:
                    status = Status.ABORTED()

            return DeleteDeploymentResponse(status=status)
        except BentoMLException as error:
            return DeleteDeploymentResponse(
                status=exception_to_return_status(error))
Exemple #5
0
    def describe(self, deployment_pb, yatai_service=None):
        try:
            deployment_spec = deployment_pb.spec
            aws_config = deployment_spec.aws_lambda_operator_config
            info_json = {'endpoints': []}

            bento_pb = yatai_service.GetBento(
                GetBentoRequest(
                    bento_name=deployment_spec.bento_name,
                    bento_version=deployment_spec.bento_version,
                )
            )
            bento_service_metadata = bento_pb.bento.bento_service_metadata
            api_names = (
                [aws_config.api_name]
                if aws_config.api_name
                else [api.name for api in bento_service_metadata.apis]
            )

            try:
                cloud_formation_stack_result = boto3.client(
                    'cloudformation'
                ).describe_stacks(
                    StackName='{name}-{ns}'.format(
                        ns=deployment_pb.namespace, name=deployment_pb.name
                    )
                )
                outputs = cloud_formation_stack_result.get('Stacks')[0]['Outputs']
            except Exception as error:
                state = DeploymentState(
                    state=DeploymentState.ERROR, error_message=str(error)
                )
                state.timestamp.GetCurrentTime()
                return DescribeDeploymentResponse(
                    status=Status.INTERNAL(str(error)), state=state
                )

            base_url = ''
            for output in outputs:
                if output['OutputKey'] == 'ServiceEndpoint':
                    base_url = output['OutputValue']
                    break
            if base_url:
                info_json['endpoints'] = [
                    base_url + '/' + api_name for api_name in api_names
                ]
            state = DeploymentState(
                state=DeploymentState.RUNNING, info_json=json.dumps(info_json)
            )
            state.timestamp.GetCurrentTime()
            return DescribeDeploymentResponse(status=Status.OK(), state=state)
        except BentoMLException as error:
            return DescribeDeploymentResponse(status=exception_to_return_status(error))
Exemple #6
0
 def AddBento(self, request, context=None):
     try:
         # TODO: validate request
         new_bento_uri = self.repo.add(request.bento_name, request.bento_version)
         self.bento_metadata_store.add(
             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("INTERNAL ERROR: %s", e)
         return AddBentoResponse(status=Status.INTERNAL(str(e)))
 def GetDeployment(self, request, context=None):
     try:
         request.namespace = request.namespace or self.default_namespace
         deployment_pb = self.deployment_store.get(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("INTERNAL ERROR: %s", e)
         return GetDeploymentResponse(status=Status.INTERNAL(str(e)))
 def GetBento(self, request, context=None):
     try:
         # TODO: validate request
         bento_metadata_pb = self.bento_metadata_store.get(
             request.bento_name, request.bento_version)
         if bento_metadata_pb:
             return GetBentoResponse(status=Status.OK(),
                                     bento=bento_metadata_pb)
         else:
             return GetBentoResponse(status=Status.NOT_FOUND(
                 "Bento `{}:{}` is not found".format(
                     request.bento_name, request.bento_version)))
     except BentoMLException as e:
         logger.error("INTERNAL ERROR: %s", e)
         return GetBentoResponse(status=Status.INTERNAL(str(e)))
Exemple #9
0
    def describe(self, deployment_pb, repo=None):
        deployment_spec = deployment_pb.spec
        sagemaker_config = deployment_spec.sagemaker_operator_config
        if sagemaker_config is None:
            raise BentoMLDeploymentException(
                'Sagemaker configuration is missing.')
        sagemaker_client = boto3.client('sagemaker', sagemaker_config.region)
        endpoint_name = generate_aws_compatible_string(
            deployment_pb.namespace + '-' + deployment_spec.bento_name)
        try:
            endpoint_status_response = sagemaker_client.describe_endpoint(
                EndpointName=endpoint_name)
        except ClientError as e:
            status = _parse_aws_client_exception_or_raise(e)
            status.error_message = (
                'Failed to describe SageMaker deployment: %s',
                status.error_message,
            )
            return DescribeDeploymentResponse(status=status)

        logger.debug("AWS describe endpoint response: %s",
                     endpoint_status_response)
        endpoint_status = endpoint_status_response["EndpointStatus"]

        service_state = ENDPOINT_STATUS_TO_STATE[endpoint_status]

        deployment_state = DeploymentState(
            state=service_state,
            info_json=json.dumps(endpoint_status_response, default=str),
        )

        return DescribeDeploymentResponse(state=deployment_state,
                                          status=Status.OK())
Exemple #10
0
    def describe(self, deployment_pb):
        try:
            deployment_spec = deployment_pb.spec
            sagemaker_config = deployment_spec.sagemaker_operator_config
            sagemaker_client = boto3.client('sagemaker', sagemaker_config.region)
            _, _, sagemaker_endpoint_name = _get_sagemaker_resource_names(deployment_pb)

            try:
                endpoint_status_response = sagemaker_client.describe_endpoint(
                    EndpointName=sagemaker_endpoint_name
                )
            except ClientError as e:
                raise _aws_client_error_to_bentoml_exception(
                    e,
                    f"Failed to fetch current status of sagemaker endpoint "
                    f"'{sagemaker_endpoint_name}'",
                )

            logger.debug("AWS describe endpoint response: %s", endpoint_status_response)
            endpoint_status = endpoint_status_response["EndpointStatus"]

            service_state = ENDPOINT_STATUS_TO_STATE[endpoint_status]

            deployment_state = DeploymentState(
                state=service_state,
                info_json=json.dumps(endpoint_status_response, default=str),
            )
            deployment_state.timestamp.GetCurrentTime()

            return DescribeDeploymentResponse(
                state=deployment_state, status=Status.OK()
            )
        except BentoMLException as error:
            return DescribeDeploymentResponse(status=error.status_proto)
Exemple #11
0
    def _add(self, deployment_pb, bento_pb, bento_path):
        try:
            if loader._is_remote_path(bento_path):
                with loader._resolve_remote_bundle_path(
                        bento_path) as local_path:
                    return self._add(deployment_pb, bento_pb, local_path)

            deployment_spec = deployment_pb.spec
            aws_ec2_deployment_config = deployment_spec.aws_ec2_operator_config

            user_id = get_aws_user_id()
            artifact_s3_bucket_name = generate_aws_compatible_string(
                "btml-{user_id}-{namespace}".format(
                    user_id=user_id,
                    namespace=deployment_pb.namespace,
                ))
            create_s3_bucket_if_not_exists(artifact_s3_bucket_name,
                                           aws_ec2_deployment_config.region)
            self.deploy_service(
                deployment_pb,
                deployment_spec,
                bento_path,
                aws_ec2_deployment_config,
                artifact_s3_bucket_name,
                aws_ec2_deployment_config.region,
            )
        except BentoMLException as error:
            if artifact_s3_bucket_name and aws_ec2_deployment_config.region:
                cleanup_s3_bucket_if_exist(artifact_s3_bucket_name,
                                           aws_ec2_deployment_config.region)
            raise error
        return ApplyDeploymentResponse(status=Status.OK(),
                                       deployment=deployment_pb)
Exemple #12
0
    def DescribeDeployment(self, request, context=None):
        try:
            request.namespace = request.namespace or self.default_namespace
            deployment_pb = self.deployment_store.get(
                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.deployment_store.update_deployment(
                        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)
Exemple #13
0
    def delete(self, deployment_pb):
        try:
            deployment_spec = deployment_pb.spec
            sagemaker_config = deployment_spec.sagemaker_operator_config
            sagemaker_config.region = (sagemaker_config.region
                                       or get_default_aws_region())
            if not sagemaker_config.region:
                raise InvalidArgument('AWS region is missing')

            sagemaker_client = boto3.client('sagemaker',
                                            sagemaker_config.region)
            _, _, sagemaker_endpoint_name = _get_sagemaker_resource_names(
                deployment_pb)

            try:
                delete_endpoint_response = sagemaker_client.delete_endpoint(
                    EndpointName=sagemaker_endpoint_name)
                logger.debug("AWS delete endpoint response: %s",
                             delete_endpoint_response)
            except ClientError as e:
                raise _aws_client_error_to_bentoml_exception(e)

            _try_clean_up_sagemaker_deployment_resource(deployment_pb)

            return DeleteDeploymentResponse(status=Status.OK())
        except BentoMLException as error:
            return DeleteDeploymentResponse(status=error.status_proto)
Exemple #14
0
    def describe(self, deployment_pb, repo=None):
        deployment_spec = deployment_pb.spec
        aws_config = deployment_spec.aws_lambda_operator_config

        bento_path = repo.get(deployment_spec.bento_name,
                              deployment_spec.bento_version)
        bento_config = load_bentoml_config(bento_path)
        with TemporaryServerlessConfig(
                archive_path=bento_path,
                deployment_name=deployment_pb.name,
                region=aws_config.region,
                stage=deployment_pb.namespace,
                provider_name='aws',
                functions=generate_aws_handler_functions_config(
                    bento_config['apis']),
        ) as tempdir:
            try:
                response = call_serverless_command(["serverless", "info"],
                                                   tempdir)
                info_json = parse_serverless_info_response_to_json_string(
                    response)
                state = DeploymentState(state=DeploymentState.RUNNING,
                                        info_json=info_json)
            except BentoMLException as e:
                state = DeploymentState(state=DeploymentState.ERROR,
                                        error_message=str(e))

        return DescribeDeploymentResponse(status=Status.OK(), state=state)
Exemple #15
0
    def _update(self, deployment_pb, current_deployment, bento_pb, bento_path):
        if loader._is_remote_path(bento_path):
            with loader._resolve_remote_bundle_path(bento_path) as local_path:
                return self._update(deployment_pb, current_deployment,
                                    bento_pb, local_path)
        updated_deployment_spec = deployment_pb.spec
        updated_lambda_deployment_config = (
            updated_deployment_spec.aws_lambda_operator_config)
        updated_bento_service_metadata = bento_pb.bento.bento_service_metadata
        describe_result = self.describe(deployment_pb)
        if describe_result.status.status_code != status_pb2.Status.OK:
            error_code, error_message = status_pb_to_error_code_and_message(
                describe_result.status)
            raise YataiDeploymentException(
                f'Failed fetching Lambda deployment current status - '
                f'{error_code}:{error_message}')
        latest_deployment_state = json.loads(describe_result.state.info_json)
        if 's3_bucket' in latest_deployment_state:
            lambda_s3_bucket = latest_deployment_state['s3_bucket']
        else:
            raise BentoMLException(
                'S3 Bucket is missing in the AWS Lambda deployment, please make sure '
                'it exists and try again')

        _deploy_lambda_function(
            deployment_pb=deployment_pb,
            bento_service_metadata=updated_bento_service_metadata,
            deployment_spec=updated_deployment_spec,
            lambda_s3_bucket=lambda_s3_bucket,
            lambda_deployment_config=updated_lambda_deployment_config,
            bento_path=bento_path,
        )

        return ApplyDeploymentResponse(deployment=deployment_pb,
                                       status=Status.OK())
    def ListDeployments(self, request, context=None):
        try:
            namespace = request.namespace or self.default_namespace
            deployment_pb_list = self.deployment_store.list(
                namespace=namespace,
                filter_str=request.filter,
                labels=request.labels,
                offset=request.offset,
                limit=request.limit,
            )

            return ListDeploymentsResponse(status=Status.OK(),
                                           deployments=deployment_pb_list)
        except BentoMLException as e:
            logger.error("INTERNAL ERROR: %s", e)
            return ListDeploymentsResponse(status=Status.INTERNAL(str(e)))
Exemple #17
0
    def _add(self, deployment_pb, bento_pb, bento_path):
        if loader._is_remote_path(bento_path):
            with loader._resolve_remote_bundle_path(bento_path) as local_path:
                return self._add(deployment_pb, bento_pb, local_path)

        deployment_spec = deployment_pb.spec
        lambda_deployment_config = deployment_spec.aws_lambda_operator_config
        bento_service_metadata = bento_pb.bento.bento_service_metadata
        lambda_s3_bucket = generate_aws_compatible_string(
            'btml-{namespace}-{name}-{random_string}'.format(
                namespace=deployment_pb.namespace,
                name=deployment_pb.name,
                random_string=uuid.uuid4().hex[:6].lower(),
            ))
        try:
            create_s3_bucket_if_not_exists(lambda_s3_bucket,
                                           lambda_deployment_config.region)
            _deploy_lambda_function(
                deployment_pb=deployment_pb,
                bento_service_metadata=bento_service_metadata,
                deployment_spec=deployment_spec,
                lambda_s3_bucket=lambda_s3_bucket,
                lambda_deployment_config=lambda_deployment_config,
                bento_path=bento_path,
            )
            return ApplyDeploymentResponse(status=Status.OK(),
                                           deployment=deployment_pb)
        except BentoMLException as error:
            if lambda_s3_bucket and lambda_deployment_config:
                cleanup_s3_bucket_if_exist(lambda_s3_bucket,
                                           lambda_deployment_config.region)
            raise error
Exemple #18
0
    def delete(self, deployment_pb):
        try:
            logger.debug('Deleting AWS Lambda deployment')

            deployment_spec = deployment_pb.spec
            lambda_deployment_config = deployment_spec.aws_lambda_operator_config
            lambda_deployment_config.region = (lambda_deployment_config.region
                                               or get_default_aws_region())
            if not lambda_deployment_config.region:
                raise InvalidArgument('AWS region is missing')

            cf_client = boto3.client('cloudformation',
                                     lambda_deployment_config.region)
            stack_name = generate_aws_compatible_string(
                deployment_pb.namespace, deployment_pb.name)
            if deployment_pb.state.info_json:
                deployment_info_json = json.loads(
                    deployment_pb.state.info_json)
                bucket_name = deployment_info_json.get('s3_bucket')
                if bucket_name:
                    cleanup_s3_bucket_if_exist(bucket_name,
                                               lambda_deployment_config.region)

            logger.debug(
                'Deleting AWS CloudFormation: %s that includes Lambda function '
                'and related resources',
                stack_name,
            )
            cf_client.delete_stack(StackName=stack_name)
            return DeleteDeploymentResponse(status=Status.OK())

        except BentoMLException as error:
            return DeleteDeploymentResponse(status=error.status_proto)
Exemple #19
0
    def delete(self, deployment_pb):
        try:
            deployment_spec = deployment_pb.spec
            ec2_deployment_config = deployment_spec.aws_ec2_operator_config
            ec2_deployment_config.region = (ec2_deployment_config.region
                                            or get_default_aws_region())
            if not ec2_deployment_config.region:
                raise InvalidArgument("AWS region is missing")

            _, deployment_stack_name, repository_name, _ = generate_ec2_resource_names(
                deployment_pb.namespace, deployment_pb.name)
            # delete stack
            delete_cloudformation_stack(deployment_stack_name,
                                        ec2_deployment_config.region)

            # delete repo from ecr
            delete_ecr_repository(repository_name,
                                  ec2_deployment_config.region)

            # remove bucket
            if deployment_pb.state.info_json:
                deployment_info_json = json.loads(
                    deployment_pb.state.info_json)
                bucket_name = deployment_info_json.get('S3Bucket')
                if bucket_name:
                    cleanup_s3_bucket_if_exist(bucket_name,
                                               ec2_deployment_config.region)

            return DeleteDeploymentResponse(status=Status.OK())
        except BentoMLException as error:
            return DeleteDeploymentResponse(status=error.status_proto)
Exemple #20
0
 def _add(self, deployment_pb, bento_pb, bento_path):
     if loader._is_remote_path(bento_path):
         with loader._resolve_remote_bundle_path(bento_path) as local_path:
             return self._add(deployment_pb, bento_pb, local_path)
     try:
         _deploy_azure_functions(
             deployment_spec=deployment_pb.spec,
             deployment_name=deployment_pb.name,
             namespace=deployment_pb.namespace,
             bento_pb=bento_pb,
             bento_path=bento_path,
         )
         return ApplyDeploymentResponse(status=Status.OK(),
                                        deployment=deployment_pb)
     except AzureServiceError as error:
         resource_group_name, _, _, _, _, = _generate_azure_resource_names(
             deployment_pb.namespace, deployment_pb.name)
         logger.debug(
             'Failed to create Azure Functions. Cleaning up Azure resources'
         )
         try:
             _call_az_cli(
                 command=[
                     'az',
                     'group',
                     'delete',
                     '-y',
                     '--name',
                     resource_group_name,
                 ],
                 message='delete Azure resource group',
             )
         except AzureServiceError:
             pass
         raise error
Exemple #21
0
    def apply(self, deployment_pb, repo, prev_deployment=None):
        deployment_spec = deployment_pb.spec
        gcp_config = deployment_spec.gcp_function_operator_config
        bento_path = repo.get(deployment_spec.bento_name,
                              deployment_spec.bento_version)

        bento_config = load_bentoml_config(bento_path)
        with TemporaryServerlessContent(
                archive_path=bento_path,
                deployment_name=deployment_pb.name,
                bento_name=deployment_spec.bento_name,
                template_type='google-python',
        ) as output_path:
            generate_main_py(bento_config['name'], bento_config['apis'],
                             output_path)
            generate_serverless_configuration_for_gcp_function(
                service_name=bento_config['name'],
                apis=bento_config['apis'],
                output_path=output_path,
                region=gcp_config.region,
                stage=deployment_pb.namespace,
            )
            call_serverless_command(["deploy"], output_path)

        res_deployment_pb = Deployment(state=DeploymentState())
        res_deployment_pb.CopyFrom(deployment_pb)
        state = self.describe(res_deployment_pb, repo).state
        res_deployment_pb.state.CopyFrom(state)

        return ApplyDeploymentResponse(status=Status.OK(),
                                       deployment=res_deployment_pb)
Exemple #22
0
    def ApplyDeployment(self, request, context=None):
        try:
            # apply default namespace if not set
            request.deployment.namespace = (request.deployment.namespace
                                            or self.default_namespace)

            # TODO: validate deployment config

            # create or update deployment spec record
            self.deployment_store.insert_or_update(request.deployment)

            # find deployment operator based on deployment spec
            operator = get_deployment_operator(request.deployment)

            # deploying to target platform
            response = operator.apply(request.deployment, self.repo)

            # update deployment state
            self.deployment_store.insert_or_update(response.deployment)

            return response

        except BentoMLException as e:
            logger.error("INTERNAL ERROR: %s", e)
            return ApplyDeploymentResponse(Status.INTERNAL(e))
Exemple #23
0
    def delete(self, deployment_pb, yatai_service=None):
        try:
            state = self.describe(deployment_pb, yatai_service).state
            if state.state != DeploymentState.RUNNING:
                message = ('Failed to delete, no active deployment {name}. '
                           'The current state is {state}'.format(
                               name=deployment_pb.name,
                               state=DeploymentState.State.Name(state.state),
                           ))
                return DeleteDeploymentResponse(status=Status.ABORTED(message))

            deployment_spec = deployment_pb.spec
            gcp_config = deployment_spec.gcp_function_operator_config

            bento_pb = yatai_service.GetBento(
                GetBentoRequest(
                    bento_name=deployment_spec.bento_name,
                    bento_version=deployment_spec.bento_version,
                ))
            bento_service_metadata = bento_pb.bento.bento_service_metadata
            api_names = ([gcp_config.api_name] if gcp_config.api_name else
                         [api.name for api in bento_service_metadata.apis])
            with TempDirectory() as serverless_project_dir:
                generate_gcp_function_serverless_config(
                    deployment_pb.name,
                    api_names,
                    serverless_project_dir,
                    gcp_config.region,
                    # BentoML namespace is mapping to serverless stage.
                    stage=deployment_pb.namespace,
                )
                try:
                    response = call_serverless_command(['remove'],
                                                       serverless_project_dir)
                    if "Serverless: Stack removal finished..." in response:
                        status = Status.OK()
                    else:
                        status = Status.ABORTED()
                except BentoMLException as e:
                    status = Status.INTERNAL(str(e))

            return DeleteDeploymentResponse(status=status)
        except BentoMLException as error:
            return DeleteDeploymentResponse(
                status=exception_to_return_status(error))
Exemple #24
0
 def describe(self, deployment_pb):
     try:
         (
             resource_group_name,
             _,
             _,
             function_name,
             _,
         ) = _generate_azure_resource_names(deployment_pb.namespace,
                                            deployment_pb.name)
         show_function_result = _call_az_cli(
             command=[
                 'az',
                 'functionapp',
                 'show',
                 '--name',
                 function_name,
                 '--resource-group',
                 resource_group_name,
             ],
             message='show Azure functionapp detail',
         )
         keys = [
             'defaultHostName',
             'enabledHostNames',
             'hostNames',
             'id',
             'kind',
             'lastModifiedTimeUtc',
             'location',
             'name',
             'repositorySiteName',
             'reserved',
             'resourceGroup',
             'state',
             'type',
             'usageState',
         ]
         # Need find more documentation on the status of functionapp. For now, any
         # other status is error.
         if show_function_result['state'] == 'Running':
             state = DeploymentState.RUNNING
         else:
             state = DeploymentState.ERROR
         info_json = {
             k: v
             for k, v in show_function_result.items() if k in keys
         }
         deployment_state = DeploymentState(
             info_json=json.dumps(info_json, default=str),
             state=state,
         )
         deployment_state.timestamp.GetCurrentTime()
         return DescribeDeploymentResponse(state=deployment_state,
                                           status=Status.OK())
     except BentoMLException as error:
         return DescribeDeploymentResponse(status=error.status_proto)
Exemple #25
0
    def ListBento(self, request, context=None):
        try:
            # TODO: validate request
            bento_metadata_pb_list = self.bento_metadata_store.list(
                bento_name=request.bento_name,
                offset=request.offset,
                limit=request.limit,
                order_by=request.order_by,
                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())
Exemple #26
0
 def DangerouslyDeleteBento(self, request, context=None):
     try:
         # TODO: validate request
         self.bento_metadata_store.dangerously_delete(
             request.bento_name, request.bento_version
         )
     except BentoMLException as e:
         logger.error("INTERNAL ERROR: %s", e)
         return DangerouslyDeleteBentoResponse(status=Status.INTERNAL(str(e)))
 def UpdateBento(self, request, context=None):
     try:
         # TODO: validate request
         if request.upload_status:
             self.bento_metadata_store.update_upload_status(
                 request.bento_name, request.bento_version,
                 request.upload_status)
         if request.service_metadata:
             self.bento_metadata_store.update_bento_service_metadata(
                 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())
Exemple #28
0
    def GetBento(self, request, context=None):
        try:
            # TODO: validate request
            bento_metadata_pb = self.bento_metadata_store.get(
                request.bento_name, request.bento_version)
            if request.bento_version.lower() == 'latest':
                logger.info(f'Getting latest version {request.bento_name}:'
                            f'{bento_metadata_pb.version}')

            if bento_metadata_pb:
                return GetBentoResponse(status=Status.OK(),
                                        bento=bento_metadata_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)
Exemple #29
0
    def delete(self, deployment_pb, repo=None):
        deployment_spec = deployment_pb.spec
        sagemaker_config = deployment_spec.sagemaker_operator_config
        if sagemaker_config is None:
            raise BentoMLDeploymentException('Sagemaker configuration is missing.')
        sagemaker_client = boto3.client('sagemaker', sagemaker_config.region)

        endpoint_name = generate_aws_compatible_string(
            deployment_pb.namespace + '-' + deployment_spec.bento_name
        )
        delete_endpoint_response = sagemaker_client.delete_endpoint(
            EndpointName=endpoint_name
        )
        logger.debug("AWS delete endpoint response: %s", delete_endpoint_response)
        if delete_endpoint_response["ResponseMetadata"]["HTTPStatusCode"] == 200:
            # We will also try to delete both model and endpoint configuration for user.
            # Since they are not critical, even they failed, we will still count delete
            # deployment a success action
            model_name = create_sagemaker_model_name(
                deployment_spec.bento_name, deployment_spec.bento_version
            )
            delete_model_response = sagemaker_client.delete_model(ModelName=model_name)
            logger.debug("AWS delete model response: %s", delete_model_response)
            if delete_model_response["ResponseMetadata"]["HTTPStatusCode"] != 200:
                logger.error(
                    "Encounter error when deleting model: %s", delete_model_response
                )

            endpoint_config_name = create_sagemaker_endpoint_config_name(
                deployment_spec.bento_name, deployment_spec.bento_version
            )
            delete_endpoint_config_response = sagemaker_client.delete_endpoint_config(  # noqa: E501
                EndpointConfigName=endpoint_config_name
            )
            logger.debug(
                "AWS delete endpoint config response: %s",
                delete_endpoint_config_response,
            )
            return DeleteDeploymentResponse(status=Status.OK())
        else:
            return DeleteDeploymentResponse(
                status=Status.INTERNAL(str(delete_endpoint_response))
            )
Exemple #30
0
def create_yatai_service_mock():
    yatai_service_mock = Mock()
    yatai_service_mock.ApplyDeployment.return_value = ApplyDeploymentResponse()
    yatai_service_mock.DeleteDeployment.return_value = DeleteDeploymentResponse()
    yatai_service_mock.DescribeDeployment.return_value = DescribeDeploymentResponse()
    yatai_service_mock.GetDeployment.return_value = GetDeploymentResponse(
        status=Status.NOT_FOUND()
    )
    yatai_service_mock.ListDeployments.return_value = ListDeploymentsResponse()
    return yatai_service_mock