Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/dstack/_internal/server/services/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from dstack._internal.core.models.services import OpenAIChatModel
from dstack._internal.server import settings
from dstack._internal.server.models import GatewayModel, RunModel
from dstack._internal.server.services import events
from dstack._internal.server.services.gateways import (
get_gateway_compute_models,
get_gateway_configuration,
Expand Down Expand Up @@ -73,7 +74,7 @@ async def register_service(session: AsyncSession, run_model: RunModel, run_spec:
service_spec = await _register_service_in_gateway(session, run_model, run_spec, gateway)
run_model.gateway = gateway
elif not settings.FORBID_SERVICES_WITHOUT_GATEWAY:
service_spec = _register_service_in_server(run_model, run_spec)
service_spec = _register_service_in_server(session, run_model, run_spec)
else:
raise ResourceNotExistsError(
"This dstack-server installation forbids services without a gateway."
Expand Down Expand Up @@ -137,10 +138,18 @@ async def _register_service_in_gateway(
service_url=service_url,
model_url=model_url,
)
events.emit(
session,
"Service assigned to gateway",
actor=events.SystemActor(),
targets=[events.Target.from_model(run_model), events.Target.from_model(gateway)],
)
return service_spec


def _register_service_in_server(run_model: RunModel, run_spec: RunSpec) -> ServiceSpec:
def _register_service_in_server(
session: AsyncSession, run_model: RunModel, run_spec: RunSpec
) -> ServiceSpec:
assert run_spec.configuration.type == "service"
if run_spec.configuration.https not in (
None,
Expand Down Expand Up @@ -170,6 +179,12 @@ def _register_service_in_server(run_model: RunModel, run_spec: RunSpec) -> Servi
model_url = service_url.rstrip("/") + run_spec.configuration.model.prefix
else:
model_url = f"/proxy/models/{run_model.project.name}/"
events.emit(
session,
"Service assigned to run without a gateway",
actor=events.SystemActor(),
targets=[events.Target.from_model(run_model)],
)
return _get_service_spec(
configuration=run_spec.configuration,
service_url=service_url,
Expand Down
7 changes: 7 additions & 0 deletions src/tests/_internal/server/routers/test_runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4005,6 +4005,13 @@ async def test_submit_to_correct_proxy(
res = await session.execute(select(RunModel))
run = res.scalar_one()
assert (run.gateway_id is not None) == is_gateway
event_messages = {e.message for e in await list_events(session)}
if is_gateway:
assert "Service assigned to gateway" in event_messages
assert "Service assigned to run without a gateway" not in event_messages
else:
assert "Service assigned to gateway" not in event_messages
assert "Service assigned to run without a gateway" in event_messages

@pytest.mark.asyncio
@pytest.mark.parametrize("populate_configuration", [True, False])
Expand Down
21 changes: 0 additions & 21 deletions src/tests/_internal/server/services/services/test_services.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
from typing import Literal, Optional, Union
from unittest.mock import MagicMock

import pytest

from dstack._internal.core.errors import ServerClientError
from dstack._internal.core.models.backends.base import BackendType
from dstack._internal.core.models.configurations import ServiceConfiguration
from dstack._internal.core.models.gateways import (
Expand All @@ -14,7 +11,6 @@
)
from dstack._internal.core.models.runs import RunSpec
from dstack._internal.server.services.services import (
_register_service_in_server,
_should_show_service_https,
should_configure_service_https_on_gateway,
)
Expand Down Expand Up @@ -118,20 +114,3 @@ def test_false_disables_https_regardless_of_gateway_certificate(self) -> None:
run_spec = _service_run_spec(https=False)
gw = _gateway_config(certificate=LetsEncryptGatewayCertificate())
assert _should_show_service_https(run_spec, gw) is False


class TestRegisterServiceInServerHttps:
def test_allows_default_true_without_gateway(self) -> None:
run_spec = _service_run_spec(https=True)
result = _register_service_in_server(_mock_run_model(), run_spec)
assert result is not None

def test_allows_auto_without_gateway(self) -> None:
run_spec = _service_run_spec(https="auto")
result = _register_service_in_server(_mock_run_model(), run_spec)
assert result is not None

def test_rejects_explicit_false_without_gateway(self) -> None:
run_spec = _service_run_spec(https=False)
with pytest.raises(ServerClientError, match="not allowed without a gateway"):
_register_service_in_server(_mock_run_model(), run_spec)
Loading