Skip to content

Commit 5f8fe65

Browse files
authored
✨ Computational backend: set container limits as labels ⚠️ (devops checks on grafana dashboards!) (#4453)
1 parent bef80bb commit 5f8fe65

File tree

25 files changed

+763
-475
lines changed

25 files changed

+763
-475
lines changed
Lines changed: 171 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1+
import contextlib
12
import re
3+
from typing import Any, ClassVar, Final
24

35
from models_library.generated_models.docker_rest_api import Task
46
from models_library.products import ProductName
57
from models_library.projects import ProjectID
68
from models_library.projects_nodes import NodeID
79
from models_library.users import UserID
8-
from pydantic import BaseModel, ConstrainedStr, Field
10+
from pydantic import (
11+
BaseModel,
12+
ByteSize,
13+
ConstrainedStr,
14+
Field,
15+
ValidationError,
16+
parse_obj_as,
17+
root_validator,
18+
)
919

1020
from .basic_regex import DOCKER_GENERIC_TAG_KEY_RE, DOCKER_LABEL_KEY_REGEX
1121

@@ -21,29 +31,176 @@ class DockerGenericTag(ConstrainedStr):
2131
regex: re.Pattern[str] | None = DOCKER_GENERIC_TAG_KEY_RE
2232

2333

24-
class SimcoreServiceDockerLabelKeys(BaseModel):
25-
# NOTE: in a next PR, this should be moved to packages models-library and used
26-
# all over, and aliases should use io.simcore.service.*
27-
# https://github.com/ITISFoundation/osparc-simcore/issues/3638
34+
_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX: Final[str] = "io.simcore.runtime."
35+
_BACKWARDS_COMPATIBILITY_SIMCORE_RUNTIME_DOCKER_LABELS_MAP: Final[dict[str, str]] = {
36+
"node_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}node-id",
37+
"product_name": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}product-name",
38+
"project_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}project-id",
39+
"simcore_user_agent": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}simcore-user-agent",
40+
"study_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}project-id",
41+
"user_id": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}user-id",
42+
"uuid": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}node-id",
43+
"mem_limit": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}memory-limit",
44+
"swarm_stack_name": f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}swarm-stack-name",
45+
}
46+
_UNDEFINED_LABEL_VALUE_STR: Final[str] = "undefined"
47+
_UNDEFINED_LABEL_VALUE_INT: Final[str] = "0"
2848

29-
user_id: UserID = Field(..., alias="user_id")
30-
project_id: ProjectID = Field(..., alias="study_id")
31-
node_id: NodeID = Field(..., alias="uuid")
3249

33-
product_name: ProductName = "opsarc"
34-
simcore_user_agent: str = ""
50+
def to_simcore_runtime_docker_label_key(key: str) -> DockerLabelKey:
51+
return DockerLabelKey(
52+
f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}{key.replace('_', '-').lower()}"
53+
)
3554

36-
def to_docker_labels(self) -> dict[str, str]:
55+
56+
class StandardSimcoreDockerLabels(BaseModel):
57+
"""
58+
Represents the standard label on oSparc created containers (not yet services)
59+
In order to create this object in code, please use construct() method!
60+
"""
61+
62+
user_id: UserID = Field(..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}user-id")
63+
project_id: ProjectID = Field(
64+
..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}project-id"
65+
)
66+
node_id: NodeID = Field(..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}node-id")
67+
68+
product_name: ProductName = Field(
69+
..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}product-name"
70+
)
71+
simcore_user_agent: str = Field(
72+
..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}simcore-user-agent"
73+
)
74+
75+
swarm_stack_name: str = Field(
76+
..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}swarm-stack-name"
77+
)
78+
79+
memory_limit: ByteSize = Field(
80+
..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}memory-limit"
81+
)
82+
cpu_limit: float = Field(
83+
..., alias=f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}cpu-limit"
84+
)
85+
86+
@root_validator(pre=True)
87+
@classmethod
88+
def _backwards_compatibility(cls, values: dict[str, Any]) -> dict[str, Any]:
89+
# NOTE: this is necessary for dy-sidecar and legacy service until they are adjusted
90+
if mapped_values := {
91+
_BACKWARDS_COMPATIBILITY_SIMCORE_RUNTIME_DOCKER_LABELS_MAP[k]: v
92+
for k, v in values.items()
93+
if k in _BACKWARDS_COMPATIBILITY_SIMCORE_RUNTIME_DOCKER_LABELS_MAP
94+
}:
95+
# these values were sometimes omitted, so let's provide some defaults
96+
for key in ["product-name", "simcore-user-agent", "swarm-stack-name"]:
97+
mapped_values.setdefault(
98+
f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}{key}",
99+
_UNDEFINED_LABEL_VALUE_STR,
100+
)
101+
102+
mapped_values.setdefault(
103+
f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}memory-limit",
104+
_UNDEFINED_LABEL_VALUE_INT,
105+
)
106+
107+
def _convert_nano_cpus_to_cpus(nano_cpu: str) -> str:
108+
with contextlib.suppress(ValidationError):
109+
return f"{parse_obj_as(float, nano_cpu) / (1.0*10**9):.2f}"
110+
return _UNDEFINED_LABEL_VALUE_INT
111+
112+
mapped_values.setdefault(
113+
f"{_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX}cpu-limit",
114+
_convert_nano_cpus_to_cpus(
115+
values.get("nano_cpus_limit", _UNDEFINED_LABEL_VALUE_INT)
116+
),
117+
)
118+
return mapped_values
119+
return values
120+
121+
def to_simcore_runtime_docker_labels(self) -> dict[DockerLabelKey, str]:
37122
"""returns a dictionary of strings as required by docker"""
38-
std_export = self.dict(by_alias=True)
39-
return {k: f"{v}" for k, v in sorted(std_export.items())}
123+
return {
124+
to_simcore_runtime_docker_label_key(k): f"{v}"
125+
for k, v in sorted(self.dict().items())
126+
}
40127

