Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Labels for metrics scraping #3881

Merged
merged 15 commits into from
Feb 20, 2023
9 changes: 8 additions & 1 deletion packages/models-library/src/models_library/docker.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import re
from typing import Optional
from typing import Any, Final, Optional

from pydantic import ConstrainedStr, constr

Expand All @@ -23,3 +23,10 @@ class DockerLabelKey(ConstrainedStr):
class DockerGenericTag(ConstrainedStr):
# NOTE: https://docs.docker.com/engine/reference/commandline/tag/#description
regex: Optional[re.Pattern[str]] = DOCKER_GENERIC_TAG_KEY_RE


LABEL_PREFIX_CONTAINER: Final[str] = "io.simcore.container"


def get_prefixed_container_label(label: str, value: Any) -> str:
return f"{LABEL_PREFIX_CONTAINER}.{label}={value}"
22 changes: 21 additions & 1 deletion packages/models-library/tests/test_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
# pylint: disable=unused-argument
# pylint: disable=unused-variable

from typing import Any

import pytest
from models_library.docker import DockerGenericTag, DockerLabelKey
from models_library.docker import (
LABEL_PREFIX_CONTAINER,
DockerGenericTag,
DockerLabelKey,
get_prefixed_container_label,
)
from pydantic import ValidationError, parse_obj_as


Expand Down Expand Up @@ -93,3 +100,16 @@ def test_docker_generic_tag(image_name: str, valid: bool):
else:
with pytest.raises(ValidationError):
parse_obj_as(DockerGenericTag, image_name)


@pytest.mark.parametrize(
"label, value, expected",
[
("string", "1", f"{LABEL_PREFIX_CONTAINER}.string=1"),
("none-value", None, f"{LABEL_PREFIX_CONTAINER}.none-value=None"),
("dict", {"ciao": 1}, f"{LABEL_PREFIX_CONTAINER}.dict={{'ciao': 1}}"),
("float", 34.56, f"{LABEL_PREFIX_CONTAINER}.float=34.56"),
],
)
def test_get_prefixed_container_label(label: str, value: Any, expected: str):
assert get_prefixed_container_label(label, value) == expected
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import Optional, Union

from fastapi.applications import FastAPI
from models_library.docker import get_prefixed_container_label
from models_library.service_settings_labels import (
ComposeSpecLabel,
PathMappingsLabel,
Expand Down Expand Up @@ -182,6 +183,18 @@ def _update_resource_limits_and_reservations(
spec["environment"] = environment


def _update_container_labels(service_spec: ComposeSpecLabel, user_id: UserID) -> None:
for spec in service_spec["services"].values():
labels: set[str] = spec.get("labels", [])

# this labels is primarily used for metrics scraping
use_id_label = get_prefixed_container_label("user.id", user_id)
if use_id_label not in labels:
labels.add(use_id_label)

spec["labels"] = labels


def assemble_spec(
*,
app: FastAPI,
Expand Down Expand Up @@ -255,8 +268,9 @@ def assemble_spec(
egress_proxy_settings=egress_proxy_settings,
)

_update_container_labels(service_spec=service_spec, user_id=user_id)

# TODO: will be used in next PR
assert user_id # nosec
assert product_name # nosec

stringified_service_spec = replace_env_vars_in_compose_spec(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pytest
import yaml
from models_library.docker import LABEL_PREFIX_CONTAINER
from models_library.services_resources import (
DEFAULT_SINGLE_SERVICE_NAME,
ResourcesDict,
Expand Down Expand Up @@ -139,3 +140,34 @@ async def test_inject_resource_limits_and_reservations(
in spec["environment"]
)
assert f"{MEM_RESOURCE_LIMIT_KEY}={memory.limit}" in spec["environment"]


@pytest.mark.parametrize(
"service_spec, expected_result",
[
pytest.param(
{"services": {"service-1": {}}},
{
"services": {
"service-1": {"labels": [f"{LABEL_PREFIX_CONTAINER}.user.id=1"]}
}
},
id="single_service",
),
pytest.param(
{"services": {"service-1": {}, "service-2": {}}},
{
"services": {
"service-1": {"labels": [f"{LABEL_PREFIX_CONTAINER}.user.id=1"]},
"service-2": {"labels": [f"{LABEL_PREFIX_CONTAINER}.user.id=1"]},
}
},
id="multiple_services",
),
],
)
async def test_update_container_labels(
service_spec: dict[str, Any], expected_result: dict[str, Any]
):
docker_compose_specs._update_container_labels(service_spec, 1)
assert service_spec == expected_result