Unverified Commit 12f72a42 authored by mohammedabdulwahhab's avatar mohammedabdulwahhab Committed by GitHub
Browse files

fix: remove dynamo cloud login (#824)

parent d346782c
...@@ -22,7 +22,6 @@ import importlib.metadata ...@@ -22,7 +22,6 @@ import importlib.metadata
import typer import typer
from rich.console import Console from rich.console import Console
from dynamo.sdk.cli.cloud import app as cloud_app
from dynamo.sdk.cli.deployment import app as deployment_app from dynamo.sdk.cli.deployment import app as deployment_app
from dynamo.sdk.cli.deployment import deploy from dynamo.sdk.cli.deployment import deploy
from dynamo.sdk.cli.env import env from dynamo.sdk.cli.env import env
...@@ -78,7 +77,6 @@ cli.command( ...@@ -78,7 +77,6 @@ cli.command(
context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
add_help_option=False, add_help_option=False,
)(run) )(run)
cli.add_typer(cloud_app, name="cloud")
cli.add_typer(deployment_app, name="deployment") cli.add_typer(deployment_app, name="deployment")
cli.command()(deploy) cli.command()(deploy)
cli.command()(build) cli.command()(build)
......
# SPDX-FileCopyrightText: Copyright (c) 2020 Atalaya Tech. Inc
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# #
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Modifications Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES
from __future__ import annotations
import rich
import typer
from bentoml._internal.cloud.client import RestApiClient
from bentoml._internal.cloud.config import CloudClientConfig, CloudClientContext
from bentoml._internal.configuration.containers import BentoMLContainer
from bentoml.exceptions import CLIException, CloudRESTApiClientError
app = typer.Typer(
help="Interact with your Dynamo Cloud Server",
add_completion=True,
no_args_is_help=True,
)
console = rich.console.Console()
@app.command()
def login(
endpoint: str = typer.Argument(
..., help="Dynamo Cloud endpoint", envvar="DYNAMO_CLOUD_API_ENDPOINT"
)
) -> None:
"""Connect to your Dynamo Cloud. You can find deployment instructions for this in our docs"""
try:
api_token = "" # Using empty string for now as it's not used
cloud_rest_client = RestApiClient(endpoint, api_token)
user = cloud_rest_client.v1.get_current_user()
if user is None:
raise CLIException("current user is not found")
org = cloud_rest_client.v1.get_current_organization()
if org is None:
raise CLIException("current organization is not found")
current_context_name = CloudClientConfig.get_config().current_context_name
cloud_context = BentoMLContainer.cloud_context.get()
ctx = CloudClientContext(
name=cloud_context if cloud_context is not None else current_context_name,
endpoint=endpoint,
api_token=api_token,
email=user.email,
)
ctx.save()
console.print(
f":white_check_mark: Configured Dynamo Cloud credentials (current-context: {ctx.name})"
)
console.print(
f":white_check_mark: Logged in as [blue]{user.email}[/] at [blue]{org.name}[/] organization"
)
except CloudRESTApiClientError as e:
if e.error_code == 401:
console.print(
f":police_car_light: Error validating token: HTTP 401: Bad credentials ({endpoint}/api-token)"
)
else:
console.print(
f":police_car_light: Error validating token: HTTP {e.error_code}"
)
...@@ -27,9 +27,11 @@ from typing import Any, Dict, List, Optional, TextIO ...@@ -27,9 +27,11 @@ from typing import Any, Dict, List, Optional, TextIO
import typer import typer
from bentoml._internal.cloud.base import Spinner from bentoml._internal.cloud.base import Spinner
from bentoml._internal.cloud.client import RestApiClient
from bentoml._internal.cloud.config import CloudClientConfig, CloudClientContext
from bentoml._internal.cloud.deployment import Deployment, DeploymentConfigParameters from bentoml._internal.cloud.deployment import Deployment, DeploymentConfigParameters
from bentoml._internal.configuration.containers import BentoMLContainer from bentoml._internal.configuration.containers import BentoMLContainer
from bentoml.exceptions import BentoMLException from bentoml.exceptions import BentoMLException, CLIException, CloudRESTApiClientError
from rich.console import Console from rich.console import Console
from simple_di import Provide, inject from simple_di import Provide, inject
...@@ -45,10 +47,11 @@ configure_server_logging() ...@@ -45,10 +47,11 @@ configure_server_logging()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
app = typer.Typer( app = typer.Typer(
help="Deploy Dynamo applications to Kubernetes cluster", help="Deploy Dynamo applications to Dynamo Cloud Kubernetes Platform",
add_completion=True, add_completion=True,
no_args_is_help=True, no_args_is_help=True,
) )
console = Console(highlight=False) console = Console(highlight=False)
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
...@@ -58,7 +61,7 @@ if t.TYPE_CHECKING: ...@@ -58,7 +61,7 @@ if t.TYPE_CHECKING:
def raise_deployment_config_error(err: BentoMLException, action: str) -> t.NoReturn: def raise_deployment_config_error(err: BentoMLException, action: str) -> t.NoReturn:
if err.error_code == HTTPStatus.UNAUTHORIZED: if err.error_code == HTTPStatus.UNAUTHORIZED:
raise BentoMLException( raise BentoMLException(
f"{err}\n* Dynamo Cloud API token is required for authorization. Run `dynamo cloud login` command to login" f"{err}\n* Dynamo Cloud API token is required for authorization. Please provide a valid endpoint with --endpoint option."
) from None ) from None
raise BentoMLException( raise BentoMLException(
f"Failed to {action} deployment due to invalid configuration: {err}" f"Failed to {action} deployment due to invalid configuration: {err}"
...@@ -67,7 +70,7 @@ def raise_deployment_config_error(err: BentoMLException, action: str) -> t.NoRet ...@@ -67,7 +70,7 @@ def raise_deployment_config_error(err: BentoMLException, action: str) -> t.NoRet
@inject @inject
def create_deployment( def create_deployment(
bento: Optional[str] = None, pipeline: Optional[str] = None,
name: Optional[str] = None, name: Optional[str] = None,
config_file: Optional[TextIO] = None, config_file: Optional[TextIO] = None,
wait: bool = True, wait: bool = True,
...@@ -86,7 +89,7 @@ def create_deployment( ...@@ -86,7 +89,7 @@ def create_deployment(
config_params = DeploymentConfigParameters( config_params = DeploymentConfigParameters(
name=name, name=name,
bento=bento, bento=pipeline,
envs=env_dicts, envs=env_dicts,
secrets=None, secrets=None,
cli=True, cli=True,
...@@ -194,7 +197,7 @@ def get_deployment( ...@@ -194,7 +197,7 @@ def get_deployment(
error_msg = str(e) error_msg = str(e)
if "No cloud context default found" in error_msg: if "No cloud context default found" in error_msg:
spinner.log( spinner.log(
"[red]:x: Error:[/] Not logged in to Dynamo Cloud. Please run 'dynamo cloud login' first." "[red]:x: Error:[/] Not logged in to Dynamo Cloud. Please provide a valid endpoint with --endpoint option."
) )
sys.exit(1) sys.exit(1)
if "404 Not Found" in error_msg or "Deployment not found" in error_msg: if "404 Not Found" in error_msg or "Deployment not found" in error_msg:
...@@ -221,7 +224,7 @@ def delete_deployment( ...@@ -221,7 +224,7 @@ def delete_deployment(
error_msg = str(e) error_msg = str(e)
if "No cloud context default found" in error_msg: if "No cloud context default found" in error_msg:
spinner.log( spinner.log(
"[red]:x: Error:[/] Not logged in to Dynamo Cloud. Please run 'dynamo cloud login' first." "[red]:x: Error:[/] Not logged in to Dynamo Cloud. Please provide a valid endpoint with --endpoint option."
) )
sys.exit(1) sys.exit(1)
if "404 Not Found" in error_msg or "Deployment not found" in error_msg: if "404 Not Found" in error_msg or "Deployment not found" in error_msg:
...@@ -269,7 +272,7 @@ def list_deployments( ...@@ -269,7 +272,7 @@ def list_deployments(
except BentoMLException as e: except BentoMLException as e:
if "No cloud context default found" in str(e): if "No cloud context default found" in str(e):
spinner.log( spinner.log(
"[red]:x: Error:[/] Not logged in to Dynamo Cloud. Please run 'dynamo cloud login' first." "[red]:x: Error:[/] Not logged in to Dynamo Cloud. Please provide a valid endpoint with --endpoint option."
) )
sys.exit(1) sys.exit(1)
spinner.log(f"[red]:x: Error:[/] Failed to list deployments: {str(e)}") spinner.log(f"[red]:x: Error:[/] Failed to list deployments: {str(e)}")
...@@ -279,8 +282,8 @@ def list_deployments( ...@@ -279,8 +282,8 @@ def list_deployments(
@app.command() @app.command()
def create( def create(
ctx: typer.Context, ctx: typer.Context,
bento: Optional[str] = typer.Argument(None, help="Bento to deploy"), pipeline: Optional[str] = typer.Argument(..., help="Dynamo pipeline to deploy"),
name: Optional[str] = typer.Option(None, "--name", "-n", help="Deployment name"), name: Optional[str] = typer.Option(..., "--name", "-n", help="Deployment name"),
config_file: Optional[typer.FileText] = typer.Option( config_file: Optional[typer.FileText] = typer.Option(
None, "--config-file", "-f", help="Configuration file path" None, "--config-file", "-f", help="Configuration file path"
), ),
...@@ -290,13 +293,17 @@ def create( ...@@ -290,13 +293,17 @@ def create(
timeout: int = typer.Option( timeout: int = typer.Option(
3600, "--timeout", help="Timeout for deployment to be ready in seconds" 3600, "--timeout", help="Timeout for deployment to be ready in seconds"
), ),
endpoint: str = typer.Option(
..., "--endpoint", "-e", help="Dynamo Cloud endpoint", envvar="DYNAMO_CLOUD"
),
) -> None: ) -> None:
"""Create a deployment on Dynamo Cloud. """Create a deployment on Dynamo Cloud.
Create a deployment using parameters, or using config yaml file. Create a deployment using parameters, or using config yaml file.
""" """
login_to_cloud(endpoint)
create_deployment( create_deployment(
bento=bento, pipeline=pipeline,
name=name, name=name,
config_file=config_file, config_file=config_file,
wait=wait, wait=wait,
...@@ -309,11 +316,15 @@ def create( ...@@ -309,11 +316,15 @@ def create(
def get( def get(
name: str = typer.Argument(..., help="Deployment name"), name: str = typer.Argument(..., help="Deployment name"),
cluster: Optional[str] = typer.Option(None, "--cluster", help="Cluster name"), cluster: Optional[str] = typer.Option(None, "--cluster", help="Cluster name"),
endpoint: str = typer.Option(
..., "--endpoint", "-e", help="Dynamo Cloud endpoint", envvar="DYNAMO_CLOUD"
),
) -> None: ) -> None:
"""Get deployment details from Dynamo Cloud. """Get deployment details from Dynamo Cloud.
Get deployment details by name. Get deployment details by name.
""" """
login_to_cloud(endpoint)
get_deployment(name, cluster=cluster) get_deployment(name, cluster=cluster)
...@@ -325,11 +336,19 @@ def list( ...@@ -325,11 +336,19 @@ def list(
query: Optional[str] = typer.Option( query: Optional[str] = typer.Option(
None, "--query", "-q", help="Advanced query string" None, "--query", "-q", help="Advanced query string"
), ),
endpoint: str = typer.Option(
...,
"--endpoint",
"-e",
help="Dynamo Cloud endpoint",
envvar="DYNAMO_CLOUD",
),
) -> None: ) -> None:
"""List all deployments from Dynamo Cloud. """List all deployments from Dynamo Cloud.
List and filter deployments. List and filter deployments.
""" """
login_to_cloud(endpoint)
list_deployments(cluster=cluster, search=search, dev=dev, q=query) list_deployments(cluster=cluster, search=search, dev=dev, q=query)
...@@ -337,18 +356,26 @@ def list( ...@@ -337,18 +356,26 @@ def list(
def delete( def delete(
name: str = typer.Argument(..., help="Deployment name"), name: str = typer.Argument(..., help="Deployment name"),
cluster: Optional[str] = typer.Option(None, "--cluster", help="Cluster name"), cluster: Optional[str] = typer.Option(None, "--cluster", help="Cluster name"),
endpoint: str = typer.Option(
...,
"--endpoint",
"-e",
help="Dynamo Cloud endpoint",
envvar="DYNAMO_CLOUD",
),
) -> None: ) -> None:
"""Delete a deployment from Dynamo Cloud. """Delete a deployment from Dynamo Cloud.
Delete deployment by name. Delete deployment by name.
""" """
login_to_cloud(endpoint)
delete_deployment(name, cluster=cluster) delete_deployment(name, cluster=cluster)
def deploy( def deploy(
ctx: typer.Context, ctx: typer.Context,
bento: Optional[str] = typer.Argument(None, help="Bento to deploy"), pipeline: Optional[str] = typer.Argument(..., help="Dynamo pipeline to deploy"),
name: Optional[str] = typer.Option(None, "--name", "-n", help="Deployment name"), name: Optional[str] = typer.Option(..., "--name", "-n", help="Deployment name"),
config_file: Optional[typer.FileText] = typer.Option( config_file: Optional[typer.FileText] = typer.Option(
None, "--config-file", "-f", help="Configuration file path" None, "--config-file", "-f", help="Configuration file path"
), ),
...@@ -358,16 +385,70 @@ def deploy( ...@@ -358,16 +385,70 @@ def deploy(
timeout: int = typer.Option( timeout: int = typer.Option(
3600, "--timeout", help="Timeout for deployment to be ready in seconds" 3600, "--timeout", help="Timeout for deployment to be ready in seconds"
), ),
endpoint: str = typer.Option(
...,
"--endpoint",
"-e",
help="Dynamo Cloud endpoint",
envvar="DYNAMO_CLOUD",
),
) -> None: ) -> None:
"""Create a deployment on Dynamo Cloud. """Create a deployment on Dynamo Cloud.
Create a deployment using parameters, or using config yaml file. Create a deployment using parameters, or using config yaml file.
""" """
login_to_cloud(endpoint)
create_deployment( create_deployment(
bento=bento, pipeline=pipeline,
name=name, name=name,
config_file=config_file, config_file=config_file,
wait=wait, wait=wait,
timeout=timeout, timeout=timeout,
args=ctx.args if hasattr(ctx, "args") else None, args=ctx.args if hasattr(ctx, "args") else None,
) )
def login_to_cloud(endpoint: str) -> None:
"""Connect to Dynamo Cloud silently using logging for success and console for errors."""
try:
logger.info(f"Running against Dynamo Cloud at {endpoint}")
api_token = "" # Using empty string for now as it's not used
cloud_rest_client = RestApiClient(endpoint, api_token)
user = cloud_rest_client.v1.get_current_user()
if user is None:
raise CLIException("current user is not found")
org = cloud_rest_client.v1.get_current_organization()
if org is None:
raise CLIException("current organization is not found")
current_context_name = CloudClientConfig.get_config().current_context_name
cloud_context = BentoMLContainer.cloud_context.get()
ctx = CloudClientContext(
name=cloud_context if cloud_context is not None else current_context_name,
endpoint=endpoint,
api_token=api_token,
email=user.email,
)
ctx.save()
logger.debug(
f"Configured Dynamo Cloud credentials (current-context: {ctx.name})"
)
logger.debug(f"Logged in as {user.email} at {org.name} organization")
except CloudRESTApiClientError as e:
if e.error_code == 401:
console.print(
f":police_car_light: Error validating token: HTTP 401: Bad credentials ({endpoint}/api-token)"
)
else:
console.print(
f":police_car_light: Error validating token: HTTP {e.error_code}"
)
raise BentoMLException(f"Failed to login to Dynamo Cloud: {str(e)}") from e
except Exception as e:
console.print(f":police_car_light: Error connecting to Dynamo Cloud: {str(e)}")
raise BentoMLException(f"Failed to login to Dynamo Cloud: {str(e)}") from e
...@@ -50,13 +50,12 @@ export PROJECT_ROOT=$(pwd) ...@@ -50,13 +50,12 @@ export PROJECT_ROOT=$(pwd)
# Set your Kubernetes namespace (must match the namespace where Dynamo cloud is installed) # Set your Kubernetes namespace (must match the namespace where Dynamo cloud is installed)
export KUBE_NS=hello-world export KUBE_NS=hello-world
# Externally accessible endpoint to the `dynamo-store` service within your Dynamo Cloud installation # Set the externally accessible endpoint to the `dynamo-store` service within your Dynamo Cloud installation
export DYNAMO_CLOUD=https://${KUBE_NS}.dev.aire.nvidia.com export DYNAMO_CLOUD=https://${KUBE_NS}.dev.aire.nvidia.com
# Login to the Dynamo cloud
dynamo cloud login $DYNAMO_CLOUD
``` ```
The `DYNAMO_CLOUD` environment variable is required for all Dynamo deployment commands. Make sure it's set before running any deployment operations.
### 2. Build the Dynamo Base Image ### 2. Build the Dynamo Base Image
> [!NOTE] > [!NOTE]
......
...@@ -142,9 +142,10 @@ The deployment process involves two distinct build steps: ...@@ -142,9 +142,10 @@ The deployment process involves two distinct build steps:
export PROJECT_ROOT=$(pwd) export PROJECT_ROOT=$(pwd)
export KUBE_NS=hello-world # Must match your Kubernetes namespace export KUBE_NS=hello-world # Must match your Kubernetes namespace
export DYNAMO_CLOUD=https://${KUBE_NS}.dev.aire.nvidia.com export DYNAMO_CLOUD=https://${KUBE_NS}.dev.aire.nvidia.com
dynamo cloud login $DYNAMO_CLOUD
``` ```
The `DYNAMO_CLOUD` environment variable is required for all Dynamo deployment commands. Make sure it's set before running any deployment operations.
2. **Build the Dynamo Base Image** 2. **Build the Dynamo Base Image**
> [!NOTE] > [!NOTE]
......
...@@ -185,9 +185,10 @@ You must have first followed the instructions in [deploy/dynamo/helm/README.md]( ...@@ -185,9 +185,10 @@ You must have first followed the instructions in [deploy/dynamo/helm/README.md](
export PROJECT_ROOT=$(pwd) export PROJECT_ROOT=$(pwd)
export KUBE_NS=dynamo-cloud # Note: This must match the Kubernetes namespace where you installed Dynamo Cloud export KUBE_NS=dynamo-cloud # Note: This must match the Kubernetes namespace where you installed Dynamo Cloud
export DYNAMO_CLOUD=https://${KUBE_NS}.dev.aire.nvidia.com # Externally accessible endpoint to the `dynamo-store` service within your Dynamo Cloud installation export DYNAMO_CLOUD=https://${KUBE_NS}.dev.aire.nvidia.com # Externally accessible endpoint to the `dynamo-store` service within your Dynamo Cloud installation
dynamo cloud login $DYNAMO_CLOUD
``` ```
The `DYNAMO_CLOUD` environment variable is required for all Dynamo deployment commands. Make sure it's set before running any deployment operations.
2. **Build the Dynamo Base Image** 2. **Build the Dynamo Base Image**
> [!NOTE] > [!NOTE]
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment