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)))
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)))
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)))
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))
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))
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)))
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())
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)
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)
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)
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)
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)
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)))
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
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)
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)
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
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)
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))
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))
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)
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())
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())
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)
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)) )
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