| 
1 |  | -import contextlib  | 
2 | 1 | import re  | 
3 |  | -from typing import Annotated, Any, Final, TypeAlias  | 
 | 2 | +from typing import Annotated, TypeAlias  | 
4 | 3 | 
 
  | 
5 | 4 | from pydantic import (  | 
6 |  | -    BaseModel,  | 
7 |  | -    ByteSize,  | 
8 |  | -    ConfigDict,  | 
9 |  | -    Field,  | 
10 | 5 |     StringConstraints,  | 
11 |  | -    TypeAdapter,  | 
12 |  | -    ValidationError,  | 
13 |  | -    model_validator,  | 
14 | 6 | )  | 
15 | 7 | 
 
  | 
16 | 8 | from .basic_regex import DOCKER_GENERIC_TAG_KEY_RE, DOCKER_LABEL_KEY_REGEX  | 
17 | 9 | from .basic_types import ConstrainedStr  | 
18 |  | -from .generated_models.docker_rest_api import Task  | 
19 |  | -from .products import ProductName  | 
20 |  | -from .projects import ProjectID  | 
21 |  | -from .projects_nodes_io import NodeID  | 
22 |  | -from .users import UserID  | 
23 | 10 | 
 
  | 
24 | 11 | 
 
  | 
25 | 12 | class DockerLabelKey(ConstrainedStr):  | 
@@ -47,192 +34,6 @@ def from_key(cls, key: str) -> "DockerLabelKey":  | 
47 | 34 |     ),  | 
48 | 35 | ]  | 
49 | 36 | 
 
  | 
50 |  | -_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX: Final[str] = "io.simcore.runtime."  | 
51 |  | -_BACKWARDS_COMPATIBILITY_SIMCORE_RUNTIME_DOCKER_LABELS_MAP: Final[dict[str, str]] = {  | 
52 |  | -    "node_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}node-id",  | 
53 |  | -    "product_name": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}product-name",  | 
54 |  | -    "project_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}project-id",  | 
55 |  | -    "simcore_user_agent": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}simcore-user-agent",  | 
56 |  | -    "study_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}project-id",  | 
57 |  | -    "user_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}user-id",  | 
58 |  | -    "uuid": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}node-id",  | 
59 |  | -    "mem_limit": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}memory-limit",  | 
60 |  | -    "swarm_stack_name": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}swarm-stack-name",  | 
61 |  | -}  | 
62 |  | -_UNDEFINED_LABEL_VALUE_STR: Final[str] = "undefined"  | 
63 |  | -_UNDEFINED_LABEL_VALUE_INT: Final[str] = "0"  | 
64 |  | - | 
65 |  | - | 
66 |  | -DOCKER_TASK_EC2_INSTANCE_TYPE_PLACEMENT_CONSTRAINT_KEY: Final[DockerLabelKey] = (  | 
67 |  | -    TypeAdapter(DockerLabelKey).validate_python("ec2-instance-type")  | 
68 |  | -)  | 
69 |  | - | 
70 |  | - | 
71 |  | -def to_simcore_runtime_docker_label_key(key: str) -> DockerLabelKey:  | 
72 |  | -    return DockerLabelKey(  | 
73 |  | -        f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}{key.replace('_', '-').lower()}"  | 
74 |  | -    )  | 
75 |  | - | 
76 |  | - | 
77 |  | -class StandardSimcoreDockerLabels(BaseModel):  | 
78 |  | -    """  | 
79 |  | -    Represents the standard label on oSparc created containers (not yet services)  | 
80 |  | -    In order to create this object in code, please use model_construct() method!  | 
81 |  | -    """  | 
82 |  | - | 
83 |  | -    user_id: UserID = Field(..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}user-id")  # type: ignore[literal-required]  | 
84 |  | -    project_id: ProjectID = Field(  # type: ignore[literal-required]  | 
85 |  | -        ..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}project-id"  | 
86 |  | -    )  | 
87 |  | -    node_id: NodeID = Field(..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}node-id")  # type: ignore[literal-required]  | 
88 |  | - | 
89 |  | -    product_name: ProductName = Field(  # type: ignore[literal-required]  | 
90 |  | -        ..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}product-name"  | 
91 |  | -    )  | 
92 |  | -    simcore_user_agent: str = Field(  # type: ignore[literal-required]  | 
93 |  | -        ..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}simcore-user-agent"  | 
94 |  | -    )  | 
95 |  | - | 
96 |  | -    swarm_stack_name: str = Field(  # type: ignore[literal-required]  | 
97 |  | -        ..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}swarm-stack-name"  | 
98 |  | -    )  | 
99 |  | - | 
100 |  | -    memory_limit: ByteSize = Field(  # type: ignore[literal-required]  | 
101 |  | -        ..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}memory-limit"  | 
102 |  | -    )  | 
103 |  | -    cpu_limit: float = Field(  # type: ignore[literal-required]  | 
104 |  | -        ..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}cpu-limit"  | 
105 |  | -    )  | 
106 |  | - | 
107 |  | -    @model_validator(mode="before")  | 
108 |  | -    @classmethod  | 
109 |  | -    def _backwards_compatibility(cls, values: dict[str, Any]) -> dict[str, Any]:  | 
110 |  | -        # NOTE: this is necessary for dy-sidecar and legacy service until they are adjusted  | 
111 |  | -        if mapped_values := {  | 
112 |  | -            _BACKWARDS_COMPATIBILITY_SIMCORE_RUNTIME_DOCKER_LABELS_MAP[k]: v  | 
113 |  | -            for k, v in values.items()  | 
114 |  | -            if k in _BACKWARDS_COMPATIBILITY_SIMCORE_RUNTIME_DOCKER_LABELS_MAP  | 
115 |  | -        }:  | 
116 |  | -            # these values were sometimes omitted, so let's provide some defaults  | 
117 |  | -            for key in ["product-name", "simcore-user-agent", "swarm-stack-name"]:  | 
118 |  | -                mapped_values.setdefault(  | 
119 |  | -                    f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}{key}",  | 
120 |  | -                    _UNDEFINED_LABEL_VALUE_STR,  | 
121 |  | -                )  | 
122 |  | - | 
123 |  | -            mapped_values.setdefault(  | 
124 |  | -                f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}memory-limit",  | 
125 |  | -                values.get("memory_limit", _UNDEFINED_LABEL_VALUE_INT),  | 
126 |  | -            )  | 
127 |  | - | 
128 |  | -            def _convert_nano_cpus_to_cpus(nano_cpu: str) -> str:  | 
129 |  | -                with contextlib.suppress(ValidationError):  | 
130 |  | -                    return f"{TypeAdapter(float).validate_python(nano_cpu) / (1.0 * 10**9):.2f}"  | 
131 |  | -                return _UNDEFINED_LABEL_VALUE_INT  | 
132 |  | - | 
133 |  | -            mapped_values.setdefault(  | 
134 |  | -                f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}cpu-limit",  | 
135 |  | -                values.get(  | 
136 |  | -                    "cpu_limit",  | 
137 |  | -                    _convert_nano_cpus_to_cpus(  | 
138 |  | -                        values.get(  | 
139 |  | -                            "nano_cpus_limit",  | 
140 |  | -                            _UNDEFINED_LABEL_VALUE_INT,  | 
141 |  | -                        )  | 
142 |  | -                    ),  | 
143 |  | -                ),  | 
144 |  | -            )  | 
145 |  | -            return mapped_values  | 
146 |  | -        return values  | 
147 |  | - | 
148 |  | -    def to_simcore_runtime_docker_labels(self) -> dict[DockerLabelKey, str]:  | 
149 |  | -        """returns a dictionary of strings as required by docker"""  | 
150 |  | -        return {  | 
151 |  | -            to_simcore_runtime_docker_label_key(k): f"{v}"  | 
152 |  | -            for k, v in sorted(self.model_dump().items())  | 
153 |  | -        }  | 
154 |  | - | 
155 |  | -    @classmethod  | 
156 |  | -    def from_docker_task(cls, docker_task: Task) -> "StandardSimcoreDockerLabels":  | 
157 |  | -        assert docker_task.spec  # nosec  | 
158 |  | -        assert docker_task.spec.container_spec  # nosec  | 
159 |  | -        task_labels = docker_task.spec.container_spec.labels or {}  | 
160 |  | -        return cls.model_validate(task_labels)  | 
161 |  | - | 
162 |  | -    model_config = ConfigDict(  | 
163 |  | -        populate_by_name=True,  | 
164 |  | -        json_schema_extra={  | 
165 |  | -            "examples": [  | 
166 |  | -                # legacy service labels  | 
167 |  | -                {  | 
168 |  | -                    "study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
169 |  | -                    "swarm_stack_name": "devel-simcore",  | 
170 |  | -                    "user_id": "5",  | 
171 |  | -                    "uuid": "1f963626-66e1-43f1-a777-33955c08b909",  | 
172 |  | -                },  | 
173 |  | -                # legacy container labels  | 
174 |  | -                {  | 
175 |  | -                    "mem_limit": "1073741824",  | 
176 |  | -                    "nano_cpus_limit": "4000000000",  | 
177 |  | -                    "node_id": "1f963626-66e1-43f1-a777-33955c08b909",  | 
178 |  | -                    "simcore_user_agent": "puppeteer",  | 
179 |  | -                    "study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
180 |  | -                    "swarm_stack_name": "devel-simcore",  | 
181 |  | -                    "user_id": "5",  | 
182 |  | -                },  | 
183 |  | -                # dy-sidecar service labels  | 
184 |  | -                {  | 
185 |  | -                    "study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
186 |  | -                    "swarm_stack_name": "devel-simcore",  | 
187 |  | -                    "user_id": "5",  | 
188 |  | -                    "uuid": "1f963626-66e1-43f1-a777-33955c08b909",  | 
189 |  | -                },  | 
190 |  | -                # dy-sidecar container labels  | 
191 |  | -                {  | 
192 |  | -                    "mem_limit": "1073741824",  | 
193 |  | -                    "nano_cpus_limit": "4000000000",  | 
194 |  | -                    "study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
195 |  | -                    "user_id": "5",  | 
196 |  | -                    "uuid": "1f963626-66e1-43f1-a777-33955c08b909",  | 
197 |  | -                },  | 
198 |  | -                # dy-proxy service labels  | 
199 |  | -                {  | 
200 |  | -                    "dynamic-type": "dynamic-sidecar",  | 
201 |  | -                    "study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
202 |  | -                    "swarm_stack_name": "devel-simcore",  | 
203 |  | -                    "type": "dependency-v2",  | 
204 |  | -                    "user_id": "5",  | 
205 |  | -                    "uuid": "1f963626-66e1-43f1-a777-33955c08b909",  | 
206 |  | -                },  | 
207 |  | -                # dy-proxy container labels  | 
208 |  | -                {  | 
209 |  | -                    "study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
210 |  | -                    "user_id": "5",  | 
211 |  | -                    "uuid": "1f963626-66e1-43f1-a777-33955c08b909",  | 
212 |  | -                },  | 
213 |  | -                # dy-sidecar user-services labels  | 
214 |  | -                {  | 
215 |  | -                    "product_name": "osparc",  | 
216 |  | -                    "simcore_user_agent": "puppeteer",  | 
217 |  | -                    "study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
218 |  | -                    "user_id": "5",  | 
219 |  | -                    "uuid": "1f963626-66e1-43f1-a777-33955c08b909",  | 
220 |  | -                },  | 
221 |  | -                # modern both dynamic-sidecar services and computational services  | 
222 |  | -                {  | 
223 |  | -                    "io.simcore.runtime.cpu-limit": "2.4",  | 
224 |  | -                    "io.simcore.runtime.memory-limit": "1073741824",  | 
225 |  | -                    "io.simcore.runtime.node-id": "1f963626-66e1-43f1-a777-33955c08b909",  | 
226 |  | -                    "io.simcore.runtime.product-name": "osparc",  | 
227 |  | -                    "io.simcore.runtime.project-id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",  | 
228 |  | -                    "io.simcore.runtime.simcore-user-agent": "puppeteer",  | 
229 |  | -                    "io.simcore.runtime.swarm-stack-name": "devel-osparc",  | 
230 |  | -                    "io.simcore.runtime.user-id": "5",  | 
231 |  | -                },  | 
232 |  | -            ]  | 
233 |  | -        },  | 
234 |  | -    )  | 
235 |  | - | 
236 | 37 | 
 
  | 
237 | 38 | DockerNodeID: TypeAlias = Annotated[  | 
238 | 39 |     str, StringConstraints(strip_whitespace=True, pattern=re.compile(r"[a-zA-Z0-9]"))  | 
 | 
0 commit comments