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

⬆️ Upgrade Director v2 service (Pydantic v2) #6619

Merged
Show file tree
Hide file tree
Changes from 94 commits
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
e3f9e8a
upgrade reqs
giancarloromeo Oct 28, 2024
3c9391a
run bump-pydantic
giancarloromeo Oct 28, 2024
6ac0750
continue fixing
giancarloromeo Oct 28, 2024
6112886
continue fixing
giancarloromeo Oct 29, 2024
c0cc052
fix validation
giancarloromeo Nov 1, 2024
4f32d2d
pin pydantic-settings
giancarloromeo Nov 1, 2024
44d7896
Merge branch 'pydantic_v2_migration_do_not_squash_updates' into is448…
giancarloromeo Nov 4, 2024
7fa5728
fix errors
giancarloromeo Nov 5, 2024
a9de9e4
Merge branch 'pydantic_v2_migration_do_not_squash_updates' into is448…
giancarloromeo Nov 5, 2024
7761829
fix config
giancarloromeo Nov 5, 2024
ce1082e
fix deprecated
giancarloromeo Nov 5, 2024
3a005d8
Merge branch 'is4481/upgrade-director-v2-service' of github.com:gianc…
giancarloromeo Nov 5, 2024
de5b8d4
fix deprecated
giancarloromeo Nov 5, 2024
510fb0a
fix deprecated
giancarloromeo Nov 5, 2024
392bcd4
fix field access
giancarloromeo Nov 5, 2024
6f2e8db
fix field auto_default prop
giancarloromeo Nov 5, 2024
8618098
continue fixing
giancarloromeo Nov 5, 2024
d8dfa69
remove pydantic-settings constraint
giancarloromeo Nov 6, 2024
ddafb2f
fix settings
giancarloromeo Nov 6, 2024
95c714f
continue fix
giancarloromeo Nov 6, 2024
bb10df1
fix deprecated
giancarloromeo Nov 6, 2024
28dd6fd
fix deprecated
giancarloromeo Nov 6, 2024
ff27811
fix url
giancarloromeo Nov 6, 2024
75ac5c8
fix root field
giancarloromeo Nov 6, 2024
edbd1c6
continue fixing
giancarloromeo Nov 6, 2024
c091c4b
fix deprecated
giancarloromeo Nov 6, 2024
7b73bcf
fix deprecated
giancarloromeo Nov 6, 2024
e0047c4
fix required
giancarloromeo Nov 6, 2024
4053946
fix settings
giancarloromeo Nov 6, 2024
722d6b9
fix json
giancarloromeo Nov 6, 2024
9293a26
fix pricing_info
giancarloromeo Nov 6, 2024
152148c
Revert "fix json"
giancarloromeo Nov 6, 2024
25d8657
fix default
giancarloromeo Nov 7, 2024
0a39fd0
continue fixing
giancarloromeo Nov 7, 2024
86318f5
fix url
giancarloromeo Nov 7, 2024
16538e3
fix url
giancarloromeo Nov 7, 2024
037d651
fix url
giancarloromeo Nov 7, 2024
4c73ea0
fix urls
giancarloromeo Nov 7, 2024
eed9e89
fix dateformat
giancarloromeo Nov 7, 2024
bf09d1c
fix
giancarloromeo Nov 7, 2024
4c8d6f5
fix match
giancarloromeo Nov 7, 2024
32be27e
fix
giancarloromeo Nov 7, 2024
d2a18b7
fix serialization
giancarloromeo Nov 7, 2024
9dac744
fix encoder
giancarloromeo Nov 7, 2024
1ce4766
fix import
giancarloromeo Nov 7, 2024
0b7e719
fix
giancarloromeo Nov 7, 2024
a34764d
fix
giancarloromeo Nov 7, 2024
0302b4b
reduce complexity
giancarloromeo Nov 7, 2024
92a40de
continue fixing
giancarloromeo Nov 7, 2024
250a9c0
continue fixing
giancarloromeo Nov 7, 2024
3f6bf87
fix timedelta
giancarloromeo Nov 7, 2024
531d3dd
fix mypy
giancarloromeo Nov 7, 2024
bd60f94
fix mypy
giancarloromeo Nov 7, 2024
cc6470f
fixing
giancarloromeo Nov 7, 2024
b68ffc0
fix mypy
giancarloromeo Nov 7, 2024
820dcfa
fix version pattern
giancarloromeo Nov 7, 2024
6e06e45
regenerate docker models
Nov 8, 2024
63be043
fix nullable
giancarloromeo Nov 8, 2024
d629f95
fix
giancarloromeo Nov 8, 2024
203ab11
Merge branch 'pydantic_v2_migration_do_not_squash_updates' into is448…
giancarloromeo Nov 8, 2024
dfcef2e
fix serializer
giancarloromeo Nov 8, 2024
23f242c
Merge branch 'pydantic_v2_migration_do_not_squash_updates' into is448…
giancarloromeo Nov 8, 2024
740886b
fix
giancarloromeo Nov 8, 2024
3c40aed
fix url
giancarloromeo Nov 8, 2024
3f768a6
fix url
giancarloromeo Nov 8, 2024
6001005
fix import
giancarloromeo Nov 8, 2024
889167f
fix url types
giancarloromeo Nov 8, 2024
a55950b
fix datetime.utc
giancarloromeo Nov 8, 2024
c14aaff
remove deprecated
giancarloromeo Nov 8, 2024
b6194a1
fix validator
giancarloromeo Nov 8, 2024
93226cd
fix url
giancarloromeo Nov 8, 2024
67da4f8
fix model_dumps
giancarloromeo Nov 8, 2024
4dfad54
fix deprecated
giancarloromeo Nov 8, 2024
44557eb
change var
giancarloromeo Nov 8, 2024
c466996
fix nodeid serialization
giancarloromeo Nov 8, 2024
a0cf317
fix serialization
giancarloromeo Nov 8, 2024
5b64e8e
fix serialization
giancarloromeo Nov 11, 2024
2af6c94
Merge branch 'pydantic_v2_migration_do_not_squash_updates' into is448…
giancarloromeo Nov 11, 2024
736064d
fix validator
giancarloromeo Nov 11, 2024
87112f6
fix fields
giancarloromeo Nov 11, 2024
f795bcc
fix serialization
giancarloromeo Nov 11, 2024
a9983b3
fix validator
giancarloromeo Nov 11, 2024
845867d
fix test
giancarloromeo Nov 11, 2024
469c228
fix
giancarloromeo Nov 11, 2024
f3e4857
fix typo
giancarloromeo Nov 11, 2024
fa9028c
fix test
giancarloromeo Nov 11, 2024
b8b8c68
fix typecheck
giancarloromeo Nov 12, 2024
189d84c
fix test
giancarloromeo Nov 12, 2024
0681310
fix deprecated
giancarloromeo Nov 12, 2024
ccfe07f
revert endpoint type
giancarloromeo Nov 12, 2024
90f887e
fix url path
giancarloromeo Nov 12, 2024
0a0016f
Update services/director-v2/src/simcore_service_director_v2/utils/osp…
giancarloromeo Nov 12, 2024
f10922f
set specific type: ignore
giancarloromeo Nov 12, 2024
151ea7d
Merge branch 'is4481/upgrade-director-v2-service' of github.com:gianc…
giancarloromeo Nov 12, 2024
ebccb82
revert example
giancarloromeo Nov 12, 2024
0858664
revert date
giancarloromeo Nov 12, 2024
c3015df
Update services/director-v2/src/simcore_service_director_v2/core/appl…
giancarloromeo Nov 12, 2024
337db2d
fix datetime
giancarloromeo Nov 12, 2024
23afc1d
Merge branch 'is4481/upgrade-director-v2-service' of github.com:gianc…
giancarloromeo Nov 12, 2024
5fe0858
null fields
giancarloromeo Nov 12, 2024
31d1fff
revert
giancarloromeo Nov 12, 2024
fa33465
fix unuseful alias
giancarloromeo Nov 12, 2024
5b3685b
revert datetime
giancarloromeo Nov 12, 2024
0b80e72
revert required field
giancarloromeo Nov 12, 2024
d3c2680
revert bump-pydantic
giancarloromeo Nov 12, 2024
b86e516
fix validator
giancarloromeo Nov 12, 2024
e73acec
add comment
giancarloromeo Nov 12, 2024
18a619e
remove unused code
giancarloromeo Nov 12, 2024
f31ba37
fix mypy
giancarloromeo Nov 12, 2024
0679046
revert
giancarloromeo Nov 13, 2024
149b7b5
fix datetime
giancarloromeo Nov 13, 2024
1d197e5
fix nullable
giancarloromeo Nov 13, 2024
5928317
Merge branch 'pydantic_v2_migration_do_not_squash_updates' into is448…
giancarloromeo Nov 13, 2024
2ce21df
fix mock
giancarloromeo Nov 13, 2024
fd03fbc
fix serialization
giancarloromeo Nov 13, 2024
c5a66a9
revert validator
giancarloromeo Nov 13, 2024
be1e296
fix type
giancarloromeo Nov 13, 2024
f3ebf75
fix test
giancarloromeo Nov 13, 2024
0bacc3b
compose_spec nullable
giancarloromeo Nov 13, 2024
6e0dc86
fix nullable
giancarloromeo Nov 13, 2024
5e69184
fix cluster serialization
giancarloromeo Nov 14, 2024
3746196
fix datetime
giancarloromeo Nov 14, 2024
6057dd4
fix url
giancarloromeo Nov 14, 2024
e0f8b08
fix url
giancarloromeo Nov 14, 2024
0f2e38e
revert
giancarloromeo Nov 14, 2024
fcf9631
fix json
giancarloromeo Nov 14, 2024
a10e5b8
fix url
giancarloromeo Nov 14, 2024
6c6d8bd
fix url
giancarloromeo Nov 14, 2024
1e9ff2d
fix url
giancarloromeo Nov 14, 2024
6c9af46
fix env serialization
giancarloromeo Nov 14, 2024
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
giancarloromeo marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

