Skip to content

Commit

Permalink
feat: list v2 for org
Browse files Browse the repository at this point in the history
  • Loading branch information
Haivilo committed Dec 27, 2023
1 parent 0a41520 commit d157ccd
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 136 deletions.
9 changes: 6 additions & 3 deletions src/bentoml/_internal/cloud/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import logging
import typing as t
from urllib.parse import urljoin
import httpx

import httpx

from ...exceptions import CloudRESTApiClientError
from ..configuration import BENTOML_VERSION
Expand Down Expand Up @@ -581,7 +581,9 @@ def get_deployment(

def list_deployment(
self,
cluster_name: str,
cluster_name: str | None = None,
all: bool | None = None,
# if both of the above is none, list default cluster's deployments
count: int | None = None,
q: str | None = None,
search: str | None = None,
Expand All @@ -592,6 +594,7 @@ def list_deployment(
url,
params={
"cluster": cluster_name,
"all": all,
"count": count,
"q": q,
"search": search,
Expand Down Expand Up @@ -632,7 +635,7 @@ def delete_deployment(

class RestApiClient:
def __init__(self, endpoint: str, api_token: str) -> None:
self.session = httpx.Client()
self.session = httpx.Client()
self.session.headers.update(
{
"X-YATAI-API-TOKEN": api_token,
Expand Down
212 changes: 88 additions & 124 deletions src/bentoml/_internal/cloud/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,39 +78,90 @@ def _fix_scaling(

@staticmethod
def _validate_input_on_distributed(
distributed: bool, config_dct: dict[str, t.Any]
config_struct: UpdateDeploymentSchemaV2, distributed: bool
) -> None:
if distributed:
if "instance_type" in config_dct:
if config_struct.instance_type is not None:
raise BentoMLException(
"The 'instance_type' field is not allowed for distributed deployments. Please specify it per service in the services field."
)
if "scaling" in config_dct:
if config_struct.scaling is not None:
raise BentoMLException(
"The 'scaling' field is not allowed for distributed deployments. Please specify it per service in the services field."
)
if "deployment_strategy" in config_dct:
if config_struct.deployment_strategy is not None:
raise BentoMLException(
"The 'deployment_strategy' field is not allowed for distributed deployments. Please specify it per service in the services field."
)
if "extras" in config_dct:
if config_struct.extras is not None:
raise BentoMLException(
"The 'extras' field is not allowed for distributed deployments. Please specify it per service in the services field."
)
if "cold_start_timeout" in config_dct:
if config_struct.cold_start_timeout is not None:
raise BentoMLException(
"The 'cold_start_timeout' field is not allowed for distributed deployments. Please specify it per service in the services field."
)
elif not distributed:
if (
"services" in config_dct
and config_dct["services"] is not None
and config_dct["services"] != {}
):
if config_struct.services != {}:
raise BentoMLException(
"The 'services' field is only allowed for distributed deployments."
)

@staticmethod
def _get_real_bento_tag(
project_path: str | None,
bento: str | Tag | None,
context: str | None,
_bento_store: BentoStore,
_cloud_client: BentoCloudClient,
) -> Tag:
if bento and project_path:
raise BentoMLException(
"Create a deployment needs only one target - either a project path or a bento"
)
if project_path:
from bentoml.bentos import build_bentofile

logger.info(f"Building bento: {project_path}")
bento_obj = build_bentofile(
build_ctx=project_path, _bento_store=_bento_store
)
_cloud_client.push_bento(bento=bento_obj, context=context)
return bento_obj.tag
elif bento:
bento = Tag.from_taglike(bento)
try:
bento_obj = _bento_store.get(bento)
except NotFound as e:
# "bento repo needs to exist if it is latest"
if bento.version is None or bento.version == "latest":
raise e
bento_obj = None

# try to push if bento exists, otherwise expects bentocloud to have it
if bento_obj:
_cloud_client.push_bento(bento=bento_obj, context=context)
bento = bento_obj.tag
return bento
else:
raise BentoMLException(
"Create a deployment needs a target; project path or bento is necessary"
)

@classmethod
def _fix_and_validate_schema(
cls, config_struct: UpdateDeploymentSchemaV2, distributed: bool
):
cls._validate_input_on_distributed(config_struct, distributed)
# fix scaling
if distributed:
if len(config_struct.services) == 0:
raise BentoMLException("The configuration for services is mandatory")
for _, svc in config_struct.services.items():
svc.scaling = cls._fix_scaling(svc.scaling)
else:
config_struct.scaling = cls._fix_scaling(config_struct.scaling)

@classmethod
def _get_default_kube_namespace(
cls,
Expand Down Expand Up @@ -192,7 +243,7 @@ def get_bento(self) -> str:

def get_status(self) -> str:
self._refetch()
return str(self._schema.status)
return self._schema.status.value

def get_client(
self,
Expand All @@ -218,7 +269,7 @@ def wait_until_ready(self, timeout: int = 300, check_interval: int = 5) -> None:
start_time = time.time()
while time.time() - start_time < timeout:
status = self.get_status()
if status == "running":
if status == DeploymentStatus.Running.value:
logger.info(f"Deployment '{self.name}' is ready.")
return
logger.info(
Expand All @@ -239,16 +290,13 @@ def list(
) -> list[Deployment]:
cloud_rest_client = get_rest_api_client(context)
if cluster_name is None:
# TODO: org list
res_count = cloud_rest_client.v1.get_organization_deployment_list(
search=search
)
res_count = cloud_rest_client.v2.list_deployment(all=True, search=search)
if res_count is None:
raise BentoMLException("List deployments request failed")
if res_count.total == 0:
return []
res = cloud_rest_client.v1.get_organization_deployment_list(
search=search, count=res_count.total
res = cloud_rest_client.v2.list_deployment(
search=search, count=res_count.total, all=True
)
if res is None:
raise BentoMLException("List deployments request failed")
Expand Down Expand Up @@ -298,37 +346,14 @@ def create(
_cloud_client: BentoCloudClient = Provide[BentoMLContainer.bentocloud_client],
) -> Deployment:
cloud_rest_client = get_rest_api_client(context)
if bento and project_path:
raise BentoMLException(
"Create a deployment needs only one target - either a project path or a bento"
)
if project_path:
from bentoml import bentos

logger.info(f"Building bento: {project_path}")
bento = bentos.build_bentofile(
build_ctx=project_path, _bento_store=_bento_store
).tag

if bento is None:
raise BentoMLException(
"Create a deployment needs a target; project path or bento is necessary"
dct: dict[str, t.Any] = {
"bento": str(
cls._get_real_bento_tag(
project_path, bento, context, _bento_store, _cloud_client
)
)
bento = Tag.from_taglike(bento)
try:
bento_obj = _bento_store.get(bento)
except NotFound as e:
# "bento repo needs to exist if it is latest"
if bento.version is None or bento.version == "latest":
raise e
bento_obj = None

# try to push if bento exists, otherwise expects bentocloud to have it
if bento_obj:
_cloud_client.push_bento(bento=bento_obj, context=context)
bento = bento_obj.tag

dct: dict[str, t.Any] = {"bento": str(bento)}
}
if name:
dct["name"] = name
else:
Expand Down Expand Up @@ -378,17 +403,8 @@ def create(
and dct["services"] != {}
)

cls._validate_input_on_distributed(dct["distributed"], dct)
config_struct = bentoml_cattr.structure(dct, CreateDeploymentSchemaV2)

# add scaling
if config_struct.distributed:
if len(config_struct.services) == 0:
raise BentoMLException("The configuration for services is mandatory")
for _, svc in config_struct.services.items():
svc.scaling = cls._fix_scaling(svc.scaling)
else:
config_struct.scaling = cls._fix_scaling(config_struct.scaling)
cls._fix_and_validate_schema(config_struct, dct["distributed"])

res = cloud_rest_client.v2.create_deployment(
create_schema=config_struct, cluster_name=config_struct.cluster
Expand Down Expand Up @@ -427,38 +443,14 @@ def update(
name=name, context=context, cluster_name=cluster_name
)
orig_dct = deployment._conver_schema_to_update_schema()
distributed = (
"services" in orig_dct
and orig_dct["services"] is not None
and orig_dct["services"] != {}
)
distributed = deployment._schema.distributed
cloud_rest_client = get_rest_api_client(context)
if bento and project_path:
raise BentoMLException(
"Update a deployment needs one and only one target - either a project path or a bento"
if bento or project_path:
orig_dct["bento"] = str(
cls._get_real_bento_tag(
project_path, bento, context, _bento_store, _cloud_client
)
)
if project_path:
from bentoml import bentos

logger.info(f"Building bento: {project_path}")
bento = bentos.build_bentofile(build_ctx=project_path).tag

if bento is not None:
bento = Tag.from_taglike(bento)
try:
bento_obj = _bento_store.get(bento)
except NotFound as e:
# "bento repo needs to exist if it is latest"
if bento.version is None or bento.version == "latest":
raise e
bento_obj = None

# try to push if bento exists, otherwise expects bentocloud to have it
if bento_obj:
_cloud_client.push_bento(bento=bento_obj, context=context)
bento = bento_obj.tag

orig_dct["bento"] = str(bento)

if config_dct:
merging_dct = config_dct
Expand Down Expand Up @@ -498,19 +490,11 @@ def update(
if access_type is not None:
merging_dct["access_type"] = access_type

cls._validate_input_on_distributed(distributed, merging_dct)

config_merger.merge(orig_dct, merging_dct)

config_struct = bentoml_cattr.structure(orig_dct, UpdateDeploymentSchemaV2)

# fix scaling
if distributed:
if len(config_struct.services) == 0:
raise BentoMLException("The configuration for services is mandatory")
for _, svc in config_struct.services.items():
svc.scaling = cls._fix_scaling(svc.scaling)
else:
config_struct.scaling = cls._fix_scaling(config_struct.scaling)
cls._fix_and_validate_schema(config_struct, distributed)

res = cloud_rest_client.v2.update_deployment(
cluster_name=deployment.cluster_name,
Expand Down Expand Up @@ -557,36 +541,15 @@ def apply(
_cloud_client=_cloud_client,
)
cloud_rest_client = get_rest_api_client(context)

if bento and project_path:
raise BentoMLException(
"Update a deployment needs one and only one target - either a project path or a bento"
if bento or project_path:
bento = cls._get_real_bento_tag(
project_path, bento, context, _bento_store, _cloud_client
)

if project_path:
from bentoml import bentos

logger.info(f"Building bento: {project_path}")
bento = bentos.build_bentofile(build_ctx=project_path).tag

if bento is not None:
bento = Tag.from_taglike(bento)
try:
bento_obj = _bento_store.get(bento)
except NotFound as e:
# "bento repo needs to exist if it is latest"
if bento.version is None or bento.version == "latest":
raise e
bento_obj = None

# try to push if bento exists, otherwise expects bentocloud to have it
if bento_obj:
_cloud_client.push_bento(bento=bento_obj, context=context)
bento = bento_obj.tag
else:
bento = deployment._conver_schema_to_bento()

schema_dct: dict[str, t.Any] = {"bento": str(bento)}
distributed = deployment._schema.distributed

if config_file:
real_path = resolve_user_filepath(config_file, path_context)
Expand All @@ -606,8 +569,9 @@ def apply(
raise BentoMLException("Apply a deployment needs a configuration input")

schema_dct.update(config_dct)
cls._validate_input_on_distributed(config_dct["distributed"], schema_dct)
config_struct = bentoml_cattr.structure(schema_dct, UpdateDeploymentSchemaV2)
cls._fix_and_validate_schema(config_struct, distributed)

res = cloud_rest_client.v2.update_deployment(
deployment_name=name,
update_schema=config_struct,
Expand Down
6 changes: 2 additions & 4 deletions src/bentoml/_internal/cloud/schemas/modelschemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class BentoManifestSchema:
apis: t.Dict[str, BentoApiSchema] = attr.field(factory=dict)
models: t.List[str] = attr.field(factory=list)
runners: t.Optional[t.List[BentoRunnerSchema]] = attr.field(factory=list)
config: ServiceConfig = attr.field(factory=dict)
config: t.Dict[str, BentoConfigServiceSchema] = attr.field(factory=dict)


if TYPE_CHECKING:
Expand Down Expand Up @@ -373,9 +373,7 @@ class DeploymentTargetConfig:
)
traffic_control: t.Optional[TrafficControlConfig] = attr.field(default=None)
deployment_cold_start_wait_timeout: t.Optional[int] = attr.field(default=None)
bentoml_config_overrides: t.Optional[
dict[str, BentoConfigServiceSchema]
] = attr.field(default=None)
bentoml_config_overrides: t.Optional[dict[str, t.Any]] = attr.field(default=None)


@attr.define
Expand Down
Loading

0 comments on commit d157ccd

Please sign in to comment.