Skip to content

Commit f88a41f

Browse files
GitHKAndrei Neagu
andauthored
✨Adding boot-options support (ITISFoundation#2686)
* boot-options support * fixed spec regeneration * applying patch * pylint * fixed integration test * added mising env var * fixed comment * codestyle * using exclude_patterns * rename * adding missing_ok option * fix message * added more information * refactor * refactor for readability * added reason for usage * updated openapi.json * revert change and add clarificatio for usage * pylint * refactor exceptions * removed subprocess call * renamed functions * better 204 handling * docstring update * refactor directory-watcher * fixed tests for directory watcher * fixed API interface * fixed inerface to be more stanrad looking * fixed usage in dynamic_sidecar * added mising requirement * refactor examples * updated all modules * removed function * removed defult, not needed * revert change * refactor * added some explenation for the cehcks * remoing fixture * simpler debugging * using tmp_dir * removed unused * refactor * further refactors to simplify * refactor naming * removing checks and disabling tests * properly fixed signature * renamed * more renaming * fixing tests * updated comment * codestyle * update comment and example * pylint * fixing failing dv2 tests * fix pylint * fix webserver06 tests * added misisng bootOptions * foxed integration test * update templates * proper service state management * pylint * fix error messages * wrong indentation level * fixed test to allow for ports pulling * fix failing public-api tests * adding more information for debugging * fix schema * fix pylint * fixing template * codeclimate fix: refactor code * fix import * renamed and fixed validators * using renamed version * moved boot_options to node from project * reverted change * reverted change on template * reverted change * reverted tenoakte * revert tempaltes * removing column * remove migration * reverted to original * regenerated correct schemas * renaming * frotend uses boot_optiosn on node * fixed boot mode * codestyle * rename constants * refactor to use context manager * using prpoer constant type * refactor to make more readable * replaced formatter with error * codestyle * imports and codestyle * fix jslint * rename function * refactor locationa nd name * rename boot_options to bootOptions * reverting changes Co-authored-by: Andrei Neagu <[email protected]>
1 parent bee16a3 commit f88a41f

File tree

37 files changed

+1079
-253
lines changed

37 files changed

+1079
-253
lines changed

api/specs/common/schemas/node-meta-v0.0.1-converted.yaml

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,11 @@ properties:
246246
- type
247247
properties:
248248
displayOrder:
249-
description: >-
250-
DEPRECATED: new display order is taken from the item position.
251-
This property will be removed.
252-
deprecated: true
253249
type: number
250+
description: use this to numerically sort the properties for display
251+
example:
252+
- 1
253+
- -0.2
254254
label:
255255
type: string
256256
description: short name for the property
@@ -291,3 +291,45 @@ properties:
291291
description: Units of the output value, if a physical quantity
292292
type: string
293293
additionalProperties: true
294+
boot-options:
295+
title: Boot Options
296+
description: >-
297+
Service defined boot options. These get injected in the service as env
298+
variables.
299+
type: object
300+
x-patternProperties:
301+
^[_a-zA-Z0-9]+$:
302+
title: BootOptionMode
303+
type: object
304+
properties:
305+
label:
306+
title: Label
307+
type: string
308+
description:
309+
title: Description
310+
type: string
311+
default:
312+
title: Default
313+
type: string
314+
items:
315+
title: Items
316+
type: object
317+
additionalProperties:
318+
title: BootOptionItem
319+
type: object
320+
properties:
321+
label:
322+
title: Label
323+
type: string
324+
description:
325+
title: Description
326+
type: string
327+
required:
328+
- label
329+
- description
330+
required:
331+
- label
332+
- description
333+
- default
334+
- items
335+
additionalProperties: true

api/specs/common/schemas/node-meta-v0.0.1.json

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,9 @@
180180
],
181181
"properties": {
182182
"displayOrder": {
183-
"description": "DEPRECATED: new display order is taken from the item position. This property will be removed.",
184-
"deprecated": true,
185-
"type": "number"
183+
"description": "DEPRECATED: new display order is taken from the item position. This property will be removed.",
184+
"deprecated": true,
185+
"type": "number"
186186
},
187187
"label": {
188188
"type": "string",
@@ -409,6 +409,59 @@
409409
}
410410
}
411411
}
412+
},
413+
"boot-options": {
414+
"title": "Boot Options",
415+
"description": "Service defined boot options. These get injected in the service as env variables.",
416+
"type": "object",
417+
"patternProperties": {
418+
"^[_a-zA-Z0-9]+$": {
419+
"title": "BootOptionMode",
420+
"type": "object",
421+
"properties": {
422+
"label": {
423+
"title": "Label",
424+
"type": "string"
425+
},
426+
"description": {
427+
"title": "Description",
428+
"type": "string"
429+
},
430+
"default": {
431+
"title": "Default",
432+
"type": "string"
433+
},
434+
"items": {
435+
"title": "Items",
436+
"type": "object",
437+
"additionalProperties": {
438+
"title": "BootOptionItem",
439+
"type": "object",
440+
"properties": {
441+
"label": {
442+
"title": "Label",
443+
"type": "string"
444+
},
445+
"description": {
446+
"title": "Description",
447+
"type": "string"
448+
}
449+
},
450+
"required": [
451+
"label",
452+
"description"
453+
]
454+
}
455+
}
456+
},
457+
"required": [
458+
"label",
459+
"description",
460+
"default",
461+
"items"
462+
]
463+
}
464+
}
412465
}
413466
}
414-
}
467+
}

