CLI improvements (#2026)
This commit is contained in:
parent
93d19d6dc3
commit
f404205c3f
@ -275,10 +275,10 @@ def export(
|
|||||||
help="The directory to export the zip files to.",
|
help="The directory to export the zip files to.",
|
||||||
show_default=False,
|
show_default=False,
|
||||||
),
|
),
|
||||||
backend_exclude_sqlite_db_files: bool = typer.Option(
|
upload_db_file: bool = typer.Option(
|
||||||
True,
|
False,
|
||||||
help="Whether to exclude sqlite db files when exporting backend.",
|
help="Whether to exclude sqlite db files when exporting backend.",
|
||||||
show_default=False,
|
hidden=True,
|
||||||
),
|
),
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
loglevel: constants.LogLevel = typer.Option(
|
||||||
console._LOG_LEVEL, help="The log level to use."
|
console._LOG_LEVEL, help="The log level to use."
|
||||||
@ -310,7 +310,7 @@ def export(
|
|||||||
zip=zipping,
|
zip=zipping,
|
||||||
zip_dest_dir=zip_dest_dir,
|
zip_dest_dir=zip_dest_dir,
|
||||||
deploy_url=config.deploy_url,
|
deploy_url=config.deploy_url,
|
||||||
backend_exclude_sqlite_db_files=backend_exclude_sqlite_db_files,
|
upload_db_file=upload_db_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Post a telemetry event.
|
# Post a telemetry event.
|
||||||
@ -431,6 +431,7 @@ def deploy(
|
|||||||
config.app_name,
|
config.app_name,
|
||||||
"--app-name",
|
"--app-name",
|
||||||
help="The name of the App to deploy under.",
|
help="The name of the App to deploy under.",
|
||||||
|
hidden=True,
|
||||||
),
|
),
|
||||||
regions: List[str] = typer.Option(
|
regions: List[str] = typer.Option(
|
||||||
list(),
|
list(),
|
||||||
@ -441,20 +442,29 @@ def deploy(
|
|||||||
envs: List[str] = typer.Option(
|
envs: List[str] = typer.Option(
|
||||||
list(),
|
list(),
|
||||||
"--env",
|
"--env",
|
||||||
help="The environment variables to set: <key>=<value>. For multiple envs, repeat this option followed by the env name.",
|
help="The environment variables to set: <key>=<value>. For multiple envs, repeat this option, e.g. --env k1=v2 --env k2=v2.",
|
||||||
|
),
|
||||||
|
cpus: Optional[int] = typer.Option(
|
||||||
|
None, help="The number of CPUs to allocate.", hidden=True
|
||||||
),
|
),
|
||||||
cpus: Optional[int] = typer.Option(None, help="The number of CPUs to allocate."),
|
|
||||||
memory_mb: Optional[int] = typer.Option(
|
memory_mb: Optional[int] = typer.Option(
|
||||||
None, help="The amount of memory to allocate."
|
None, help="The amount of memory to allocate.", hidden=True
|
||||||
),
|
),
|
||||||
auto_start: Optional[bool] = typer.Option(
|
auto_start: Optional[bool] = typer.Option(
|
||||||
True, help="Whether to auto start the instance."
|
True,
|
||||||
|
help="Whether to auto start the instance.",
|
||||||
|
hidden=True,
|
||||||
),
|
),
|
||||||
auto_stop: Optional[bool] = typer.Option(
|
auto_stop: Optional[bool] = typer.Option(
|
||||||
True, help="Whether to auto stop the instance."
|
True,
|
||||||
|
help="Whether to auto stop the instance.",
|
||||||
|
hidden=True,
|
||||||
),
|
),
|
||||||
frontend_hostname: Optional[str] = typer.Option(
|
frontend_hostname: Optional[str] = typer.Option(
|
||||||
None, "--frontend-hostname", help="The hostname of the frontend."
|
None,
|
||||||
|
"--frontend-hostname",
|
||||||
|
help="The hostname of the frontend.",
|
||||||
|
hidden=True,
|
||||||
),
|
),
|
||||||
interactive: Optional[bool] = typer.Option(
|
interactive: Optional[bool] = typer.Option(
|
||||||
True,
|
True,
|
||||||
@ -463,14 +473,17 @@ def deploy(
|
|||||||
with_metrics: Optional[str] = typer.Option(
|
with_metrics: Optional[str] = typer.Option(
|
||||||
None,
|
None,
|
||||||
help="Setting for metrics scraping for the deployment. Setup required in user code.",
|
help="Setting for metrics scraping for the deployment. Setup required in user code.",
|
||||||
|
hidden=True,
|
||||||
),
|
),
|
||||||
with_tracing: Optional[str] = typer.Option(
|
with_tracing: Optional[str] = typer.Option(
|
||||||
None,
|
None,
|
||||||
help="Setting to export tracing for the deployment. Setup required in user code.",
|
help="Setting to export tracing for the deployment. Setup required in user code.",
|
||||||
|
hidden=True,
|
||||||
),
|
),
|
||||||
backend_exclude_sqlite_db_files: bool = typer.Option(
|
upload_db_file: bool = typer.Option(
|
||||||
True,
|
False,
|
||||||
help="Whether to exclude sqlite db files from the backend export.",
|
help="Whether to include local sqlite db files when uploading to hosting service.",
|
||||||
|
hidden=True,
|
||||||
),
|
),
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
loglevel: constants.LogLevel = typer.Option(
|
||||||
config.loglevel, help="The log level to use."
|
config.loglevel, help="The log level to use."
|
||||||
@ -558,16 +571,15 @@ def deploy(
|
|||||||
zipping=True,
|
zipping=True,
|
||||||
zip_dest_dir=tmp_dir,
|
zip_dest_dir=tmp_dir,
|
||||||
loglevel=loglevel,
|
loglevel=loglevel,
|
||||||
backend_exclude_sqlite_db_files=backend_exclude_sqlite_db_files,
|
upload_db_file=upload_db_file,
|
||||||
)
|
)
|
||||||
except ImportError as ie:
|
except ImportError as ie:
|
||||||
console.error(
|
console.error(
|
||||||
f"Encountered ImportError, did you install all the dependencies? {ie}"
|
f"Encountered ImportError, did you install all the dependencies? {ie}"
|
||||||
)
|
)
|
||||||
raise typer.Exit(1) from ie
|
|
||||||
finally:
|
|
||||||
if os.path.exists(tmp_dir):
|
if os.path.exists(tmp_dir):
|
||||||
shutil.rmtree(tmp_dir)
|
shutil.rmtree(tmp_dir)
|
||||||
|
raise typer.Exit(1) from ie
|
||||||
|
|
||||||
frontend_file_name = constants.ComponentName.FRONTEND.zip()
|
frontend_file_name = constants.ComponentName.FRONTEND.zip()
|
||||||
backend_file_name = constants.ComponentName.BACKEND.zip()
|
backend_file_name = constants.ComponentName.BACKEND.zip()
|
||||||
@ -750,43 +762,6 @@ def get_deployment_logs(
|
|||||||
raise typer.Exit(1) from ex
|
raise typer.Exit(1) from ex
|
||||||
|
|
||||||
|
|
||||||
@deployments_cli.command(name="all-logs")
|
|
||||||
def get_deployment_all_logs(
|
|
||||||
key: str = typer.Argument(..., help="The name of the deployment."),
|
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
|
||||||
config.loglevel, help="The log level to use."
|
|
||||||
),
|
|
||||||
):
|
|
||||||
"""Get the logs for a deployment."""
|
|
||||||
console.set_log_level(loglevel)
|
|
||||||
|
|
||||||
console.print("Note: there is a few seconds delay for logs to be available.")
|
|
||||||
try:
|
|
||||||
asyncio.run(hosting.get_logs(key, log_type=hosting.LogType.ALL_LOG))
|
|
||||||
except Exception as ex:
|
|
||||||
console.error(f"Unable to get deployment logs due to: {ex}")
|
|
||||||
raise typer.Exit(1) from ex
|
|
||||||
|
|
||||||
|
|
||||||
@deployments_cli.command(name="deploy-logs")
|
|
||||||
def get_deployment_deploy_logs(
|
|
||||||
key: str = typer.Argument(..., help="The name of the deployment."),
|
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
|
||||||
config.loglevel, help="The log level to use."
|
|
||||||
),
|
|
||||||
):
|
|
||||||
"""Get the logs for a deployment."""
|
|
||||||
console.set_log_level(loglevel)
|
|
||||||
|
|
||||||
console.print("Note: there is a few seconds delay for logs to be available.")
|
|
||||||
try:
|
|
||||||
# TODO: we need to pass in the from time stamp
|
|
||||||
asyncio.run(hosting.get_logs(key, log_type=hosting.LogType.DEPLOY_LOG))
|
|
||||||
except Exception as ex:
|
|
||||||
console.error(f"Unable to get deployment logs due to: {ex}")
|
|
||||||
raise typer.Exit(1) from ex
|
|
||||||
|
|
||||||
|
|
||||||
@deployments_cli.command(name="regions")
|
@deployments_cli.command(name="regions")
|
||||||
def get_deployment_regions(
|
def get_deployment_regions(
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
loglevel: constants.LogLevel = typer.Option(
|
||||||
|
@ -60,7 +60,7 @@ def _zip(
|
|||||||
target: str,
|
target: str,
|
||||||
root_dir: str,
|
root_dir: str,
|
||||||
exclude_venv_dirs: bool,
|
exclude_venv_dirs: bool,
|
||||||
exclude_sqlite_db_files: bool = True,
|
upload_db_file: bool = False,
|
||||||
dirs_to_exclude: set[str] | None = None,
|
dirs_to_exclude: set[str] | None = None,
|
||||||
files_to_exclude: set[str] | None = None,
|
files_to_exclude: set[str] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -71,7 +71,7 @@ def _zip(
|
|||||||
target: The target zip file.
|
target: The target zip file.
|
||||||
root_dir: The root directory to zip.
|
root_dir: The root directory to zip.
|
||||||
exclude_venv_dirs: Whether to exclude venv directories.
|
exclude_venv_dirs: Whether to exclude venv directories.
|
||||||
exclude_sqlite_db_files: Whether to exclude sqlite db files.
|
upload_db_file: Whether to include local sqlite db files.
|
||||||
dirs_to_exclude: The directories to exclude.
|
dirs_to_exclude: The directories to exclude.
|
||||||
files_to_exclude: The files to exclude.
|
files_to_exclude: The files to exclude.
|
||||||
|
|
||||||
@ -97,8 +97,7 @@ def _zip(
|
|||||||
files[:] = [
|
files[:] = [
|
||||||
f
|
f
|
||||||
for f in files
|
for f in files
|
||||||
if not f.startswith(".")
|
if not f.startswith(".") and (upload_db_file or not f.endswith(".db"))
|
||||||
and (not exclude_sqlite_db_files or not f.endswith(".db"))
|
|
||||||
]
|
]
|
||||||
files_to_zip += [
|
files_to_zip += [
|
||||||
os.path.join(root, file) for file in files if file not in files_to_exclude
|
os.path.join(root, file) for file in files if file not in files_to_exclude
|
||||||
@ -127,7 +126,7 @@ def export(
|
|||||||
zip: bool = False,
|
zip: bool = False,
|
||||||
zip_dest_dir: str = os.getcwd(),
|
zip_dest_dir: str = os.getcwd(),
|
||||||
deploy_url: str | None = None,
|
deploy_url: str | None = None,
|
||||||
backend_exclude_sqlite_db_files: bool = True,
|
upload_db_file: bool = False,
|
||||||
):
|
):
|
||||||
"""Export the app for deployment.
|
"""Export the app for deployment.
|
||||||
|
|
||||||
@ -137,7 +136,7 @@ def export(
|
|||||||
zip: Whether to zip the app.
|
zip: Whether to zip the app.
|
||||||
zip_dest_dir: The destination directory for created zip files (if any)
|
zip_dest_dir: The destination directory for created zip files (if any)
|
||||||
deploy_url: The URL of the deployed app.
|
deploy_url: The URL of the deployed app.
|
||||||
backend_exclude_sqlite_db_files: Whether to exclude sqlite db files from the backend zip.
|
upload_db_file: Whether to include local sqlite db files from the backend zip.
|
||||||
"""
|
"""
|
||||||
# Remove the static folder.
|
# Remove the static folder.
|
||||||
path_ops.rm(constants.Dirs.WEB_STATIC)
|
path_ops.rm(constants.Dirs.WEB_STATIC)
|
||||||
@ -196,7 +195,7 @@ def export(
|
|||||||
dirs_to_exclude={"assets", "__pycache__"},
|
dirs_to_exclude={"assets", "__pycache__"},
|
||||||
files_to_exclude=files_to_exclude,
|
files_to_exclude=files_to_exclude,
|
||||||
exclude_venv_dirs=True,
|
exclude_venv_dirs=True,
|
||||||
exclude_sqlite_db_files=backend_exclude_sqlite_db_files,
|
upload_db_file=upload_db_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -405,6 +405,7 @@ def deploy(
|
|||||||
with_metrics: A string indicating the metrics endpoint.
|
with_metrics: A string indicating the metrics endpoint.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
AssertionError: If the request is rejected by the hosting server.
|
||||||
Exception: If the operation fails. The exception message is the reason.
|
Exception: If the operation fails. The exception message is the reason.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -449,13 +450,16 @@ def deploy(
|
|||||||
# If the server explicitly states bad request,
|
# If the server explicitly states bad request,
|
||||||
# display a different error
|
# display a different error
|
||||||
if response.status_code == HTTPStatus.BAD_REQUEST:
|
if response.status_code == HTTPStatus.BAD_REQUEST:
|
||||||
raise ValueError(response.json()["detail"])
|
raise AssertionError("Server rejected this request")
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
response_json = response.json()
|
response_json = response.json()
|
||||||
return DeploymentPostResponse(
|
return DeploymentPostResponse(
|
||||||
frontend_url=response_json["frontend_url"],
|
frontend_url=response_json["frontend_url"],
|
||||||
backend_url=response_json["backend_url"],
|
backend_url=response_json["backend_url"],
|
||||||
)
|
)
|
||||||
|
except OSError as oe:
|
||||||
|
console.debug(f"Client side error related to file operation: {oe}")
|
||||||
|
raise
|
||||||
except httpx.RequestError as re:
|
except httpx.RequestError as re:
|
||||||
console.debug(f"Unable to deploy due to request error: {re}")
|
console.debug(f"Unable to deploy due to request error: {re}")
|
||||||
raise Exception("request error") from re
|
raise Exception("request error") from re
|
||||||
@ -468,9 +472,10 @@ def deploy(
|
|||||||
except (KeyError, ValidationError) as kve:
|
except (KeyError, ValidationError) as kve:
|
||||||
console.debug(f"Post params or server response format unexpected: {kve}")
|
console.debug(f"Post params or server response format unexpected: {kve}")
|
||||||
raise Exception("internal errors") from kve
|
raise Exception("internal errors") from kve
|
||||||
except ValueError as ve:
|
except AssertionError as ve:
|
||||||
console.debug(f"Unable to deploy due to request error: {ve}")
|
console.debug(f"Unable to deploy due to request error: {ve}")
|
||||||
raise Exception("request error") from ve
|
# re-raise the error back to the user as client side error
|
||||||
|
raise
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
console.debug(f"Unable to deploy due to internal errors: {ex}.")
|
console.debug(f"Unable to deploy due to internal errors: {ex}.")
|
||||||
raise Exception("internal errors") from ex
|
raise Exception("internal errors") from ex
|
||||||
@ -950,8 +955,12 @@ def interactive_get_deployment_key_from_user_input(
|
|||||||
key=key_input,
|
key=key_input,
|
||||||
frontend_hostname=frontend_hostname,
|
frontend_hostname=frontend_hostname,
|
||||||
)
|
)
|
||||||
assert pre_deploy_response.reply is not None
|
if (
|
||||||
assert key_input == pre_deploy_response.reply.key
|
pre_deploy_response.reply is None
|
||||||
|
or key_input != pre_deploy_response.reply.key
|
||||||
|
):
|
||||||
|
# Rejected by server, try again
|
||||||
|
continue
|
||||||
key_candidate = pre_deploy_response.reply.key
|
key_candidate = pre_deploy_response.reply.key
|
||||||
api_url = pre_deploy_response.reply.api_url
|
api_url = pre_deploy_response.reply.api_url
|
||||||
deploy_url = pre_deploy_response.reply.deploy_url
|
deploy_url = pre_deploy_response.reply.deploy_url
|
||||||
@ -1105,13 +1114,16 @@ def get_regions() -> list[dict]:
|
|||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
response_json = response.json()
|
response_json = response.json()
|
||||||
assert response_json and isinstance(
|
if response_json is None or not isinstance(response_json, list):
|
||||||
response_json, list
|
console.debug("Expect server to return a list ")
|
||||||
), "Expect server to return a list "
|
return []
|
||||||
assert not response_json or (
|
if (
|
||||||
response_json[0] is not None and isinstance(response_json[0], dict)
|
response_json
|
||||||
), "Expect return values are dict's"
|
and response_json[0] is not None
|
||||||
|
and isinstance(response_json[0], dict)
|
||||||
|
):
|
||||||
|
console.debug("Expect return values are dict's")
|
||||||
|
return []
|
||||||
return response_json
|
return response_json
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
console.debug(f"Unable to get regions due to {ex}.")
|
console.debug(f"Unable to get regions due to {ex}.")
|
||||||
|
Loading…
Reference in New Issue
Block a user