1
+ import contextlib
1
2
import re
2
3
from typing import Any , Final
3
4
6
7
from models_library .projects import ProjectID
7
8
from models_library .projects_nodes import NodeID
8
9
from models_library .users import UserID
9
- from pydantic import BaseModel , ByteSize , ConstrainedStr , Field , root_validator
10
+ from pydantic import (
11
+ BaseModel ,
12
+ ByteSize ,
13
+ ConstrainedStr ,
14
+ Field ,
15
+ ValidationError ,
16
+ parse_obj_as ,
17
+ root_validator ,
18
+ )
10
19
11
20
from .basic_regex import DOCKER_GENERIC_TAG_KEY_RE , DOCKER_LABEL_KEY_REGEX
21
+ from .utils .fastapi_encoders import jsonable_encoder
12
22
13
23
14
24
class DockerLabelKey (ConstrainedStr ):
@@ -24,14 +34,18 @@ class DockerGenericTag(ConstrainedStr):
24
34
25
35
_SIMCORE_CONTAINER_PREFIX : Final [str ] = "io.simcore.container."
26
36
_BACKWARDS_COMPATIBILITY_MAP : Final [dict [str , str ]] = {
27
- "user_id" : f"{ _SIMCORE_CONTAINER_PREFIX } user-id" ,
28
- "study_id" : f"{ _SIMCORE_CONTAINER_PREFIX } project-id" ,
29
- "project_id" : f"{ _SIMCORE_CONTAINER_PREFIX } project-id" ,
30
- "uuid" : f"{ _SIMCORE_CONTAINER_PREFIX } node-id" ,
31
37
"node_id" : f"{ _SIMCORE_CONTAINER_PREFIX } node-id" ,
32
38
"product_name" : f"{ _SIMCORE_CONTAINER_PREFIX } product-name" ,
39
+ "project_id" : f"{ _SIMCORE_CONTAINER_PREFIX } project-id" ,
33
40
"simcore_user_agent" : f"{ _SIMCORE_CONTAINER_PREFIX } simcore-user-agent" ,
41
+ "study_id" : f"{ _SIMCORE_CONTAINER_PREFIX } project-id" ,
42
+ "user_id" : f"{ _SIMCORE_CONTAINER_PREFIX } user-id" ,
43
+ "uuid" : f"{ _SIMCORE_CONTAINER_PREFIX } node-id" ,
44
+ "mem_limit" : f"{ _SIMCORE_CONTAINER_PREFIX } memory-limit" ,
45
+ "swarm_stack_name" : f"{ _SIMCORE_CONTAINER_PREFIX } swarm-stack-name" ,
34
46
}
47
+ _UNDEFINED_VALUE_STR : Final [str ] = "undefined"
48
+ _UNDEFINED_VALUE_INT : Final [str ] = "0"
35
49
36
50
37
51
class SimcoreServiceDockerLabelKeys (BaseModel ):
@@ -46,36 +60,52 @@ class SimcoreServiceDockerLabelKeys(BaseModel):
46
60
..., alias = f"{ _SIMCORE_CONTAINER_PREFIX } simcore-user-agent"
47
61
)
48
62
49
- # None is for backwards compatibility, can be removed in a few sprints
50
- memory_limit : ByteSize | None = Field (
63
+ swarm_stack_name : str = Field (
64
+ ..., alias = f"{ _SIMCORE_CONTAINER_PREFIX } swarm-stack-name"
65
+ )
66
+
67
+ memory_limit : ByteSize = Field (
51
68
..., alias = f"{ _SIMCORE_CONTAINER_PREFIX } memory-limit"
52
69
)
53
- cpu_limit : float | None = Field (..., alias = f"{ _SIMCORE_CONTAINER_PREFIX } cpu-limit" )
70
+ cpu_limit : float = Field (..., alias = f"{ _SIMCORE_CONTAINER_PREFIX } cpu-limit" )
54
71
55
72
@root_validator (pre = True )
56
73
def _backwards_compatibility (cls , values : dict [str , Any ]) -> dict [str , Any ]:
57
- # NOTE: this is necessary for deployment and legacy service
74
+ # NOTE: this is necessary for dy-sidecar and legacy service until they are adjusted
58
75
if mapped_values := {
59
76
_BACKWARDS_COMPATIBILITY_MAP [k ]: v
60
77
for k , v in values .items ()
61
78
if k in _BACKWARDS_COMPATIBILITY_MAP
62
79
}:
80
+ # these values were sometimes omitted, so let's provide some defaults
81
+ for key in ["product-name" , "simcore-user-agent" , "swarm-stack-name" ]:
82
+ mapped_values .setdefault (
83
+ f"{ _SIMCORE_CONTAINER_PREFIX } { key } " , _UNDEFINED_VALUE_STR
84
+ )
85
+
63
86
mapped_values .setdefault (
64
- f"{ _SIMCORE_CONTAINER_PREFIX } product-name " , "osparc"
87
+ f"{ _SIMCORE_CONTAINER_PREFIX } memory-limit " , _UNDEFINED_VALUE_INT
65
88
)
89
+
90
+ def _convert_nano_cpus_to_cpus (nano_cpu : str ) -> str :
91
+ with contextlib .suppress (ValidationError ):
92
+ return f"{ parse_obj_as (float , nano_cpu ) / (1.0 * 10 ** 9 ):.2f} "
93
+ return _UNDEFINED_VALUE_INT
94
+
66
95
mapped_values .setdefault (
67
- f"{ _SIMCORE_CONTAINER_PREFIX } simcore-user-agent" , "undefined"
96
+ f"{ _SIMCORE_CONTAINER_PREFIX } cpu-limit" ,
97
+ _convert_nano_cpus_to_cpus (
98
+ values .get ("nano_cpus_limit" , _UNDEFINED_VALUE_INT )
99
+ ),
68
100
)
69
- mapped_values .setdefault (f"{ _SIMCORE_CONTAINER_PREFIX } memory-limit" , "0" )
70
- mapped_values .setdefault (f"{ _SIMCORE_CONTAINER_PREFIX } cpu-limit" , "0" )
71
101
return mapped_values
72
102
return values
73
103
74
104
def to_docker_labels (self ) -> dict [DockerLabelKey , str ]:
75
105
"""returns a dictionary of strings as required by docker"""
76
- std_export = self . dict ( by_alias = True )
106
+ std_export = jsonable_encoder ( self , by_alias = True )
77
107
return {
78
- DockerLabelKey (f"{ _SIMCORE_CONTAINER_PREFIX } { k .replace ('_' , '-' )} " ): f"{ v } "
108
+ DockerLabelKey (f"{ k .replace ('_' , '-' )} " ): f"{ v } "
79
109
for k , v in sorted (std_export .items ())
80
110
}
81
111
@@ -90,23 +120,80 @@ class Config:
90
120
allow_population_by_field_name = True
91
121
schema_extra = {
92
122
"examples" : [
93
- # legacy with no limits (a.k.a all dynamic services)
123
+ # legacy service labels
94
124
{
125
+ "port" : "8080" , # TODO: go away
126
+ "study_id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
127
+ "swarm_stack_name" : "devel-simcore" ,
128
+ "type" : "main" , # TODO: go away
129
+ "user_id" : "5" ,
95
130
"uuid" : "1f963626-66e1-43f1-a777-33955c08b909" ,
131
+ },
132
+ # legacy container labels
133
+ {
134
+ "mem_limit" : "1073741824" ,
135
+ "nano_cpus_limit" : "4000000000" ,
136
+ "node_id" : "1f963626-66e1-43f1-a777-33955c08b909" ,
137
+ "simcore_user_agent" : "puppeteer" ,
96
138
"study_id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
139
+ "swarm_stack_name" : "devel-simcore" ,
97
140
"user_id" : "5" ,
141
+ },
142
+ # dy-sidecar service labels
143
+ {
144
+ "key" : "simcore/services/dynamic/jupyter-math" , # TODO: go away
145
+ "port" : "8888" , # TODO: go away
146
+ "service_image" : "itisfoundation/dynamic-sidecar:master-github-latest" , # TODO: go away
147
+ "service_port" : "8888" , # TODO: go away
148
+ "study_id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
149
+ "swarm_stack_name" : "devel-simcore" ,
150
+ "type" : "main-v2" , # TODO: go away
151
+ "user_id" : "5" ,
152
+ "uuid" : "1f963626-66e1-43f1-a777-33955c08b909" ,
153
+ "version" : "2.0.9" , # TODO: go away
154
+ # "simcore_user_agent": "???????misssing??"
155
+ },
156
+ # dy-sidecar container labels
157
+ {
158
+ "mem_limit" : "1073741824" ,
159
+ "nano_cpus_limit" : "4000000000" ,
160
+ "study_id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
161
+ "user_id" : "5" ,
162
+ "uuid" : "1f963626-66e1-43f1-a777-33955c08b909" ,
163
+ },
164
+ # dy-proxy service labels
165
+ {
166
+ "dynamic-type" : "dynamic-sidecar" ,
167
+ "study_id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
168
+ "swarm_stack_name" : "devel-simcore" ,
169
+ "type" : "dependency-v2" ,
170
+ "user_id" : "5" ,
171
+ "uuid" : "1f963626-66e1-43f1-a777-33955c08b909" ,
172
+ },
173
+ # dy-proxy container labels
174
+ {
175
+ "study_id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
176
+ "user_id" : "5" ,
177
+ "uuid" : "1f963626-66e1-43f1-a777-33955c08b909" ,
178
+ },
179
+ # dy-sidecar user-services labels
180
+ {
98
181
"product_name" : "osparc" ,
99
182
"simcore_user_agent" : "puppeteer" ,
183
+ "study_id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
184
+ "user_id" : "5" ,
185
+ "uuid" : "1f963626-66e1-43f1-a777-33955c08b909" ,
100
186
},
101
187
# modern both dynamic-sidecar services and computational services
102
188
{
189
+ "io.simcore.container.cpu-limit" : "2.4" ,
190
+ "io.simcore.container.memory-limit" : "1073741824" ,
103
191
"io.simcore.container.node-id" : "1f963626-66e1-43f1-a777-33955c08b909" ,
104
- "io.simcore.container.project-id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
105
- "io.simcore.container.user-id" : "5" ,
106
192
"io.simcore.container.product-name" : "osparc" ,
193
+ "io.simcore.container.project-id" : "29f393fc-1410-47b3-b4b9-61dfce21a2a6" ,
107
194
"io.simcore.container.simcore-user-agent" : "puppeteer" ,
108
- "io.simcore.container.memory-limit " : "1073741824 " ,
109
- "io.simcore.container.cpu-limit " : "2.4 " ,
195
+ "io.simcore.container.swarm-stack-name " : "devel-osparc " ,
196
+ "io.simcore.container.user-id " : "5 " ,
110
197
},
111
198
]
112
199
}
0 commit comments