def _upload_bento_service(self, saved_bento_path): bento_service_metadata = load_bento_service_metadata(saved_bento_path) get_bento_response = self.yatai_service.GetBento( GetBentoRequest( bento_name=bento_service_metadata.name, bento_version=bento_service_metadata.version, )) if get_bento_response.status.status_code == status_pb2.Status.OK: raise BentoMLException( "BentoService bundle {}:{} already registered in repository. Reset " "BentoService version with BentoService#set_version or bypass BentoML's" " model registry feature with BentoService#save_to_dir".format( bento_service_metadata.name, bento_service_metadata.version)) elif get_bento_response.status.status_code != status_pb2.Status.NOT_FOUND: raise BentoMLException( 'Failed accessing YataiService. {error_code}:' '{error_message}'.format( error_code=Status.Name( get_bento_response.status.status_code), error_message=get_bento_response.status.error_message, )) request = AddBentoRequest( bento_name=bento_service_metadata.name, bento_version=bento_service_metadata.version, ) response = self.yatai_service.AddBento(request) if response.status.status_code != status_pb2.Status.OK: raise BentoMLException( "Error adding BentoService bundle to repository: {}:{}".format( Status.Name(response.status.status_code), response.status.error_message, )) if response.uri.type == BentoUri.LOCAL: if os.path.exists(response.uri.uri): # due to copytree dst must not already exist shutil.rmtree(response.uri.uri) shutil.copytree(saved_bento_path, response.uri.uri) self._update_bento_upload_progress(bento_service_metadata) logger.info( "BentoService bundle '%s:%s' saved to: %s", bento_service_metadata.name, bento_service_metadata.version, response.uri.uri, ) # Return URI to saved bento in repository storage return response.uri.uri elif response.uri.type == BentoUri.S3: self._update_bento_upload_progress(bento_service_metadata, UploadStatus.UPLOADING, 0) fileobj = io.BytesIO() with tarfile.open(mode="w:gz", fileobj=fileobj) as tar: tar.add(saved_bento_path, arcname=bento_service_metadata.name) fileobj.seek(0, 0) files = { 'file': ('dummy', fileobj) } # dummy file name because file name # has been generated when getting the pre-signed signature. data = json.loads(response.uri.additional_fields) uri = data.pop('url') http_response = requests.post(uri, data=data, files=files) if http_response.status_code != 204: self._update_bento_upload_progress(bento_service_metadata, UploadStatus.ERROR) raise BentoMLException( f"Error saving BentoService bundle to S3. " f"{http_response.status_code}: {http_response.text}") self._update_bento_upload_progress(bento_service_metadata) logger.info( "Successfully saved BentoService bundle '%s:%s' to S3: %s", bento_service_metadata.name, bento_service_metadata.version, response.uri.uri, ) return response.uri.uri else: raise BentoMLException( f"Error saving Bento to target repository, URI type {response.uri.type}" f" at {response.uri.uri} not supported")
def upload_from_dir(self, saved_bento_path, labels=None): bento_service_metadata = load_bento_service_metadata(saved_bento_path) if labels: _validate_labels(labels) bento_service_metadata.labels.update(labels) get_bento_response = self.yatai_service.GetBento( GetBentoRequest( bento_name=bento_service_metadata.name, bento_version=bento_service_metadata.version, )) if get_bento_response.status.status_code == status_pb2.Status.OK: raise BentoMLException( "BentoService bundle {}:{} already registered in repository. Reset " "BentoService version with BentoService#set_version or bypass BentoML's" " model registry feature with BentoService#save_to_dir".format( bento_service_metadata.name, bento_service_metadata.version)) elif get_bento_response.status.status_code != status_pb2.Status.NOT_FOUND: raise BentoMLException( 'Failed accessing YataiService. {error_code}:' '{error_message}'.format( error_code=Status.Name( get_bento_response.status.status_code), error_message=get_bento_response.status.error_message, )) request = AddBentoRequest( bento_name=bento_service_metadata.name, bento_version=bento_service_metadata.version, ) response = self.yatai_service.AddBento(request) if response.status.status_code != status_pb2.Status.OK: raise BentoMLException( "Error adding BentoService bundle to repository: {}:{}".format( Status.Name(response.status.status_code), response.status.error_message, )) if response.uri.type == BentoUri.LOCAL: if os.path.exists(response.uri.uri): # due to copytree dst must not already exist shutil.rmtree(response.uri.uri) shutil.copytree(saved_bento_path, response.uri.uri) self._update_bento_upload_progress(bento_service_metadata) logger.info( "BentoService bundle '%s:%s' saved to: %s", bento_service_metadata.name, bento_service_metadata.version, response.uri.uri, ) # Return URI to saved bento in repository storage return response.uri.uri elif response.uri.type == BentoUri.S3 or response.uri.type == BentoUri.GCS: uri_type = 'S3' if response.uri.type == BentoUri.S3 else 'GCS' self._update_bento_upload_progress(bento_service_metadata, UploadStatus.UPLOADING, 0) fileobj = io.BytesIO() with tarfile.open(mode="w:gz", fileobj=fileobj) as tar: tar.add(saved_bento_path, arcname=bento_service_metadata.name) fileobj.seek(0, 0) if response.uri.type == BentoUri.S3: http_response = requests.put(response.uri.s3_presigned_url, data=fileobj) elif response.uri.type == BentoUri.GCS: http_response = requests.put(response.uri.gcs_presigned_url, data=fileobj) if http_response.status_code != 200: self._update_bento_upload_progress(bento_service_metadata, UploadStatus.ERROR) raise BentoMLException( f"Error saving BentoService bundle to {uri_type}." f"{http_response.status_code}: {http_response.text}") self._update_bento_upload_progress(bento_service_metadata) logger.info( "Successfully saved BentoService bundle '%s:%s' to {uri_type}: %s", bento_service_metadata.name, bento_service_metadata.version, response.uri.uri, ) return response.uri.uri else: raise BentoMLException( f"Error saving Bento to target repository, URI type {response.uri.type}" f" at {response.uri.uri} not supported")
def create_deployment( deployment_name, namespace, bento_name, bento_version, platform, operator_spec, labels=None, annotations=None, yatai_service=None, ): if yatai_service is None: from bentoml.yatai import get_yatai_service yatai_service = get_yatai_service() try: # Make sure there is no active deployment with the same deployment name get_deployment_pb = yatai_service.GetDeployment( GetDeploymentRequest(deployment_name=deployment_name, namespace=namespace) ) if get_deployment_pb.status.status_code == status_pb2.Status.OK: raise BentoMLDeploymentException( 'Deployment "{name}" already existed, use Update or Apply for updating' 'existing deployment, or create the deployment with a different name or' 'under a different deployment namespace'.format(name=deployment_name) ) if get_deployment_pb.status.status_code != status_pb2.Status.NOT_FOUND: raise BentoMLDeploymentException( 'Failed accesing YataiService deployment store. {error_code}:' '{error_message}'.format( error_code=Status.Name(get_deployment_pb.status.status_code), error_message=get_deployment_pb.status.error_message, ) ) deployment_dict = { "name": deployment_name, "namespace": namespace or config().get('deployment', 'default_namespace'), "labels": labels, "annotations": annotations, "spec": { "bento_name": bento_name, "bento_version": bento_version, "operator": platform, }, } operator = platform.replace('-', '_').upper() try: operator_value = DeploymentSpec.DeploymentOperator.Value(operator) except ValueError: return ApplyDeploymentResponse( status=Status.INVALID_ARGUMENT('Invalid platform "{}"'.format(platform)) ) if operator_value == DeploymentSpec.AWS_SAGEMAKER: deployment_dict['spec']['sagemaker_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region'), 'instance_count': operator_spec.get('instance_count') or config().getint('sagemaker', 'default_instance_count'), 'instance_type': operator_spec.get('instance_type') or config().get('sagemaker', 'default_instance_type'), 'api_name': operator_spec.get('api_name', ''), } elif operator_value == DeploymentSpec.AWS_LAMBDA: deployment_dict['spec']['aws_lambda_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region') } if operator_spec.get('api_name'): deployment_dict['spec']['aws_lambda_operator_config'][ 'api_name' ] = operator_spec['api_name'] elif operator_value == DeploymentSpec.GCP_FCUNTION: deployment_dict['spec']['gcp_function_operatorConfig'] = { 'region': operator_spec.get('region') or config().get('google-cloud', 'default_region') } if operator_spec.get('api_name'): deployment_dict['spec']['gcp_function_operator_config'][ 'api_name' ] = operator_spec['api_name'] elif operator_value == DeploymentSpec.KUBERNETES: deployment_dict['spec']['kubernetes_operator_config'] = { 'kube_namespace': operator_spec.get('kube_namespace', ''), 'replicas': operator_spec.get('replicas', 0), 'service_name': operator_spec.get('service_name', ''), 'service_type': operator_spec.get('service_type', ''), } else: raise BentoMLDeploymentException( 'Platform "{}" is not supported in the current version of ' 'BentoML'.format(platform) ) return apply_deployment(deployment_dict, yatai_service) except BentoMLException as error: return ApplyDeploymentResponse(status=Status.INTERNAL(str(error)))
def create_deployment( deployment_name, namespace, bento_name, bento_version, platform, operator_spec, labels=None, annotations=None, yatai_service=None, ): if yatai_service is None: from bentoml.yatai import get_yatai_service yatai_service = get_yatai_service() # Make sure there is no active deployment with the same deployment name get_deployment_pb = yatai_service.GetDeployment( GetDeploymentRequest(deployment_name=deployment_name, namespace=namespace)) if get_deployment_pb.status.status_code == status_pb2.Status.OK: raise YataiDeploymentException( 'Deployment "{name}" already existed, use Update or Apply for updating ' 'existing deployment, delete the deployment, or use a different deployment ' 'name'.format(name=deployment_name)) if get_deployment_pb.status.status_code != status_pb2.Status.NOT_FOUND: raise YataiDeploymentException( 'Failed accesing YataiService deployment store. {error_code}:' '{error_message}'.format( error_code=Status.Name(get_deployment_pb.status.status_code), error_message=get_deployment_pb.status.error_message, )) deployment_dict = { "name": deployment_name, "namespace": namespace or config().get('deployment', 'default_namespace'), "labels": labels, "annotations": annotations, "spec": { "bento_name": bento_name, "bento_version": bento_version, "operator": platform, }, } operator = platform.replace('-', '_').upper() try: operator_value = DeploymentSpec.DeploymentOperator.Value(operator) except ValueError: return ApplyDeploymentResponse(status=Status.INVALID_ARGUMENT( 'Invalid platform "{}"'.format(platform))) if operator_value == DeploymentSpec.AWS_SAGEMAKER: deployment_dict['spec']['sagemaker_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region'), 'instance_count': operator_spec.get('instance_count'), 'instance_type': operator_spec.get('instance_type'), 'api_name': operator_spec.get('api_name', ''), } if operator_spec.get('num_of_gunicorn_workers_per_instance'): deployment_dict['spec']['sagemaker_operator_config'][ 'num_of_gunicorn_workers_per_instance'] = operator_spec.get( 'num_of_gunicorn_workers_per_instance') elif operator_value == DeploymentSpec.AWS_LAMBDA: deployment_dict['spec']['aws_lambda_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region') } for field in ['api_name', 'memory_size', 'timeout']: if operator_spec.get(field): deployment_dict['spec']['aws_lambda_operator_config'][ field] = operator_spec[field] elif operator_value == DeploymentSpec.KUBERNETES: deployment_dict['spec']['kubernetes_operator_config'] = { 'kube_namespace': operator_spec.get('kube_namespace', ''), 'replicas': operator_spec.get('replicas', 0), 'service_name': operator_spec.get('service_name', ''), 'service_type': operator_spec.get('service_type', ''), } else: raise YataiDeploymentException( 'Platform "{}" is not supported in the current version of ' 'BentoML'.format(platform)) apply_response = apply_deployment(deployment_dict, yatai_service) if apply_response.status.status_code == status_pb2.Status.OK: describe_response = describe_deployment(deployment_name, namespace, yatai_service) if describe_response.status.status_code == status_pb2.Status.OK: deployment_state = describe_response.state apply_response.deployment.state.CopyFrom(deployment_state) return apply_response return apply_response
def upload_from_dir(self, saved_bento_path: str, labels: Dict = None) -> "BentoUri": from bentoml.yatai.db.stores.label import _validate_labels bento_service_metadata = load_bento_service_metadata(saved_bento_path) if labels: _validate_labels(labels) bento_service_metadata.labels.update(labels) get_bento_response = self.yatai_service.GetBento( GetBentoRequest( bento_name=bento_service_metadata.name, bento_version=bento_service_metadata.version, )) if get_bento_response.status.status_code == status_pb2.Status.OK: raise BentoMLException( "BentoService bundle {}:{} already registered in repository. Reset " "BentoService version with BentoService#set_version or bypass BentoML's" " model registry feature with BentoService#save_to_dir".format( bento_service_metadata.name, bento_service_metadata.version)) elif get_bento_response.status.status_code != status_pb2.Status.NOT_FOUND: raise BentoMLException( 'Failed accessing YataiService. {error_code}:' '{error_message}'.format( error_code=Status.Name( get_bento_response.status.status_code), error_message=get_bento_response.status.error_message, )) request = AddBentoRequest( bento_name=bento_service_metadata.name, bento_version=bento_service_metadata.version, ) response = self.yatai_service.AddBento(request) if response.status.status_code != status_pb2.Status.OK: raise BentoMLException( "Error adding BentoService bundle to repository: {}:{}".format( Status.Name(response.status.status_code), response.status.error_message, )) if response.uri.type == BentoUri.LOCAL: # When using Yatai backed by File System repository, # if Yatai is a local instance, copy the files directly. # Otherwise, use UploadBento RPC to stream files to remote Yatai server if is_remote_yatai(self.yatai_service): self._upload_bento( bento_service_metadata.name, bento_service_metadata.version, saved_bento_path, ) update_bento_service = UpdateBentoRequest( bento_name=bento_service_metadata.name, bento_version=bento_service_metadata.version, service_metadata=bento_service_metadata, ) self.yatai_service.UpdateBento(update_bento_service) else: if os.path.exists(response.uri.uri): raise BentoMLException( f'Bento bundle directory {response.uri.uri} already exist' ) shutil.copytree(saved_bento_path, response.uri.uri) upload_status = UploadStatus.DONE self._update_bento_upload_progress(bento_service_metadata, status=upload_status) logger.info( "BentoService bundle '%s:%s' saved to: %s", bento_service_metadata.name, bento_service_metadata.version, response.uri.uri, ) # Return URI to saved bento in repository storage return response.uri.uri elif response.uri.type == BentoUri.S3 or response.uri.type == BentoUri.GCS: uri_type = 'S3' if response.uri.type == BentoUri.S3 else 'GCS' self._update_bento_upload_progress(bento_service_metadata, UploadStatus.UPLOADING, 0) fileobj = io.BytesIO() with tarfile.open(mode="w:gz", fileobj=fileobj) as tar: tar.add(saved_bento_path, arcname=bento_service_metadata.name) fileobj.seek(0, 0) if response.uri.type == BentoUri.S3: http_response = requests.put(response.uri.s3_presigned_url, data=fileobj) elif response.uri.type == BentoUri.GCS: http_response = requests.put(response.uri.gcs_presigned_url, data=fileobj) if http_response.status_code != 200: self._update_bento_upload_progress(bento_service_metadata, UploadStatus.ERROR) raise BentoMLException( f"Error saving BentoService bundle to {uri_type}." f"{http_response.status_code}: {http_response.text}") self._update_bento_upload_progress(bento_service_metadata) logger.info( "Successfully saved BentoService bundle '%s:%s' to {uri_type}: %s", bento_service_metadata.name, bento_service_metadata.version, response.uri.uri, ) return response.uri.uri else: raise BentoMLException( f"Error saving Bento to target repository, URI type {response.uri.type}" f" at {response.uri.uri} not supported")