4949 LetsEncryptGatewayCertificate ,
5050)
5151from dstack ._internal .core .services import validate_dstack_resource_name
52- from dstack ._internal .core .services .diff import (
53- ModelDiff ,
54- diff_models ,
55- format_diff_fields_for_event ,
56- )
52+ from dstack ._internal .core .services .diff import ModelDiff , format_diff_fields_for_event
53+ from dstack ._internal .core .services .gateways import diff_gateway_configurations
5754from dstack ._internal .proxy .gateway .const import SERVICE_SCALING_WINDOWS
5855from dstack ._internal .proxy .gateway .schemas .stats import PerWindowStats , Stat
5956from dstack ._internal .server import settings
9289from dstack ._internal .utils .logging import get_logger
9390
9491logger = get_logger (__name__ )
95- _CONF_UPDATABLE_FIELDS = frozenset ({"domain" })
92+ _CONF_UPDATABLE_FIELDS = frozenset ({"domain" , "default" })
9693if FeatureFlags .GATEWAY_SCALING :
9794 _CONF_UPDATABLE_FIELDS |= {"replicas" }
9895
@@ -292,7 +289,7 @@ async def create_gateway(
292289 await session .commit ()
293290
294291 default_gateway = await get_project_default_gateway_model (session = session , project = project )
295- if default_gateway is None or configuration .default :
292+ if default_gateway is None and configuration . default is None or configuration .default :
296293 await set_default_gateway (
297294 session = session ,
298295 project = project ,
@@ -309,7 +306,9 @@ async def create_gateway(
309306 load_backend_type = True ,
310307 )
311308 assert gateway is not None
312- return gateway_model_to_gateway (gateway , default_gateway_id = default_gateway .id )
309+ return gateway_model_to_gateway (
310+ gateway , default_gateway_id = default_gateway .id if default_gateway is not None else None
311+ )
313312
314313
315314async def connect_to_gateway_with_retry (
@@ -430,7 +429,11 @@ async def set_gateway_wildcard_domain(
430429
431430
432431async def set_default_gateway (
433- session : AsyncSession , project : ProjectModel , ref : EntityReference , user : Optional [UserModel ]
432+ session : AsyncSession ,
433+ project : ProjectModel ,
434+ ref : EntityReference ,
435+ user : Optional [UserModel ],
436+ commit : bool = True ,
434437):
435438 gateway = await get_project_gateway_model_by_reference (
436439 session = session , project = project , ref = ref
@@ -470,7 +473,28 @@ async def set_default_gateway(
470473 events .Target .from_model (project ),
471474 ],
472475 )
473- await session .commit ()
476+ if commit :
477+ await session .commit ()
478+
479+
480+ async def unset_default_gateway (
481+ session : AsyncSession , project : ProjectModel , expect_gateway_id : uuid .UUID , user : UserModel
482+ ) -> None :
483+ gateway = await get_project_default_gateway_model (session , project )
484+ if gateway is None or gateway .id != expect_gateway_id :
485+ return
486+ await session .execute (
487+ update (ProjectModel ).where (ProjectModel .id == project .id ).values (default_gateway_id = None )
488+ )
489+ events .emit (
490+ session ,
491+ "Gateway unset as project default" ,
492+ actor = events .UserActor .from_user (user ),
493+ targets = [
494+ events .Target .from_model (gateway ),
495+ events .Target .from_model (project ),
496+ ],
497+ )
474498
475499
476500async def list_project_gateway_models (
@@ -849,7 +873,6 @@ def get_gateway_configuration(gateway_model: GatewayModel) -> GatewayConfigurati
849873 # Handle gateways created before GatewayConfiguration was introduced
850874 return GatewayConfiguration (
851875 name = gateway_model .name ,
852- default = False ,
853876 backend = gateway_model .backend .type ,
854877 region = gateway_model .region ,
855878 domain = gateway_model .wildcard_domain ,
@@ -979,7 +1002,10 @@ async def get_plan(
9791002 current_gateway_model , default_gateway_id = project .default_gateway_id
9801003 )
9811004 if _can_update_gateway_in_place (
982- diff_models (current_gateway .configuration , effective_spec .configuration )
1005+ diff_gateway_configurations (
1006+ current_gateway .configuration ,
1007+ effective_spec .configuration ,
1008+ )
9831009 ):
9841010 action = ApplyAction .UPDATE
9851011
@@ -1055,7 +1081,10 @@ async def apply_plan(
10551081 "Failed to apply plan. Resource has been changed. Try again or use force apply."
10561082 )
10571083
1058- diff = diff_models (current_configuration , new_configuration )
1084+ diff = diff_gateway_configurations (
1085+ current_configuration ,
1086+ new_configuration ,
1087+ )
10591088 if not _can_update_gateway_in_place (diff ):
10601089 raise ServerClientError (
10611090 f"Gateway { new_configuration .name !r} cannot be updated in-place."
@@ -1069,6 +1098,21 @@ async def apply_plan(
10691098 if new_configuration .replicas is not None
10701099 else GATEWAY_REPLICAS_DEFAULT
10711100 )
1101+ if new_configuration .default is True :
1102+ await set_default_gateway (
1103+ session = session ,
1104+ project = project ,
1105+ ref = EntityReference (name = gateway_model .name , project = None ),
1106+ user = user ,
1107+ commit = False ,
1108+ )
1109+ elif new_configuration .default is False :
1110+ await unset_default_gateway (
1111+ session = session ,
1112+ project = project ,
1113+ expect_gateway_id = gateway_model .id ,
1114+ user = user ,
1115+ )
10721116 gateway_model .configuration = new_configuration .model_dump_json ()
10731117 gateway_model .last_update_at = get_current_datetime ()
10741118 events .emit (
0 commit comments