api/specs/common/schemas/project-v0.0.1-converted.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,16 @@ properties:
322322
- ABORTED
323323
type: string
324324
additionalProperties: false
325+
bootOptions:
326+
title: Boot Options
327+
description: >-
328+
Some services provide alternative parameters to be injected at
329+
boot time. The user selection should be stored here, and it will
330+
overwrite the services's defaults.
331+
type: object
332+
patternProperties:
333+
'[a-zA-Z][a-azA-Z0-9_]*':
334+
type: string
325335
additionalProperties: true
326336
ui:
327337
type: object

api/specs/common/schemas/project-v0.0.1.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,16 @@
438438
}
439439
},
440440
"additionalProperties": false
441+
},
442+
"bootOptions": {
443+
"title": "Boot Options",
444+
"description": "Some services provide alternative parameters to be injected at boot time. The user selection should be stored here, and it will overwrite the services's defaults.",
445+
"type": "object",
446+
"patternProperties": {
447+
"[a-zA-Z][a-azA-Z0-9_]*": {
448+
"type": "string"
449+
}
450+
}
441451
}
442452
}
443453
}
@@ -668,4 +678,4 @@
668678
"description": "Object containing Quality Assessment related data"
669679
}
670680
}
671-
}
681+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from typing import Dict
2+
3+
from pydantic import BaseModel, constr, validator
4+
from typing_extensions import TypedDict
5+
6+
ENV_VAR_KEY_RE = r"[a-zA-Z][a-azA-Z0-9_]*"
7+
EnvVarKey = constr(regex=ENV_VAR_KEY_RE)
8+
9+
10+
class BootChoice(TypedDict):
11+
label: str
12+
description: str
13+
14+
15+
class BootOption(BaseModel):
16+
label: str
17+
description: str
18+
default: str
19+
items: Dict[str, BootChoice]
20+
21+
@validator("items")
22+
@classmethod
23+
def ensure_default_included(cls, v, values):
24+
default = values["default"]
25+
if default not in v:
26+
raise ValueError(
27+
f"Expected default={default} to be present a key of items={v}"
28+
)
29+
return v
30+
31+
class Config:
32+
schema_extra = {
33+
"examples": [
34+
{
35+
"label": "Boot mode",
36+
"description": "Start it in web page mode",
37+
"default": "0",
38+
"items": {
39+
"0": {
40+
"label": "Non Voila",
41+
"description": "Tooltip for non Voila boot mode",
42+
},
43+
"1": {
44+
"label": "Voila",
45+
"description": "Tooltip for Voila boot mode",
46+
},
47+
},
48+
},
49+
{
50+
"label": "Application theme",
51+
"description": "Select a theme for the application",
52+
"default": "b",
53+
"items": {
54+
"a": {
55+
"label": "Clear",
56+
"description": "Using white background",
57+
},
58+
"b": {
59+
"label": "Dark",
60+
"description": "Using black and gray tones",
61+
},
62+
},
63+
},
64+
]
65+
}
66+
67+
68+
BootOptions = Dict[EnvVarKey, BootOption]

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
PortLink,
2727
SimCoreFileLink,
2828
)
29+
from .boot_options import EnvVarKey
2930
from .projects_nodes_ui import Position
3031
from .projects_state import RunningState
3132
from .services import PROPERTY_KEY_RE, SERVICE_KEY_RE
@@ -168,6 +169,16 @@ class Node(BaseModel):
168169
default_factory=NodeState, description="The node's state object"
169170
)
170171

