Skip to content

Commit 58ee4cd

Browse files
GitHKAndrei Neagupcrespov
authored
🔨 Adding monitor to director-v2 (ITISFoundation#2411)
* codestyle * codestyle * changes required by monitor * required * refactor * added test for settings * added partial tests for models * added director-v2 requirements * codestyle models_library * added refactored services_settings_labels * added stripped down monitor code * added requirement (will come in via a different PR with tests) * added comment * fixed import * mocking out env vars * removed unused module * added tests to module * renamed module * fixed pylance suggestion * added tests for docker_api * fixign issue with starlette's TestClient mixed with async tests * added missing env vars * slots are not required * renamed module * marked asyncio tests at module level * refactor and clean shutdown * added allowed exported vars * removed missleading erro block * adds missing APIs * moved shared fixtures * added tests for monitor * added fixture for the port * codeclimate reducing module size * trying to fix failing test in CI * moved ServiceType to models * mocked out missing env vars * refactor shorter tag name * removed caplog tests, not viable in CI * Contribution to PR: adding dv2 monitor (#6) * added missing intra-repo deps in director2) * Minor annotation fixes * fixed testds typing * reverted changes * changed requirements * minor refactor * fix pylint deprected warning * migrated to settings_library * renamed to standard naming convention * @pcrespov refactored search * added missing types * fixed failing test * removed autouse in tests * fixing failing tests in CI * removed fixture which makes no sense * refactored PathMappingsLabels * refactored SimcoreServiceLabels * improved readability * refacted application setup for dynamic_sidecar * refactored some imports * using direct assignment * moved to constants * increse coverage * refactored and inheriting from common models * refactor to inherit from common model * extracted settings env var * fixing typos in docstrings * raising error insterad * removed defaults * added and enhanced docstrings * remove unused * minor refactoring * foxed test * fixing issues with factory method * renamed monitor to scheduler * added missing task states * updated link with reference * updated doc references * removed unused env-vars * added mising permissions * changed order of states * updated comment * added more tests * using proper naming * added tests to ensure expected ordering * renamed test files to reflect code structure * resyncs director-v2 drequirements with master branch (#7) * renamed test Co-authored-by: Andrei Neagu <[email protected]> Co-authored-by: Pedro Crespo-Valero <[email protected]>
1 parent dd74034 commit 58ee4cd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+3555
-159
lines changed

‎packages/models-library/src/models_library/projects_nodes_io.py‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222

2323
class PortLink(BaseModel):
24-
""" I/O port type to reference to an output port of another node in the same project """
24+
"""I/O port type to reference to an output port of another node in the same project"""
2525

2626
node_uuid: NodeID = Field(
2727
...,
@@ -41,7 +41,7 @@ class Config:
4141

4242

4343
class DownloadLink(BaseModel):
44-
""" I/O port type to hold a generic download link to a file (e.g. S3 pre-signed link, etc) """
44+
"""I/O port type to hold a generic download link to a file (e.g. S3 pre-signed link, etc)"""
4545

4646
download_link: AnyUrl = Field(..., alias="downloadLink")
4747
label: Optional[str]
@@ -51,7 +51,7 @@ class Config:
5151

5252

5353
class BaseFileLink(BaseModel):
54-
""" Base class for I/O port types with links to storage services"""
54+
"""Base class for I/O port types with links to storage services"""
5555

5656
# TODO: constructor will always cast to str here. We should perhaps set is as str. Actually
5757
# if we want to do hash in inputs/outputs ... we should have a single type for identifiers
@@ -86,7 +86,7 @@ class BaseFileLink(BaseModel):
8686

8787

8888
class SimCoreFileLink(BaseFileLink):
89-
""" I/O port type to hold a link to a file in simcore S3 storage """
89+
"""I/O port type to hold a link to a file in simcore S3 storage"""
9090

9191
dataset: Optional[str] = Field(
9292
None,
@@ -97,7 +97,7 @@ class SimCoreFileLink(BaseFileLink):
9797
@validator("store", always=True)
9898
@classmethod
9999
def check_discriminator(cls, v):
100-
""" Used as discriminator to cast to this class """
100+
"""Used as discriminator to cast to this class"""
101101
if v != "0":
102102
raise ValueError(f"SimCore store identifier must be set to 0, got {v}")
103103
return "0"
@@ -129,7 +129,7 @@ class Config:
129129

130130

131131
class DatCoreFileLink(BaseFileLink):
132-
""" I/O port type to hold a link to a file in DATCORE storage """
132+
"""I/O port type to hold a link to a file in DATCORE storage"""
133133

134134
label: str = Field(
135135
...,
@@ -146,7 +146,7 @@ class DatCoreFileLink(BaseFileLink):
146146
@validator("store", always=True)
147147
@classmethod
148148
def check_discriminator(cls, v):
149-
""" Used as discriminator to cast to this class """
149+
"""Used as discriminator to cast to this class"""
150150

151151
if v != "1":
152152
raise ValueError(f"DatCore store must be set to 1, got {v}")

‎packages/models-library/src/models_library/projects_nodes_ui.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from pydantic import BaseModel, Extra, Field
66

7+
78
class Position(BaseModel):
89
x: int = Field(..., description="The x position", example=["12"])
910
y: int = Field(..., description="The y position", example=["15"])

‎packages/models-library/src/models_library/service_settings.py‎

Lines changed: 0 additions & 69 deletions
This file was deleted.
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
# pylint: disable=unsubscriptable-object
2+
import json
3+
from functools import cached_property
4+
from pathlib import Path
5+
from typing import Any, Dict, List, Optional
6+
7+
from pydantic import BaseModel, Extra, Field, Json, PrivateAttr, validator
8+
9+
10+
class _BaseConfig:
11+
extra = Extra.forbid
12+
keep_untouched = (cached_property,)
13+
14+
15+
class SimcoreServiceSettingLabelEntry(BaseModel):
16+
_destination_container: str = PrivateAttr()
17+
name: str = Field(..., description="The name of the service setting")
18+
setting_type: str = Field(
19+
...,
20+
description="The type of the service setting (follows Docker REST API naming scheme)",
21+
alias="type",
22+
)
23+
value: Any = Field(
24+
...,
25+
description="The value of the service setting (shall follow Docker REST API scheme for services",
26+
)
27+
28+
class Config(_BaseConfig):
29+
schema_extra = {
30+
"examples": [
31+
# constraints
32+
{
33+
"name": "constraints",
34+
"type": "string",
35+
"value": ["node.platform.os == linux"],
36+
},
37+
# resources
38+
{
39+
"name": "Resources",
40+
"type": "Resources",
41+
"value": {
42+
"Limits": {"NanoCPUs": 4000000000, "MemoryBytes": 17179869184},
43+
"Reservations": {
44+
"NanoCPUs": 100000000,
45+
"MemoryBytes": 536870912,
46+
"GenericResources": [
47+
{"DiscreteResourceSpec": {"Kind": "VRAM", "Value": 1}}
48+
],
49+
},
50+
},
51+
},
52+
# mounts
53+
{
54+
"name": "mount",
55+
"type": "object",
56+
"value": [
57+
{
58+
"ReadOnly": True,
59+
"Source": "/tmp/.X11-unix", # nosec
60+
"Target": "/tmp/.X11-unix", # nosec
61+
"Type": "bind",
62+
}
63+
],
64+
},
65+
# environments
66+
{"name": "env", "type": "string", "value": ["DISPLAY=:0"]},
67+
]
68+
}
69+
70+
71+
class SimcoreServiceSettingsLabel(BaseModel):
72+
__root__: List[SimcoreServiceSettingLabelEntry]
73+
74+
def __iter__(self):
75+
return iter(self.__root__)
76+
77+
def __getitem__(self, item):
78+
return self.__root__[item]
79+
80+
def __len__(self):
81+
return len(self.__root__)
82+
83+
84+
class PathMappingsLabel(BaseModel):
85+
inputs_path: Path = Field(
86+
..., description="folder path where the service expects all the inputs"
87+
)
88+
outputs_path: Path = Field(
89+
...,
90+
description="folder path where the service is expected to provide all its outputs",
91+
)
92+
state_paths: List[Path] = Field(
93+
[],
94+
description="optional list of paths which contents need to be persisted",
95+
)
96+
97+
class Config(_BaseConfig):
98+
schema_extra = {
99+
"examples": {
100+
"outputs_path": "/tmp/outputs", # nosec
101+
"inputs_path": "/tmp/inputs", # nosec
102+
"state_paths": ["/tmp/save_1", "/tmp_save_2"], # nosec
103+
}
104+
}
105+
106+
107+
ComposeSpecLabel = Optional[Dict[str, Any]]
108+
109+
110+
class DynamicSidecarServiceLabels(BaseModel):
111+
paths_mapping: Optional[Json[PathMappingsLabel]] = Field(
112+
None,
113+
alias="simcore.service.paths-mapping",
114+
description=(
115+
"json encoded, determines how the folders are mapped in "
116+
"the service. Required by dynamic-sidecar."
117+
),
118+
)
119+
120+
compose_spec: Optional[Json[ComposeSpecLabel]] = Field(
121+
None,
122+
alias="simcore.service.compose-spec",
123+
description=(
124+
"json encoded docker-compose specifications. see "
125+
"https://docs.docker.com/compose/compose-file/, "
126+
"only used by dynamic-sidecar."
127+
),
128+
)
129+
container_http_entry: Optional[str] = Field(
130+
None,
131+
alias="simcore.service.container-http-entrypoint",
132+
description=(
133+
"When a docker-compose specifications is provided, "
134+
"the container where the traffic must flow has to be "
135+
"specified. Required by dynamic-sidecar when "
136+
"compose_spec is set."
137+
),
138+
)
139+
140+
@cached_property
141+
def needs_dynamic_sidecar(self) -> bool:
142+
"""if paths mapping is present the service needs to be ran via dynamic-sidecar"""
143+
return self.paths_mapping is not None
144+
145+
@validator("container_http_entry", always=True)
146+
@classmethod
147+
def compose_spec_requires_container_http_entry(cls, v, values):
148+
if v is None and values.get("compose_spec") is not None:
149+
raise ValueError(
150+
"Field `container_http_entry` must be defined but is missing"
151+
)
152+
if v is not None and values.get("compose_spec") is None:
153+
raise ValueError(
154+
"`container_http_entry` not allowed if `compose_spec` is missing"
155+
)
156+
return v
157+
158+
class Config(_BaseConfig):
159+
pass
160+
161+
162+
class SimcoreServiceLabels(DynamicSidecarServiceLabels):
163+
"""
164+
Validate all the simcores.services.* labels on a service.
165+
166+
When no other fields expect `settings` are present
167+
the service will be started as legacy by director-v0.
168+
169+
If `paths_mapping` is present the service will be started
170+
via dynamic-sidecar by director-v2.
171+
172+
When starting via dynamic-sidecar, if `compose_spec` is
173+
present, also `container_http_entry` must be present.
174+
When both of these fields are missing a docker-compose
175+
spec will be generated before starting the service.
176+
"""
177+
178+
settings: Json[SimcoreServiceSettingsLabel] = Field(
179+
...,
180+
alias="simcore.service.settings",
181+
description=(
182+
"Json encoded. Contains setting like environment variables and "
183+
"resource constraints which are required by the service. "
184+
"Should be compatible with Docker REST API."
185+
),
186+
)
187+
188+
class Config(_BaseConfig):
189+
schema_extra = {
190+
"examples": [
191+
# legacy service
192+
{
193+
"simcore.service.settings": json.dumps(
194+
SimcoreServiceSettingLabelEntry.Config.schema_extra["examples"]
195+
)
196+
},
197+
# dynamic-service
198+
{
199+
"simcore.service.settings": json.dumps(
200+
SimcoreServiceSettingLabelEntry.Config.schema_extra["examples"]
201+
),
202+
"simcore.service.paths-mapping": json.dumps(
203+
PathMappingsLabel.Config.schema_extra["examples"]
204+
),
205+
},
206+
# dynamic-service with compose spec
207+
{
208+
"simcore.service.settings": json.dumps(
209+
SimcoreServiceSettingLabelEntry.Config.schema_extra["examples"]
210+
),
211+
"simcore.service.paths-mapping": json.dumps(
212+
PathMappingsLabel.Config.schema_extra["examples"]
213+
),
214+
"simcore.service.compose-spec": json.dumps(
215+
{
216+
"version": "2.3",
217+
"services": {
218+
"rt-web": {
219+
"image": "${REGISTRY_URL}/simcore/services/dynamic/sim4life:${SERVICE_TAG}",
220+
"init": True,
221+
"depends_on": ["s4l-core"],
222+
},
223+
"s4l-core": {
224+
"image": "${REGISTRY_URL}/simcore/services/dynamic/s4l-core:${SERVICE_TAG}",
225+
"runtime": "nvidia",
226+
"init": True,
227+
"environment": ["DISPLAY=${DISPLAY}"],
228+
"volumes": [
229+
"/tmp/.X11-unix:/tmp/.X11-unix" # nosec
230+
],
231+
},
232+
},
233+
}
234+
),
235+
"simcore.service.container-http-entrypoint": "rt-web",
236+
},
237+
]
238+
}

0 commit comments

Comments
 (0)