Skip to content

Commit de15f43

Browse files
authored
Replace trafaret by pydantic for services config (iteration 1/2) (#1890)
- storage service updated - EXTRA: adds .env-devel and Makefile recipe for local development - EXTRA: adds swagger UI at /dev/doc - webserver keeps for the moment both More details in PR#1890
1 parent 80f7da4 commit de15f43

File tree

112 files changed

+1613
-1105
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+1613
-1105
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from enum import Enum
2+
3+
from pydantic import conint, constr, confloat
4+
5+
PortInt = conint(gt=0, lt=65535)
6+
7+
VersionTag = constr(regex=r"^v\d$")
8+
9+
NonNegativeFloat = confloat(ge=0) # NOTE: = 0.0 + PositiveFloat
10+
11+
12+
class LogLevel(str, Enum):
13+
DEBUG = "DEBUG"
14+
INFO = "INFO"
15+
WARNING = "WARNING"
16+
ERROR = "ERROR"
17+
18+
19+
class BootModeEnum(str, Enum):
20+
"""
21+
Values taken by SC_BOOT_MODE environment variable
22+
set in Dockerfile and used during docker/boot.sh
23+
"""
24+
25+
DEFAULT = "default"
26+
LOCAL = "local-development"
27+
DEBUG = "debug-ptvsd"
28+
PRODUCTION = "production"
29+
DEVELOPMENT = "development"
30+
31+
32+
class BuildTargetEnum(str, Enum):
33+
"""
34+
Values taken by SC_BUILD_TARGET environment variable
35+
set in Dockerfile that defines the stage targeted in the
36+
docker image build
37+
"""
38+
39+
BUILD = "build"
40+
CACHE = "cache"
41+
PRODUCTION = "production"
42+
DEVELOPMENT = "development"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Common settings
3+
"""
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Bases to build ApplicationSettings
3+
4+
TODO: when py 3.7 the use https://pydantic-docs.helpmanual.io/usage/models/#generic-models
5+
"""
6+
import logging
7+
from typing import Optional
8+
from pydantic import Field, BaseSettings, validator, AnyHttpUrl
9+
from ..basic_types import PortInt, BootModeEnum, LogLevel, VersionTag
10+
11+
12+
class BaseFastApiAppSettings(BaseSettings):
13+
# DOCKER
14+
boot_mode: Optional[BootModeEnum] = Field(..., env="SC_BOOT_MODE")
15+
16+
# LOGGING
17+
log_level: LogLevel = Field("DEBUG", env=["LOG_LEVEL", "LOGLEVEL"])
18+
19+
@property
20+
def logging_level(self) -> int:
21+
return getattr(logging, self.log_level)
22+
23+
# SERVER (see : https://www.uvicorn.org/settings/)
24+
host: str = "0.0.0.0" # nosec
25+
port: PortInt = 8000
26+
debug: bool = False # If True, debug tracebacks should be returned on errors.
27+
28+
# DEBUGGING
29+
remote_debug_port: PortInt = 3000
30+
31+
32+
class BaseAiohttpAppSettings(BaseFastApiAppSettings):
33+
port: PortInt = 8080
34+
35+
36+
class BaseServiceAPISettings(BaseSettings):
37+
host: str
38+
port: PortInt
39+
vtag: VersionTag = Field("v0", description="Service API's version tag")
40+
41+
url: Optional[AnyHttpUrl] = None
42+
43+
@validator("url", pre=True)
44+
@classmethod
45+
def autofill_url(cls, v, values):
46+
if v is None:
47+
return AnyHttpUrl.build(
48+
scheme="http",
49+
host=values["host"],
50+
port=f"{values['port']}",
51+
path=f"/{values['vtag']}",
52+
)
53+
return v

packages/models-library/src/models_library/celery.py renamed to packages/models-library/src/models_library/settings/celery.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55

66
class CeleryConfig(BaseSettings):
77
@classmethod
8-
def create_from_env(cls) -> "CeleryConfig":
8+
def create_from_env(cls, **override) -> "CeleryConfig":
99
# this calls trigger env parsers
10-
return cls(rabbit=RabbitConfig(), redis=RedisConfig())
10+
return cls(rabbit=RabbitConfig(), redis=RedisConfig(), **override)
1111

12+
# NOTE: Defaults are evaluated at import time, use create_from_env to build instances
1213
rabbit: RabbitConfig = RabbitConfig()
1314
redis: RedisConfig = RedisConfig()
15+
1416
task_name: str = "simcore.comp.task"
17+
1518
publication_timeout: PositiveInt = 60
1619

1720
@property
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from typing import Optional
2+
3+
from pydantic import BaseSettings, Field, PostgresDsn, SecretStr, conint, validator
4+
5+
from ..basic_types import PortInt
6+
7+
8+
class PostgresSettings(BaseSettings):
9+
dns: Optional[PostgresDsn] = None
10+
11+
# entrypoint
12+
host: str
13+
port: PortInt = 5432
14+
15+
# auth
16+
user: str
17+
password: SecretStr
18+
19+
# database
20+
db: str
21+
22+
# pool connection limits
23+
minsize: conint(ge=1) = 10
24+
maxsize: conint(ge=1) = 10
25+
26+
dsn: Optional[PostgresDsn] = Field(None, description="Database Source Name")
27+
28+
@validator("maxsize")
29+
@classmethod
30+
def check_size(cls, v, values):
31+
if not (values["minsize"] <= v):
32+
raise ValueError(f"assert minsize={values['minsize']} <= maxsize={v}")
33+
return v
34+
35+
@validator("dsn", pre=True)
36+
@classmethod
37+
def autofill_dsn(cls, v, values):
38+
if v is None:
39+
return PostgresDsn.build(
40+
scheme="postgresql",
41+
user=values["user"],
42+
password=values["password"].get_secret_value(),
43+
host=values["host"],
44+
port=f"{values['port']}",
45+
path=f"/{values['db']}",
46+
)
47+
return v
48+
49+
class Config:
50+
case_sensitive = False
51+
env_prefix = "POSTGRES_"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from pydantic import BaseSettings
2+
3+
4+
class S3Config(BaseSettings):
5+
endpoint: str = "minio:9000"
6+
access_key: str = "12345678"
7+
secret_key: str = "12345678"
8+
bucket_name: str = "simcore"
9+
secure: bool = False
10+
11+
class Config:
12+
case_sensitive = False
13+
env_prefix = "S3_"

packages/pytest-simcore/src/pytest_simcore/rabbit_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import pytest
1313
import tenacity
1414

15-
from models_library.rabbit import RabbitConfig
15+
from models_library.settings.rabbit import RabbitConfig
1616

1717
from .helpers.utils_docker import get_service_published_port
1818

packages/pytest-simcore/src/pytest_simcore/redis_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import aioredis
99
import pytest
1010
import tenacity
11-
from models_library.redis import RedisConfig
11+
from models_library.settings.redis import RedisConfig
1212
from servicelib.redis_utils import RedisRetryPolicyUponInitialization
1313
from yarl import URL
1414

packages/service-library/requirements/_base.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ tenacity
1919
attrs
2020
trafaret
2121
aiodebug
22+
pydantic

packages/service-library/requirements/_base.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ aiozipkin==0.7.1 # via -r requirements/_base.in
1111
async-timeout==3.0.1 # via aiohttp
1212
attrs==20.2.0 # via -r requirements/_base.in, aiohttp, jsonschema, openapi-core
1313
chardet==3.0.4 # via aiohttp
14+
dataclasses==0.7 # via pydantic
1415
idna-ssl==1.1.0 # via aiohttp
1516
idna==2.10 # via idna-ssl, yarl
1617
importlib-metadata==2.0.0 # via jsonschema
@@ -22,6 +23,7 @@ openapi-core==0.12.0 # via -r requirements/_base.in
2223
openapi-spec-validator==0.2.9 # via openapi-core
2324
prometheus-client==0.8.0 # via -r requirements/_base.in
2425
psycopg2-binary==2.8.6 # via -r requirements/_base.in, aiopg, sqlalchemy
26+
pydantic==1.6.1 # via -r requirements/_base.in
2527
pyrsistent==0.17.3 # via jsonschema
2628
pyyaml==5.3.1 # via -r requirements/_base.in, openapi-spec-validator
2729
six==1.15.0 # via isodate, jsonschema, openapi-core, openapi-spec-validator, tenacity

packages/service-library/requirements/_test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ prometheus-client==0.8.0 # via -r requirements/_base.txt
4343
psycopg2-binary==2.8.6 # via -r requirements/_base.txt, aiopg, sqlalchemy
4444
py==1.9.0 # via pytest
4545
pycparser==2.20 # via cffi
46+
pydantic==1.6.1 # via -r requirements/_base.txt
4647
pylint==2.6.0 # via -r requirements/_test.in
4748
pynacl==1.4.0 # via paramiko
4849
pyparsing==2.4.7 # via packaging

packages/service-library/src/servicelib/tracing.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
"""
2-
3-
Adds aiohttp middleware for tracing using zipkin server instrumentation.
4-
1+
""" Adds aiohttp middleware for tracing using zipkin server instrumentation.
52
63
"""
7-
84
import asyncio
95
import logging
10-
from typing import Dict
6+
from typing import Dict, Optional
117

128
import aiozipkin as az
139
import trafaret as T
1410
from aiohttp import web
11+
from pydantic import AnyHttpUrl, BaseSettings
1512

1613
log = logging.getLogger(__name__)
1714

@@ -35,3 +32,8 @@ def setup_tracing(
3532
T.Key("zipkin_endpoint", default="http://jaeger:9411"): T.String(),
3633
}
3734
)
35+
36+
37+
class TracingSettings(BaseSettings):
38+
enabled: Optional[bool] = True
39+
zipkin_endpoint: AnyHttpUrl = "http://jaeger:9411"

packages/simcore-sdk/src/simcore_sdk/config/s3.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
""" Basic configuration file for S3
22
33
"""
4-
from os import environ as env
54
import logging
5+
import warnings
6+
from os import environ as env
67

78
import trafaret as T
89

10+
warnings.warn(
11+
f"Module '{__name__}' will be remove in future versions. "
12+
"All common settings have been moved to packages/model-slibrary. "
13+
"Use instead 'models_library.settings.s3' ",
14+
DeprecationWarning,
15+
)
16+
917
log = logging.getLogger(__name__)
1018

1119

12-
CONFIG_SCHEMA = T.Dict({
13-
"endpoint": T.String(),
14-
"access_key": T.String(),
15-
"secret_key": T.String(),
16-
"bucket_name": T.String(),
17-
T.Key("secure", default=0): T.ToInt(),
18-
})
20+
CONFIG_SCHEMA = T.Dict(
21+
{
22+
"endpoint": T.String(),
23+
"access_key": T.String(),
24+
"secret_key": T.String(),
25+
"bucket_name": T.String(),
26+
T.Key("secure", default=0): T.ToInt(),
27+
}
28+
)
1929

2030

21-
# TODO: deprecate!
22-
class Config():
31+
class Config:
2332
def __init__(self):
24-
# TODO: uniform config classes . see server.config file
2533
S3_ENDPOINT = env.get("S3_ENDPOINT", "minio:9000")
2634
S3_ACCESS_KEY = env.get("S3_ACCESS_KEY", "12345678")
2735
S3_SECRET_KEY = env.get("S3_SECRET_KEY", "12345678")

services/catalog/openapi.json

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -863,13 +863,7 @@
863863
"required": [
864864
"key",
865865
"version",
866-
"label",
867-
"progress",
868-
"inputs",
869-
"inputAccess",
870-
"inputNodes",
871-
"outputNodes",
872-
"position"
866+
"label"
873867
],
874868
"type": "object",
875869
"properties": {
@@ -976,7 +970,16 @@
976970
]
977971
},
978972
"position": {
979-
"$ref": "#/components/schemas/Position"
973+
"title": "Position",
974+
"allOf": [
975+
{
976+
"$ref": "#/components/schemas/Position"
977+
}
978+
],
979+
"deprecated": true
980+
},
981+
"state": {
982+
"$ref": "#/components/schemas/RunningState"
980983
}
981984
},
982985
"additionalProperties": false
@@ -1037,6 +1040,22 @@
10371040
},
10381041
"additionalProperties": false
10391042
},
1043+
"RunningState": {
1044+
"title": "RunningState",
1045+
"enum": [
1046+
"UNKNOWN",
1047+
"PUBLISHED",
1048+
"NOT_STARTED",
1049+
"PENDING",
1050+
"STARTED",
1051+
"RETRY",
1052+
"SUCCESS",
1053+
"FAILURE",
1054+
"ABORTED"
1055+
],
1056+
"type": "string",
1057+
"description": "An enumeration."
1058+
},
10401059
"SelectBox": {
10411060
"title": "SelectBox",
10421061
"required": [
@@ -1375,6 +1394,7 @@
13751394
"ServiceType": {
13761395
"title": "ServiceType",
13771396
"enum": [
1397+
"frontend",
13781398
"computational",
13791399
"dynamic"
13801400
],

services/catalog/src/simcore_service_catalog/services/director.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,10 @@ async def request_wrapper(zelf: "DirectorApi", path: str, *args, **kwargs):
7474
normalized_path = path.lstrip("/")
7575
resp = await request_func(zelf, path=normalized_path, *args, **kwargs)
7676
except Exception as err:
77-
#pylint: disable=protected-access
7877
logger.exception(
7978
"Failed request %s to %s%s",
8079
request_func.__name__,
81-
zelf._client.base_url,
80+
zelf.client.base_url,
8281
normalized_path,
8382
)
8483
raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE) from err

services/sidecar/src/simcore_service_sidecar/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pathlib import Path
55
from typing import Optional
66

7-
from models_library.celery import CeleryConfig
7+
from models_library.settings.celery import CeleryConfig
88

99
SERVICES_MAX_NANO_CPUS: int = min(
1010
multiprocessing.cpu_count() * pow(10, 9),

0 commit comments

Comments
 (0)