172+
boot_options: Optional[Dict[EnvVarKey, str]] = Field(
173+
None,
174+
alias="bootOptions",
175+
description=(
176+
"Some services provide alternative parameters to be injected at boot time. "
177+
"The user selection should be stored here, and it will overwrite the "
178+
"services's defaults."
179+
),
180+
)
181+
171182
@validator("thumbnail", pre=True)
172183
@classmethod
173184
def convert_empty_str_to_none(cls, v):

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

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

3-
43
import json
54
from enum import Enum
65
from functools import cached_property
@@ -97,12 +96,18 @@ class PathMappingsLabel(BaseModel):
9796
description="optional list of paths which contents need to be persisted",
9897
)
9998

99+
state_exclude: Optional[List[str]] = Field(
100+
None,
101+
description="optional list unix shell rules used to exclude files from the state",
102+
)
103+
100104
class Config(_BaseConfig):
101105
schema_extra = {
102106
"example": {
103107
"outputs_path": "/tmp/outputs", # nosec
104108
"inputs_path": "/tmp/inputs", # nosec
105109
"state_paths": ["/tmp/save_1", "/tmp_save_2"], # nosec
110+
"state_exclude": ["/tmp/strip_me/*", "*.py"], # nosec
106111
}
107112
}
108113

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from pydantic.types import PositiveInt
2222

2323
from .basic_regex import VERSION_RE
24+
from .boot_options import BootOption, BootOptions
2425

2526
# NOTE: needs to end with / !!
2627
SERVICE_KEY_RE = r"^(simcore)/(services)/(comp|dynamic|frontend)(/[\w/-]+)+$"
@@ -373,6 +374,12 @@ class ServiceDockerData(ServiceKeyVersion, ServiceCommonData):
373374
..., description="definition of the outputs of this node"
374375
)
375376

377+
boot_options: Optional[BootOptions] = Field(
378+
None,
379+
alias="boot-options",
380+
description="Service defined boot options. These get injected in the service as env variables.",
381+
)
382+
376383
class Config:
377384
description = "Description of a simcore node 'class' with input and output"
378385
extra = Extra.forbid
@@ -453,6 +460,18 @@ class Config:
453460
"fileToKeyMap": {"output_data.zip": "output_1"},
454461
}
455462
},
463+
"boot-options": {
464+
"example_service_defined_boot_mode": BootOption.Config.schema_extra[
465+
"examples"
466+
][
467+
0
468+
],
469+
"example_service_defined_theme_selection": BootOption.Config.schema_extra[
470+
"examples"
471+
][
472+
1
473+
],
474+
},
456475
},
457476
]
458477
}

packages/models-library/tests/test_services.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# pylint:disable=redefined-outer-name
44

55
import re
6+
from copy import deepcopy
67
from pathlib import Path
78
from pprint import pformat
89
from typing import Any, Callable, Dict, List
@@ -14,6 +15,7 @@
1415
COMPUTATIONAL_SERVICE_KEY_FORMAT,
1516
DYNAMIC_SERVICE_KEY_FORMAT,
1617
SERVICE_KEY_RE,
18+
BootOption,
1719
ServiceAccessRightsAtDB,
1820
ServiceCommonData,
1921
ServiceDockerData,
@@ -230,3 +232,15 @@ def _find_pattern_entry(obj: Dict[str, Any], key: str) -> Any:
230232
for x_path in json_schema_entry_paths:
231233
json_pattern = _find_pattern_entry(json_schema_config, x_path)
232234
assert json_pattern == python_regex_pattern
235+
236+
237+
def test_boot_option() -> None:
238+
for example in BootOption.Config.schema_extra["examples"]:
239+
assert BootOption(**example)
240+
241+
242+
def test_boot_option_wrong_default() -> None:
243+
for example in [deepcopy(x) for x in BootOption.Config.schema_extra["examples"]]:
244+
with pytest.raises(ValueError):
245+
example["default"] = "__undefined__"
246+
assert BootOption(**example)

packages/service-library/src/servicelib/aiohttp/jsonschema_validation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ def validate_instance(instance: Dict, schema: Dict, *, log_errors=True):
1111
validate(instance, schema)
1212
except ValidationError:
1313
if log_errors:
14-
log.exception("Node validation error:")
14+
log.exception("%s\n%s\nNode validation error:", f"{instance}", f"{schema=}")
1515
raise
1616
except SchemaError:
1717
if log_errors:
18-
log.exception("Schema validation error:")
18+
log.exception(
19+
"%s\n%s\nSchema valdation error:", f"{instance}", f"{schema=}"
20+
)
1921
raise

0 commit comments

Comments
 (0)