digitalhub 0.10.2__py3-none-any.whl → 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +10 -0
- digitalhub/context/api.py +10 -4
- digitalhub/context/builder.py +35 -20
- digitalhub/context/context.py +35 -24
- digitalhub/entities/_base/entity/builder.py +11 -0
- digitalhub/entities/_base/executable/entity.py +52 -5
- digitalhub/entities/_base/material/utils.py +11 -11
- digitalhub/entities/_commons/enums.py +4 -0
- digitalhub/entities/_processors/base.py +15 -15
- digitalhub/entities/_processors/context.py +62 -15
- digitalhub/entities/_processors/utils.py +2 -2
- digitalhub/entities/builders.py +2 -0
- digitalhub/entities/function/_base/entity.py +3 -3
- digitalhub/entities/project/_base/builder.py +4 -0
- digitalhub/entities/project/_base/entity.py +5 -2
- digitalhub/entities/project/_base/models.py +18 -0
- digitalhub/entities/project/_base/spec.py +6 -0
- digitalhub/entities/project/crud.py +2 -13
- digitalhub/entities/run/_base/entity.py +6 -12
- digitalhub/entities/task/_base/entity.py +4 -4
- digitalhub/entities/task/_base/models.py +20 -2
- digitalhub/entities/trigger/__init__.py +0 -0
- digitalhub/entities/trigger/_base/__init__.py +0 -0
- digitalhub/entities/trigger/_base/builder.py +70 -0
- digitalhub/entities/trigger/_base/entity.py +34 -0
- digitalhub/entities/trigger/_base/spec.py +40 -0
- digitalhub/entities/trigger/_base/status.py +9 -0
- digitalhub/entities/trigger/crud.py +309 -0
- digitalhub/entities/trigger/lifecycle/__init__.py +0 -0
- digitalhub/entities/trigger/lifecycle/builder.py +19 -0
- digitalhub/entities/trigger/lifecycle/entity.py +32 -0
- digitalhub/entities/trigger/lifecycle/spec.py +38 -0
- digitalhub/entities/trigger/lifecycle/status.py +9 -0
- digitalhub/entities/trigger/scheduler/__init__.py +0 -0
- digitalhub/entities/trigger/scheduler/builder.py +19 -0
- digitalhub/entities/trigger/scheduler/entity.py +32 -0
- digitalhub/entities/trigger/scheduler/spec.py +29 -0
- digitalhub/entities/trigger/scheduler/status.py +9 -0
- digitalhub/entities/workflow/_base/entity.py +3 -3
- digitalhub/factory/factory.py +113 -26
- digitalhub/factory/utils.py +31 -14
- digitalhub/runtimes/_base.py +22 -11
- digitalhub/runtimes/builder.py +16 -3
- digitalhub/runtimes/enums.py +11 -1
- digitalhub/stores/client/dhcore/client.py +1 -0
- digitalhub/stores/client/dhcore/configurator.py +80 -11
- digitalhub/stores/client/dhcore/utils.py +1 -0
- digitalhub/stores/configurator/configurator.py +5 -2
- digitalhub/stores/configurator/enums.py +9 -0
- digitalhub/stores/configurator/ini_module.py +58 -4
- digitalhub/stores/data/api.py +2 -2
- digitalhub/stores/data/builder.py +5 -6
- digitalhub/stores/data/enums.py +11 -0
- digitalhub/stores/data/local/store.py +0 -3
- digitalhub/stores/data/remote/store.py +0 -3
- digitalhub/stores/data/s3/configurator.py +0 -20
- digitalhub/stores/data/s3/enums.py +2 -3
- digitalhub/stores/data/s3/store.py +4 -10
- digitalhub/stores/data/s3/utils.py +13 -18
- digitalhub/stores/data/sql/configurator.py +9 -22
- digitalhub/stores/data/sql/store.py +1 -3
- digitalhub/stores/data/utils.py +34 -0
- digitalhub/utils/file_utils.py +1 -1
- digitalhub/utils/generic_utils.py +37 -0
- digitalhub/utils/uri_utils.py +5 -0
- {digitalhub-0.10.2.dist-info → digitalhub-0.11.0.dist-info}/METADATA +1 -1
- {digitalhub-0.10.2.dist-info → digitalhub-0.11.0.dist-info}/RECORD +69 -52
- digitalhub/factory/api.py +0 -277
- digitalhub/stores/data/s3/models.py +0 -21
- digitalhub/stores/data/sql/models.py +0 -24
- {digitalhub-0.10.2.dist-info → digitalhub-0.11.0.dist-info}/WHEEL +0 -0
- {digitalhub-0.10.2.dist-info → digitalhub-0.11.0.dist-info}/licenses/LICENSE.txt +0 -0
digitalhub/factory/factory.py
CHANGED
|
@@ -8,7 +8,7 @@ if typing.TYPE_CHECKING:
|
|
|
8
8
|
from digitalhub.entities._base.entity.builder import EntityBuilder
|
|
9
9
|
from digitalhub.entities._base.entity.entity import Entity
|
|
10
10
|
from digitalhub.entities._base.entity.metadata import Metadata
|
|
11
|
-
from digitalhub.entities._base.entity.spec import Spec
|
|
11
|
+
from digitalhub.entities._base.entity.spec import Spec, SpecValidator
|
|
12
12
|
from digitalhub.entities._base.entity.status import Status
|
|
13
13
|
from digitalhub.entities._base.runtime_entity.builder import RuntimeEntityBuilder
|
|
14
14
|
from digitalhub.runtimes._base import Runtime
|
|
@@ -17,7 +17,26 @@ if typing.TYPE_CHECKING:
|
|
|
17
17
|
|
|
18
18
|
class Factory:
|
|
19
19
|
"""
|
|
20
|
-
Factory
|
|
20
|
+
Factory for creating and managing entity and runtime builders.
|
|
21
|
+
|
|
22
|
+
This class implements the Factory pattern to manage the creation of
|
|
23
|
+
entities and runtimes through their respective builders. It maintains
|
|
24
|
+
separate registries for entity and runtime builders.
|
|
25
|
+
|
|
26
|
+
Many function arguments are called kind_to_build_from to avoid overwriting
|
|
27
|
+
kind in kwargs.
|
|
28
|
+
|
|
29
|
+
Attributes
|
|
30
|
+
----------
|
|
31
|
+
_entity_builders : dict[str, EntityBuilder | RuntimeEntityBuilder]
|
|
32
|
+
Registry of entity builders indexed by kind.
|
|
33
|
+
_runtime_builders : dict[str, RuntimeBuilder]
|
|
34
|
+
Registry of runtime builders indexed by kind.
|
|
35
|
+
|
|
36
|
+
Notes
|
|
37
|
+
-----
|
|
38
|
+
All builder methods may raise BuilderError if the requested kind
|
|
39
|
+
is not found in the registry.
|
|
21
40
|
"""
|
|
22
41
|
|
|
23
42
|
def __init__(self):
|
|
@@ -26,18 +45,19 @@ class Factory:
|
|
|
26
45
|
|
|
27
46
|
def add_entity_builder(self, name: str, builder: EntityBuilder | RuntimeEntityBuilder) -> None:
|
|
28
47
|
"""
|
|
29
|
-
|
|
48
|
+
Register an entity builder.
|
|
30
49
|
|
|
31
50
|
Parameters
|
|
32
51
|
----------
|
|
33
52
|
name : str
|
|
34
|
-
|
|
35
|
-
builder : EntityBuilder
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
The unique identifier for the builder.
|
|
54
|
+
builder : EntityBuilder | RuntimeEntityBuilder
|
|
55
|
+
The builder instance to register.
|
|
56
|
+
|
|
57
|
+
Raises
|
|
58
|
+
------
|
|
59
|
+
BuilderError
|
|
60
|
+
If a builder with the same name already exists.
|
|
41
61
|
"""
|
|
42
62
|
if name in self._entity_builders:
|
|
43
63
|
raise BuilderError(f"Builder {name} already exists.")
|
|
@@ -45,31 +65,30 @@ class Factory:
|
|
|
45
65
|
|
|
46
66
|
def add_runtime_builder(self, name: str, builder: RuntimeBuilder) -> None:
|
|
47
67
|
"""
|
|
48
|
-
|
|
68
|
+
Register a runtime builder.
|
|
49
69
|
|
|
50
70
|
Parameters
|
|
51
71
|
----------
|
|
52
72
|
name : str
|
|
53
|
-
|
|
73
|
+
The unique identifier for the builder.
|
|
54
74
|
builder : RuntimeBuilder
|
|
55
|
-
|
|
75
|
+
The builder instance to register.
|
|
56
76
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
Raises
|
|
78
|
+
------
|
|
79
|
+
BuilderError
|
|
80
|
+
If a builder with the same name already exists.
|
|
60
81
|
"""
|
|
61
82
|
if name in self._runtime_builders:
|
|
62
83
|
raise BuilderError(f"Builder {name} already exists.")
|
|
63
84
|
self._runtime_builders[name] = builder()
|
|
64
85
|
|
|
65
|
-
def build_entity_from_params(self,
|
|
86
|
+
def build_entity_from_params(self, **kwargs) -> Entity:
|
|
66
87
|
"""
|
|
67
88
|
Build an entity from parameters.
|
|
68
89
|
|
|
69
90
|
Parameters
|
|
70
91
|
----------
|
|
71
|
-
kind_to_build_from : str
|
|
72
|
-
Entity type.
|
|
73
92
|
**kwargs
|
|
74
93
|
Entity parameters.
|
|
75
94
|
|
|
@@ -78,17 +97,20 @@ class Factory:
|
|
|
78
97
|
Entity
|
|
79
98
|
Entity object.
|
|
80
99
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
try:
|
|
101
|
+
kind = kwargs["kind"]
|
|
102
|
+
except KeyError:
|
|
103
|
+
raise BuilderError("Missing 'kind' parameter.")
|
|
104
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
105
|
+
return self._entity_builders[kind].build(**kwargs)
|
|
106
|
+
|
|
107
|
+
def build_entity_from_dict(self, obj: dict) -> Entity:
|
|
84
108
|
"""
|
|
85
109
|
Build an entity from a dictionary.
|
|
86
110
|
|
|
87
111
|
Parameters
|
|
88
112
|
----------
|
|
89
|
-
|
|
90
|
-
Entity type.
|
|
91
|
-
dict_data : dict
|
|
113
|
+
obj : dict
|
|
92
114
|
Dictionary with entity data.
|
|
93
115
|
|
|
94
116
|
Returns
|
|
@@ -96,7 +118,12 @@ class Factory:
|
|
|
96
118
|
Entity
|
|
97
119
|
Entity object.
|
|
98
120
|
"""
|
|
99
|
-
|
|
121
|
+
try:
|
|
122
|
+
kind = obj["kind"]
|
|
123
|
+
except KeyError:
|
|
124
|
+
raise BuilderError("Missing 'kind' parameter.")
|
|
125
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
126
|
+
return self._entity_builders[kind].from_dict(obj)
|
|
100
127
|
|
|
101
128
|
def build_spec(self, kind_to_build_from: str, **kwargs) -> Spec:
|
|
102
129
|
"""
|
|
@@ -112,6 +139,7 @@ class Factory:
|
|
|
112
139
|
Spec
|
|
113
140
|
Spec object.
|
|
114
141
|
"""
|
|
142
|
+
self._raise_if_entity_builder_not_found(kind_to_build_from)
|
|
115
143
|
return self._entity_builders[kind_to_build_from].build_spec(**kwargs)
|
|
116
144
|
|
|
117
145
|
def build_metadata(self, kind_to_build_from: str, **kwargs) -> Metadata:
|
|
@@ -128,6 +156,7 @@ class Factory:
|
|
|
128
156
|
Metadata
|
|
129
157
|
Metadata object.
|
|
130
158
|
"""
|
|
159
|
+
self._raise_if_entity_builder_not_found(kind_to_build_from)
|
|
131
160
|
return self._entity_builders[kind_to_build_from].build_metadata(**kwargs)
|
|
132
161
|
|
|
133
162
|
def build_status(self, kind_to_build_from: str, **kwargs) -> Status:
|
|
@@ -144,6 +173,7 @@ class Factory:
|
|
|
144
173
|
Status
|
|
145
174
|
Status object.
|
|
146
175
|
"""
|
|
176
|
+
self._raise_if_entity_builder_not_found(kind_to_build_from)
|
|
147
177
|
return self._entity_builders[kind_to_build_from].build_status(**kwargs)
|
|
148
178
|
|
|
149
179
|
def build_runtime(self, kind_to_build_from: str, project: str) -> Runtime:
|
|
@@ -162,6 +192,7 @@ class Factory:
|
|
|
162
192
|
Runtime
|
|
163
193
|
Runtime object.
|
|
164
194
|
"""
|
|
195
|
+
self._raise_if_runtime_builder_not_found(kind_to_build_from)
|
|
165
196
|
return self._runtime_builders[kind_to_build_from].build(project=project)
|
|
166
197
|
|
|
167
198
|
def get_entity_type_from_kind(self, kind: str) -> str:
|
|
@@ -178,6 +209,7 @@ class Factory:
|
|
|
178
209
|
str
|
|
179
210
|
Entity type.
|
|
180
211
|
"""
|
|
212
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
181
213
|
return self._entity_builders[kind].get_entity_type()
|
|
182
214
|
|
|
183
215
|
def get_executable_kind(self, kind: str) -> str:
|
|
@@ -194,6 +226,7 @@ class Factory:
|
|
|
194
226
|
str
|
|
195
227
|
Executable kind.
|
|
196
228
|
"""
|
|
229
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
197
230
|
return self._entity_builders[kind].get_executable_kind()
|
|
198
231
|
|
|
199
232
|
def get_action_from_task_kind(self, kind: str, task_kind: str) -> str:
|
|
@@ -212,6 +245,7 @@ class Factory:
|
|
|
212
245
|
str
|
|
213
246
|
Action.
|
|
214
247
|
"""
|
|
248
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
215
249
|
return self._entity_builders[kind].get_action_from_task_kind(task_kind)
|
|
216
250
|
|
|
217
251
|
def get_task_kind_from_action(self, kind: str, action: str) -> list[str]:
|
|
@@ -230,6 +264,7 @@ class Factory:
|
|
|
230
264
|
list[str]
|
|
231
265
|
Task kinds.
|
|
232
266
|
"""
|
|
267
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
233
268
|
return self._entity_builders[kind].get_task_kind_from_action(action)
|
|
234
269
|
|
|
235
270
|
def get_run_kind(self, kind: str) -> str:
|
|
@@ -246,6 +281,7 @@ class Factory:
|
|
|
246
281
|
str
|
|
247
282
|
Run kind.
|
|
248
283
|
"""
|
|
284
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
249
285
|
return self._entity_builders[kind].get_run_kind()
|
|
250
286
|
|
|
251
287
|
def get_all_kinds(self, kind: str) -> list[str]:
|
|
@@ -264,5 +300,56 @@ class Factory:
|
|
|
264
300
|
"""
|
|
265
301
|
return self._entity_builders[kind].get_all_kinds()
|
|
266
302
|
|
|
303
|
+
def get_spec_validator(self, kind: str) -> SpecValidator:
|
|
304
|
+
"""
|
|
305
|
+
Get spec validators.
|
|
306
|
+
|
|
307
|
+
Parameters
|
|
308
|
+
----------
|
|
309
|
+
kind : str
|
|
310
|
+
Kind.
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
-------
|
|
314
|
+
SpecValidator
|
|
315
|
+
Spec validator.
|
|
316
|
+
"""
|
|
317
|
+
self._raise_if_entity_builder_not_found(kind)
|
|
318
|
+
return self._entity_builders[kind].get_spec_validator()
|
|
319
|
+
|
|
320
|
+
def _raise_if_entity_builder_not_found(self, kind: str) -> None:
|
|
321
|
+
"""
|
|
322
|
+
Verify entity builder existence.
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
kind : str
|
|
327
|
+
The entity kind to verify.
|
|
328
|
+
|
|
329
|
+
Raises
|
|
330
|
+
------
|
|
331
|
+
BuilderError
|
|
332
|
+
If no builder exists for the specified kind.
|
|
333
|
+
"""
|
|
334
|
+
if kind not in self._entity_builders:
|
|
335
|
+
raise BuilderError(f"Entity builder for kind '{kind}' not found.")
|
|
336
|
+
|
|
337
|
+
def _raise_if_runtime_builder_not_found(self, kind: str) -> None:
|
|
338
|
+
"""
|
|
339
|
+
Verify runtime builder existence.
|
|
340
|
+
|
|
341
|
+
Parameters
|
|
342
|
+
----------
|
|
343
|
+
kind : str
|
|
344
|
+
The runtime kind to verify.
|
|
345
|
+
|
|
346
|
+
Raises
|
|
347
|
+
------
|
|
348
|
+
BuilderError
|
|
349
|
+
If no builder exists for the specified kind.
|
|
350
|
+
"""
|
|
351
|
+
if kind not in self._runtime_builders:
|
|
352
|
+
raise BuilderError(f"Runtime builder for kind '{kind}' not found.")
|
|
353
|
+
|
|
267
354
|
|
|
268
355
|
factory = Factory()
|
digitalhub/factory/utils.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
-
import importlib.metadata
|
|
5
4
|
import pkgutil
|
|
6
5
|
import re
|
|
7
6
|
from types import ModuleType
|
|
@@ -11,17 +10,24 @@ from digitalhub.factory.factory import factory
|
|
|
11
10
|
|
|
12
11
|
def import_module(package: str) -> ModuleType:
|
|
13
12
|
"""
|
|
14
|
-
Import
|
|
13
|
+
Import a module dynamically by package name.
|
|
15
14
|
|
|
16
15
|
Parameters
|
|
17
16
|
----------
|
|
18
17
|
package : str
|
|
19
|
-
|
|
18
|
+
The fully qualified package name to import.
|
|
20
19
|
|
|
21
20
|
Returns
|
|
22
21
|
-------
|
|
23
22
|
ModuleType
|
|
24
|
-
|
|
23
|
+
The imported module object.
|
|
24
|
+
|
|
25
|
+
Raises
|
|
26
|
+
------
|
|
27
|
+
ModuleNotFoundError
|
|
28
|
+
If the specified package cannot be found.
|
|
29
|
+
RuntimeError
|
|
30
|
+
If any other error occurs during import.
|
|
25
31
|
"""
|
|
26
32
|
try:
|
|
27
33
|
return importlib.import_module(package)
|
|
@@ -33,12 +39,20 @@ def import_module(package: str) -> ModuleType:
|
|
|
33
39
|
|
|
34
40
|
def list_runtimes() -> list[str]:
|
|
35
41
|
"""
|
|
36
|
-
List installed
|
|
42
|
+
List all installed DigitalHub runtime packages.
|
|
43
|
+
|
|
44
|
+
Scans installed packages for those matching the pattern
|
|
45
|
+
'digitalhub_runtime_*'.
|
|
37
46
|
|
|
38
47
|
Returns
|
|
39
48
|
-------
|
|
40
49
|
list[str]
|
|
41
|
-
List of
|
|
50
|
+
List of runtime package names.
|
|
51
|
+
|
|
52
|
+
Raises
|
|
53
|
+
------
|
|
54
|
+
RuntimeError
|
|
55
|
+
If an error occurs while scanning for runtime packages.
|
|
42
56
|
"""
|
|
43
57
|
pattern = r"digitalhub_runtime_.*"
|
|
44
58
|
runtimes: list[str] = []
|
|
@@ -53,11 +67,10 @@ def list_runtimes() -> list[str]:
|
|
|
53
67
|
|
|
54
68
|
def register_runtimes_entities() -> None:
|
|
55
69
|
"""
|
|
56
|
-
Register
|
|
70
|
+
Register all runtime builders and their entities into the factory.
|
|
57
71
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
None
|
|
72
|
+
Imports each runtime package and registers its entity and runtime
|
|
73
|
+
builders with the global factory instance.
|
|
61
74
|
"""
|
|
62
75
|
for package in list_runtimes():
|
|
63
76
|
module = import_module(package)
|
|
@@ -74,11 +87,15 @@ def register_runtimes_entities() -> None:
|
|
|
74
87
|
|
|
75
88
|
def register_entities() -> None:
|
|
76
89
|
"""
|
|
77
|
-
Register
|
|
90
|
+
Register core entity builders into the factory.
|
|
78
91
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
Imports the core entities module and registers all entity builders
|
|
93
|
+
with the global factory instance.
|
|
94
|
+
|
|
95
|
+
Raises
|
|
96
|
+
------
|
|
97
|
+
RuntimeError
|
|
98
|
+
If registration of core entities fails.
|
|
82
99
|
"""
|
|
83
100
|
try:
|
|
84
101
|
module = import_module("digitalhub.entities.builders")
|
digitalhub/runtimes/_base.py
CHANGED
|
@@ -3,14 +3,14 @@ from __future__ import annotations
|
|
|
3
3
|
from abc import abstractmethod
|
|
4
4
|
from typing import Any, Callable
|
|
5
5
|
|
|
6
|
-
from digitalhub.factory.
|
|
6
|
+
from digitalhub.factory.factory import factory
|
|
7
7
|
from digitalhub.utils.exceptions import EntityError
|
|
8
8
|
from digitalhub.utils.logger import LOGGER
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class Runtime:
|
|
12
12
|
"""
|
|
13
|
-
Base Runtime class.
|
|
13
|
+
Base Runtime class for executing tasks in the DigitalHub platform.
|
|
14
14
|
|
|
15
15
|
Runtimes are the entities responsible for the actual execution
|
|
16
16
|
of a given run. They are highly specialized components which
|
|
@@ -25,13 +25,13 @@ class Runtime:
|
|
|
25
25
|
@abstractmethod
|
|
26
26
|
def build(self, executable: dict, task: dict, run: dict) -> dict:
|
|
27
27
|
"""
|
|
28
|
-
Build run
|
|
28
|
+
Build run specification from executable, task and run configurations.
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
31
|
@abstractmethod
|
|
32
32
|
def run(self, run: dict) -> dict:
|
|
33
33
|
"""
|
|
34
|
-
Execute run task.
|
|
34
|
+
Execute run task based on the run specification.
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
37
|
##############################
|
|
@@ -40,9 +40,10 @@ class Runtime:
|
|
|
40
40
|
|
|
41
41
|
def _validate_task(self, run: dict) -> str:
|
|
42
42
|
"""
|
|
43
|
-
Check if task is allowed
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
Check if task is allowed by validating against allowed actions.
|
|
44
|
+
|
|
45
|
+
This method presumes that the runtime holds a list of allowed actions
|
|
46
|
+
in the self.allowed_actions attribute.
|
|
46
47
|
|
|
47
48
|
Parameters
|
|
48
49
|
----------
|
|
@@ -52,7 +53,12 @@ class Runtime:
|
|
|
52
53
|
Returns
|
|
53
54
|
-------
|
|
54
55
|
str
|
|
55
|
-
|
|
56
|
+
The validated action name to execute.
|
|
57
|
+
|
|
58
|
+
Raises
|
|
59
|
+
------
|
|
60
|
+
RuntimeError
|
|
61
|
+
If the run specification is malformed or task is not allowed.
|
|
56
62
|
"""
|
|
57
63
|
try:
|
|
58
64
|
task_kind = run["spec"]["task"].split(":")[0]
|
|
@@ -62,7 +68,7 @@ class Runtime:
|
|
|
62
68
|
raise RuntimeError(msg)
|
|
63
69
|
|
|
64
70
|
try:
|
|
65
|
-
return get_action_from_task_kind(task_kind, task_kind)
|
|
71
|
+
return factory.get_action_from_task_kind(task_kind, task_kind)
|
|
66
72
|
except EntityError:
|
|
67
73
|
msg = f"Task {task_kind} not allowed."
|
|
68
74
|
LOGGER.exception(msg)
|
|
@@ -71,7 +77,7 @@ class Runtime:
|
|
|
71
77
|
@staticmethod
|
|
72
78
|
def _execute(func: Callable, *args, **kwargs) -> Any:
|
|
73
79
|
"""
|
|
74
|
-
Execute function.
|
|
80
|
+
Execute a function with provided arguments safely.
|
|
75
81
|
|
|
76
82
|
Parameters
|
|
77
83
|
----------
|
|
@@ -85,7 +91,12 @@ class Runtime:
|
|
|
85
91
|
Returns
|
|
86
92
|
-------
|
|
87
93
|
Any
|
|
88
|
-
Function
|
|
94
|
+
Function return value.
|
|
95
|
+
|
|
96
|
+
Raises
|
|
97
|
+
------
|
|
98
|
+
RuntimeError
|
|
99
|
+
If any exception occurs during function execution.
|
|
89
100
|
"""
|
|
90
101
|
try:
|
|
91
102
|
return func(*args, **kwargs)
|
digitalhub/runtimes/builder.py
CHANGED
|
@@ -10,10 +10,23 @@ if typing.TYPE_CHECKING:
|
|
|
10
10
|
|
|
11
11
|
class RuntimeBuilder:
|
|
12
12
|
"""
|
|
13
|
-
Builder class for
|
|
13
|
+
Builder class for instantiating runtime objects.
|
|
14
|
+
|
|
15
|
+
This class implements the Builder pattern to create Runtime instances.
|
|
16
|
+
Subclasses must set the RUNTIME_CLASS class variable to specify which
|
|
17
|
+
Runtime implementation to build.
|
|
18
|
+
|
|
19
|
+
Attributes
|
|
20
|
+
----------
|
|
21
|
+
RUNTIME_CLASS : Runtime
|
|
22
|
+
The Runtime class to be instantiated by this builder.
|
|
23
|
+
|
|
24
|
+
Raises
|
|
25
|
+
------
|
|
26
|
+
BuilderError
|
|
27
|
+
If RUNTIME_CLASS is not set in the implementing class.
|
|
14
28
|
"""
|
|
15
29
|
|
|
16
|
-
# Class variables
|
|
17
30
|
RUNTIME_CLASS: Runtime = None
|
|
18
31
|
|
|
19
32
|
def __init__(self) -> None:
|
|
@@ -27,6 +40,6 @@ class RuntimeBuilder:
|
|
|
27
40
|
Returns
|
|
28
41
|
-------
|
|
29
42
|
Runtime
|
|
30
|
-
Runtime
|
|
43
|
+
A new instance of the configured Runtime class.
|
|
31
44
|
"""
|
|
32
45
|
return self.RUNTIME_CLASS(project, *args, **kwargs)
|
digitalhub/runtimes/enums.py
CHANGED
|
@@ -5,7 +5,17 @@ from enum import Enum
|
|
|
5
5
|
|
|
6
6
|
class RuntimeEnvVar(Enum):
|
|
7
7
|
"""
|
|
8
|
-
Environment variables.
|
|
8
|
+
Environment variables used by runtime execution environments.
|
|
9
|
+
|
|
10
|
+
These variables are automatically set in the runtime context
|
|
11
|
+
and can be accessed during task execution.
|
|
12
|
+
|
|
13
|
+
Values
|
|
14
|
+
------
|
|
15
|
+
PROJECT : str
|
|
16
|
+
Environment variable name for the current project identifier
|
|
17
|
+
RUN_ID : str
|
|
18
|
+
Environment variable name for the current run identifier
|
|
9
19
|
"""
|
|
10
20
|
|
|
11
21
|
PROJECT = "PROJECT_NAME"
|
|
@@ -22,8 +22,8 @@ AUTH_KEY = "_auth"
|
|
|
22
22
|
|
|
23
23
|
# API levels that are supported
|
|
24
24
|
MAX_API_LEVEL = 20
|
|
25
|
-
MIN_API_LEVEL =
|
|
26
|
-
LIB_VERSION =
|
|
25
|
+
MIN_API_LEVEL = 11
|
|
26
|
+
LIB_VERSION = 11
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class ClientDHCoreConfigurator:
|
|
@@ -31,10 +31,29 @@ class ClientDHCoreConfigurator:
|
|
|
31
31
|
Configurator object used to configure the client.
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
+
def __init__(self) -> None:
|
|
35
|
+
self._current_env = configurator.get_current_env()
|
|
36
|
+
|
|
34
37
|
##############################
|
|
35
38
|
# Configuration methods
|
|
36
39
|
##############################
|
|
37
40
|
|
|
41
|
+
def check_config(self) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Check if the config is valid.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
config : dict
|
|
48
|
+
Configuration dictionary.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
None
|
|
53
|
+
"""
|
|
54
|
+
if configurator.get_current_env() != self._current_env:
|
|
55
|
+
self.configure()
|
|
56
|
+
|
|
38
57
|
def configure(self, config: dict | None = None) -> None:
|
|
39
58
|
"""
|
|
40
59
|
Configure the client attributes with config (given or from
|
|
@@ -140,10 +159,10 @@ class ClientDHCoreConfigurator:
|
|
|
140
159
|
None
|
|
141
160
|
"""
|
|
142
161
|
# Give priority to access token
|
|
143
|
-
access_token =
|
|
162
|
+
access_token = self._load_dhcore_oauth_vars(DhcoreEnvVar.ACCESS_TOKEN.value)
|
|
144
163
|
if access_token is not None:
|
|
145
164
|
configurator.set_credential(AUTH_KEY, AuthType.OAUTH2.value)
|
|
146
|
-
configurator.set_credential(DhcoreEnvVar.ACCESS_TOKEN.value, access_token)
|
|
165
|
+
configurator.set_credential(DhcoreEnvVar.ACCESS_TOKEN.value.removeprefix("DHCORE_"), access_token)
|
|
147
166
|
|
|
148
167
|
# Fallback to basic
|
|
149
168
|
else:
|
|
@@ -204,7 +223,7 @@ class ClientDHCoreConfigurator:
|
|
|
204
223
|
elif self.oauth2_auth():
|
|
205
224
|
if "headers" not in kwargs:
|
|
206
225
|
kwargs["headers"] = {}
|
|
207
|
-
access_token = creds[DhcoreEnvVar.ACCESS_TOKEN.value]
|
|
226
|
+
access_token = creds[DhcoreEnvVar.ACCESS_TOKEN.value.removeprefix("DHCORE_")]
|
|
208
227
|
kwargs["headers"]["Authorization"] = f"Bearer {access_token}"
|
|
209
228
|
return kwargs
|
|
210
229
|
|
|
@@ -227,7 +246,7 @@ class ClientDHCoreConfigurator:
|
|
|
227
246
|
|
|
228
247
|
# Otherwise try token from file
|
|
229
248
|
if response.status_code in (400, 401, 403):
|
|
230
|
-
refresh_token = configurator.load_from_file(DhcoreEnvVar.REFRESH_TOKEN.value)
|
|
249
|
+
refresh_token = configurator.load_from_file(DhcoreEnvVar.REFRESH_TOKEN.value.removeprefix("DHCORE_"))
|
|
231
250
|
response = self._call_refresh_token_endpoint(url, refresh_token)
|
|
232
251
|
|
|
233
252
|
response.raise_for_status()
|
|
@@ -248,12 +267,43 @@ class ClientDHCoreConfigurator:
|
|
|
248
267
|
-------
|
|
249
268
|
None
|
|
250
269
|
"""
|
|
251
|
-
keys =
|
|
270
|
+
keys = [
|
|
271
|
+
*self._remove_prefix_dhcore(list_enum(DhcoreEnvVar)),
|
|
272
|
+
*list_enum(S3StoreEnv),
|
|
273
|
+
*list_enum(SqlStoreEnv),
|
|
274
|
+
]
|
|
252
275
|
for key in keys:
|
|
253
|
-
if (value := response.get(key.lower()
|
|
276
|
+
if (value := response.get(key.lower())) is not None:
|
|
254
277
|
configurator.set_credential(key, value)
|
|
255
278
|
configurator.write_env(keys)
|
|
256
279
|
|
|
280
|
+
def _remove_prefix_dhcore(self, keys: list[str]) -> list[str]:
|
|
281
|
+
"""
|
|
282
|
+
Remove prefix from selected keys. (Compatibility with CLI)
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
keys : list[str]
|
|
287
|
+
List of keys.
|
|
288
|
+
|
|
289
|
+
Returns
|
|
290
|
+
-------
|
|
291
|
+
list[str]
|
|
292
|
+
List of keys without prefix.
|
|
293
|
+
"""
|
|
294
|
+
new_list = []
|
|
295
|
+
for key in keys:
|
|
296
|
+
if key in (
|
|
297
|
+
DhcoreEnvVar.REFRESH_TOKEN.value,
|
|
298
|
+
DhcoreEnvVar.ACCESS_TOKEN.value,
|
|
299
|
+
DhcoreEnvVar.ISSUER.value,
|
|
300
|
+
DhcoreEnvVar.CLIENT_ID.value,
|
|
301
|
+
):
|
|
302
|
+
new_list.append(key.removeprefix("DHCORE_"))
|
|
303
|
+
else:
|
|
304
|
+
new_list.append(key)
|
|
305
|
+
return new_list
|
|
306
|
+
|
|
257
307
|
def _get_refresh_endpoint(self) -> str:
|
|
258
308
|
"""
|
|
259
309
|
Get the refresh endpoint.
|
|
@@ -264,11 +314,11 @@ class ClientDHCoreConfigurator:
|
|
|
264
314
|
Refresh endpoint.
|
|
265
315
|
"""
|
|
266
316
|
# Get issuer endpoint
|
|
267
|
-
endpoint_issuer =
|
|
317
|
+
endpoint_issuer = self._load_dhcore_oauth_vars(DhcoreEnvVar.ISSUER.value)
|
|
268
318
|
if endpoint_issuer is None:
|
|
269
319
|
raise ClientError("Issuer endpoint not set.")
|
|
270
320
|
endpoint_issuer = self._sanitize_endpoint(endpoint_issuer)
|
|
271
|
-
configurator.set_credential(DhcoreEnvVar.ISSUER.value, endpoint_issuer)
|
|
321
|
+
configurator.set_credential(DhcoreEnvVar.ISSUER.value.removeprefix("DHCORE_"), endpoint_issuer)
|
|
272
322
|
|
|
273
323
|
# Standard issuer endpoint path
|
|
274
324
|
url = endpoint_issuer + "/.well-known/openid-configuration"
|
|
@@ -295,7 +345,7 @@ class ClientDHCoreConfigurator:
|
|
|
295
345
|
Response object.
|
|
296
346
|
"""
|
|
297
347
|
# Get client id
|
|
298
|
-
client_id =
|
|
348
|
+
client_id = self._load_dhcore_oauth_vars(DhcoreEnvVar.CLIENT_ID.value)
|
|
299
349
|
if client_id is None:
|
|
300
350
|
raise ClientError("Client id not set.")
|
|
301
351
|
|
|
@@ -308,3 +358,22 @@ class ClientDHCoreConfigurator:
|
|
|
308
358
|
}
|
|
309
359
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
|
310
360
|
return request("POST", url, data=payload, headers=headers, timeout=60)
|
|
361
|
+
|
|
362
|
+
def _load_dhcore_oauth_vars(self, oauth_var: str) -> str | None:
|
|
363
|
+
"""
|
|
364
|
+
Load DHCore oauth variables.
|
|
365
|
+
|
|
366
|
+
Parameters
|
|
367
|
+
----------
|
|
368
|
+
oauth_var : str
|
|
369
|
+
The oauth variable to load.
|
|
370
|
+
|
|
371
|
+
Returns
|
|
372
|
+
-------
|
|
373
|
+
str
|
|
374
|
+
The oauth variable.
|
|
375
|
+
"""
|
|
376
|
+
read_var = configurator.load_from_env(oauth_var)
|
|
377
|
+
if read_var is None:
|
|
378
|
+
read_var = configurator.load_from_file(oauth_var.removeprefix("DHCORE_"))
|
|
379
|
+
return read_var
|