digitalhub 0.11.0b7__py3-none-any.whl → 0.13.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 +4 -1
- digitalhub/context/api.py +9 -5
- digitalhub/context/builder.py +7 -5
- digitalhub/context/context.py +13 -1
- digitalhub/entities/__init__.py +3 -0
- digitalhub/entities/_base/__init__.py +3 -0
- digitalhub/entities/_base/_base/__init__.py +3 -0
- digitalhub/entities/_base/_base/entity.py +4 -0
- digitalhub/entities/_base/context/__init__.py +3 -0
- digitalhub/entities/_base/context/entity.py +4 -0
- digitalhub/entities/_base/entity/__init__.py +3 -0
- digitalhub/entities/_base/entity/_constructors/__init__.py +3 -0
- digitalhub/entities/_base/entity/_constructors/metadata.py +4 -0
- digitalhub/entities/_base/entity/_constructors/name.py +4 -0
- digitalhub/entities/_base/entity/_constructors/spec.py +4 -0
- digitalhub/entities/_base/entity/_constructors/status.py +4 -0
- digitalhub/entities/_base/entity/_constructors/uuid.py +4 -0
- digitalhub/entities/_base/entity/builder.py +4 -0
- digitalhub/entities/_base/entity/entity.py +4 -0
- digitalhub/entities/_base/entity/metadata.py +4 -0
- digitalhub/entities/_base/entity/spec.py +4 -0
- digitalhub/entities/_base/entity/status.py +4 -0
- digitalhub/entities/_base/executable/__init__.py +3 -0
- digitalhub/entities/_base/executable/entity.py +109 -57
- digitalhub/entities/_base/material/__init__.py +3 -0
- digitalhub/entities/_base/material/entity.py +15 -18
- digitalhub/entities/_base/material/spec.py +4 -0
- digitalhub/entities/_base/material/status.py +4 -0
- digitalhub/entities/_base/material/utils.py +5 -1
- digitalhub/entities/_base/runtime_entity/__init__.py +3 -0
- digitalhub/entities/_base/runtime_entity/builder.py +4 -0
- digitalhub/entities/_base/unversioned/__init__.py +3 -0
- digitalhub/entities/_base/unversioned/builder.py +4 -0
- digitalhub/entities/_base/unversioned/entity.py +4 -0
- digitalhub/entities/_base/versioned/__init__.py +3 -0
- digitalhub/entities/_base/versioned/builder.py +4 -0
- digitalhub/entities/_base/versioned/entity.py +4 -0
- digitalhub/entities/_commons/__init__.py +3 -0
- digitalhub/entities/_commons/enums.py +4 -0
- digitalhub/entities/_commons/metrics.py +68 -30
- digitalhub/entities/_commons/utils.py +40 -9
- digitalhub/entities/_processors/__init__.py +3 -0
- digitalhub/entities/_processors/base.py +154 -79
- digitalhub/entities/_processors/context.py +370 -215
- digitalhub/entities/_processors/utils.py +78 -30
- digitalhub/entities/artifact/__init__.py +3 -0
- digitalhub/entities/artifact/_base/__init__.py +3 -0
- digitalhub/entities/artifact/_base/builder.py +4 -0
- digitalhub/entities/artifact/_base/entity.py +4 -0
- digitalhub/entities/artifact/_base/spec.py +4 -0
- digitalhub/entities/artifact/_base/status.py +4 -0
- digitalhub/entities/artifact/artifact/__init__.py +3 -0
- digitalhub/entities/artifact/artifact/builder.py +4 -0
- digitalhub/entities/artifact/artifact/entity.py +4 -0
- digitalhub/entities/artifact/artifact/spec.py +4 -0
- digitalhub/entities/artifact/artifact/status.py +4 -0
- digitalhub/entities/artifact/crud.py +8 -0
- digitalhub/entities/artifact/utils.py +32 -13
- digitalhub/entities/builders.py +4 -0
- digitalhub/entities/dataitem/__init__.py +3 -0
- digitalhub/entities/dataitem/_base/__init__.py +3 -0
- digitalhub/entities/dataitem/_base/builder.py +4 -0
- digitalhub/entities/dataitem/_base/entity.py +4 -0
- digitalhub/entities/dataitem/_base/spec.py +4 -0
- digitalhub/entities/dataitem/_base/status.py +4 -0
- digitalhub/entities/dataitem/crud.py +18 -2
- digitalhub/entities/dataitem/dataitem/__init__.py +3 -0
- digitalhub/entities/dataitem/dataitem/builder.py +4 -0
- digitalhub/entities/dataitem/dataitem/entity.py +4 -0
- digitalhub/entities/dataitem/dataitem/spec.py +4 -0
- digitalhub/entities/dataitem/dataitem/status.py +4 -0
- digitalhub/entities/dataitem/iceberg/__init__.py +3 -0
- digitalhub/entities/dataitem/iceberg/builder.py +4 -0
- digitalhub/entities/dataitem/iceberg/entity.py +4 -0
- digitalhub/entities/dataitem/iceberg/spec.py +4 -0
- digitalhub/entities/dataitem/iceberg/status.py +4 -0
- digitalhub/entities/dataitem/table/__init__.py +3 -0
- digitalhub/entities/dataitem/table/builder.py +4 -0
- digitalhub/entities/dataitem/table/entity.py +7 -3
- digitalhub/entities/dataitem/table/models.py +4 -0
- digitalhub/entities/dataitem/table/spec.py +4 -0
- digitalhub/entities/dataitem/table/status.py +4 -0
- digitalhub/entities/dataitem/table/utils.py +4 -0
- digitalhub/entities/dataitem/utils.py +88 -35
- digitalhub/entities/function/__init__.py +3 -0
- digitalhub/entities/function/_base/__init__.py +3 -0
- digitalhub/entities/function/_base/builder.py +4 -0
- digitalhub/entities/function/_base/entity.py +4 -0
- digitalhub/entities/function/_base/spec.py +4 -0
- digitalhub/entities/function/_base/status.py +4 -0
- digitalhub/entities/function/crud.py +4 -0
- digitalhub/entities/model/__init__.py +3 -0
- digitalhub/entities/model/_base/__init__.py +3 -0
- digitalhub/entities/model/_base/builder.py +4 -0
- digitalhub/entities/model/_base/entity.py +4 -0
- digitalhub/entities/model/_base/spec.py +4 -0
- digitalhub/entities/model/_base/status.py +4 -0
- digitalhub/entities/model/crud.py +8 -0
- digitalhub/entities/model/huggingface/__init__.py +3 -0
- digitalhub/entities/model/huggingface/builder.py +4 -0
- digitalhub/entities/model/huggingface/entity.py +4 -0
- digitalhub/entities/model/huggingface/spec.py +4 -0
- digitalhub/entities/model/huggingface/status.py +4 -0
- digitalhub/entities/model/mlflow/__init__.py +3 -0
- digitalhub/entities/model/mlflow/builder.py +4 -0
- digitalhub/entities/model/mlflow/entity.py +4 -0
- digitalhub/entities/model/mlflow/models.py +4 -0
- digitalhub/entities/model/mlflow/spec.py +4 -0
- digitalhub/entities/model/mlflow/status.py +4 -0
- digitalhub/entities/model/mlflow/utils.py +4 -0
- digitalhub/entities/model/model/__init__.py +3 -0
- digitalhub/entities/model/model/builder.py +4 -0
- digitalhub/entities/model/model/entity.py +4 -0
- digitalhub/entities/model/model/spec.py +4 -0
- digitalhub/entities/model/model/status.py +4 -0
- digitalhub/entities/model/sklearn/__init__.py +3 -0
- digitalhub/entities/model/sklearn/builder.py +4 -0
- digitalhub/entities/model/sklearn/entity.py +4 -0
- digitalhub/entities/model/sklearn/spec.py +4 -0
- digitalhub/entities/model/sklearn/status.py +4 -0
- digitalhub/entities/model/utils.py +32 -13
- digitalhub/entities/project/__init__.py +3 -0
- digitalhub/entities/project/_base/__init__.py +3 -0
- digitalhub/entities/project/_base/builder.py +4 -0
- digitalhub/entities/project/_base/entity.py +4 -2
- digitalhub/entities/project/_base/models.py +4 -0
- digitalhub/entities/project/_base/spec.py +4 -0
- digitalhub/entities/project/_base/status.py +4 -0
- digitalhub/entities/project/crud.py +4 -0
- digitalhub/entities/project/utils.py +4 -0
- digitalhub/entities/run/__init__.py +3 -0
- digitalhub/entities/run/_base/__init__.py +3 -0
- digitalhub/entities/run/_base/builder.py +4 -0
- digitalhub/entities/run/_base/entity.py +6 -2
- digitalhub/entities/run/_base/spec.py +4 -0
- digitalhub/entities/run/_base/status.py +4 -0
- digitalhub/entities/run/crud.py +4 -0
- digitalhub/entities/secret/__init__.py +3 -0
- digitalhub/entities/secret/_base/__init__.py +3 -0
- digitalhub/entities/secret/_base/builder.py +4 -0
- digitalhub/entities/secret/_base/entity.py +4 -0
- digitalhub/entities/secret/_base/spec.py +4 -0
- digitalhub/entities/secret/_base/status.py +4 -0
- digitalhub/entities/secret/crud.py +4 -0
- digitalhub/entities/task/__init__.py +3 -0
- digitalhub/entities/task/_base/__init__.py +3 -0
- digitalhub/entities/task/_base/builder.py +4 -0
- digitalhub/entities/task/_base/entity.py +4 -0
- digitalhub/entities/task/_base/models.py +16 -3
- digitalhub/entities/task/_base/spec.py +4 -0
- digitalhub/entities/task/_base/status.py +4 -0
- digitalhub/entities/task/_base/utils.py +4 -0
- digitalhub/entities/task/crud.py +4 -0
- digitalhub/entities/trigger/__init__.py +3 -0
- digitalhub/entities/trigger/_base/__init__.py +3 -0
- digitalhub/entities/trigger/_base/builder.py +4 -0
- digitalhub/entities/trigger/_base/entity.py +15 -0
- digitalhub/entities/trigger/_base/spec.py +4 -0
- digitalhub/entities/trigger/_base/status.py +4 -0
- digitalhub/entities/trigger/crud.py +4 -0
- digitalhub/entities/trigger/lifecycle/__init__.py +3 -0
- digitalhub/entities/trigger/lifecycle/builder.py +4 -0
- digitalhub/entities/trigger/lifecycle/entity.py +4 -0
- digitalhub/entities/trigger/lifecycle/spec.py +4 -0
- digitalhub/entities/trigger/lifecycle/status.py +4 -0
- digitalhub/entities/trigger/scheduler/__init__.py +3 -0
- digitalhub/entities/trigger/scheduler/builder.py +4 -0
- digitalhub/entities/trigger/scheduler/entity.py +4 -0
- digitalhub/entities/trigger/scheduler/spec.py +4 -0
- digitalhub/entities/trigger/scheduler/status.py +4 -0
- digitalhub/entities/workflow/__init__.py +3 -0
- digitalhub/entities/workflow/_base/__init__.py +3 -0
- digitalhub/entities/workflow/_base/builder.py +4 -0
- digitalhub/entities/workflow/_base/entity.py +4 -0
- digitalhub/entities/workflow/_base/spec.py +4 -0
- digitalhub/entities/workflow/_base/status.py +4 -0
- digitalhub/entities/workflow/crud.py +4 -0
- digitalhub/factory/__init__.py +3 -0
- digitalhub/factory/factory.py +29 -3
- digitalhub/factory/utils.py +15 -3
- digitalhub/runtimes/__init__.py +3 -0
- digitalhub/runtimes/_base.py +5 -1
- digitalhub/runtimes/builder.py +22 -1
- digitalhub/runtimes/enums.py +4 -0
- digitalhub/stores/__init__.py +3 -0
- digitalhub/stores/client/__init__.py +15 -0
- digitalhub/stores/client/_base/__init__.py +3 -0
- digitalhub/stores/client/_base/api_builder.py +18 -0
- digitalhub/stores/client/_base/client.py +97 -0
- digitalhub/stores/client/_base/key_builder.py +32 -0
- digitalhub/stores/client/_base/params_builder.py +18 -0
- digitalhub/stores/client/api.py +14 -5
- digitalhub/stores/client/builder.py +7 -1
- digitalhub/stores/client/dhcore/__init__.py +3 -0
- digitalhub/stores/client/dhcore/api_builder.py +21 -0
- digitalhub/stores/client/dhcore/client.py +329 -70
- digitalhub/stores/client/dhcore/configurator.py +489 -193
- digitalhub/stores/client/dhcore/enums.py +7 -0
- digitalhub/stores/client/dhcore/error_parser.py +39 -1
- digitalhub/stores/client/dhcore/key_builder.py +4 -0
- digitalhub/stores/client/dhcore/models.py +4 -0
- digitalhub/stores/client/dhcore/params_builder.py +117 -17
- digitalhub/stores/client/dhcore/utils.py +44 -22
- digitalhub/stores/client/local/__init__.py +3 -0
- digitalhub/stores/client/local/api_builder.py +21 -0
- digitalhub/stores/client/local/client.py +10 -8
- digitalhub/stores/client/local/enums.py +4 -0
- digitalhub/stores/client/local/key_builder.py +4 -0
- digitalhub/stores/client/local/params_builder.py +4 -0
- digitalhub/stores/credentials/__init__.py +3 -0
- digitalhub/stores/credentials/api.py +35 -0
- digitalhub/stores/credentials/configurator.py +210 -0
- digitalhub/stores/credentials/enums.py +68 -0
- digitalhub/stores/credentials/handler.py +176 -0
- digitalhub/stores/credentials/ini_module.py +164 -0
- digitalhub/stores/credentials/store.py +81 -0
- digitalhub/stores/data/__init__.py +3 -0
- digitalhub/stores/data/_base/__init__.py +3 -0
- digitalhub/stores/data/_base/store.py +31 -9
- digitalhub/stores/data/api.py +53 -9
- digitalhub/stores/data/builder.py +94 -41
- digitalhub/stores/data/enums.py +4 -0
- digitalhub/stores/data/local/__init__.py +3 -0
- digitalhub/stores/data/local/store.py +8 -7
- digitalhub/stores/data/remote/__init__.py +3 -0
- digitalhub/stores/data/remote/store.py +8 -7
- digitalhub/stores/data/s3/__init__.py +3 -0
- digitalhub/stores/data/s3/configurator.py +69 -80
- digitalhub/stores/data/s3/store.py +73 -81
- digitalhub/stores/data/s3/utils.py +14 -10
- digitalhub/stores/data/sql/__init__.py +3 -0
- digitalhub/stores/data/sql/configurator.py +80 -73
- digitalhub/stores/data/sql/store.py +195 -102
- digitalhub/stores/readers/__init__.py +3 -0
- digitalhub/stores/readers/data/__init__.py +3 -0
- digitalhub/stores/readers/data/_base/__init__.py +3 -0
- digitalhub/stores/readers/data/_base/builder.py +4 -0
- digitalhub/stores/readers/data/_base/reader.py +4 -0
- digitalhub/stores/readers/data/api.py +4 -0
- digitalhub/stores/readers/data/factory.py +4 -0
- digitalhub/stores/readers/data/pandas/__init__.py +3 -0
- digitalhub/stores/readers/data/pandas/builder.py +4 -0
- digitalhub/stores/readers/data/pandas/reader.py +4 -0
- digitalhub/stores/readers/query/__init__.py +3 -0
- digitalhub/utils/__init__.py +3 -0
- digitalhub/utils/enums.py +4 -0
- digitalhub/utils/exceptions.py +10 -0
- digitalhub/utils/file_utils.py +57 -30
- digitalhub/utils/generic_utils.py +45 -33
- digitalhub/utils/git_utils.py +28 -14
- digitalhub/utils/io_utils.py +23 -18
- digitalhub/utils/logger.py +4 -0
- digitalhub/utils/types.py +4 -0
- digitalhub/utils/uri_utils.py +35 -31
- digitalhub-0.13.0.dist-info/METADATA +301 -0
- digitalhub-0.13.0.dist-info/RECORD +259 -0
- digitalhub-0.13.0.dist-info/licenses/AUTHORS +5 -0
- digitalhub-0.13.0.dist-info/licenses/LICENSE +201 -0
- digitalhub/entities/_commons/types.py +0 -5
- digitalhub/stores/configurator/__init__.py +0 -0
- digitalhub/stores/configurator/api.py +0 -31
- digitalhub/stores/configurator/configurator.py +0 -198
- digitalhub/stores/configurator/credentials_store.py +0 -65
- digitalhub/stores/configurator/enums.py +0 -21
- digitalhub/stores/configurator/ini_module.py +0 -128
- digitalhub/stores/data/s3/enums.py +0 -16
- digitalhub/stores/data/sql/enums.py +0 -16
- digitalhub/stores/data/utils.py +0 -34
- digitalhub-0.11.0b7.dist-info/METADATA +0 -259
- digitalhub-0.11.0b7.dist-info/RECORD +0 -261
- digitalhub-0.11.0b7.dist-info/licenses/LICENSE.txt +0 -216
- {digitalhub-0.11.0b7.dist-info → digitalhub-0.13.0.dist-info}/WHEEL +0 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
1
5
|
from __future__ import annotations
|
|
2
6
|
|
|
7
|
+
import typing
|
|
3
8
|
from io import BytesIO
|
|
4
9
|
from pathlib import Path
|
|
5
10
|
from typing import Any, Type
|
|
@@ -7,17 +12,21 @@ from urllib.parse import urlparse
|
|
|
7
12
|
|
|
8
13
|
import boto3
|
|
9
14
|
import botocore.client # pylint: disable=unused-import
|
|
15
|
+
from boto3.s3.transfer import TransferConfig
|
|
10
16
|
from botocore.exceptions import ClientError, NoCredentialsError
|
|
11
17
|
|
|
12
|
-
from digitalhub.stores.configurator.enums import CredsOrigin
|
|
13
18
|
from digitalhub.stores.data._base.store import Store
|
|
14
|
-
from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
|
|
15
19
|
from digitalhub.stores.data.s3.utils import get_bucket_name
|
|
16
20
|
from digitalhub.stores.readers.data.api import get_reader_by_object
|
|
17
|
-
from digitalhub.utils.exceptions import StoreError
|
|
21
|
+
from digitalhub.utils.exceptions import ConfigError, StoreError
|
|
18
22
|
from digitalhub.utils.file_utils import get_file_info_from_s3, get_file_mime_type
|
|
19
23
|
from digitalhub.utils.types import SourcesOrListOfSources
|
|
20
24
|
|
|
25
|
+
if typing.TYPE_CHECKING:
|
|
26
|
+
from digitalhub.stores.credentials.configurator import Configurator
|
|
27
|
+
from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
|
|
28
|
+
|
|
29
|
+
|
|
21
30
|
# Type aliases
|
|
22
31
|
S3Client = Type["botocore.client.S3"]
|
|
23
32
|
|
|
@@ -28,8 +37,9 @@ class S3Store(Store):
|
|
|
28
37
|
artifacts on S3 based storage.
|
|
29
38
|
"""
|
|
30
39
|
|
|
31
|
-
def __init__(self) -> None:
|
|
32
|
-
|
|
40
|
+
def __init__(self, configurator: Configurator | None = None) -> None:
|
|
41
|
+
super().__init__(configurator)
|
|
42
|
+
self._configurator: S3StoreConfigurator
|
|
33
43
|
|
|
34
44
|
##############################
|
|
35
45
|
# I/O methods
|
|
@@ -37,9 +47,8 @@ class S3Store(Store):
|
|
|
37
47
|
|
|
38
48
|
def download(
|
|
39
49
|
self,
|
|
40
|
-
|
|
50
|
+
src: str,
|
|
41
51
|
dst: Path,
|
|
42
|
-
src: list[str],
|
|
43
52
|
overwrite: bool = False,
|
|
44
53
|
) -> str:
|
|
45
54
|
"""
|
|
@@ -47,21 +56,19 @@ class S3Store(Store):
|
|
|
47
56
|
|
|
48
57
|
Parameters
|
|
49
58
|
----------
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
src : str
|
|
60
|
+
Path of the material entity.
|
|
52
61
|
dst : str
|
|
53
|
-
The destination of the
|
|
54
|
-
src : list[str]
|
|
55
|
-
List of sources.
|
|
62
|
+
The destination of the material entity on local filesystem.
|
|
56
63
|
overwrite : bool
|
|
57
64
|
Specify if overwrite existing file(s).
|
|
58
65
|
|
|
59
66
|
Returns
|
|
60
67
|
-------
|
|
61
68
|
str
|
|
62
|
-
Destination path of the downloaded
|
|
69
|
+
Destination path of the downloaded files.
|
|
63
70
|
"""
|
|
64
|
-
client, bucket = self._check_factory(
|
|
71
|
+
client, bucket = self._check_factory(src)
|
|
65
72
|
|
|
66
73
|
# Build destination directory
|
|
67
74
|
if dst.suffix == "":
|
|
@@ -70,20 +77,13 @@ class S3Store(Store):
|
|
|
70
77
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
71
78
|
|
|
72
79
|
# Handle src and tree destination
|
|
73
|
-
if self.is_partition(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
trees = [k.removeprefix(strip_root) for k in keys]
|
|
78
|
-
else:
|
|
79
|
-
keys = self._build_key_from_root(root, src)
|
|
80
|
-
trees = [s for s in src]
|
|
80
|
+
if self.is_partition(src):
|
|
81
|
+
keys = self._list_objects(client, bucket, src)
|
|
82
|
+
strip_root = self._get_key(src)
|
|
83
|
+
trees = [k.removeprefix(strip_root) for k in keys]
|
|
81
84
|
else:
|
|
82
|
-
keys = [self._get_key(
|
|
83
|
-
|
|
84
|
-
trees = [Path(self._get_key(root)).name]
|
|
85
|
-
else:
|
|
86
|
-
trees = [s for s in src]
|
|
85
|
+
keys = [self._get_key(src)]
|
|
86
|
+
trees = [Path(self._get_key(src)).name]
|
|
87
87
|
|
|
88
88
|
if len(keys) != len(trees):
|
|
89
89
|
raise StoreError("Keys and trees must have the same length.")
|
|
@@ -124,7 +124,7 @@ class S3Store(Store):
|
|
|
124
124
|
src : SourcesOrListOfSources
|
|
125
125
|
Source(s).
|
|
126
126
|
dst : str
|
|
127
|
-
The destination of the
|
|
127
|
+
The destination of the material entity on storage.
|
|
128
128
|
|
|
129
129
|
Returns
|
|
130
130
|
-------
|
|
@@ -410,7 +410,7 @@ class S3Store(Store):
|
|
|
410
410
|
src : str
|
|
411
411
|
List of sources.
|
|
412
412
|
dst : str
|
|
413
|
-
The destination of the
|
|
413
|
+
The destination of the material entity on storage.
|
|
414
414
|
client : S3Client
|
|
415
415
|
The S3 client object.
|
|
416
416
|
bucket : str
|
|
@@ -453,7 +453,7 @@ class S3Store(Store):
|
|
|
453
453
|
src : list
|
|
454
454
|
List of sources.
|
|
455
455
|
dst : str
|
|
456
|
-
The destination of the
|
|
456
|
+
The destination of the material entity on storage.
|
|
457
457
|
client : S3Client
|
|
458
458
|
The S3 client object.
|
|
459
459
|
bucket : str
|
|
@@ -493,7 +493,7 @@ class S3Store(Store):
|
|
|
493
493
|
src : str
|
|
494
494
|
List of sources.
|
|
495
495
|
dst : str
|
|
496
|
-
The destination of the
|
|
496
|
+
The destination of the material entity on storage.
|
|
497
497
|
client : S3Client
|
|
498
498
|
The S3 client object.
|
|
499
499
|
bucket : str
|
|
@@ -542,7 +542,13 @@ class S3Store(Store):
|
|
|
542
542
|
mime_type = get_file_mime_type(src)
|
|
543
543
|
if mime_type is not None:
|
|
544
544
|
extra_args["ContentType"] = mime_type
|
|
545
|
-
client.upload_file(
|
|
545
|
+
client.upload_file(
|
|
546
|
+
Filename=src,
|
|
547
|
+
Bucket=bucket,
|
|
548
|
+
Key=key,
|
|
549
|
+
ExtraArgs=extra_args,
|
|
550
|
+
Config=TransferConfig(multipart_threshold=100 * 1024 * 1024),
|
|
551
|
+
)
|
|
546
552
|
|
|
547
553
|
@staticmethod
|
|
548
554
|
def _upload_fileobject(
|
|
@@ -569,7 +575,12 @@ class S3Store(Store):
|
|
|
569
575
|
-------
|
|
570
576
|
None
|
|
571
577
|
"""
|
|
572
|
-
client.
|
|
578
|
+
client.upload_fileobj(
|
|
579
|
+
Fileobj=fileobj,
|
|
580
|
+
Bucket=bucket,
|
|
581
|
+
Key=key,
|
|
582
|
+
Config=TransferConfig(multipart_threshold=100 * 1024 * 1024),
|
|
583
|
+
)
|
|
573
584
|
|
|
574
585
|
##############################
|
|
575
586
|
# Helper methods
|
|
@@ -602,55 +613,59 @@ class S3Store(Store):
|
|
|
602
613
|
"""
|
|
603
614
|
return boto3.client("s3", **cfg)
|
|
604
615
|
|
|
605
|
-
def _check_factory(self,
|
|
616
|
+
def _check_factory(self, s3_path: str, retry: bool = True) -> tuple[S3Client, str]:
|
|
606
617
|
"""
|
|
607
|
-
|
|
618
|
+
Checks if the S3 bucket collected from the URI is accessible.
|
|
619
|
+
|
|
620
|
+
Parameters
|
|
621
|
+
----------
|
|
622
|
+
s3_path : str
|
|
623
|
+
Path to the S3 bucket (e.g., 's3://bucket/path').
|
|
624
|
+
retry : bool, optional
|
|
625
|
+
Whether to retry the operation if a ConfigError is raised. Default is True.
|
|
608
626
|
|
|
609
627
|
Returns
|
|
610
628
|
-------
|
|
611
|
-
tuple
|
|
612
|
-
|
|
613
|
-
"""
|
|
614
|
-
bucket = self._get_bucket(root)
|
|
629
|
+
tuple of S3Client and str
|
|
630
|
+
Tuple containing the S3 client object and the name of the S3 bucket.
|
|
615
631
|
|
|
616
|
-
|
|
632
|
+
Raises
|
|
633
|
+
------
|
|
634
|
+
ConfigError
|
|
635
|
+
If access to the specified bucket is not available and retry is False.
|
|
636
|
+
"""
|
|
637
|
+
bucket = self._get_bucket(s3_path)
|
|
617
638
|
try:
|
|
618
|
-
cfg = self._configurator.
|
|
639
|
+
cfg = self._configurator.get_client_config()
|
|
619
640
|
client = self._get_client(cfg)
|
|
620
641
|
self._check_access_to_storage(client, bucket)
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
return client, bucket
|
|
642
|
+
return client, bucket
|
|
643
|
+
except ConfigError as e:
|
|
644
|
+
if retry:
|
|
645
|
+
self._configurator.eval_change_origin()
|
|
646
|
+
return self._check_factory(s3_path, False)
|
|
647
|
+
raise e
|
|
629
648
|
|
|
630
649
|
def _check_access_to_storage(self, client: S3Client, bucket: str) -> None:
|
|
631
650
|
"""
|
|
632
|
-
|
|
651
|
+
Checks if the S3 bucket is accessible by sending a head_bucket request.
|
|
633
652
|
|
|
634
653
|
Parameters
|
|
635
654
|
----------
|
|
636
655
|
client : S3Client
|
|
637
|
-
|
|
656
|
+
S3 client object.
|
|
638
657
|
bucket : str
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
Returns
|
|
642
|
-
-------
|
|
643
|
-
None
|
|
658
|
+
Name of the S3 bucket.
|
|
644
659
|
|
|
645
660
|
Raises
|
|
646
661
|
------
|
|
647
|
-
|
|
662
|
+
ConfigError
|
|
648
663
|
If access to the specified bucket is not available.
|
|
649
664
|
"""
|
|
650
665
|
try:
|
|
651
666
|
client.head_bucket(Bucket=bucket)
|
|
652
667
|
except (ClientError, NoCredentialsError) as err:
|
|
653
|
-
raise
|
|
668
|
+
raise ConfigError(f"No access to s3 bucket! Error: {err}")
|
|
654
669
|
|
|
655
670
|
@staticmethod
|
|
656
671
|
def _get_key(path: str) -> str:
|
|
@@ -672,29 +687,6 @@ class S3Store(Store):
|
|
|
672
687
|
key = key[1:]
|
|
673
688
|
return key
|
|
674
689
|
|
|
675
|
-
def _build_key_from_root(self, root: str, paths: list[str]) -> list[str]:
|
|
676
|
-
"""
|
|
677
|
-
Method to build object path.
|
|
678
|
-
|
|
679
|
-
Parameters
|
|
680
|
-
----------
|
|
681
|
-
root : str
|
|
682
|
-
The root of the object path.
|
|
683
|
-
paths : list[str]
|
|
684
|
-
The path to build.
|
|
685
|
-
|
|
686
|
-
Returns
|
|
687
|
-
-------
|
|
688
|
-
list[str]
|
|
689
|
-
List of keys.
|
|
690
|
-
"""
|
|
691
|
-
keys = []
|
|
692
|
-
for path in paths:
|
|
693
|
-
clean_path = self._get_key(path)
|
|
694
|
-
key = self._get_key(f"{root}{clean_path}")
|
|
695
|
-
keys.append(key)
|
|
696
|
-
return keys
|
|
697
|
-
|
|
698
690
|
def _list_objects(self, client: S3Client, bucket: str, partition: str) -> list[str]:
|
|
699
691
|
"""
|
|
700
692
|
List objects in a S3 partition.
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
1
5
|
from __future__ import annotations
|
|
2
6
|
|
|
3
7
|
from pathlib import Path
|
|
@@ -5,41 +9,41 @@ from urllib.parse import urlparse
|
|
|
5
9
|
|
|
6
10
|
from boto3 import client as boto3_client
|
|
7
11
|
|
|
8
|
-
from digitalhub.stores.
|
|
12
|
+
from digitalhub.stores.credentials.enums import CredsOrigin
|
|
9
13
|
from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
|
|
10
14
|
from digitalhub.utils.exceptions import StoreError
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
def get_bucket_name(path: str) -> str:
|
|
14
18
|
"""
|
|
15
|
-
|
|
19
|
+
Extract the bucket name from an S3 path.
|
|
16
20
|
|
|
17
21
|
Parameters
|
|
18
22
|
----------
|
|
19
23
|
path : str
|
|
20
|
-
|
|
24
|
+
S3 URI (e.g., 's3://bucket/key').
|
|
21
25
|
|
|
22
26
|
Returns
|
|
23
27
|
-------
|
|
24
28
|
str
|
|
25
|
-
The bucket name.
|
|
29
|
+
The bucket name extracted from the URI.
|
|
26
30
|
"""
|
|
27
31
|
return urlparse(path).netloc
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
def get_bucket_and_key(path: str) -> tuple[str, str]:
|
|
31
35
|
"""
|
|
32
|
-
|
|
36
|
+
Extract the bucket name and key from an S3 path.
|
|
33
37
|
|
|
34
38
|
Parameters
|
|
35
39
|
----------
|
|
36
40
|
path : str
|
|
37
|
-
|
|
41
|
+
S3 URI (e.g., 's3://bucket/key').
|
|
38
42
|
|
|
39
43
|
Returns
|
|
40
44
|
-------
|
|
41
|
-
tuple
|
|
42
|
-
|
|
45
|
+
tuple of str
|
|
46
|
+
Tuple containing (bucket, key) extracted from the URI.
|
|
43
47
|
"""
|
|
44
48
|
parsed = urlparse(path)
|
|
45
49
|
return parsed.netloc, parsed.path
|
|
@@ -47,7 +51,7 @@ def get_bucket_and_key(path: str) -> tuple[str, str]:
|
|
|
47
51
|
|
|
48
52
|
def get_s3_source(bucket: str, key: str, filename: Path) -> None:
|
|
49
53
|
"""
|
|
50
|
-
|
|
54
|
+
Download an object from S3 and save it to a local file.
|
|
51
55
|
|
|
52
56
|
Parameters
|
|
53
57
|
----------
|
|
@@ -56,7 +60,7 @@ def get_s3_source(bucket: str, key: str, filename: Path) -> None:
|
|
|
56
60
|
key : str
|
|
57
61
|
S3 object key.
|
|
58
62
|
filename : Path
|
|
59
|
-
|
|
63
|
+
Local path where the downloaded object will be saved.
|
|
60
64
|
|
|
61
65
|
Returns
|
|
62
66
|
-------
|
|
@@ -1,113 +1,120 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
1
5
|
from __future__ import annotations
|
|
2
6
|
|
|
3
|
-
from digitalhub.stores.
|
|
4
|
-
from digitalhub.stores.
|
|
5
|
-
from digitalhub.stores.data.sql.enums import SqlStoreEnv
|
|
6
|
-
from digitalhub.utils.exceptions import StoreError
|
|
7
|
+
from digitalhub.stores.credentials.configurator import Configurator
|
|
8
|
+
from digitalhub.stores.credentials.enums import CredsEnvVar
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
class SqlStoreConfigurator:
|
|
11
|
+
class SqlStoreConfigurator(Configurator):
|
|
10
12
|
"""
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
SQL store configuration manager for database connections.
|
|
14
|
+
|
|
15
|
+
Handles credential management and configuration for SQL database
|
|
16
|
+
connections. Loads credentials from environment variables or
|
|
17
|
+
configuration files and provides connection string generation
|
|
18
|
+
for database access.
|
|
19
|
+
|
|
20
|
+
Attributes
|
|
21
|
+
----------
|
|
22
|
+
keys : list[str]
|
|
23
|
+
List of all supported credential keys for SQL connections.
|
|
24
|
+
required_keys : list[str]
|
|
25
|
+
List of mandatory credential keys that must be provided.
|
|
13
26
|
"""
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
keys = [
|
|
29
|
+
CredsEnvVar.DB_USERNAME.value,
|
|
30
|
+
CredsEnvVar.DB_PASSWORD.value,
|
|
31
|
+
CredsEnvVar.DB_HOST.value,
|
|
32
|
+
CredsEnvVar.DB_PORT.value,
|
|
33
|
+
CredsEnvVar.DB_DATABASE.value,
|
|
34
|
+
CredsEnvVar.DB_PLATFORM.value,
|
|
35
|
+
]
|
|
36
|
+
required_keys = [
|
|
37
|
+
CredsEnvVar.DB_USERNAME.value,
|
|
38
|
+
CredsEnvVar.DB_PASSWORD.value,
|
|
39
|
+
CredsEnvVar.DB_HOST.value,
|
|
40
|
+
CredsEnvVar.DB_PORT.value,
|
|
41
|
+
CredsEnvVar.DB_DATABASE.value,
|
|
21
42
|
]
|
|
22
43
|
|
|
44
|
+
def __init__(self):
|
|
45
|
+
super().__init__()
|
|
46
|
+
self.load_configs()
|
|
47
|
+
|
|
23
48
|
##############################
|
|
24
49
|
# Configuration methods
|
|
25
50
|
##############################
|
|
26
51
|
|
|
27
|
-
def
|
|
52
|
+
def load_env_vars(self) -> None:
|
|
28
53
|
"""
|
|
29
|
-
|
|
54
|
+
Load database credentials from environment variables.
|
|
30
55
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
The origin of the credentials.
|
|
56
|
+
Retrieves SQL database connection credentials from the system
|
|
57
|
+
environment variables and stores them in the configurator's
|
|
58
|
+
credential handler for use in database connections.
|
|
35
59
|
|
|
36
60
|
Returns
|
|
37
61
|
-------
|
|
38
|
-
|
|
39
|
-
The connection string.
|
|
62
|
+
None
|
|
40
63
|
"""
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
elif origin == CredsOrigin.FILE.value:
|
|
44
|
-
creds = self._get_file_config()
|
|
45
|
-
else:
|
|
46
|
-
raise StoreError(f"Unknown origin: {origin}")
|
|
47
|
-
|
|
48
|
-
user = creds[SqlStoreEnv.USERNAME.value]
|
|
49
|
-
password = creds[SqlStoreEnv.PASSWORD.value]
|
|
50
|
-
host = creds[SqlStoreEnv.HOST.value]
|
|
51
|
-
port = creds[SqlStoreEnv.PORT.value]
|
|
52
|
-
database = creds[SqlStoreEnv.DATABASE.value]
|
|
53
|
-
return f"postgresql://{user}:{password}@{host}:{port}/{database}"
|
|
64
|
+
env_creds = self._creds_handler.load_from_env(self.keys)
|
|
65
|
+
self._creds_handler.set_credentials(self._env, env_creds)
|
|
54
66
|
|
|
55
|
-
def
|
|
67
|
+
def load_file_vars(self) -> None:
|
|
56
68
|
"""
|
|
57
|
-
|
|
69
|
+
Load database credentials from configuration file.
|
|
58
70
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
The credentials.
|
|
63
|
-
"""
|
|
64
|
-
credentials = {var.value: configurator.load_from_env(var.value) for var in self.required_vars}
|
|
65
|
-
self._set_credentials(credentials)
|
|
66
|
-
return credentials
|
|
67
|
-
|
|
68
|
-
def _get_file_config(self) -> dict:
|
|
69
|
-
"""
|
|
70
|
-
Get the store configuration from file.
|
|
71
|
+
Retrieves SQL database connection credentials from a
|
|
72
|
+
configuration file and stores them in the configurator's
|
|
73
|
+
credential handler for use in database connections.
|
|
71
74
|
|
|
72
75
|
Returns
|
|
73
76
|
-------
|
|
74
|
-
|
|
75
|
-
The credentials.
|
|
77
|
+
None
|
|
76
78
|
"""
|
|
77
|
-
|
|
78
|
-
self.
|
|
79
|
-
return credentials
|
|
79
|
+
file_creds = self._creds_handler.load_from_file(self.keys)
|
|
80
|
+
self._creds_handler.set_credentials(self._file, file_creds)
|
|
80
81
|
|
|
81
|
-
def
|
|
82
|
+
def get_sql_conn_string(self) -> str:
|
|
82
83
|
"""
|
|
83
|
-
|
|
84
|
+
Generate PostgreSQL connection string from stored credentials.
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
The credentials.
|
|
86
|
+
Constructs a PostgreSQL connection string using the configured
|
|
87
|
+
database credentials including username, password, host, port,
|
|
88
|
+
and database name.
|
|
89
89
|
|
|
90
90
|
Returns
|
|
91
91
|
-------
|
|
92
|
-
|
|
92
|
+
str
|
|
93
|
+
A PostgreSQL connection string in the format:
|
|
94
|
+
'postgresql://username:password@host:port/database'
|
|
93
95
|
"""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
creds = self.get_sql_credentials()
|
|
97
|
+
user = creds[CredsEnvVar.DB_USERNAME.value]
|
|
98
|
+
password = creds[CredsEnvVar.DB_PASSWORD.value]
|
|
99
|
+
host = creds[CredsEnvVar.DB_HOST.value]
|
|
100
|
+
port = creds[CredsEnvVar.DB_PORT.value]
|
|
101
|
+
database = creds[CredsEnvVar.DB_DATABASE.value]
|
|
102
|
+
return f"postgresql://{user}:{password}@{host}:{port}/{database}"
|
|
97
103
|
|
|
98
|
-
def
|
|
104
|
+
def get_sql_credentials(self) -> dict:
|
|
99
105
|
"""
|
|
100
|
-
|
|
106
|
+
Get all configured database credentials as a dictionary.
|
|
101
107
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
The credentials.
|
|
108
|
+
Retrieves all available database credentials from the configured
|
|
109
|
+
source and returns them as a dictionary with all credential keys
|
|
110
|
+
from self.keys mapped to their values.
|
|
106
111
|
|
|
107
112
|
Returns
|
|
108
113
|
-------
|
|
109
|
-
|
|
114
|
+
dict
|
|
115
|
+
Dictionary containing all credential key-value pairs from self.keys.
|
|
116
|
+
Keys correspond to database connection parameters such as
|
|
117
|
+
username, password, host, port, database, and platform.
|
|
110
118
|
"""
|
|
111
|
-
|
|
112
|
-
for key
|
|
113
|
-
configurator.set_credential(key, value)
|
|
119
|
+
creds = self.get_credentials(self._origin)
|
|
120
|
+
return {key: creds.get(key) for key in self.keys}
|