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

✨ Personalized resources: Allow override of computational service needed resources #3971

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions packages/models-library/src/models_library/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,9 @@
from models_library.projects import ProjectID
from models_library.projects_nodes import NodeID
from models_library.users import UserID
from pydantic import BaseModel, ConstrainedStr, Field, constr
from pydantic import BaseModel, ConstrainedStr, Field

from .basic_regex import (
DOCKER_GENERIC_TAG_KEY_RE,
DOCKER_IMAGE_KEY_RE,
DOCKER_IMAGE_VERSION_RE,
DOCKER_LABEL_KEY_REGEX,
)

DockerImageKey = constr(regex=DOCKER_IMAGE_KEY_RE)
DockerImageVersion = constr(regex=DOCKER_IMAGE_VERSION_RE)
from .basic_regex import DOCKER_GENERIC_TAG_KEY_RE, DOCKER_LABEL_KEY_REGEX


class DockerLabelKey(ConstrainedStr):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
validator,
)

from .basic_regex import VERSION_RE
from .basic_types import EnvVarKey
from .projects_access import AccessEnum
from .projects_nodes_io import (
Expand All @@ -30,7 +29,7 @@
)
from .projects_nodes_ui import Position
from .projects_state import RunningState
from .services import PROPERTY_KEY_RE, SERVICE_KEY_RE
from .services import PROPERTY_KEY_RE, ServiceKey, ServiceVersion

# NOTE: WARNING the order here matters

Expand Down Expand Up @@ -100,20 +99,18 @@ class Config:


