Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/bentoml/_internal/cloud/bento.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,19 @@ def list(self) -> BentoWithRepositoryListSchema:
]
return res

def delete(self, name: str, version: str) -> None:
"""Delete a bento from the remote bento store

Args:
name: The name of the bento repository
version: The version of the bento to delete
"""
rest_client = self._client
res = rest_client.v1.get_bento(bento_repository_name=name, version=version)
if res is None:
raise NotFound(f'Bento "{name}:{version}" not found on remote')
rest_client.v1.delete_bento(bento_repository_name=name, version=version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if passing non-existing name or version to delete endpoint? Confirm this so we can decide if the pre-check is necessary.


def get(self, name: str, version: str | None) -> BentoSchema:
"""Get a bento by name and version

Expand Down
5 changes: 5 additions & 0 deletions src/bentoml/_internal/cloud/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ def finish_upload_bento(
self._check_resp(resp)
return schema_from_object(resp.json(), BentoSchema)

def delete_bento(self, bento_repository_name: str, version: str) -> None:
url = f"/api/v1/bento_repositories/{bento_repository_name}/bentos/{version}"
resp = self.session.delete(url)
self._check_resp(resp)

def upload_bento(
self, bento_repository_name: str, version: str, data: t.IO[bytes]
) -> None:
Expand Down
12 changes: 11 additions & 1 deletion src/bentoml/bentos.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,19 @@ def get(
def delete(
tag: Tag | str,
*,
cloud: bool = False,
_bento_store: BentoStore = Provide[BentoMLContainer.bento_store],
_cloud_client: BentoCloudClient = Provide[BentoMLContainer.bentocloud_client],
):
_bento_store.delete(tag)
if cloud:
_tag = Tag.from_taglike(tag)
if _tag.version is None:
raise BentoMLException(
f'Bento version is required for cloud deletion. Please specify a version, e.g. "{_tag.name}:<version>"'
)
_cloud_client.bento.delete(_tag.name, _tag.version)
else:
_bento_store.delete(tag)


@inject
Expand Down
44 changes: 32 additions & 12 deletions src/bentoml_cli/bentos.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,21 @@ def list_bentos(
is_flag=True,
help="Skip confirmation when deleting a specific bento bundle",
)
@click.option(
"--cloud",
is_flag=True,
default=False,
help="Delete Bento from BentoCloud remote server",
)
@inject
def delete(
delete_targets: list[str],
yes: bool,
cloud: bool,
bento_store: BentoStore = Provide[BentoMLContainer.bento_store],
cloud_client: BentoCloudClient = Provide[BentoMLContainer.bentocloud_client],
) -> None: # type: ignore (not accessed)
"""Delete Bento in local bento store.
"""Delete Bento in local bento store or from BentoCloud.

\b
Examples:
Expand All @@ -235,25 +243,37 @@ def delete(
* Bulk delete multiple bento bundles by name and version, separated by ",", e.g.: `bentoml delete Irisclassifier:v1,MyPredictService:v2`
* Bulk delete multiple bento bundles by name and version, separated by " ", e.g.: `bentoml delete Irisclassifier:v1 MyPredictService:v2`
* Bulk delete without confirmation, e.g.: `bentoml delete IrisClassifier --yes`
* Delete from BentoCloud, e.g.: `bentoml delete IrisClassifier:v1 --cloud`
"""

def delete_target(target: str) -> None:
tag = Tag.from_str(target)

if tag.version is None:
to_delete_bentos = bento_store.list(target)
if cloud:
if tag.version is None:
raise click.BadParameter(
f'Deleting all versions is not supported for cloud bentos. Please specify a version, e.g. "{tag.name}:<version>"'
)
if not yes:
if not click.confirm(f"delete bento {tag} from BentoCloud?"):
return
cloud_client.bento.delete(tag.name, tag.version)
rich.print(f"Bento {tag} deleted from BentoCloud.")
else:
to_delete_bentos = [bento_store.get(tag)]

for bento in to_delete_bentos:
if yes:
delete_confirmed = True
if tag.version is None:
to_delete_bentos = bento_store.list(target)
else:
delete_confirmed = click.confirm(f"delete bento {bento.tag}?")
to_delete_bentos = [bento_store.get(tag)]

for bento in to_delete_bentos:
if yes:
delete_confirmed = True
else:
delete_confirmed = click.confirm(f"delete bento {bento.tag}?")

if delete_confirmed:
bento_store.delete(bento.tag)
rich.print(f"{bento} deleted.")
if delete_confirmed:
bento_store.delete(bento.tag)
rich.print(f"{bento} deleted.")

for target in delete_targets:
delete_target(target)
Expand Down
Loading