Skip to content

Commit a25c044

Browse files
authored
✨ Vendor secrets and session oenvs (part 3) 🗃️ (#3921)
1 parent 5c86827 commit a25c044

File tree

46 files changed

+1545
-133
lines changed

Some content is hidden

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

46 files changed

+1545
-133
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ repos:
2929
- "--py310-plus"
3030
name: upgrade code
3131
- repo: https://github.com/hadialqattan/pycln
32-
rev: v1.2.5
32+
rev: v2.1.4
3333
hooks:
3434
- id: pycln
3535
args: [--all, --expand-stars]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ This project is licensed under the terms of the [MIT license](LICENSE).
144144
---
145145

146146
<p align="center">
147-
<image src="https://github.com/ITISFoundation/osparc-simcore-python-client/blob/4e8b18494f3191d55f6692a6a605818aeeb83f95/docs/_media/mwl.png" alt="Made with love at www.z43.swiss" width="20%" />
147+
<image src="https://raw.githubusercontent.com/ITISFoundation/osparc-simcore-python-client/4e8b18494f3191d55f6692a6a605818aeeb83f95/docs/_media/mwl.png" alt="Made with love (and lots of hard work) at www.z43.swiss" width="20%" />
148148
</p>
149149

150150
<!-- ADD REFERENCES BELOW AND KEEP THEM IN ALPHABETICAL ORDER -->

packages/models-library/requirements/_test.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ pytest-instafail
1919
pytest-mock
2020
pytest-runner
2121
pytest-sugar
22+
python-dotenv
2223
pyyaml

packages/models-library/requirements/_test.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ python-dateutil==2.8.2
8282
# via
8383
# -c requirements/_base.txt
8484
# faker
85+
python-dotenv==1.0.0
86+
# via -r requirements/_test.in
8587
pyyaml==6.0
8688
# via -r requirements/_test.in
8789
six==1.16.0

packages/models-library/setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ test = pytest
1515

1616
[tool:pytest]
1717
asyncio_mode = auto
18-
markers =
18+
markers =
1919
diagnostics: "can be used to run diagnostics against deployed data (e.g. database, registry etc)"
20+
testit: "marks test to run during development"

packages/models-library/src/models_library/service_settings_labels.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# pylint: disable=unsubscriptable-object
22

33
import json
4+
import re
45
from enum import Enum
56
from functools import cached_property
67
from pathlib import Path
@@ -9,6 +10,7 @@
910
from pydantic import (
1011
BaseModel,
1112
ByteSize,
13+
ConstrainedStr,
1214
Extra,
1315
Field,
1416
Json,
@@ -22,10 +24,18 @@
2224
from .basic_types import PortInt
2325
from .generics import ListModel
2426
from .services_resources import DEFAULT_SINGLE_SERVICE_NAME
27+
from .utils.string_substitution import OSPARC_IDENTIFIER_PREFIX
2528

2629
# Cloudflare DNS server address
2730
DEFAULT_DNS_SERVER_ADDRESS: Final[str] = "1.1.1.1" # NOSONAR
28-
DEFAULT_DNS_SERVER_PORT: Final[PortInt] = 53
31+
DEFAULT_DNS_SERVER_PORT: Final[PortInt] = parse_obj_as(PortInt, 53)
32+
33+
34+
# NOTE: To allow parametrized value, set the type to Union[OEnvSubstitutionStr, ...]
35+
36+
37+
class OEnvSubstitutionStr(ConstrainedStr):
38+
regex = re.compile(rf"^\${OSPARC_IDENTIFIER_PREFIX}\w+$")
2939

3040

3141
class _BaseConfig:
@@ -57,7 +67,9 @@ class Config(_BaseConfig):
5767

5868

5969
class SimcoreServiceSettingLabelEntry(BaseModel):
60-
"""These values are used to build the request body of https://docs.docker.com/engine/api/v1.41/#operation/ServiceCreate
70+
"""Content of "simcore.service.settings" label
71+
72+
These values are used to build the request body of https://docs.docker.com/engine/api/v1.41/#operation/ServiceCreate
6173
Specifically the section under ``TaskTemplate``
6274
"""
6375

@@ -151,6 +163,8 @@ class Config(_BaseConfig):
151163

152164

153165
class PathMappingsLabel(BaseModel):
166+
"""Content of "simcore.service.paths-mapping" label"""
167+
154168
inputs_path: Path = Field(
155169
..., description="folder path where the service expects all the inputs"
156170
)
@@ -240,10 +254,12 @@ class Config(_BaseConfig):
240254
}
241255

242256

243-
ComposeSpecLabel: TypeAlias = dict[str, Any]
257+
ComposeSpecLabelDict: TypeAlias = dict[str, Any]
244258

245259

246260
class RestartPolicy(str, Enum):
261+
"""Content of "simcore.service.restart-policy" label"""
262+
247263
NO_RESTART = "no-restart"
248264
ON_INPUTS_DOWNLOADED = "on-inputs-downloaded"
249265

@@ -281,6 +297,8 @@ class Config(_BaseConfig):
281297

282298

283299
class NATRule(BaseModel):
300+
"""Content of "simcore.service.containers-allowed-outgoing-permit-list" label"""
301+
284302
hostname: str
285303
tcp_ports: list[_PortRange | PortInt]
286304
dns_resolver: DNSResolver = Field(
@@ -299,6 +317,8 @@ def iter_tcp_ports(self) -> Iterator[PortInt]:
299317

300318

301319
class DynamicSidecarServiceLabels(BaseModel):
320+
"""All "simcore.service.*" labels including keys"""
321+
302322
paths_mapping: Json[PathMappingsLabel] | None = Field(
303323
None,
304324
alias="simcore.service.paths-mapping",
@@ -308,7 +328,7 @@ class DynamicSidecarServiceLabels(BaseModel):
308328
),
309329
)
310330

311-
compose_spec: Json[ComposeSpecLabel] | None = Field(
331+
compose_spec: Json[ComposeSpecLabelDict] | None = Field(
312332
None,
313333
alias="simcore.service.compose-spec",
314334
description=(

packages/models-library/src/models_library/utils/docker_compose.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import yaml
22

3-
from .string_substitution import SubstitutionsDict, TemplateText
3+
from .string_substitution import SubstitutionsDict, TextTemplate
44

55
# Notes on below env var names:
66
# - SIMCORE_REGISTRY will be replaced by the url of the simcore docker registry
@@ -17,7 +17,7 @@
1717

1818

1919
def replace_env_vars_in_compose_spec(
20-
service_spec: "ComposeSpecLabel",
20+
service_spec: "ComposeSpecLabelDict",
2121
*,
2222
replace_simcore_registry: str,
2323
replace_service_version: str,
@@ -29,7 +29,7 @@ def replace_env_vars_in_compose_spec(
2929

3030
content: str = yaml.safe_dump(service_spec)
3131

32-
template = TemplateText(content)
32+
template = TextTemplate(content)
3333
substitutions = SubstitutionsDict(
3434
{
3535
"SERVICE_VERSION": replace_service_version,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from typing import Any, TypeAlias
2+
3+
import yaml
4+
from models_library.utils.string_substitution import (
5+
SubstitutionsDict,
6+
TextTemplate,
7+
substitute_all_legacy_identifiers,
8+
)
9+
from pydantic import StrictBool, StrictFloat, StrictInt
10+
11+
from .string_substitution import SubstitutionsDict, TextTemplate
12+
13+
# This constraint on substitution values is to avoid
14+
# deserialization issues on the TextTemplate substitution!
15+
SubstitutionValue: TypeAlias = StrictBool | StrictInt | StrictFloat | str
16+
17+
18+
class SpecsSubstitutionsResolver:
19+
"""
20+
Resolve specs dict by substituting identifiers
21+
22+
'specs' is defined here as dict[str, Any]. E.g. a docker-compose.yml loaded as a dict are 'specs'.
23+
24+
"""
25+
26+
def __init__(self, specs: dict[str, Any], upgrade: bool):
27+
self._template = self._create_text_template(specs, upgrade=upgrade)
28+
self._substitutions: SubstitutionsDict = SubstitutionsDict()
29+
30+
@classmethod
31+
def _create_text_template(
32+
cls, specs: dict[str, Any], *, upgrade: bool
33+
) -> TextTemplate:
34+
# convert to yaml (less symbols as in json)
35+
service_spec_str: str = yaml.safe_dump(specs)
36+
37+
if upgrade: # legacy
38+
service_spec_str = substitute_all_legacy_identifiers(service_spec_str)
39+
40+
# template
41+
template = TextTemplate(service_spec_str)
42+
assert template.is_valid() # nosec
43+
44+
return template
45+
46+
def get_identifiers(self) -> list[str]:
47+
"""lists identifiers in specs in order of apperance. Can have repetitions"""
48+
return self._template.get_identifiers()
49+
50+
def get_replaced(self) -> set[str]:
51+
return self._substitutions.used
52+
53+
@property
54+
def substitutions(self):
55+
return self._substitutions
56+
57+
def set_substitutions(
58+
self, environs: dict[str, SubstitutionValue]
59+
) -> SubstitutionsDict:
60+
"""NOTE: ONLY targets identifiers declared in the specs"""
61+
identifiers_needed = self.get_identifiers()
62+
63+
# picks only needed for substitution
64+
self._substitutions = SubstitutionsDict(
65+
{
66+
identifier: environs[identifier]
67+
for identifier in identifiers_needed
68+
if identifier in environs
69+
}
70+
)
71+
return self._substitutions
72+
73+
def run(self) -> dict[str, Any]:
74+
new_specs_txt: str = self._template.safe_substitute(self._substitutions)
75+
new_specs: dict = yaml.safe_load(new_specs_txt)
76+
return new_specs

packages/models-library/src/models_library/utils/string_substitution.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from string import Template
88
from typing import Any
99

10-
OSPARC_IDENTIFIER_PREFIX = "OSPARC_ENVIRONMENT"
10+
OSPARC_IDENTIFIER_PREFIX = "OSPARC_ENVIRONMENT_"
1111

1212

1313
def upgrade_identifier(identifier: str) -> str:
@@ -18,7 +18,8 @@ def upgrade_identifier(identifier: str) -> str:
1818
identifier = re.sub(r"[.-]", "_", identifier)
1919
identifier = identifier.upper()
2020
if not identifier.startswith(OSPARC_IDENTIFIER_PREFIX):
21-
identifier = f"{OSPARC_IDENTIFIER_PREFIX}_{identifier}"
21+
assert OSPARC_IDENTIFIER_PREFIX.endswith("_") # nosec
22+
identifier = OSPARC_IDENTIFIER_PREFIX + identifier
2223
return identifier
2324

2425

@@ -40,7 +41,7 @@ def _upgrade(match):
4041
return re.sub(_LEGACY_IDENTIFIER_RE_PATTERN, _upgrade, text)
4142

4243

43-
class TemplateText(Template):
44+
class TextTemplate(Template):
4445
"""Template strings support `$`-based substitutions, using the following rules:
4546
4647
- `$$` is an escape; it is replaced with a single `$`.

0 commit comments

Comments
 (0)