class Node(BaseModel):
key: str = Field(
key: ServiceKey = Field(
...,
description="distinctive name for the node based on the docker registry path",
regex=SERVICE_KEY_RE,
examples=[
"simcore/services/comp/itis/sleeper",
"simcore/services/dynamic/3dviewer",
"simcore/services/frontend/file-picker",
],
)
version: str = Field(
version: ServiceVersion = Field(
...,
description="semantic version number of the node",
regex=VERSION_RE,
examples=["1.0.0", "0.0.1"],
)
label: str = Field(
Expand Down
12 changes: 10 additions & 2 deletions packages/models-library/src/models_library/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
python -c "from models_library.services import ServiceDockerData as cls; print(cls.schema_json(indent=2))" > services-schema.json
"""

import re
from datetime import datetime
from enum import Enum
from typing import Any, Optional, Union
from uuid import UUID

from pydantic import (
BaseModel,
ConstrainedStr,
EmailStr,
Extra,
Field,
Expand Down Expand Up @@ -54,8 +56,14 @@
ServicePortKey = constr(regex=PROPERTY_KEY_RE)
FileName = constr(regex=FILENAME_RE)

ServiceKey = constr(regex=KEY_RE)
ServiceVersion = constr(regex=VERSION_RE)

class ServiceKey(ConstrainedStr):
regex = re.compile(SERVICE_KEY_RE)


class ServiceVersion(ConstrainedStr):
regex = re.compile(VERSION_RE)


RunID = UUID

Expand Down
43 changes: 33 additions & 10 deletions packages/models-library/src/models_library/services_resources.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import logging
from typing import Any, Final, Union
from enum import auto
from typing import Any, Final, Optional, Union

from models_library.docker import DockerGenericTag
from models_library.utils.enums import StrAutoEnum
from pydantic import (
BaseModel,
ByteSize,
Field,
StrictFloat,
StrictInt,
constr,
parse_obj_as,
root_validator,
)
Expand All @@ -16,13 +18,14 @@

logger = logging.getLogger(__name__)

DockerImage = constr(regex=r"[\w/-]+:[\w.@]+")
DockerComposeServiceName = constr(regex=r"^[a-zA-Z0-9._-]+$")

ResourceName = str

# NOTE: replace hard coded `container` with function which can
# extract the name from the `service_key` or `registry_address/service_key`
DEFAULT_SINGLE_SERVICE_NAME: Final[DockerComposeServiceName] = "container"
DEFAULT_SINGLE_SERVICE_NAME: Final[DockerGenericTag] = parse_obj_as(
DockerGenericTag, "container"
)

MEMORY_50MB: Final[int] = parse_obj_as(ByteSize, "50mib")
MEMORY_250MB: Final[int] = parse_obj_as(ByteSize, "250mib")
Expand Down Expand Up @@ -57,8 +60,14 @@ class Config:
ResourcesDict = dict[ResourceName, ResourceValue]


class BootMode(StrAutoEnum):
CPU = auto()
GPU = auto()
MPI = auto()


class ImageResources(BaseModel):
image: DockerImage = Field(
image: DockerGenericTag = Field(
...,
description=(
"Used by the frontend to provide a context for the users."
Expand All @@ -68,6 +77,10 @@ class ImageResources(BaseModel):
),
)
resources: ResourcesDict
boot_modes: list[BootMode] = Field(
default=[BootMode.CPU],
description="describe how a service shall be booted, using CPU, MPI, openMP or GPU",
)

class Config:
schema_extra = {
Expand All @@ -87,23 +100,33 @@ class Config:
}


ServiceResourcesDict = dict[DockerComposeServiceName, ImageResources]
ServiceResourcesDict = dict[DockerGenericTag, ImageResources]


class ServiceResourcesDictHelpers:
@staticmethod
def create_from_single_service(
image: DockerComposeServiceName, resources: ResourcesDict
image: DockerGenericTag,
resources: ResourcesDict,
boot_modes: Optional[list[BootMode]] = None,
) -> ServiceResourcesDict:
if boot_modes is None:
boot_modes = [BootMode.CPU]
return parse_obj_as(
ServiceResourcesDict,
{DEFAULT_SINGLE_SERVICE_NAME: {"image": image, "resources": resources}},
{
DEFAULT_SINGLE_SERVICE_NAME: {
"image": image,
"resources": resources,
"boot_modes": boot_modes,
}
},
)

@staticmethod
def create_jsonable(
service_resources: ServiceResourcesDict,
) -> dict[DockerComposeServiceName, Any]:
) -> dict[DockerGenericTag, Any]:
return jsonable_encoder(service_resources)

class Config:
Expand Down
13 changes: 6 additions & 7 deletions packages/models-library/tests/test_service_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
from typing import Any

import pytest
from models_library.docker import DockerGenericTag
from models_library.services_resources import (
DockerComposeServiceName,
DockerImage,
ImageResources,
ResourcesDict,
ResourceValue,
Expand All @@ -28,7 +27,7 @@
),
)
def test_compose_image(example: str) -> None:
parse_obj_as(DockerImage, example)
parse_obj_as(DockerGenericTag, example)


@pytest.fixture
Expand All @@ -39,8 +38,8 @@ def resources_dict() -> ResourcesDict:


@pytest.fixture
def compose_image() -> DockerImage:
return parse_obj_as(DockerImage, "image:latest")
def compose_image() -> DockerGenericTag:
return parse_obj_as(DockerGenericTag, "image:latest")


def _ensure_resource_value_is_an_object(data: ResourcesDict) -> None:
Expand Down Expand Up @@ -74,7 +73,7 @@ def test_image_resources_parsed_as_expected() -> None:
"example", ServiceResourcesDictHelpers.Config.schema_extra["examples"]
)
def test_service_resource_parsed_as_expected(
example: dict[DockerComposeServiceName, Any], compose_image: DockerImage
example: dict[DockerGenericTag, Any], compose_image: DockerGenericTag
) -> None:
def _assert_service_resources_dict(
service_resources_dict: ServiceResourcesDict,
Expand Down Expand Up @@ -103,7 +102,7 @@ def _assert_service_resources_dict(
@pytest.mark.parametrize(
"example", ServiceResourcesDictHelpers.Config.schema_extra["examples"]
)
def test_create_jsonable_dict(example: dict[DockerComposeServiceName, Any]) -> None:
def test_create_jsonable_dict(example: dict[DockerGenericTag, Any]) -> None:
service_resources_dict: ServiceResourcesDict = parse_obj_as(
ServiceResourcesDict, example
)
Expand Down
Loading