Skip to content

Commit

Permalink
CLI/Yatai server improvement - Enhance list operation with order-by-c…
Browse files Browse the repository at this point in the history
…olumn, order and other options (#479)

* add order by for proto

* add order by option and update proto

* add ascending-order optin

* add output for apply cli command

* add order-by for bento

* update

* update bento table

* remove comment out code

* update base on comments

* update
  • Loading branch information
yubozhao authored and parano committed Jan 20, 2020
1 parent f2fd838 commit af5e323
Show file tree
Hide file tree
Showing 15 changed files with 301 additions and 120 deletions.
5 changes: 2 additions & 3 deletions bentoml/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
)
from bentoml.cli.aws_lambda import get_aws_lambda_sub_command
from bentoml.cli.aws_sagemaker import get_aws_sagemaker_sub_command
from bentoml.cli.bento import get_bento_sub_command
from bentoml.cli.bento import add_bento_sub_command
from bentoml.server import BentoAPIServer, get_docs
from bentoml.cli.click_utils import (
BentoMLCommandGroup,
Expand Down Expand Up @@ -305,13 +305,12 @@ def create_bentoml_cli():
config_sub_command = get_configuration_sub_command()
aws_sagemaker_sub_command = get_aws_sagemaker_sub_command()
aws_lambda_sub_command = get_aws_lambda_sub_command()
bento_sub_command = get_bento_sub_command()
deployment_sub_command = get_deployment_sub_command()
add_bento_sub_command(_cli)
_cli.add_command(config_sub_command)
_cli.add_command(aws_sagemaker_sub_command)
_cli.add_command(aws_lambda_sub_command)
_cli.add_command(deployment_sub_command)
_cli.add_command(bento_sub_command)

return _cli

Expand Down
22 changes: 14 additions & 8 deletions bentoml/cli/aws_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,31 +363,37 @@ def get(name, namespace, output):
default=ALL_NAMESPACE_TAG,
)
@click.option(
'--limit', type=click.INT, help='Limit how many deployments will be retrieved'
)
@click.option(
'--filters',
type=click.STRING,
help='List deployments containing the filter string in name or version',
'--limit',
type=click.INT,
help='The maximum amount of AWS Lambda deployments to be listed at once',
)
@click.option(
'-l',
'--labels',
type=click.STRING,
help='List deployments matching the giving labels',
)
@click.option(
'--order-by', type=click.Choice(['created_at', 'name']), default='created_at',
)
@click.option(
'--asc/--desc',
default=False,
help='Ascending or descending order for list deployments',
)
@click.option(
'-o', '--output', type=click.Choice(['json', 'yaml', 'table']), default='table'
)
def list_deployments(namespace, limit, filters, labels, output):
def list_deployments(namespace, limit, labels, order_by, asc, output):
yatai_client = YataiClient()
track_cli('deploy-list', PLATFORM_NAME)
try:
list_result = yatai_client.deployment.list_lambda_deployments(
limit=limit,
filters=filters,
labels=labels,
namespace=namespace,
order_by=order_by,
ascending_order=asc,
)
if list_result.status.status_code != status_pb2.Status.OK:
error_code, error_message = status_pb_to_error_code_and_message(
Expand Down
22 changes: 14 additions & 8 deletions bentoml/cli/aws_sagemaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,31 +374,37 @@ def get(name, namespace, output):
default=ALL_NAMESPACE_TAG,
)
@click.option(
'--limit', type=click.INT, help='Limit how many deployments will be retrieved'
)
@click.option(
'--filters',
type=click.STRING,
help='List deployments containing the filter string in name or version',
'--limit',
type=click.INT,
help='The maximum amount of AWS Sagemaker deployments to be listed at once',
)
@click.option(
'-l',
'--labels',
type=click.STRING,
help='List deployments matching the giving labels',
)
@click.option(
'--order-by', type=click.Choice(['created_at', 'name']), default='created_at',
)
@click.option(
'--asc/--desc',
default=False,
help='Ascending or descending order for list deployments',
)
@click.option(
'-o', '--output', type=click.Choice(['json', 'yaml', 'table']), default='table'
)
def list_deployment(namespace, limit, filters, labels, output):
def list_deployment(namespace, limit, labels, order_by, asc, output):
yatai_client = YataiClient()
track_cli('deploy-list', PLATFORM_NAME)
try:
list_result = yatai_client.deployment.list_sagemaker_deployments(
limit=limit,
filters=filters,
labels=labels,
namespace=namespace,
order_by=order_by,
ascending_order=asc,
)
if list_result.status.status_code != status_pb2.Status.OK:
error_code, error_message = status_pb_to_error_code_and_message(
Expand Down
49 changes: 22 additions & 27 deletions bentoml/cli/bento.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def _print_bento_info(bento, output_type):

def _print_bento_table(bentos):
table = []
headers = ['NAME', 'VERSION', 'CREATED_AT', 'APIS', 'ARTIFACTS']
headers = ['BentoService', 'CREATED_AT', 'APIS', 'ARTIFACTS']
for bento in bentos:
artifacts = [
f'{artifact.name}({artifact.artifact_type})'
Expand All @@ -45,8 +45,7 @@ def _print_bento_table(bentos):
for api in bento.bento_service_metadata.apis
]
row = [
bento.name,
bento.version,
f'{bento.name}:{bento.version}',
bento.bento_service_metadata.created_at.ToDatetime(),
', '.join(apis),
', '.join(artifacts),
Expand All @@ -64,25 +63,16 @@ def _print_bentos_info(bentos, output_type):
_print_bento_info(bento, output_type)


def get_bento_sub_command():
def add_bento_sub_command(cli):
# pylint: disable=unused-variable

@click.group(name='bento', help='BentoService management and operation commands')
def bento_repo():
pass

@bento_repo.command(help='Get BentoService information')
@cli.command(help='Get BentoService information')
@click.argument('bento', type=click.STRING)
@click.option(
'--limit', type=click.INT, help='Limit how many resources will be retrieved'
)
@click.option(
'--filters',
type=click.STRING,
help='List resources containing the filter string in name',
)
@click.option('--ascending-order', is_flag=True)
@click.option('-o', '--output', type=click.Choice(['json', 'yaml', 'table']))
def get(bento, limit, filters, output):
def get(bento, limit, ascending_order, output):
if ':' in bento:
name, version = bento.split(':')
else:
Expand Down Expand Up @@ -110,7 +100,7 @@ def get(bento, limit, filters, output):
track_cli('bento-list')
output = output or 'table'
list_bento_versions_result = yatai_client.repository.list(
bento_name=name, filters=filters, limit=limit
bento_name=name, limit=limit, ascending_order=ascending_order
)
if list_bento_versions_result.status.status_code != status_pb2.Status.OK:
error_code, error_message = status_pb_to_error_code_and_message(
Expand All @@ -125,22 +115,29 @@ def get(bento, limit, filters, output):

_print_bentos_info(list_bento_versions_result.bentos, output)

@bento_repo.command(name='list', help='List BentoServices information')
@cli.command(name='list', help='List BentoServices information')
@click.option(
'--limit', type=click.INT, help='Limit how many resources will be retrieved'
'--limit', type=click.INT, help='Limit how many BentoServices will be retrieved'
)
@click.option(
'--filters',
type=click.STRING,
help='List resources containing the filter string in name',
'--offset', type=click.INT, help='How many BentoServices will be skipped'
)
@click.option(
'--order-by', type=click.Choice(['created_at', 'name']), default='created_at',
)
@click.option('--ascending-order', is_flag=True)
@click.option(
'-o', '--output', type=click.Choice(['json', 'yaml', 'table']), default='table'
)
def list_bentos(limit, filters, output):
def list_bentos(limit, offset, order_by, ascending_order, output):
yatai_client = YataiClient()
track_cli('bento-list')
list_bentos_result = yatai_client.repository.list(limit=limit, filters=filters)
list_bentos_result = yatai_client.repository.list(
limit=limit,
offset=offset,
order_by=order_by,
ascending_order=ascending_order,
)
if list_bentos_result.status.status_code != status_pb2.Status.OK:
error_code, error_message = status_pb_to_error_code_and_message(
list_bentos_result.status
Expand All @@ -153,7 +150,7 @@ def list_bentos(limit, filters, output):

_print_bentos_info(list_bentos_result.bentos, output)

@bento_repo.command(help='Delete BentoService')
@cli.command(help='Delete BentoService')
@click.argument('bento', type=click.STRING)
def delete(bento):
yatai_client = YataiClient()
Expand Down Expand Up @@ -184,5 +181,3 @@ def delete(bento):
)
return
_echo(f'BentoService {name}:{version} deleted')

return bento_repo
45 changes: 33 additions & 12 deletions bentoml/cli/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ def apply(deployment_yaml, output, wait):
f'{error_code}:{error_message}',
CLI_COLOR_ERROR,
)
return
track_cli('deploy-create-success', platform_name)
_echo(
f'Successfully applied deployment {deployment_name}', CLI_COLOR_SUCCESS,
)
_print_deployment_info(result.deployment, output)
except BentoMLException as e:
track_cli(
'deploy-apply-failure', platform_name, {'error_message': str(e)},
Expand Down Expand Up @@ -167,10 +173,10 @@ def apply(deployment_yaml, output, wait):
def delete(name, namespace, force):
yatai_client = YataiClient()
get_deployment_result = yatai_client.deployment.get(namespace, name)
error_code, error_message = status_pb_to_error_code_and_message(
get_deployment_result.status
)
if error_code and error_message:
if get_deployment_result.status.status_code != status_pb2.Status.OK:
error_code, error_message = status_pb_to_error_code_and_message(
get_deployment_result.status
)
_echo(
f'Failed to get deployment {name} for deletion. '
f'{error_code}:{error_message}',
Expand All @@ -182,8 +188,10 @@ def delete(name, namespace, force):
)
track_cli('deploy-delete', platform)
result = yatai_client.deployment.delete(name, namespace, force)
error_code, error_message = status_pb_to_error_code_and_message(result.status)
if error_code and error_message:
if result.status.status_code != status_pb2.Status.OK:
error_code, error_message = status_pb_to_error_code_and_message(
result.status
)
_echo(
f'Failed to delete deployment {name}. {error_code}:{error_message}',
CLI_COLOR_ERROR,
Expand Down Expand Up @@ -250,28 +258,41 @@ def get(name, namespace, output):
default=ALL_NAMESPACE_TAG,
)
@click.option(
'--limit', type=click.INT, help='Limit how many resources will be retrieved'
'-p', '--platform', type=click.Choice(['sagemaker', 'lambda']), help='platform',
)
@click.option(
'--filters',
type=click.STRING,
help='List resources containing the filter string in name',
'--limit',
type=click.INT,
help='The maximum amount of deployments to be listed at once',
)
@click.option(
'-l',
'--labels',
type=click.STRING,
help='List deployments matching the giving labels',
)
@click.option(
'--order-by', type=click.Choice(['created_at', 'name']), default='created_at',
)
@click.option(
'--asc/--desc',
default=False,
help='Ascending or descending order for list deployments',
)
@click.option(
'-o', '--output', type=click.Choice(['json', 'yaml', 'table']), default='table'
)
def list_deployments(namespace, limit, filters, labels, output):
def list_deployments(namespace, platform, limit, labels, order_by, asc, output):
yatai_client = YataiClient()
track_cli('deploy-list')
try:
list_result = yatai_client.deployment.list(
limit=limit, filters=filters, labels=labels, namespace=namespace,
limit=limit,
labels=labels,
namespace=namespace,
operator=platform,
order_by=order_by,
ascending_order=asc,
)
if list_result.status.status_code != status_pb2.Status.OK:
error_code, error_message = status_pb_to_error_code_and_message(
Expand Down
54 changes: 44 additions & 10 deletions bentoml/deployment/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,22 @@
import datetime
from contextlib import contextmanager

from sqlalchemy import Column, String, Integer, DateTime, JSON, UniqueConstraint
from sqlalchemy import (
Column,
String,
Integer,
DateTime,
JSON,
UniqueConstraint,
desc,
)
from sqlalchemy.orm.exc import NoResultFound
from google.protobuf.json_format import ParseDict

from bentoml.exceptions import YataiDeploymentException
from bentoml.db import Base, create_session
from bentoml.proto import deployment_pb2
from bentoml.proto.deployment_pb2 import DeploymentSpec, ListDeploymentsRequest
from bentoml.utils import ProtoMessageToDict


Expand Down Expand Up @@ -146,17 +155,42 @@ def delete(self, name, namespace):
"Deployment '%s' in namespace: '%s' is not found" % name, namespace
)

def list(self, namespace, filter_str=None, labels=None, offset=None, limit=None):
def list(
self,
namespace,
operator=None,
labels=None,
offset=None,
limit=None,
order_by=ListDeploymentsRequest.created_at,
ascending_order=False,
):
with create_session(self.sess_maker) as sess:
query = sess.query(Deployment)
order_by = ListDeploymentsRequest.SORTABLE_COLUMN.Name(order_by)
order_by_field = getattr(Deployment, order_by)
order_by_action = (
order_by_field if ascending_order else desc(order_by_field)
)
query = query.order_by(order_by_action)
if namespace != ALL_NAMESPACE_TAG: # else query all namespaces
query.filter_by(namespace=namespace)
if limit:
query.limit(limit)
if offset:
query.offset(offset)
if filter_str:
query.filter(Deployment.name.contains(filter_str))
query = query.filter_by(namespace=namespace)
if operator:
operator_name = DeploymentSpec.DeploymentOperator.Name(operator)
query = query.filter(
Deployment.spec['operator'].contains(operator_name)
)
if labels:
raise NotImplementedError("Listing by labels is not yet implemented")
return list(map(_deployment_orm_obj_to_pb, query.all()))

# We are not defaulting limit to 200 in the signature,
# because protobuf will pass 0 as value
limit = limit or 200
# Limit and offset need to be called after order_by filter/filter_by is
# called
query = query.limit(limit)
if offset:
query = query.offset(offset)
query_result = query.all()

return list(map(_deployment_orm_obj_to_pb, query_result))
Loading

0 comments on commit af5e323

Please sign in to comment.