if __name__ == "__main__":
with Path.open(CURRENT_DIR.parent / "node-meta-v0.0.1-pydantic.json", "w") as f:
schema = ServiceMetaDataPublished.schema_json()
schema = ServiceMetaDataPublished.model_json_schema()
schema_without_ref = jsonref.loads(schema)

json.dump(schema_without_ref, f, indent=2)
17 changes: 7 additions & 10 deletions packages/common-library/src/common_library/serialization.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from datetime import timedelta
from typing import Any

from pydantic import BaseModel, SecretStr
from pydantic import BaseModel, SecretStr, TypeAdapter
from pydantic_core import Url

from .pydantic_fields_extension import get_type


def model_dump_with_secrets(
settings_obj: BaseModel, *, show_secrets: bool, **pydantic_export_options
Expand All @@ -22,19 +20,18 @@ def model_dump_with_secrets(
data[field_name] = field_data.total_seconds()

elif isinstance(field_data, SecretStr):
if show_secrets:
data[field_name] = field_data.get_secret_value()
else:
data[field_name] = str(field_data)
data[field_name] = (
field_data.get_secret_value() if show_secrets else str(field_data)
)

elif isinstance(field_data, Url):
data[field_name] = str(field_data)

elif isinstance(field_data, dict):
field_type = get_type(settings_obj.model_fields[field_name])
if issubclass(field_type, BaseModel):
field_type = settings_obj.model_fields[field_name].annotation
if field_type:
data[field_name] = model_dump_with_secrets(
field_type.model_validate(field_data),
TypeAdapter(field_type).validate_python(field_data),
show_secrets=show_secrets,
**pydantic_export_options,
)
Expand Down
19 changes: 14 additions & 5 deletions packages/common-library/tests/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,31 @@


class Credentials(BaseModel):
USERNAME: str | None = None
PASSWORD: SecretStr | None = None
username: str
password: SecretStr


class Access(BaseModel):
credentials: Credentials


@pytest.mark.parametrize(
"expected,show_secrets",
[
(
{"USERNAME": "DeepThought", "PASSWORD": "42"},
{"credentials": {"username": "DeepThought", "password": "42"}},
True,
),
(
{"USERNAME": "DeepThought", "PASSWORD": "**********"},
{"credentials": {"username": "DeepThought", "password": "**********"}},
False, # hide secrets
),
],
)
def test_model_dump_with_secrets(expected: dict, show_secrets: bool):
assert expected == model_dump_with_secrets(Credentials(USERNAME="DeepThought", PASSWORD=SecretStr("42")), show_secrets=show_secrets)
assert expected == model_dump_with_secrets(
Access(
credentials=Credentials(username="DeepThought", password=SecretStr("42"))
),
show_secrets=show_secrets,
)
20 changes: 9 additions & 11 deletions packages/models-library/src/models_library/aiodocker_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@


class AioDockerContainerSpec(ContainerSpec):
Env: dict[str, str | None] | None = Field(
env: dict[str, str | None] | None = Field( # type: ignore[assignment]
default=None,
description="aiodocker expects here a dictionary and re-convert it back internally`.\n",
alias="Env",
description="aiodocker expects here a dictionary and re-convert it back internally",
)

@field_validator("Env", mode="before")
@field_validator("env", mode="before")
@classmethod
def convert_list_to_dict(cls, v):
if v is not None and isinstance(v, list):
Expand All @@ -33,25 +34,22 @@ def convert_list_to_dict(cls, v):
class AioDockerResources1(Resources1):
# NOTE: The Docker REST API documentation is wrong!!!
# Do not set that back to singular Reservation.
Reservation: ResourceObject | None = Field(
reservation: ResourceObject | None = Field(
None, description="Define resources reservation.", alias="Reservations"
giancarloromeo marked this conversation as resolved.
Show resolved Hide resolved
)

model_config = ConfigDict(populate_by_name=True)


class AioDockerTaskSpec(TaskSpec):
ContainerSpec: AioDockerContainerSpec | None = Field(
None,
container_spec: AioDockerContainerSpec | None = Field(
default=None, alias="ContainerSpec"
)

Resources: AioDockerResources1 | None = Field(
None,
description="Resource requirements which apply to each individual container created\nas part of the service.\n",
)
resources: AioDockerResources1 | None = Field(default=None, alias="Resources")


class AioDockerServiceSpec(ServiceSpec):
TaskTemplate: AioDockerTaskSpec | None = None
task_template: AioDockerTaskSpec | None = Field(default=None, alias="TaskTemplate")

model_config = ConfigDict(populate_by_name=True, alias_generator=camel_to_snake)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TypeAlias
from typing import Any, TypeAlias

from pydantic import (
AnyHttpUrl,
Expand Down Expand Up @@ -48,13 +48,12 @@ class WorkerMetrics(BaseModel):
class UsedResources(DictModel[str, NonNegativeFloat]):
@model_validator(mode="before")
@classmethod
def ensure_negative_value_is_zero(cls, values):
def ensure_negative_value_is_zero(cls, values: dict[str, Any]):
# dasks adds/remove resource values and sometimes
# they end up being negative instead of 0
if v := values.get("__root__", {}):
for res_key, res_value in v.items():
if res_value < 0:
v[res_key] = 0
for res_key, res_value in values.items():
if res_value < 0:
values[res_key] = 0
return values


Expand Down Expand Up @@ -126,6 +125,7 @@ class ClusterCreate(BaseCluster):
"name": "My awesome cluster",
"type": ClusterTypeInModel.ON_PREMISE,
"endpoint": "https://registry.osparc-development.fake.dev",
"owner": None,
"authentication": {
giancarloromeo marked this conversation as resolved.
Show resolved Hide resolved
"type": "simple",
"username": "someuser",
Expand Down Expand Up @@ -168,15 +168,16 @@ def set_default_thumbnail_if_empty(cls, v, info: ValidationInfo):


class ClusterPatch(BaseCluster):
name: str | None = None # type: ignore[assignment]
description: str | None = None
type: ClusterTypeInModel | None = None # type: ignore[assignment]
owner: GroupID | None = None # type: ignore[assignment]
thumbnail: HttpUrl | None = None
endpoint: AnyUrl | None = None # type: ignore[assignment]
authentication: ExternalClusterAuthentication | None = None # type: ignore[assignment]
name: str | None = Field(default=None) # type: ignore[assignment]
description: str | None = Field(default=None)
giancarloromeo marked this conversation as resolved.
Show resolved Hide resolved
type: ClusterTypeInModel | None = Field(default=None) # type: ignore[assignment]
owner: GroupID | None = Field(default=None) # type: ignore[assignment]
thumbnail: HttpUrl | None = Field(default=None)
endpoint: AnyUrl | None = Field(default=None) # type: ignore[assignment]
authentication: ExternalClusterAuthentication | None = Field(default=None) # type: ignore[assignment]
access_rights: dict[GroupID, ClusterAccessRights] | None = Field( # type: ignore[assignment]
giancarloromeo marked this conversation as resolved.
Show resolved Hide resolved
default=None, alias="accessRights"
default=None,
alias="accessRights"
)

model_config = ConfigDict(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
from typing import Any, TypeAlias

from models_library.basic_types import IDStr
from pydantic import AnyHttpUrl, AnyUrl, BaseModel, ConfigDict, Field, field_validator
from pydantic import (
AnyHttpUrl,
AnyUrl,
BaseModel,
ConfigDict,
Field,
ValidationInfo,
field_validator,
)

from ..clusters import ClusterID
from ..projects import ProjectID
Expand Down Expand Up @@ -63,16 +71,18 @@ class ComputationCreate(BaseModel):

@field_validator("product_name")
@classmethod
def ensure_product_name_defined_if_computation_starts(cls, v, values):
if "start_pipeline" in values and values["start_pipeline"] and v is None:
def _ensure_product_name_defined_if_computation_starts(
cls, v, info: ValidationInfo
):
if info.data.get("start_pipeline") and v is None:
msg = "product_name must be set if computation shall start!"
raise ValueError(msg)
return v

@field_validator("use_on_demand_clusters")
@classmethod
def ensure_expected_options(cls, v, values):
if v is True and ("cluster_id" in values and values["cluster_id"] is not None):
def _ensure_expected_options(cls, v, info: ValidationInfo):
if v and info.data.get("cluster_id") is not None:
msg = "cluster_id cannot be set if use_on_demand_clusters is set"
raise ValueError(msg)
return v
Expand Down
12 changes: 6 additions & 6 deletions packages/models-library/src/models_library/clusters.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,14 @@ class Cluster(BaseCluster):
},
)

@model_validator(mode="before")
@model_validator(mode="after")
@classmethod
def check_owner_has_access_rights(cls, values):
is_default_cluster = bool(values["id"] == DEFAULT_CLUSTER_ID)
owner_gid = values["owner"]
def _check_owner_has_access_rights(cls, values):
is_default_cluster = bool(values.id == DEFAULT_CLUSTER_ID)
owner_gid = values.owner
giancarloromeo marked this conversation as resolved.
Show resolved Hide resolved

# check owner is in the access rights, if not add it
access_rights = values.get("access_rights", values.get("accessRights", {}))
access_rights = values.access_rights or {}
if owner_gid not in access_rights:
access_rights[owner_gid] = (
CLUSTER_USER_RIGHTS if is_default_cluster else CLUSTER_ADMIN_RIGHTS
Expand All @@ -239,5 +239,5 @@ def check_owner_has_access_rights(cls, values):
):
msg = f"the cluster owner access rights are incorrectly set: {access_rights[owner_gid]}"
raise ValueError(msg)
values["access_rights"] = access_rights
values.access_rights = access_rights
return values
8 changes: 1 addition & 7 deletions packages/models-library/src/models_library/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,7 @@ def from_key(cls, key: str) -> "DockerLabelKey":
str, StringConstraints(pattern=DOCKER_GENERIC_TAG_KEY_RE)
]


class DockerPlacementConstraint(ConstrainedStr):
strip_whitespace = True
regex = re.compile(
r"^(?!-)(?![.])(?!.*--)(?!.*[.][.])[a-zA-Z0-9.-]*(?<!-)(?<![.])(!=|==)[a-zA-Z0-9_. -]*$"
)

DockerPlacementConstraint: TypeAlias = Annotated[str, StringConstraints(strip_whitespace = True, pattern = re.compile(r"^(?!-)(?![.])(?!.*--)(?!.*[.][.])[a-zA-Z0-9.-]*(?<!-)(?<![.])(!=|==)[a-zA-Z0-9_. -]*$"))]

_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX: Final[str] = "io.simcore.runtime."
_BACKWARDS_COMPATIBILITY_SIMCORE_RUNTIME_DOCKER_LABELS_MAP: Final[dict[str, str]] = {
Expand Down
4 changes: 3 additions & 1 deletion packages/models-library/src/models_library/errors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Any, TypedDict
from typing import Any
from typing_extensions import TypedDict

giancarloromeo marked this conversation as resolved.
Show resolved Hide resolved

Loc = tuple[int | str, ...]

Expand Down
Loading
Loading