From 5a2369c52e92a3ed553cd02a8aebc6a9816f42a8 Mon Sep 17 00:00:00 2001 From: dbczumar Date: Wed, 15 Nov 2023 20:25:19 -0800 Subject: [PATCH 01/11] Test too Signed-off-by: dbczumar --- mlflow/deployments/cli.py | 40 ++++++++++++++++++++++++++++ mlflow/gateway/cli.py | 47 -------------------------------- tests/deployments/test_cli.py | 47 ++++++++++++++++++++++++++++++++ tests/gateway/test_cli.py | 50 ----------------------------------- 4 files changed, 87 insertions(+), 97 deletions(-) delete mode 100644 mlflow/gateway/cli.py delete mode 100644 tests/gateway/test_cli.py diff --git a/mlflow/deployments/cli.py b/mlflow/deployments/cli.py index 68be85f2cbf0a..d1d4c0f9e2c89 100644 --- a/mlflow/deployments/cli.py +++ b/mlflow/deployments/cli.py @@ -4,8 +4,12 @@ import click +from mlflow.environment_variables import MLFLOW_GATEWAY_CONFIG from mlflow.deployments import interface +from mlflow.gateway.config import _validate_config +from mlflow.gateway.runner import run_app from mlflow.utils import cli_args +from mlflow.utils.annotations import experimental from mlflow.utils.proto_json_utils import NumpyEncoder, _get_jsonable_obj @@ -30,6 +34,14 @@ def _user_args_to_dict(user_list): return user_dict +def validate_config_path(_ctx, _param, value): + try: + _validate_config(value) + return value + except Exception as e: + raise click.BadParameter(str(e)) + + installed_targets = list(interface.plugin_store.registry) if len(installed_targets) > 0: supported_targets_msg = "Support is currently installed for deployment to: {targets}".format( @@ -451,3 +463,31 @@ def get_endpoint(target, endpoint): for key, val in desc.items(): click.echo(f"{key}: {val}") click.echo("\n") + + +@experimental +@commands.command("start-server", help="Start the MLflow Deployments server") +@click.option( + "--config-path", + envvar=MLFLOW_GATEWAY_CONFIG.name, + callback=validate_config_path, + required=True, + help="The path to the deployments configuration file.", +) +@click.option( + "--host", + default="127.0.0.1", + help="The network address to listen on (default: 127.0.0.1).", +) +@click.option( + "--port", + default=5000, + help="The port to listen on (default: 5000).", +) +@click.option( + "--workers", + default=2, + help="The number of workers.", +) +def start_server(config_path: str, host: str, port: str, workers: int): + run_app(config_path=config_path, host=host, port=port, workers=workers) diff --git a/mlflow/gateway/cli.py b/mlflow/gateway/cli.py deleted file mode 100644 index 468f09eea2527..0000000000000 --- a/mlflow/gateway/cli.py +++ /dev/null @@ -1,47 +0,0 @@ -import click - -from mlflow.environment_variables import MLFLOW_GATEWAY_CONFIG -from mlflow.gateway.config import _validate_config -from mlflow.gateway.runner import run_app -from mlflow.utils.annotations import experimental - - -def validate_config_path(_ctx, _param, value): - try: - _validate_config(value) - return value - except Exception as e: - raise click.BadParameter(str(e)) - - -@click.group("gateway", help="Manage the MLflow Gateway service") -def commands(): - pass - - -@experimental -@commands.command("start", help="Start the MLflow Gateway service") -@click.option( - "--config-path", - envvar=MLFLOW_GATEWAY_CONFIG.name, - callback=validate_config_path, - required=True, - help="The path to the gateway configuration file.", -) -@click.option( - "--host", - default="127.0.0.1", - help="The network address to listen on (default: 127.0.0.1).", -) -@click.option( - "--port", - default=5000, - help="The port to listen on (default: 5000).", -) -@click.option( - "--workers", - default=2, - help="The number of workers.", -) -def start(config_path: str, host: str, port: str, workers: int): - run_app(config_path=config_path, host=host, port=port, workers=workers) diff --git a/tests/deployments/test_cli.py b/tests/deployments/test_cli.py index 2d6b6a40c886a..40da349bf6ddc 100644 --- a/tests/deployments/test_cli.py +++ b/tests/deployments/test_cli.py @@ -151,3 +151,50 @@ def test_explain_with_no_target_implementation(tmp_path): ) assert type(res) == MlflowException mock_explain.assert_called_once() + + +def test_start_help(): + runner = CliRunner() + res = runner.invoke( + cli.start_server, + ["--help"], + catch_exceptions=False, + ) + assert res.exit_code == 0 + + +def test_start_invalid_config(tmp_path): + runner = CliRunner() + config = tmp_path.joinpath("config.yml") + res = runner.invoke( + cli.start_server, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "does not exist" in res.output + + config.write_text("\t") + res = runner.invoke( + cli.start_server, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "not a valid yaml file" in res.output + + config.write_text( + """ +routes: + - model: + name: invalid +""" + ) + res = runner.invoke( + cli.start_server, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "The gateway configuration is invalid" in res.output + assert "routes" in res.output diff --git a/tests/gateway/test_cli.py b/tests/gateway/test_cli.py deleted file mode 100644 index 41729374f038e..0000000000000 --- a/tests/gateway/test_cli.py +++ /dev/null @@ -1,50 +0,0 @@ -from click.testing import CliRunner - -from mlflow.gateway.cli import start - - -def test_start_help(): - runner = CliRunner() - res = runner.invoke( - start, - ["--help"], - catch_exceptions=False, - ) - assert res.exit_code == 0 - - -def test_start_invalid_config(tmp_path): - runner = CliRunner() - config = tmp_path.joinpath("config.yml") - res = runner.invoke( - start, - ["--config-path", config], - catch_exceptions=False, - ) - assert res.exit_code == 2 - assert "does not exist" in res.output - - config.write_text("\t") - res = runner.invoke( - start, - ["--config-path", config], - catch_exceptions=False, - ) - assert res.exit_code == 2 - assert "not a valid yaml file" in res.output - - config.write_text( - """ -routes: - - model: - name: invalid -""" - ) - res = runner.invoke( - start, - ["--config-path", config], - catch_exceptions=False, - ) - assert res.exit_code == 2 - assert "The gateway configuration is invalid" in res.output - assert "routes" in res.output From 496f4be99c0dec86d1ebce7151707d6c93cb5c27 Mon Sep 17 00:00:00 2001 From: mlflow-automation Date: Thu, 16 Nov 2023 04:29:48 +0000 Subject: [PATCH 02/11] Autoformat: https://github.com/mlflow/mlflow/actions/runs/6886333021 Signed-off-by: mlflow-automation --- mlflow/deployments/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlflow/deployments/cli.py b/mlflow/deployments/cli.py index d1d4c0f9e2c89..ade712823c521 100644 --- a/mlflow/deployments/cli.py +++ b/mlflow/deployments/cli.py @@ -4,8 +4,8 @@ import click -from mlflow.environment_variables import MLFLOW_GATEWAY_CONFIG from mlflow.deployments import interface +from mlflow.environment_variables import MLFLOW_GATEWAY_CONFIG from mlflow.gateway.config import _validate_config from mlflow.gateway.runner import run_app from mlflow.utils import cli_args From ffa913c4421fd34dca26dbae2ebba21a183ecf25 Mon Sep 17 00:00:00 2001 From: dbczumar Date: Mon, 20 Nov 2023 15:34:31 -0800 Subject: [PATCH 03/11] restore gateway cli Signed-off-by: dbczumar --- mlflow/deployments/cli.py | 4 +-- mlflow/environment_variables.py | 4 +++ mlflow/gateway/cli.py | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 mlflow/gateway/cli.py diff --git a/mlflow/deployments/cli.py b/mlflow/deployments/cli.py index ade712823c521..d83368d97c97e 100644 --- a/mlflow/deployments/cli.py +++ b/mlflow/deployments/cli.py @@ -5,7 +5,7 @@ import click from mlflow.deployments import interface -from mlflow.environment_variables import MLFLOW_GATEWAY_CONFIG +from mlflow.environment_variables import MLFLOW_DEPLOYMENTS_CONFIG from mlflow.gateway.config import _validate_config from mlflow.gateway.runner import run_app from mlflow.utils import cli_args @@ -469,7 +469,7 @@ def get_endpoint(target, endpoint): @commands.command("start-server", help="Start the MLflow Deployments server") @click.option( "--config-path", - envvar=MLFLOW_GATEWAY_CONFIG.name, + envvar=MLFLOW_DEPLOYMENTS_CONFIG.name, callback=validate_config_path, required=True, help="The path to the deployments configuration file.", diff --git a/mlflow/environment_variables.py b/mlflow/environment_variables.py index 59f2ca94c5ee3..2727a79b70a56 100644 --- a/mlflow/environment_variables.py +++ b/mlflow/environment_variables.py @@ -376,6 +376,10 @@ def get(self): #: (default: ``None``) MLFLOW_GATEWAY_CONFIG = _EnvironmentVariable("MLFLOW_GATEWAY_CONFIG", str, None) +#: Specifies the path of the config file for the MLflow Deployments server. +#: (default: ``None``) +MLFLOW_DEPLOYMENTS_CONFIG = _EnvironmentVariable("MLFLOW_DEPLOYMENTS_CONFIG", str, None) + #: Specifies whether to display the progress bar when uploading/downloading artifacts. #: (default: ``True``) MLFLOW_ENABLE_ARTIFACTS_PROGRESS_BAR = _BooleanEnvironmentVariable( diff --git a/mlflow/gateway/cli.py b/mlflow/gateway/cli.py new file mode 100644 index 0000000000000..468f09eea2527 --- /dev/null +++ b/mlflow/gateway/cli.py @@ -0,0 +1,47 @@ +import click + +from mlflow.environment_variables import MLFLOW_GATEWAY_CONFIG +from mlflow.gateway.config import _validate_config +from mlflow.gateway.runner import run_app +from mlflow.utils.annotations import experimental + + +def validate_config_path(_ctx, _param, value): + try: + _validate_config(value) + return value + except Exception as e: + raise click.BadParameter(str(e)) + + +@click.group("gateway", help="Manage the MLflow Gateway service") +def commands(): + pass + + +@experimental +@commands.command("start", help="Start the MLflow Gateway service") +@click.option( + "--config-path", + envvar=MLFLOW_GATEWAY_CONFIG.name, + callback=validate_config_path, + required=True, + help="The path to the gateway configuration file.", +) +@click.option( + "--host", + default="127.0.0.1", + help="The network address to listen on (default: 127.0.0.1).", +) +@click.option( + "--port", + default=5000, + help="The port to listen on (default: 5000).", +) +@click.option( + "--workers", + default=2, + help="The number of workers.", +) +def start(config_path: str, host: str, port: str, workers: int): + run_app(config_path=config_path, host=host, port=port, workers=workers) From 5c618504684d54fa219cc349f42d9c5cc42dd481 Mon Sep 17 00:00:00 2001 From: dbczumar Date: Mon, 20 Nov 2023 15:57:08 -0800 Subject: [PATCH 04/11] Restore CLI test Signed-off-by: dbczumar --- tests/gateway/test_cli.py | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 tests/gateway/test_cli.py diff --git a/tests/gateway/test_cli.py b/tests/gateway/test_cli.py new file mode 100644 index 0000000000000..41729374f038e --- /dev/null +++ b/tests/gateway/test_cli.py @@ -0,0 +1,50 @@ +from click.testing import CliRunner + +from mlflow.gateway.cli import start + + +def test_start_help(): + runner = CliRunner() + res = runner.invoke( + start, + ["--help"], + catch_exceptions=False, + ) + assert res.exit_code == 0 + + +def test_start_invalid_config(tmp_path): + runner = CliRunner() + config = tmp_path.joinpath("config.yml") + res = runner.invoke( + start, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "does not exist" in res.output + + config.write_text("\t") + res = runner.invoke( + start, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "not a valid yaml file" in res.output + + config.write_text( + """ +routes: + - model: + name: invalid +""" + ) + res = runner.invoke( + start, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "The gateway configuration is invalid" in res.output + assert "routes" in res.output From 2ba26060f9752b1df27043b0f99105a5284619e0 Mon Sep 17 00:00:00 2001 From: dbczumar Date: Mon, 20 Nov 2023 20:49:05 -0800 Subject: [PATCH 05/11] fix Signed-off-by: dbczumar --- mlflow/deployments/cli.py | 77 +++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/mlflow/deployments/cli.py b/mlflow/deployments/cli.py index d83368d97c97e..1c48e54b51136 100644 --- a/mlflow/deployments/cli.py +++ b/mlflow/deployments/cli.py @@ -6,8 +6,6 @@ from mlflow.deployments import interface from mlflow.environment_variables import MLFLOW_DEPLOYMENTS_CONFIG -from mlflow.gateway.config import _validate_config -from mlflow.gateway.runner import run_app from mlflow.utils import cli_args from mlflow.utils.annotations import experimental from mlflow.utils.proto_json_utils import NumpyEncoder, _get_jsonable_obj @@ -34,14 +32,6 @@ def _user_args_to_dict(user_list): return user_dict -def validate_config_path(_ctx, _param, value): - try: - _validate_config(value) - return value - except Exception as e: - raise click.BadParameter(str(e)) - - installed_targets = list(interface.plugin_store.registry) if len(installed_targets) > 0: supported_targets_msg = "Support is currently installed for deployment to: {targets}".format( @@ -465,29 +455,44 @@ def get_endpoint(target, endpoint): click.echo("\n") -@experimental -@commands.command("start-server", help="Start the MLflow Deployments server") -@click.option( - "--config-path", - envvar=MLFLOW_DEPLOYMENTS_CONFIG.name, - callback=validate_config_path, - required=True, - help="The path to the deployments configuration file.", -) -@click.option( - "--host", - default="127.0.0.1", - help="The network address to listen on (default: 127.0.0.1).", -) -@click.option( - "--port", - default=5000, - help="The port to listen on (default: 5000).", -) -@click.option( - "--workers", - default=2, - help="The number of workers.", -) -def start_server(config_path: str, host: str, port: str, workers: int): - run_app(config_path=config_path, host=host, port=port, workers=workers) +try: + from mlflow.gateway.config import _validate_config + from mlflow.gateway.runner import run_app + + def validate_config_path(_ctx, _param, value): + try: + _validate_config(value) + return value + except Exception as e: + raise click.BadParameter(str(e)) + + @experimental + @commands.command("start-server", help="Start the MLflow Deployments server") + @click.option( + "--config-path", + envvar=MLFLOW_DEPLOYMENTS_CONFIG.name, + callback=validate_config_path, + required=True, + help="The path to the deployments configuration file.", + ) + @click.option( + "--host", + default="127.0.0.1", + help="The network address to listen on (default: 127.0.0.1).", + ) + @click.option( + "--port", + default=5000, + help="The port to listen on (default: 5000).", + ) + @click.option( + "--workers", + default=2, + help="The number of workers.", + ) + def start_server(config_path: str, host: str, port: str, workers: int): + run_app(config_path=config_path, host=host, port=port, workers=workers) + +except ImportError: + # Dependencies for MLflow Deployments server have not been installed + pass From 93d5ef1586fbb8053a70f974f232aa4b11d83d56 Mon Sep 17 00:00:00 2001 From: dbczumar Date: Mon, 20 Nov 2023 21:20:00 -0800 Subject: [PATCH 06/11] Test fix Signed-off-by: dbczumar --- tests/deployments/test_cli.py | 47 ------------------------ tests/deployments/test_server_cli.py | 54 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 47 deletions(-) create mode 100644 tests/deployments/test_server_cli.py diff --git a/tests/deployments/test_cli.py b/tests/deployments/test_cli.py index 40da349bf6ddc..2d6b6a40c886a 100644 --- a/tests/deployments/test_cli.py +++ b/tests/deployments/test_cli.py @@ -151,50 +151,3 @@ def test_explain_with_no_target_implementation(tmp_path): ) assert type(res) == MlflowException mock_explain.assert_called_once() - - -def test_start_help(): - runner = CliRunner() - res = runner.invoke( - cli.start_server, - ["--help"], - catch_exceptions=False, - ) - assert res.exit_code == 0 - - -def test_start_invalid_config(tmp_path): - runner = CliRunner() - config = tmp_path.joinpath("config.yml") - res = runner.invoke( - cli.start_server, - ["--config-path", config], - catch_exceptions=False, - ) - assert res.exit_code == 2 - assert "does not exist" in res.output - - config.write_text("\t") - res = runner.invoke( - cli.start_server, - ["--config-path", config], - catch_exceptions=False, - ) - assert res.exit_code == 2 - assert "not a valid yaml file" in res.output - - config.write_text( - """ -routes: - - model: - name: invalid -""" - ) - res = runner.invoke( - cli.start_server, - ["--config-path", config], - catch_exceptions=False, - ) - assert res.exit_code == 2 - assert "The gateway configuration is invalid" in res.output - assert "routes" in res.output diff --git a/tests/deployments/test_server_cli.py b/tests/deployments/test_server_cli.py new file mode 100644 index 0000000000000..c20a5aa502c68 --- /dev/null +++ b/tests/deployments/test_server_cli.py @@ -0,0 +1,54 @@ +import pytest +from click.testing import CliRunner + +from mlflow.deployments import cli + +pytest.importorskip("mlflow.gateway") +runner = CliRunner() + + +def test_start_help(): + runner = CliRunner() + res = runner.invoke( + cli.start_server, + ["--help"], + catch_exceptions=False, + ) + assert res.exit_code == 0 + + +def test_start_invalid_config(tmp_path): + runner = CliRunner() + config = tmp_path.joinpath("config.yml") + res = runner.invoke( + cli.start_server, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "does not exist" in res.output + + config.write_text("\t") + res = runner.invoke( + cli.start_server, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "not a valid yaml file" in res.output + + config.write_text( + """ +routes: + - model: + name: invalid +""" + ) + res = runner.invoke( + cli.start_server, + ["--config-path", config], + catch_exceptions=False, + ) + assert res.exit_code == 2 + assert "The gateway configuration is invalid" in res.output + assert "routes" in res.output From 07cdd58cb3531437d8a66d8f33904d67760a700d Mon Sep 17 00:00:00 2001 From: dbczumar Date: Mon, 20 Nov 2023 21:32:43 -0800 Subject: [PATCH 07/11] fix Signed-off-by: dbczumar --- .github/workflows/deployments.yml | 2 +- .../{deploymnets/databricks => deployments}/test_databricks.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{deploymnets/databricks => deployments}/test_databricks.py (100%) diff --git a/.github/workflows/deployments.yml b/.github/workflows/deployments.yml index ed67c866ff49e..e588985d5f0c7 100644 --- a/.github/workflows/deployments.yml +++ b/.github/workflows/deployments.yml @@ -33,4 +33,4 @@ jobs: pytest pytest-timeout pytest-asyncio httpx psutil - name: Run tests run: | - pytest tests/deploymnets/databricks + pytest tests/deployments diff --git a/tests/deploymnets/databricks/test_databricks.py b/tests/deployments/test_databricks.py similarity index 100% rename from tests/deploymnets/databricks/test_databricks.py rename to tests/deployments/test_databricks.py From ec5e1048d8802f4e80af83f2ed30bbb48f2e9f4d Mon Sep 17 00:00:00 2001 From: dbczumar Date: Mon, 20 Nov 2023 21:40:11 -0800 Subject: [PATCH 08/11] fix Signed-off-by: dbczumar --- .github/workflows/deployments.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deployments.yml b/.github/workflows/deployments.yml index e588985d5f0c7..9324f6b39fd52 100644 --- a/.github/workflows/deployments.yml +++ b/.github/workflows/deployments.yml @@ -29,6 +29,7 @@ jobs: - uses: ./.github/actions/setup-python - name: Install dependencies run: | + pip install --no-dependencies tests/resources/mlflow-test-plugin && \ pip install .[gateway] \ pytest pytest-timeout pytest-asyncio httpx psutil - name: Run tests From 32584c50ed663c9e1f92536c33804d35d90c3229 Mon Sep 17 00:00:00 2001 From: Corey Zumar <39497902+dbczumar@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:41:22 -0800 Subject: [PATCH 09/11] Update .github/workflows/deployments.yml Co-authored-by: Harutaka Kawamura Signed-off-by: Corey Zumar <39497902+dbczumar@users.noreply.github.com> --- .github/workflows/deployments.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deployments.yml b/.github/workflows/deployments.yml index 9324f6b39fd52..217e973bb179c 100644 --- a/.github/workflows/deployments.yml +++ b/.github/workflows/deployments.yml @@ -29,7 +29,7 @@ jobs: - uses: ./.github/actions/setup-python - name: Install dependencies run: | - pip install --no-dependencies tests/resources/mlflow-test-plugin && \ + pip install --no-dependencies tests/resources/mlflow-test-plugin pip install .[gateway] \ pytest pytest-timeout pytest-asyncio httpx psutil - name: Run tests From bf7173f934e3aed3da82896e333ff12417a46ff7 Mon Sep 17 00:00:00 2001 From: Corey Zumar <39497902+dbczumar@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:46:48 -0800 Subject: [PATCH 10/11] Update tests/deployments/test_server_cli.py Signed-off-by: Corey Zumar <39497902+dbczumar@users.noreply.github.com> --- tests/deployments/test_server_cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/deployments/test_server_cli.py b/tests/deployments/test_server_cli.py index c20a5aa502c68..21a2062769b91 100644 --- a/tests/deployments/test_server_cli.py +++ b/tests/deployments/test_server_cli.py @@ -4,7 +4,6 @@ from mlflow.deployments import cli pytest.importorskip("mlflow.gateway") -runner = CliRunner() def test_start_help(): From af7dc3c0f78b50b210c650d6457f9d3ea1e9c02e Mon Sep 17 00:00:00 2001 From: dbczumar Date: Mon, 20 Nov 2023 21:53:10 -0800 Subject: [PATCH 11/11] nest the imports instead Signed-off-by: dbczumar --- mlflow/deployments/cli.py | 73 +++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/mlflow/deployments/cli.py b/mlflow/deployments/cli.py index 1c48e54b51136..409a08b7e2dd1 100644 --- a/mlflow/deployments/cli.py +++ b/mlflow/deployments/cli.py @@ -455,44 +455,41 @@ def get_endpoint(target, endpoint): click.echo("\n") -try: +def validate_config_path(_ctx, _param, value): from mlflow.gateway.config import _validate_config - from mlflow.gateway.runner import run_app - def validate_config_path(_ctx, _param, value): - try: - _validate_config(value) - return value - except Exception as e: - raise click.BadParameter(str(e)) - - @experimental - @commands.command("start-server", help="Start the MLflow Deployments server") - @click.option( - "--config-path", - envvar=MLFLOW_DEPLOYMENTS_CONFIG.name, - callback=validate_config_path, - required=True, - help="The path to the deployments configuration file.", - ) - @click.option( - "--host", - default="127.0.0.1", - help="The network address to listen on (default: 127.0.0.1).", - ) - @click.option( - "--port", - default=5000, - help="The port to listen on (default: 5000).", - ) - @click.option( - "--workers", - default=2, - help="The number of workers.", - ) - def start_server(config_path: str, host: str, port: str, workers: int): - run_app(config_path=config_path, host=host, port=port, workers=workers) + try: + _validate_config(value) + return value + except Exception as e: + raise click.BadParameter(str(e)) + + +@experimental +@commands.command("start-server", help="Start the MLflow Deployments server") +@click.option( + "--config-path", + envvar=MLFLOW_DEPLOYMENTS_CONFIG.name, + callback=validate_config_path, + required=True, + help="The path to the deployments configuration file.", +) +@click.option( + "--host", + default="127.0.0.1", + help="The network address to listen on (default: 127.0.0.1).", +) +@click.option( + "--port", + default=5000, + help="The port to listen on (default: 5000).", +) +@click.option( + "--workers", + default=2, + help="The number of workers.", +) +def start_server(config_path: str, host: str, port: str, workers: int): + from mlflow.gateway.runner import run_app -except ImportError: - # Dependencies for MLflow Deployments server have not been installed - pass + run_app(config_path=config_path, host=host, port=port, workers=workers)