44from typing import Any , Mapping , Optional
55from uuid import UUID , uuid4
66
7+ from models_library .basic_types import PortInt
78from models_library .projects_nodes_io import NodeID
89from models_library .service_settings_labels import (
910 DynamicSidecarServiceLabels ,
1213)
1314from models_library .services import RunID
1415from models_library .services_resources import ServiceResourcesDict
15- from pydantic import (
16- AnyHttpUrl ,
17- BaseModel ,
18- Extra ,
19- Field ,
20- PositiveInt ,
21- constr ,
22- parse_obj_as ,
23- )
16+ from pydantic import AnyHttpUrl , BaseModel , Extra , Field , constr , parse_obj_as
2417from servicelib .error_codes import ErrorCodeStr
2518
2619from ..constants import (
3932DockerId = constr (max_length = 25 , regex = r"[A-Za-z0-9]{25}" )
4033ServiceId = DockerId
4134NetworkId = DockerId
35+ ServiceName = constr (strip_whitespace = True , min_length = 2 )
4236
4337logger = logging .getLogger ()
4438
@@ -143,25 +137,11 @@ def mark_removed(self) -> None:
143137
144138
145139class DynamicSidecar (BaseModel ):
146- run_id : RunID = Field (
147- default_factory = uuid4 ,
148- description = (
149- "Used to discriminate between dynamic-sidecar docker resources "
150- "generated during different runs. Sometimes artifacts remain in the"
151- "system after an error. This helps avoiding collisions."
152- "For now used by anonymous volumes involved in data sharing"
153- ),
154- )
155-
156140 status : Status = Field (
157141 Status .create_as_initially_ok (),
158142 description = "status of the service sidecar also with additional information" ,
159143 )
160144
161- hostname : str = Field (..., description = "docker hostname for this service" )
162-
163- port : PositiveInt = Field (8000 , description = "dynamic-sidecar port" )
164-
165145 is_available : bool = Field (
166146 False ,
167147 scription = (
@@ -224,6 +204,18 @@ def compose_spec_submitted(self) -> bool:
224204 ),
225205 )
226206
207+ wait_for_manual_intervention_after_error : bool = Field (
208+ False ,
209+ description = (
210+ "Marks the sidecar as untouchable since there was an error and "
211+ "important data might be lost. awaits for manual intervention."
212+ ),
213+ )
214+ were_state_and_outputs_saved : bool = Field (
215+ False ,
216+ description = "set True if the dy-sidecar saves the state and uploads the outputs" ,
217+ )
218+
227219 # below had already been validated and
228220 # used only to start the proxy
229221 dynamic_sidecar_id : Optional [ServiceId ] = Field (
@@ -239,34 +231,19 @@ def compose_spec_submitted(self) -> bool:
239231 None , description = "used for starting the proxy"
240232 )
241233
242- @property
243- def can_save_state (self ) -> bool :
244- """
245- Keeps track of the current state of the application, if it was starte successfully
246- the state of the service can be saved when stopping the service
247- """
248- # TODO: implement when adding save status hooks
249- return False
250-
251- # consider adding containers for healthchecks but this is more difficult and it depends on each service
252-
253- @property
254- def endpoint (self ) -> AnyHttpUrl :
255- """endpoint where all the services are exposed"""
256- return parse_obj_as (
257- AnyHttpUrl , f"http://{ self .hostname } :{ self .port } " # NOSONAR
258- )
234+ docker_node_id : Optional [str ] = Field (
235+ None ,
236+ description = (
237+ "contains node id of the docker node where all services "
238+ "and created containers are started"
239+ ),
240+ )
259241
260- @property
261- def are_containers_ready (self ) -> bool :
262- """returns: True if all containers are in running state"""
263- return all (
264- docker_container_inspect .status == DockerStatus .RUNNING
265- for docker_container_inspect in self .containers_inspect
266- )
242+ class Config :
243+ validate_assignment = True
267244
268245
269- class DynamicSidecarNames (BaseModel ):
246+ class DynamicSidecarNamesHelper (BaseModel ):
270247 """
271248 Service naming schema:
272249 NOTE: name is max 63 characters
@@ -304,7 +281,7 @@ class DynamicSidecarNames(BaseModel):
304281 )
305282
306283 @classmethod
307- def make (cls , node_uuid : UUID ) -> "DynamicSidecarNames " :
284+ def make (cls , node_uuid : UUID ) -> "DynamicSidecarNamesHelper " :
308285 return cls (
309286 service_name_dynamic_sidecar = assemble_service_name (
310287 DYNAMIC_SIDECAR_SERVICE_PREFIX , node_uuid
@@ -318,10 +295,28 @@ def make(cls, node_uuid: UUID) -> "DynamicSidecarNames":
318295
319296
320297class SchedulerData (CommonServiceDetails , DynamicSidecarServiceLabels ):
321- service_name : constr ( strip_whitespace = True , min_length = 2 ) = Field (
298+ service_name : ServiceName = Field (
322299 ...,
323300 description = "Name of the current dynamic-sidecar being observed" ,
324301 )
302+ run_id : RunID = Field (
303+ default_factory = uuid4 ,
304+ description = (
305+ "Uniquely identify the dynamic sidecar session (a.k.a. 2 "
306+ "subsequent exact same services will have a different run_id)"
307+ ),
308+ )
309+ hostname : str = Field (
310+ ..., description = "dy-sidecar's service hostname (provided by docker-swarm)"
311+ )
312+ port : PortInt = Field (8000 , description = "dynamic-sidecar port" )
313+
314+ @property
315+ def endpoint (self ) -> AnyHttpUrl :
316+ """endpoint where all the services are exposed"""
317+ return parse_obj_as (
318+ AnyHttpUrl , f"http://{ self .hostname } :{ self .port } " # NOSONAR
319+ )
325320
326321 dynamic_sidecar : DynamicSidecar = Field (
327322 ...,
@@ -340,7 +335,7 @@ class SchedulerData(CommonServiceDetails, DynamicSidecarServiceLabels):
340335 description = "required for Traefik to correctly route requests to the spawned container" ,
341336 )
342337
343- service_port : PositiveInt = Field (
338+ service_port : PortInt = Field (
344339 TEMPORARY_PORT_NUMBER ,
345340 description = (
346341 "port where the service is exposed defined by the service; "
@@ -353,37 +348,31 @@ class SchedulerData(CommonServiceDetails, DynamicSidecarServiceLabels):
353348 ..., description = "service resources used to enforce limits"
354349 )
355350
356- request_dns : Optional [str ] = Field (
357- None , description = "used when configuring the CORS options on the proxy"
358- )
359- request_scheme : Optional [str ] = Field (
360- None , description = "used when configuring the CORS options on the proxy"
361- )
362- proxy_service_name : Optional [str ] = Field (
363- None , description = "service name given to the proxy"
351+ request_dns : str = Field (
352+ ..., description = "used when configuring the CORS options on the proxy"
364353 )
365- docker_node_id : Optional [str ] = Field (
366- None ,
367- description = (
368- "contains node id of the docker node where all services "
369- "and created containers are started"
370- ),
354+ request_scheme : str = Field (
355+ ..., description = "used when configuring the CORS options on the proxy"
371356 )
357+ proxy_service_name : str = Field (None , description = "service name given to the proxy" )
372358
373359 @classmethod
374360 def from_http_request (
375361 # pylint: disable=too-many-arguments
376362 cls ,
377363 service : "DynamicServiceCreate" ,
378364 simcore_service_labels : SimcoreServiceLabels ,
379- port : Optional [int ],
380- request_dns : Optional [str ] = None ,
381- request_scheme : Optional [str ] = None ,
365+ port : PortInt ,
366+ request_dns : str ,
367+ request_scheme : str ,
368+ run_id : Optional [UUID ] = None ,
382369 ) -> "SchedulerData" :
383- dynamic_sidecar_names = DynamicSidecarNames .make (service .node_uuid )
370+ names_helper = DynamicSidecarNamesHelper .make (service .node_uuid )
384371
385372 obj_dict = dict (
386- service_name = dynamic_sidecar_names .service_name_dynamic_sidecar ,
373+ service_name = names_helper .service_name_dynamic_sidecar ,
374+ hostname = names_helper .service_name_dynamic_sidecar ,
375+ port = port ,
387376 node_uuid = service .node_uuid ,
388377 project_id = service .project_id ,
389378 user_id = service .user_id ,
@@ -394,16 +383,15 @@ def from_http_request(
394383 compose_spec = json .dumps (simcore_service_labels .compose_spec ),
395384 container_http_entry = simcore_service_labels .container_http_entry ,
396385 restart_policy = simcore_service_labels .restart_policy ,
397- dynamic_sidecar_network_name = dynamic_sidecar_names .dynamic_sidecar_network_name ,
398- simcore_traefik_zone = dynamic_sidecar_names .simcore_traefik_zone ,
386+ dynamic_sidecar_network_name = names_helper .dynamic_sidecar_network_name ,
387+ simcore_traefik_zone = names_helper .simcore_traefik_zone ,
399388 request_dns = request_dns ,
400389 request_scheme = request_scheme ,
401- proxy_service_name = dynamic_sidecar_names .proxy_service_name ,
402- dynamic_sidecar = dict (
403- hostname = dynamic_sidecar_names .service_name_dynamic_sidecar ,
404- port = port ,
405- ),
390+ proxy_service_name = names_helper .proxy_service_name ,
391+ dynamic_sidecar = {},
406392 )
393+ if run_id :
394+ obj_dict ["run_id" ] = run_id
407395 return cls .parse_obj (obj_dict )
408396
409397 @classmethod
0 commit comments