41128
@classmethod
42-
def from_docker_task(cls, docker_task: Task) -> "SimcoreServiceDockerLabelKeys":
129+
def from_docker_task(cls, docker_task: Task) -> "StandardSimcoreDockerLabels":
43130
assert docker_task.Spec # nosec
44131
assert docker_task.Spec.ContainerSpec # nosec
45132
task_labels = docker_task.Spec.ContainerSpec.Labels or {}
46133
return cls.parse_obj(task_labels)
47134

48135
class Config:
49136
allow_population_by_field_name = True
137+
schema_extra: ClassVar[dict[str, Any]] = {
138+
"examples": [
139+
# legacy service labels
140+
{
141+
"study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
142+
"swarm_stack_name": "devel-simcore",
143+
"user_id": "5",
144+
"uuid": "1f963626-66e1-43f1-a777-33955c08b909",
145+
},
146+
# legacy container labels
147+
{
148+
"mem_limit": "1073741824",
149+
"nano_cpus_limit": "4000000000",
150+
"node_id": "1f963626-66e1-43f1-a777-33955c08b909",
151+
"simcore_user_agent": "puppeteer",
152+
"study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
153+
"swarm_stack_name": "devel-simcore",
154+
"user_id": "5",
155+
},
156+
# dy-sidecar service labels
157+
{
158+
"study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
159+
"swarm_stack_name": "devel-simcore",
160+
"user_id": "5",
161+
"uuid": "1f963626-66e1-43f1-a777-33955c08b909",
162+
},
163+
# dy-sidecar container labels
164+
{
165+
"mem_limit": "1073741824",
166+
"nano_cpus_limit": "4000000000",
167+
"study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
168+
"user_id": "5",
169+
"uuid": "1f963626-66e1-43f1-a777-33955c08b909",
170+
},
171+
# dy-proxy service labels
172+
{
173+
"dynamic-type": "dynamic-sidecar",
174+
"study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
175+
"swarm_stack_name": "devel-simcore",
176+
"type": "dependency-v2",
177+
"user_id": "5",
178+
"uuid": "1f963626-66e1-43f1-a777-33955c08b909",
179+
},
180+
# dy-proxy container labels
181+
{
182+
"study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
183+
"user_id": "5",
184+
"uuid": "1f963626-66e1-43f1-a777-33955c08b909",
185+
},
186+
# dy-sidecar user-services labels
187+
{
188+
"product_name": "osparc",
189+
"simcore_user_agent": "puppeteer",
190+
"study_id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
191+
"user_id": "5",
192+
"uuid": "1f963626-66e1-43f1-a777-33955c08b909",
193+
},
194+
# modern both dynamic-sidecar services and computational services
195+
{
196+
"io.simcore.runtime.cpu-limit": "2.4",
197+
"io.simcore.runtime.memory-limit": "1073741824",
198+
"io.simcore.runtime.node-id": "1f963626-66e1-43f1-a777-33955c08b909",
199+
"io.simcore.runtime.product-name": "osparc",
200+
"io.simcore.runtime.project-id": "29f393fc-1410-47b3-b4b9-61dfce21a2a6",
201+
"io.simcore.runtime.simcore-user-agent": "puppeteer",
202+
"io.simcore.runtime.swarm-stack-name": "devel-osparc",
203+
"io.simcore.runtime.user-id": "5",
204+
},
205+
]
206+
}

0 commit comments

Comments
 (0)