def pull(self, bento: str) -> "BentoUri": """ Pull a :class:`~bentoml.BentoService` from remote Yatai. The BentoService will then be saved and registered to local Yatai. Args: bento (`str`): a BentoService identifier in the form of ``NAME:VERSION`` Returns: :class:`reflection.GeneratedProtocolMessageType`: URI as gRPC stub for save location of BentoService. Example:: from bentoml.yatai.client import get_yatai_client client = get_yatai_client('127.0.0.1:50051') saved_path = client.repository.pull('MyService:20210808_E38F3') """ bento_pb: "Bento" = self.get(bento) with TempDirectory() as tmpdir: # Create a non-exist directory for safe_retrieve target_bundle_path = os.path.join(tmpdir, 'bundle') self.download_to_directory(bento_pb, target_bundle_path) from bentoml.yatai.client import get_yatai_client labels = (dict(bento_pb.bento_service_metadata.labels) if bento_pb.bento_service_metadata.labels else None) local_yc = get_yatai_client() return local_yc.repository.upload_from_dir(target_bundle_path, labels=labels)
def test_s3(minio_address): yatai_server_command = [ 'bentoml', 'yatai-service-start', '--no-ui', '--grpc-port', '50051', '--repo-base-url', f's3://{bucket_name}/', '--s3-endpoint-url', 'localhost:9000', ] proc = subprocess.Popen(yatai_server_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) yatai_server_url = "localhost:50051" svc = ExampleBentoService() svc.pack('model', {'model': 'abc'}) bento_tag = f'{svc.name}:{svc.version}' saved_path = svc.save(yatai_url=yatai_server_url) yc = get_yatai_client(yatai_server_url) assert saved_path.startswith('s3://') bento_pb = yc.repository.get(bento_tag) with TempDirectory() as temp_dir: yc.repository.download_to_directory(bento_pb, f'{temp_dir}/bundle') assert os.path.exists(f'{temp_dir}/bundle/bentoml.yml') proc.kill()
def resolve_bundle_path( bento: str, pip_installed_bundle_path: Optional[str] = None, yatai_url: Optional[str] = None, ) -> str: from bentoml.exceptions import BentoMLException from bentoml.yatai.client import get_yatai_client if pip_installed_bundle_path: assert ( bento is None ), "pip installed BentoService commands should not have Bento argument" return pip_installed_bundle_path if os.path.isdir(bento) or is_s3_url(bento) or is_gcs_url(bento): # saved_bundle already support loading local, s3 path and gcs path return bento elif ":" in bento: # assuming passing in BentoService in the form of Name:Version tag yatai_client = get_yatai_client(yatai_url) bento_pb = yatai_client.repository.get(bento) return resolve_bento_bundle_uri(bento_pb) else: raise BentoMLException( f'BentoService "{bento}" not found - either specify the file path of ' f"the BentoService saved bundle, or the BentoService id in the form of " f'"name:version"')
def resolve_bundle_path(bento, pip_installed_bundle_path, yatai_url=None): from bentoml.yatai.client import get_yatai_client from bentoml.exceptions import BentoMLException if pip_installed_bundle_path: assert ( bento is None ), "pip installed BentoService commands should not have Bento argument" return pip_installed_bundle_path if os.path.isdir(bento) or is_s3_url(bento) or is_gcs_url(bento): # saved_bundle already support loading local, s3 path and gcs path return bento elif ":" in bento: # assuming passing in BentoService in the form of Name:Version tag yatai_client = get_yatai_client(yatai_url) bento_pb = yatai_client.repository.get(bento) if bento_pb.uri.s3_presigned_url: # Use s3 presigned URL for downloading the repository if it is presented return bento_pb.uri.s3_presigned_url if bento_pb.uri.gcs_presigned_url: return bento_pb.uri.gcs_presigned_url else: return bento_pb.uri.uri else: raise BentoMLException( f'BentoService "{bento}" not found - either specify the file path of ' f"the BentoService saved bundle, or the BentoService id in the form of " f'"name:version"')
def push(self, bento, labels=None): """ Push a local BentoService to a remote yatai server. Args: bento: a BentoService identifier in the format of NAME:VERSION labels: optional. List of labels for the BentoService. Returns: BentoService saved path Example: >>> svc = MyBentoService() >>> svc.save() >>> >>> remote_yatai_client = get_yatai_client('http://remote.yatai.service:50050') >>> bento = f'{svc.name}:{svc.version}' >>> remote_saved_path= remote_yatai_client.repository.push(bento) """ track('py-api-push') from bentoml.yatai.client import get_yatai_client local_yc = get_yatai_client() local_bento_pb = local_yc.repository.get(bento) if local_bento_pb.uri.s3_presigned_url: bento_bundle_path = local_bento_pb.uri.s3_presigned_url elif local_bento_pb.uri.gcs_presigned_url: bento_bundle_path = local_bento_pb.uri.gcs_presigned_url else: bento_bundle_path = local_bento_pb.uri.uri return self.upload_from_dir(bento_bundle_path, labels=labels)
def test_yatai_server_with_postgres_and_s3(postgres_db_container_url): # Note: Use pre-existing bucket instead of newly created bucket, because the # bucket's global DNS needs time to get set up. # https://github.com/boto/boto3/issues/1982#issuecomment-511947643 s3_bucket_name = 's3://bentoml-e2e-test-repo/' with local_yatai_server(db_url=postgres_db_container_url, repo_base_url=s3_bucket_name) as yatai_service_url: yc = get_yatai_client(yatai_service_url) logger.info('Saving bento service') svc = SampleBentoService() svc.save(yatai_url=yatai_service_url) bento_tag = f'{svc.name}:{svc.version}' logger.info('BentoService saved') logger.info("Display bentoservice info") bento = yc.repository.get(bento_tag) logger.info(bento) assert (bento.uri.type == BentoUri.S3 ), 'BentoService storage type mismatched, expect S3' logger.info('Validate BentoService prediction result') run_result = execute_bentoml_run_command(bento_tag=bento_tag, data='[]', yatai_url=yatai_service_url) assert 'cat' in run_result, 'Unexpected BentoService prediction result' logger.info(f'Deleting saved bundle {bento_tag}') yc.repository.delete(bento_tag=bento_tag)
def test_yatai_server_with_sqlite_and_gcs(): gcs_bucket_name = 'gs://bentoml-e2e-tests/' with local_yatai_server( repo_base_url=gcs_bucket_name) as yatai_service_url: yc = get_yatai_client(yatai_service_url) logger.info('Saving bento service') svc = SampleBentoService() svc.save(yatai_url=yatai_service_url) bento_tag = f'{svc.name}:{svc.version}' logger.info('BentoService saved') logger.info("Display bentoservice info") bento = yc.repository.get(bento_tag) logger.info(bento) assert (bento.uri.type == BentoUri.GCS ), 'BentoService storage type mismatched, expect GCS' logger.info('Validate BentoService prediction result') run_result = execute_bentoml_run_command(bento_tag=bento_tag, data='[]', yatai_url=yatai_service_url) logger.info(run_result) assert 'cat' in run_result, 'Unexpected BentoService prediction result' logger.info(f'Deleting saved bundle {bento_tag}') yc.repository.delete(bento_tag=bento_tag)
def push(bento, yatai_url): if ':' not in bento: _echo(f'BentoService {bento} invalid - specify name:version') return yc = get_yatai_client(yatai_url) yc.repository.push(bento=bento) _echo(f'Pushed {bento} to {yatai_url}')
def push(self, bento, labels=None): """ Push a local BentoService to a remote yatai server. Args: bento: a BentoService identifier in the format of NAME:VERSION labels: optional. List of labels for the BentoService. Returns: BentoService saved path """ track('py-api-push') if isinstance(self.yatai_service, YataiService): raise BentoMLException('need set yatai_service_url') from bentoml.yatai.client import get_yatai_client local_yc = get_yatai_client() local_bento_pb = local_yc.repository.get(bento) if local_bento_pb.uri.s3_presigned_url: bento_bundle_path = local_bento_pb.uri.s3_presigned_url elif local_bento_pb.uri.gcs_presigned_url: bento_bundle_path = local_bento_pb.uri.gcs_presigned_url else: bento_bundle_path = local_bento_pb.uri.uri return self.upload_from_dir(bento_bundle_path, labels=labels)
def test_delete_bentos_with_tags(bento_service): yc = get_yatai_client() yc.repository.delete(prune=True) version_one = uuid.uuid4().hex[0:8] version_two = uuid.uuid4().hex[0:8] version_three = uuid.uuid4().hex[0:8] bento_service.save(version=version_one) bento_service.save(version=version_two) bento_service.save(version=version_three) yc.repository.delete(bento_tag=f'{bento_service.name}:{version_one}') bentos = yc.repository.list() assert len(bentos) == 2 runner = CliRunner() cli = create_bentoml_cli() print(cli.commands['delete']) runner.invoke( cli.commands['delete'], [ f'{bento_service.name}:{version_two},{bento_service.name}:{version_three}', '-y', ], ) bentos = yc.repository.list() assert len(bentos) == 0 # Clean up existing bentos yc.repository.delete(prune=True)
def pull(self, bento): """ Pull a BentoService from a remote yatai service. The BentoService will be saved and registered with local yatai service. Args: bento: a BentoService identifier in the form of NAME:VERSION Returns: BentoService saved path Example: >>> remote_yatai_client = get_yatai_client('remote_yatai_service_address') >>> saved_path = remote_yatai_client.repository.pull('MyService:version') """ track('py-api-pull') if isinstance(self.yatai_service, YataiService): raise BentoMLException('need set yatai_service_url') bento_pb = self.get(bento) with TempDirectory() as tmpdir: # Create a non-exist directory for safe_retrieve target_bundle_path = os.path.join(tmpdir, 'bundle') self.download_to_directory(bento_pb, target_bundle_path) from bentoml.yatai.client import get_yatai_client local_yc = get_yatai_client() return local_yc.repository.upload_from_dir(target_bundle_path)
def pull(self, bento): """ Pull a BentoService from a remote yatai service. The BentoService will be saved and registered with local yatai service. Args: bento: a BentoService identifier in the form of NAME:VERSION Returns: BentoService saved path Example: >>> client = get_yatai_client('127.0.0.1:50051') >>> saved_path = client.repository.pull('MyService:') """ track('py-api-pull') bento_pb = self.get(bento) with TempDirectory() as tmpdir: # Create a non-exist directory for safe_retrieve target_bundle_path = os.path.join(tmpdir, 'bundle') self.download_to_directory(bento_pb, target_bundle_path) from bentoml.yatai.client import get_yatai_client labels = (dict(bento_pb.bento_service_metadata.labels) if bento_pb.bento_service_metadata.labels else None) local_yc = get_yatai_client() return local_yc.repository.upload_from_dir(target_bundle_path, labels=labels)
def test_push_and_pull(): with yatai_service_container() as yatai_server_url: svc = ExampleBentoService() bento_tag = f'{svc.name}:{svc.version}' saved_path = svc.save() yc = get_yatai_client(yatai_server_url) pushed_path = yc.repository.push(bento_tag) assert pushed_path != saved_path local_yc = get_yatai_client() delete_result = local_yc.repository.delete(bento_tag) assert delete_result is None assert os.path.exists(saved_path) is False pull_result = yc.repository.pull(bento_tag) assert pull_result == saved_path
def test_pull_with_labels(yatai_service_url, example_bento_service_class): yc = get_yatai_client(yatai_service_url) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) saved_path = svc.save( yatai_url=yatai_service_url, labels={'foo': 'bar', 'abc': '123'} ) pulled_local_path = yc.repository.pull(f'{svc.name}:{svc.version}') assert pulled_local_path != saved_path local_yc = get_yatai_client() local_bento_pb = local_yc.repository.get(f'{svc.name}:{svc.version}') assert local_bento_pb.bento_service_metadata.labels labels = dict(local_bento_pb.bento_service_metadata.labels) assert labels['foo'] == 'bar' assert labels['abc'] == '123'
def test_yatai_server_containerize_without_push(example_bento_service_class): svc = example_bento_service_class() svc.save() yc = get_yatai_client() image_tag = 'mytag' built_tag = yc.repository.containerize(bento=svc.tag, tag=image_tag) assert built_tag == f'{image_tag}:{svc.version}'
def test_load(yatai_service_url, example_bento_service_class): yc = get_yatai_client(yatai_service_url) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) svc.save(yatai_url=yatai_service_url) loaded_svc = yc.repository.load(f'{svc.name}:{svc.version}') assert loaded_svc.name == svc.name
def test_get(yatai_service_url, example_bento_service_class): yc = get_yatai_client(yatai_service_url) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) svc.save(yatai_url=yatai_service_url) svc_pb = yc.repository.get(f'{svc.name}:{svc.version}') assert svc_pb.bento_service_metadata.name == svc.name assert svc_pb.bento_service_metadata.version == svc.version
def test_pull(yatai_service_url, example_bento_service_class): yc = get_yatai_client(yatai_service_url) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) saved_path = svc.save(yatai_url=yatai_service_url) pulled_local_path = yc.repository.pull(f'{svc.name}:{svc.version}') assert pulled_local_path != saved_path
def test_load_from_dir(example_bento_service_class): yc = get_yatai_client() test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) saved_path = svc.save() loaded_svc = yc.repository.load(saved_path) assert loaded_svc.name == svc.name
def test_delete_all_bentos(bento_service): bento_service.save(version=uuid.uuid4().hex[0:8]) bento_service.save(version=uuid.uuid4().hex[0:8]) bento_service.save(version=uuid.uuid4().hex[0:8]) yc = get_yatai_client() yc.repository.delete(prune=True) bentos = yc.repository.list() assert len(bentos) == 0
def test_list_by_name(yatai_service_url): class ListByNameSvc(bentoml.BentoService): pass yc = get_yatai_client(yatai_service_url) svc = ListByNameSvc() svc.save(yatai_url=yatai_service_url) bentos = yc.repository.list(bento_name=svc.name) assert len(bentos) == 1
def containerize(bento, push, tag, build_arg, yatai_url): """Containerize specified BentoService. BENTO is the target BentoService to be containerized, referenced by its name and version in format of name:version. For example: "iris_classifier:v1.2.0" `bentoml containerize` command also supports the use of the `latest` tag which will automatically use the last built version of your Bento. You can provide a tag for the image built by Bento using the `--tag` flag. Additionally, you can provide a `--push` flag, which will push the built image to the Docker repository specified by the image tag. You can also prefixing the tag with a hostname for the repository you wish to push to. e.g. `bentoml containerize IrisClassifier:latest --push --tag repo-address.com:username/iris` would build a Docker image called `username/iris:latest` and push that to docker repository at repo-address.com. By default, the `containerize` command will use the current credentials provided by Docker daemon. """ saved_bundle_path = resolve_bundle_path(bento, pip_installed_bundle_path, yatai_url) _echo(f"Found Bento: {saved_bundle_path}") # fmt: off bento_metadata: "BentoServiceMetadata" = load_bento_service_metadata( saved_bundle_path) # noqa: E501 # fmt: on bento_tag = f'{bento_metadata.name}:{bento_metadata.version}' yatai_client: "YataiClient" = get_yatai_client(yatai_url) docker_build_args = {} if build_arg: for arg in build_arg: key, value = arg.split("=", 1) docker_build_args[key] = value if yatai_url is not None: spinner_message = f'Sending containerize RPC to YataiService at {yatai_url}' else: spinner_message = ( f'Containerizing {bento_tag} with local YataiService and docker ' f'daemon from local environment') with Spinner(spinner_message): tag: str = yatai_client.repository.containerize( bento=bento_tag, tag=tag, build_args=docker_build_args, push=push, ) _echo(f'\nBuild container image: {tag}', CLI_COLOR_SUCCESS)
def test_yatai_server_containerize_without_push(): svc = ExampleBentoService() svc.pack('model', [1, 2, 3]) logger.info('Saving bento service to local yatai server') svc.save() yc = get_yatai_client() tag = 'mytag' built_tag = yc.repository.containerize(bento=f'{svc.name}:{svc.version}', tag=tag) assert built_tag == f'{tag}:{svc.version}'
def list_bentos(limit, offset, labels, order_by, ascending_order, yatai_url, output): yc = get_yatai_client(yatai_url) result = yc.repository.list( limit=limit, offset=offset, labels=labels, order_by=order_by, ascending_order=ascending_order, ) _print_bentos_info(result, output)
def test_load(yatai_server_container, example_bento_service_class): example_bento_service_class = bentoml.ver( major=2, minor=5)(example_bento_service_class) yc = get_yatai_client(yatai_server_container) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) svc.save(yatai_url=yatai_server_container) loaded_svc = yc.repository.load(f'{svc.name}:{svc.version}') assert loaded_svc.name == svc.name
def test_list(yatai_server_container, example_bento_service_class): example_bento_service_class = bentoml.ver( major=2, minor=5)(example_bento_service_class) yc = get_yatai_client(yatai_server_container) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) svc.save(yatai_url=yatai_server_container) bentos = yc.repository.list(bento_name=svc.name) assert len(bentos) == 5
def test_delete_bentos_base_on_labels(bento_service): yc = get_yatai_client() yc.repository.delete(prune=True) bento_service.save(version=uuid.uuid4().hex[0:8], labels={'cohort': '100'}) bento_service.save(version=uuid.uuid4().hex[0:8], labels={'cohort': '110'}) bento_service.save(version=uuid.uuid4().hex[0:8], labels={'cohort': '120'}) yc.repository.delete(labels='cohort in (100, 110)') bentos = yc.repository.list() assert len(bentos) == 1 # Clean up existing bentos yc.repository.delete(prune=True)
def test_pull(yatai_server_container, example_bento_service_class): example_bento_service_class = bentoml.ver( major=2, minor=3)(example_bento_service_class) yc = get_yatai_client(yatai_server_container) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) saved_path = svc.save(yatai_url=yatai_server_container) pulled_local_path = yc.repository.pull(f'{svc.name}:{svc.version}') assert pulled_local_path != saved_path
def test_get(yatai_server_container, example_bento_service_class): example_bento_service_class = bentoml.ver( major=2, minor=4)(example_bento_service_class) yc = get_yatai_client(yatai_server_container) test_model = TestModel() svc = example_bento_service_class() svc.pack('model', test_model) svc.save(yatai_url=yatai_server_container) svc_pb = yc.repository.get(f'{svc.name}:{svc.version}') assert svc_pb.bento_service_metadata.name == svc.name assert svc_pb.bento_service_metadata.version == svc.version
def test_delete_bentos_base_on_name(bento_service): yc = get_yatai_client() yc.repository.delete(prune=True) bento_service.save(version=uuid.uuid4().hex[0:8]) bento_service.save(version=uuid.uuid4().hex[0:8]) iris = IrisClassifier() iris.save() yc.repository.delete(bento_name=bento_service.name) bentos = yc.repository.list() assert len(bentos) == 1 # Clean up existing bentos yc.repository.delete(prune=True)