Skip to content

Commit

Permalink
Improve cli (#25)
Browse files Browse the repository at this point in the history
* Improve cli

* Intro ConfigLoader and fix some bugs

* Fix query
  • Loading branch information
Wh1isper committed Jul 11, 2024
1 parent 6adef43 commit 68aba69
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 82 deletions.
6 changes: 6 additions & 0 deletions example/deploy-on-k8s/compute-image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
docker build -t wh1isper/moriarty-compute-image:latest .
docker push wh1isper/moriarty-compute-image:latest
```

Then deploy it

```bash
moriarty-deploy deploy-or-update --endpoint-config-file config.json
```
8 changes: 7 additions & 1 deletion example/deploy-on-k8s/compute-image/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@
"process_timeout": 3600,
"health_check_timeout": 1200,
"health_check_interval": 5
}
},
"min_replicas": null,
"max_replicas": null,
"scale_in_cooldown": null,
"scale_out_cooldown": null,
"metrics": null,
"metrics_threshold": null
}
6 changes: 2 additions & 4 deletions example/deploy-on-k8s/compute-image/dump_config.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
from pathlib import Path

from moriarty.matrix.operator_.params import CreateEndpointParams
from moriarty.deploy.endpoint import Endpoint

_HERE = Path(__file__).parent

if __name__ == "__main__":
define = CreateEndpointParams(
endpoint_name="hello-world", image="wh1isper/moriarty-compute-image:latest"
)
define = Endpoint(endpoint_name="hello-world", image="wh1isper/moriarty-compute-image:latest")
dump_path = _HERE / "config.json"
dump_path.write_text(define.model_dump_json(indent=4))
print(f"Dumped config to {dump_path.as_posix()}")
124 changes: 58 additions & 66 deletions moriarty/deploy/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import click

from moriarty.deploy.config import ConfigLoader
from moriarty.deploy.endpoint import Endpoint
from moriarty.envs import MORIARTY_MATRIX_API_URL_ENV, MORIARTY_MATRIX_TOKEN_ENV
from moriarty.matrix.operator_.enums_ import MetricType
from moriarty.matrix.operator_.params import CreateEndpointParams, UpdateEndpointParams
Expand All @@ -8,6 +10,7 @@
request_create_endpoint_with_params,
request_delete_autoscale,
request_delete_endpoint,
request_exist_endpoint,
request_query_autoscale,
request_query_endpoint,
request_scan_autoscale_log,
Expand Down Expand Up @@ -48,28 +51,22 @@
help="Order",
type=str,
)
@click.option(
"--api-url",
help="Moriarty Operator API URL",
type=str,
envvar=MORIARTY_MATRIX_API_URL_ENV,
required=True,
)
@click.option(
"--token",
help="Moriarty Operator API token",
type=str,
envvar=MORIARTY_MATRIX_TOKEN_ENV,
)
def scan(
api_url,
token,
limit,
cursor,
keyword,
order_by,
order,
):
config = ConfigLoader()
api_url = config.get_api_url()
if not api_url:
print(
f"Please config {MORIARTY_MATRIX_API_URL_ENV} and {MORIARTY_MATRIX_TOKEN_ENV} for API URL and token, or edit {config.config_path}"
)

token = config.get_api_token()

cursor = cursor
while True:
response = request_scan_endpoint(
Expand All @@ -93,7 +90,7 @@ def scan(
)
print(f"Endpoint: {endpoint} | Autoscale: {autoscale}")

if not response.endpoints:
if not response.endpoints or len(response.endpoints) < limit:
print("\nNo more results...")
return
input("Press Enter to continue...")
Expand Down Expand Up @@ -153,65 +150,61 @@ def query(
type=str,
envvar=MORIARTY_MATRIX_TOKEN_ENV,
)
def deploy(
api_url,
endpoint_config_file,
token,
):
with open(endpoint_config_file) as f:
params: CreateEndpointParams = CreateEndpointParams.model_validate_json(
f.read(),
)

request_create_endpoint_with_params(api_url=api_url, params=params, token=token)
print(f"Created endpoint: {params.endpoint_name}")


@click.command()
@click.argument("endpoint_name")
@click.option(
"--endpoint-config-file",
help="Endpoint config file",
type=str,
required=True,
)
@click.option(
"--api-url",
help="Moriarty Operator API URL",
type=str,
envvar=MORIARTY_MATRIX_API_URL_ENV,
required=True,
)
@click.option(
"--token",
help="Moriarty Operator API token",
type=str,
envvar=MORIARTY_MATRIX_TOKEN_ENV,
"--restart",
default=True,
help="Restart endpoint",
type=bool,
is_flag=True,
required=False,
)
def update(
def deploy_or_update(
api_url,
endpoint_name,
endpoint_config_file,
token,
restart,
):
with open(endpoint_config_file) as f:
params: UpdateEndpointParams = UpdateEndpointParams.model_validate_json(
endpoint: Endpoint = Endpoint.model_validate_json(
f.read(),
)

autoscale = request_query_autoscale(endpoint_name=endpoint_name, api_url=api_url, token=token)
if autoscale:
print(f"`{endpoint_name}` has autoscale: {autoscale}, set replicas to None")
params.replicas = None

response = request_update_endpoint_with_params(
api_url=api_url,
endpoint_name = endpoint.endpoint_name
if request_exist_endpoint(
endpoint_name=endpoint_name,
params=params,
api_url=api_url,
token=token,
)
print(f"Updated endpoint: `{endpoint_name}`")
print(response)
):
update_params = UpdateEndpointParams.model_validate(endpoint)
update_params.need_restart = restart
request_update_endpoint_with_params(
endpoint_name=endpoint_name,
api_url=api_url,
params=update_params,
token=token,
)
print(f"Updated endpoint: {endpoint_name}")
else:
request_create_endpoint_with_params(
api_url=api_url,
params=CreateEndpointParams.model_validate(endpoint),
token=token,
)
print(f"Created endpoint: {endpoint_name}")

if endpoint.config_autoscale:
request_set_autoscale(
endpoint_name=endpoint_name,
min_replicas=endpoint.min_replicas,
max_replicas=endpoint.max_replicas,
scale_in_cooldown=endpoint.scale_in_cooldown,
scale_out_cooldown=endpoint.scale_out_cooldown,
metrics=endpoint.metrics,
metrics_threshold=endpoint.metrics_threshold,
api_url=api_url,
token=token,
)
print(f"Set autoscale for endpoint: {endpoint_name}")


@click.command()
Expand Down Expand Up @@ -425,7 +418,7 @@ def query_autoscale_log(
for log in response.logs:
print(log)

if not response.logs:
if not response.logs or len(response.logs) < limit:
print("\nNo more results...")
return
input("Press Enter to continue...")
Expand All @@ -438,8 +431,7 @@ def cli():

cli.add_command(scan)
cli.add_command(query)
cli.add_command(deploy)
cli.add_command(update)
cli.add_command(deploy_or_update)
cli.add_command(delete)
cli.add_command(autoscale)
cli.add_command(delete_autoscale)
Expand Down
37 changes: 37 additions & 0 deletions moriarty/deploy/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import os
from pathlib import Path
from typing import Optional

from pydantic import BaseModel

from moriarty.envs import MORIARTY_MATRIX_API_URL_ENV, MORIARTY_MATRIX_TOKEN_ENV
from moriarty.log import logger


class Config(BaseModel):
api_url: Optional[str] = None
api_token: Optional[str] = None


class ConfigLoader:
def __init__(self, config_path: str = "~/.config/moriarty/config.json") -> None:
self.config_path = Path(config_path).expanduser()
if not self.config_path.exists():
self.config = Config()
self.init_config_file()
else:
logger.info(f"Load config from: {self.config_path}")
self.config = Config.model_validate_json(self.config_path.read_text())

def get_api_url(self) -> str | None:
return self.config.api_url or os.getenv(MORIARTY_MATRIX_API_URL_ENV)

def get_api_token(self) -> str | None:
return self.config.api_token or os.getenv(MORIARTY_MATRIX_TOKEN_ENV)

def init_config_file(self) -> None:
if self.config_path.exists():
return

self.config_path.parent.mkdir(parents=True, exist_ok=True)
self.config_path.write_text(Config().model_dump_json())
44 changes: 44 additions & 0 deletions moriarty/deploy/endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import Optional

from pydantic import BaseModel

from moriarty.matrix.operator_.enums_ import MetricType
from moriarty.matrix.operator_.params import (
ContainerScope,
ResourceScope,
ScheduleScope,
SidecarScope,
)


class Endpoint(BaseModel):
endpoint_name: str
image: str
model_path: Optional[str] = None

queue_capacity: Optional[int] = 5
replicas: int = 1
resource: Optional[ResourceScope] = ResourceScope()
schedule: Optional[ScheduleScope] = ScheduleScope()
container: Optional[ContainerScope] = ContainerScope()
sidecar: Optional[SidecarScope] = SidecarScope()

min_replicas: Optional[int] = None
max_replicas: Optional[int] = None
scale_in_cooldown: Optional[int] = None
scale_out_cooldown: Optional[int] = None
metrics: Optional[MetricType] = None
metrics_threshold: Optional[float] = None

@property
def config_autoscale(self) -> bool:
return any(
[
self.min_replicas is not None,
self.max_replicas is not None,
self.scale_in_cooldown is not None,
self.scale_out_cooldown is not None,
self.metrics is not None,
self.metrics_threshold is not None,
]
)
Loading

0 comments on commit 68aba69

Please sign in to comment.