digitalhub 0.12.0__py3-none-any.whl → 0.13.0b0__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 +1 -1
- digitalhub/entities/_base/executable/entity.py +7 -0
- digitalhub/entities/_base/material/entity.py +8 -15
- digitalhub/entities/_base/material/utils.py +1 -1
- digitalhub/entities/_processors/context.py +1 -1
- digitalhub/stores/client/dhcore/client.py +56 -19
- digitalhub/stores/client/dhcore/configurator.py +278 -183
- digitalhub/stores/client/dhcore/enums.py +2 -0
- digitalhub/stores/client/dhcore/utils.py +8 -8
- digitalhub/stores/{configurator → credentials}/api.py +3 -3
- digitalhub/stores/credentials/configurator.py +37 -0
- digitalhub/stores/credentials/enums.py +54 -0
- digitalhub/stores/{configurator/configurator.py → credentials/handler.py} +23 -77
- digitalhub/stores/{configurator → credentials}/ini_module.py +1 -1
- digitalhub/stores/credentials/store.py +49 -0
- digitalhub/stores/data/_base/store.py +19 -6
- digitalhub/stores/data/api.py +37 -1
- digitalhub/stores/data/builder.py +46 -53
- digitalhub/stores/data/local/store.py +4 -7
- digitalhub/stores/data/remote/store.py +4 -7
- digitalhub/stores/data/s3/configurator.py +36 -92
- digitalhub/stores/data/s3/store.py +42 -57
- digitalhub/stores/data/s3/utils.py +1 -1
- digitalhub/stores/data/sql/configurator.py +35 -83
- digitalhub/stores/data/sql/store.py +15 -15
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/METADATA +1 -1
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/RECORD +31 -33
- digitalhub/stores/configurator/credentials_store.py +0 -69
- digitalhub/stores/configurator/enums.py +0 -25
- digitalhub/stores/data/s3/enums.py +0 -20
- digitalhub/stores/data/sql/enums.py +0 -20
- digitalhub/stores/data/utils.py +0 -38
- /digitalhub/stores/{configurator → credentials}/__init__.py +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/WHEEL +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/licenses/AUTHORS +0 -0
- {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,37 +6,50 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
from botocore.config import Config
|
|
8
8
|
|
|
9
|
-
from digitalhub.stores.
|
|
10
|
-
from digitalhub.stores.
|
|
11
|
-
from digitalhub.stores.data.s3.enums import S3StoreEnv
|
|
12
|
-
from digitalhub.utils.exceptions import StoreError
|
|
9
|
+
from digitalhub.stores.credentials.configurator import Configurator
|
|
10
|
+
from digitalhub.stores.credentials.enums import CredsEnvVar
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
class S3StoreConfigurator:
|
|
13
|
+
class S3StoreConfigurator(Configurator):
|
|
16
14
|
"""
|
|
17
15
|
Configure the store by getting the credentials from user
|
|
18
16
|
provided config or from environment.
|
|
19
17
|
"""
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
keys = [
|
|
20
|
+
CredsEnvVar.S3_ENDPOINT_URL,
|
|
21
|
+
CredsEnvVar.S3_ACCESS_KEY_ID,
|
|
22
|
+
CredsEnvVar.S3_SECRET_ACCESS_KEY,
|
|
23
|
+
CredsEnvVar.S3_REGION,
|
|
24
|
+
CredsEnvVar.S3_SIGNATURE_VERSION,
|
|
25
|
+
CredsEnvVar.S3_SESSION_TOKEN,
|
|
25
26
|
]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
required_keys = [
|
|
28
|
+
CredsEnvVar.S3_ENDPOINT_URL,
|
|
29
|
+
CredsEnvVar.S3_ACCESS_KEY_ID,
|
|
30
|
+
CredsEnvVar.S3_SECRET_ACCESS_KEY,
|
|
30
31
|
]
|
|
31
32
|
|
|
33
|
+
def __init__(self):
|
|
34
|
+
super().__init__()
|
|
35
|
+
self.load_configs()
|
|
36
|
+
|
|
32
37
|
##############################
|
|
33
38
|
# Configuration methods
|
|
34
39
|
##############################
|
|
35
40
|
|
|
36
|
-
def
|
|
41
|
+
def load_configs(self) -> None:
|
|
42
|
+
# Load from env
|
|
43
|
+
env_creds = {var.value: self._creds_handler.load_from_env(var.value) for var in self.keys}
|
|
44
|
+
self._creds_handler.set_credentials(self._env, env_creds)
|
|
45
|
+
|
|
46
|
+
# Load from file
|
|
47
|
+
file_creds = {var.value: self._creds_handler.load_from_file(var.value) for var in self.keys}
|
|
48
|
+
self._creds_handler.set_credentials(self._file, file_creds)
|
|
49
|
+
|
|
50
|
+
def get_client_config(self, origin: str) -> dict:
|
|
37
51
|
"""
|
|
38
|
-
Get S3 credentials (access key, secret key,
|
|
39
|
-
session token and other config).
|
|
52
|
+
Get S3 credentials (access key, secret key, session token and other config).
|
|
40
53
|
|
|
41
54
|
Parameters
|
|
42
55
|
----------
|
|
@@ -48,83 +61,14 @@ class S3StoreConfigurator:
|
|
|
48
61
|
dict
|
|
49
62
|
The credentials.
|
|
50
63
|
"""
|
|
51
|
-
|
|
52
|
-
creds = self._get_env_config()
|
|
53
|
-
elif origin == CredsOrigin.FILE.value:
|
|
54
|
-
creds = self._get_file_config()
|
|
55
|
-
else:
|
|
56
|
-
raise StoreError(f"Unknown origin: {origin}")
|
|
64
|
+
creds = self.get_credentials(origin)
|
|
57
65
|
return {
|
|
58
|
-
"endpoint_url": creds[
|
|
59
|
-
"aws_access_key_id": creds[
|
|
60
|
-
"aws_secret_access_key": creds[
|
|
61
|
-
"aws_session_token": creds[
|
|
66
|
+
"endpoint_url": creds[CredsEnvVar.S3_ENDPOINT_URL.value],
|
|
67
|
+
"aws_access_key_id": creds[CredsEnvVar.S3_ACCESS_KEY_ID.value],
|
|
68
|
+
"aws_secret_access_key": creds[CredsEnvVar.S3_SECRET_ACCESS_KEY.value],
|
|
69
|
+
"aws_session_token": creds[CredsEnvVar.S3_SESSION_TOKEN.value],
|
|
62
70
|
"config": Config(
|
|
63
|
-
region_name=creds[
|
|
64
|
-
signature_version=creds[
|
|
71
|
+
region_name=creds[CredsEnvVar.S3_REGION.value],
|
|
72
|
+
signature_version=creds[CredsEnvVar.S3_SIGNATURE_VERSION.value],
|
|
65
73
|
),
|
|
66
74
|
}
|
|
67
|
-
|
|
68
|
-
def _get_env_config(self) -> dict:
|
|
69
|
-
"""
|
|
70
|
-
Get the store configuration from environment variables.
|
|
71
|
-
|
|
72
|
-
Returns
|
|
73
|
-
-------
|
|
74
|
-
dict
|
|
75
|
-
The credentials.
|
|
76
|
-
"""
|
|
77
|
-
credentials = {
|
|
78
|
-
var.value: configurator.load_from_env(var.value) for var in self.required_vars + self.optional_vars
|
|
79
|
-
}
|
|
80
|
-
self._set_credentials(credentials)
|
|
81
|
-
return credentials
|
|
82
|
-
|
|
83
|
-
def _get_file_config(self) -> dict:
|
|
84
|
-
"""
|
|
85
|
-
Get the store configuration from file.
|
|
86
|
-
|
|
87
|
-
Returns
|
|
88
|
-
-------
|
|
89
|
-
dict
|
|
90
|
-
The credentials.
|
|
91
|
-
"""
|
|
92
|
-
credentials = {
|
|
93
|
-
var.value: configurator.load_from_file(var.value) for var in self.required_vars + self.optional_vars
|
|
94
|
-
}
|
|
95
|
-
self._set_credentials(credentials)
|
|
96
|
-
return credentials
|
|
97
|
-
|
|
98
|
-
def _check_credentials(self, credentials: dict) -> None:
|
|
99
|
-
"""
|
|
100
|
-
Check for missing credentials.
|
|
101
|
-
|
|
102
|
-
Parameters
|
|
103
|
-
----------
|
|
104
|
-
credentials : dict
|
|
105
|
-
The credentials.
|
|
106
|
-
|
|
107
|
-
Returns
|
|
108
|
-
-------
|
|
109
|
-
None
|
|
110
|
-
"""
|
|
111
|
-
missing_vars = [key for key, value in credentials.items() if value is None and key in self.required_vars]
|
|
112
|
-
if missing_vars:
|
|
113
|
-
raise StoreError(f"Missing credentials for S3 store: {', '.join(missing_vars)}")
|
|
114
|
-
|
|
115
|
-
def _set_credentials(self, credentials: dict) -> None:
|
|
116
|
-
"""
|
|
117
|
-
Set the store credentials into the configurator.
|
|
118
|
-
|
|
119
|
-
Parameters
|
|
120
|
-
----------
|
|
121
|
-
credentials : dict
|
|
122
|
-
The credentials.
|
|
123
|
-
|
|
124
|
-
Returns
|
|
125
|
-
-------
|
|
126
|
-
None
|
|
127
|
-
"""
|
|
128
|
-
# Set credentials
|
|
129
|
-
for key, value in credentials.items():
|
|
130
|
-
configurator.set_credential(key, value)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import typing
|
|
7
8
|
from io import BytesIO
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import Any, Type
|
|
@@ -11,17 +12,22 @@ from urllib.parse import urlparse
|
|
|
11
12
|
|
|
12
13
|
import boto3
|
|
13
14
|
import botocore.client # pylint: disable=unused-import
|
|
15
|
+
from boto3.s3.transfer import TransferConfig
|
|
14
16
|
from botocore.exceptions import ClientError, NoCredentialsError
|
|
15
17
|
|
|
16
|
-
from digitalhub.stores.
|
|
18
|
+
from digitalhub.stores.credentials.enums import CredsOrigin
|
|
17
19
|
from digitalhub.stores.data._base.store import Store
|
|
18
|
-
from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
|
|
19
20
|
from digitalhub.stores.data.s3.utils import get_bucket_name
|
|
20
21
|
from digitalhub.stores.readers.data.api import get_reader_by_object
|
|
21
22
|
from digitalhub.utils.exceptions import StoreError
|
|
22
23
|
from digitalhub.utils.file_utils import get_file_info_from_s3, get_file_mime_type
|
|
23
24
|
from digitalhub.utils.types import SourcesOrListOfSources
|
|
24
25
|
|
|
26
|
+
if typing.TYPE_CHECKING:
|
|
27
|
+
from digitalhub.stores.credentials.configurator import Configurator
|
|
28
|
+
from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
|
|
29
|
+
|
|
30
|
+
|
|
25
31
|
# Type aliases
|
|
26
32
|
S3Client = Type["botocore.client.S3"]
|
|
27
33
|
|
|
@@ -32,8 +38,9 @@ class S3Store(Store):
|
|
|
32
38
|
artifacts on S3 based storage.
|
|
33
39
|
"""
|
|
34
40
|
|
|
35
|
-
def __init__(self) -> None:
|
|
36
|
-
|
|
41
|
+
def __init__(self, configurator: Configurator | None = None) -> None:
|
|
42
|
+
super().__init__(configurator)
|
|
43
|
+
self._configurator: S3StoreConfigurator
|
|
37
44
|
|
|
38
45
|
##############################
|
|
39
46
|
# I/O methods
|
|
@@ -41,9 +48,8 @@ class S3Store(Store):
|
|
|
41
48
|
|
|
42
49
|
def download(
|
|
43
50
|
self,
|
|
44
|
-
|
|
51
|
+
src: str,
|
|
45
52
|
dst: Path,
|
|
46
|
-
src: list[str],
|
|
47
53
|
overwrite: bool = False,
|
|
48
54
|
) -> str:
|
|
49
55
|
"""
|
|
@@ -51,21 +57,19 @@ class S3Store(Store):
|
|
|
51
57
|
|
|
52
58
|
Parameters
|
|
53
59
|
----------
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
src : str
|
|
61
|
+
Path of the material entity.
|
|
56
62
|
dst : str
|
|
57
|
-
The destination of the
|
|
58
|
-
src : list[str]
|
|
59
|
-
List of sources.
|
|
63
|
+
The destination of the material entity on local filesystem.
|
|
60
64
|
overwrite : bool
|
|
61
65
|
Specify if overwrite existing file(s).
|
|
62
66
|
|
|
63
67
|
Returns
|
|
64
68
|
-------
|
|
65
69
|
str
|
|
66
|
-
Destination path of the downloaded
|
|
70
|
+
Destination path of the downloaded files.
|
|
67
71
|
"""
|
|
68
|
-
client, bucket = self._check_factory(
|
|
72
|
+
client, bucket = self._check_factory(src)
|
|
69
73
|
|
|
70
74
|
# Build destination directory
|
|
71
75
|
if dst.suffix == "":
|
|
@@ -74,20 +78,13 @@ class S3Store(Store):
|
|
|
74
78
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
75
79
|
|
|
76
80
|
# Handle src and tree destination
|
|
77
|
-
if self.is_partition(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
trees = [k.removeprefix(strip_root) for k in keys]
|
|
82
|
-
else:
|
|
83
|
-
keys = self._build_key_from_root(root, src)
|
|
84
|
-
trees = [s for s in src]
|
|
81
|
+
if self.is_partition(src):
|
|
82
|
+
keys = self._list_objects(client, bucket, src)
|
|
83
|
+
strip_root = self._get_key(src)
|
|
84
|
+
trees = [k.removeprefix(strip_root) for k in keys]
|
|
85
85
|
else:
|
|
86
|
-
keys = [self._get_key(
|
|
87
|
-
|
|
88
|
-
trees = [Path(self._get_key(root)).name]
|
|
89
|
-
else:
|
|
90
|
-
trees = [s for s in src]
|
|
86
|
+
keys = [self._get_key(src)]
|
|
87
|
+
trees = [Path(self._get_key(src)).name]
|
|
91
88
|
|
|
92
89
|
if len(keys) != len(trees):
|
|
93
90
|
raise StoreError("Keys and trees must have the same length.")
|
|
@@ -128,7 +125,7 @@ class S3Store(Store):
|
|
|
128
125
|
src : SourcesOrListOfSources
|
|
129
126
|
Source(s).
|
|
130
127
|
dst : str
|
|
131
|
-
The destination of the
|
|
128
|
+
The destination of the material entity on storage.
|
|
132
129
|
|
|
133
130
|
Returns
|
|
134
131
|
-------
|
|
@@ -414,7 +411,7 @@ class S3Store(Store):
|
|
|
414
411
|
src : str
|
|
415
412
|
List of sources.
|
|
416
413
|
dst : str
|
|
417
|
-
The destination of the
|
|
414
|
+
The destination of the material entity on storage.
|
|
418
415
|
client : S3Client
|
|
419
416
|
The S3 client object.
|
|
420
417
|
bucket : str
|
|
@@ -457,7 +454,7 @@ class S3Store(Store):
|
|
|
457
454
|
src : list
|
|
458
455
|
List of sources.
|
|
459
456
|
dst : str
|
|
460
|
-
The destination of the
|
|
457
|
+
The destination of the material entity on storage.
|
|
461
458
|
client : S3Client
|
|
462
459
|
The S3 client object.
|
|
463
460
|
bucket : str
|
|
@@ -497,7 +494,7 @@ class S3Store(Store):
|
|
|
497
494
|
src : str
|
|
498
495
|
List of sources.
|
|
499
496
|
dst : str
|
|
500
|
-
The destination of the
|
|
497
|
+
The destination of the material entity on storage.
|
|
501
498
|
client : S3Client
|
|
502
499
|
The S3 client object.
|
|
503
500
|
bucket : str
|
|
@@ -546,7 +543,13 @@ class S3Store(Store):
|
|
|
546
543
|
mime_type = get_file_mime_type(src)
|
|
547
544
|
if mime_type is not None:
|
|
548
545
|
extra_args["ContentType"] = mime_type
|
|
549
|
-
client.upload_file(
|
|
546
|
+
client.upload_file(
|
|
547
|
+
Filename=src,
|
|
548
|
+
Bucket=bucket,
|
|
549
|
+
Key=key,
|
|
550
|
+
ExtraArgs=extra_args,
|
|
551
|
+
Config=TransferConfig(multipart_threshold=100 * 1024 * 1024),
|
|
552
|
+
)
|
|
550
553
|
|
|
551
554
|
@staticmethod
|
|
552
555
|
def _upload_fileobject(
|
|
@@ -573,7 +576,12 @@ class S3Store(Store):
|
|
|
573
576
|
-------
|
|
574
577
|
None
|
|
575
578
|
"""
|
|
576
|
-
client.
|
|
579
|
+
client.upload_fileobj(
|
|
580
|
+
Fileobj=fileobj,
|
|
581
|
+
Bucket=bucket,
|
|
582
|
+
Key=key,
|
|
583
|
+
Config=TransferConfig(multipart_threshold=100 * 1024 * 1024),
|
|
584
|
+
)
|
|
577
585
|
|
|
578
586
|
##############################
|
|
579
587
|
# Helper methods
|
|
@@ -619,13 +627,13 @@ class S3Store(Store):
|
|
|
619
627
|
|
|
620
628
|
# Try to get client from environment variables
|
|
621
629
|
try:
|
|
622
|
-
cfg = self._configurator.
|
|
630
|
+
cfg = self._configurator.get_client_config(CredsOrigin.ENV.value)
|
|
623
631
|
client = self._get_client(cfg)
|
|
624
632
|
self._check_access_to_storage(client, bucket)
|
|
625
633
|
|
|
626
634
|
# Fallback to file
|
|
627
635
|
except StoreError:
|
|
628
|
-
cfg = self._configurator.
|
|
636
|
+
cfg = self._configurator.get_client_config(CredsOrigin.FILE.value)
|
|
629
637
|
client = self._get_client(cfg)
|
|
630
638
|
self._check_access_to_storage(client, bucket)
|
|
631
639
|
|
|
@@ -676,29 +684,6 @@ class S3Store(Store):
|
|
|
676
684
|
key = key[1:]
|
|
677
685
|
return key
|
|
678
686
|
|
|
679
|
-
def _build_key_from_root(self, root: str, paths: list[str]) -> list[str]:
|
|
680
|
-
"""
|
|
681
|
-
Method to build object path.
|
|
682
|
-
|
|
683
|
-
Parameters
|
|
684
|
-
----------
|
|
685
|
-
root : str
|
|
686
|
-
The root of the object path.
|
|
687
|
-
paths : list[str]
|
|
688
|
-
The path to build.
|
|
689
|
-
|
|
690
|
-
Returns
|
|
691
|
-
-------
|
|
692
|
-
list[str]
|
|
693
|
-
List of keys.
|
|
694
|
-
"""
|
|
695
|
-
keys = []
|
|
696
|
-
for path in paths:
|
|
697
|
-
clean_path = self._get_key(path)
|
|
698
|
-
key = self._get_key(f"{root}{clean_path}")
|
|
699
|
-
keys.append(key)
|
|
700
|
-
return keys
|
|
701
|
-
|
|
702
687
|
def _list_objects(self, client: S3Client, bucket: str, partition: str) -> list[str]:
|
|
703
688
|
"""
|
|
704
689
|
List objects in a S3 partition.
|
|
@@ -9,7 +9,7 @@ from urllib.parse import urlparse
|
|
|
9
9
|
|
|
10
10
|
from boto3 import client as boto3_client
|
|
11
11
|
|
|
12
|
-
from digitalhub.stores.
|
|
12
|
+
from digitalhub.stores.credentials.enums import CredsOrigin
|
|
13
13
|
from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
|
|
14
14
|
from digitalhub.utils.exceptions import StoreError
|
|
15
15
|
|
|
@@ -4,30 +4,48 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
from digitalhub.stores.
|
|
8
|
-
from digitalhub.stores.
|
|
9
|
-
from digitalhub.stores.data.sql.enums import SqlStoreEnv
|
|
10
|
-
from digitalhub.utils.exceptions import StoreError
|
|
7
|
+
from digitalhub.stores.credentials.configurator import Configurator
|
|
8
|
+
from digitalhub.stores.credentials.enums import CredsEnvVar
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
class SqlStoreConfigurator:
|
|
11
|
+
class SqlStoreConfigurator(Configurator):
|
|
14
12
|
"""
|
|
15
13
|
Configure the store by getting the credentials from user
|
|
16
14
|
provided config or from environment.
|
|
17
15
|
"""
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
keys = [
|
|
18
|
+
CredsEnvVar.DB_USERNAME,
|
|
19
|
+
CredsEnvVar.DB_PASSWORD,
|
|
20
|
+
CredsEnvVar.DB_HOST,
|
|
21
|
+
CredsEnvVar.DB_PORT,
|
|
22
|
+
CredsEnvVar.DB_DATABASE,
|
|
25
23
|
]
|
|
24
|
+
required_keys = [
|
|
25
|
+
CredsEnvVar.DB_USERNAME,
|
|
26
|
+
CredsEnvVar.DB_PASSWORD,
|
|
27
|
+
CredsEnvVar.DB_HOST,
|
|
28
|
+
CredsEnvVar.DB_PORT,
|
|
29
|
+
CredsEnvVar.DB_DATABASE,
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
super().__init__()
|
|
34
|
+
self.load_configs()
|
|
26
35
|
|
|
27
36
|
##############################
|
|
28
37
|
# Configuration methods
|
|
29
38
|
##############################
|
|
30
39
|
|
|
40
|
+
def load_configs(self) -> None:
|
|
41
|
+
# Load from env
|
|
42
|
+
env_creds = {var.value: self._creds_handler.load_from_env(var.value) for var in self.keys}
|
|
43
|
+
self._creds_handler.set_credentials(self._env, env_creds)
|
|
44
|
+
|
|
45
|
+
# Load from file
|
|
46
|
+
file_creds = {var.value: self._creds_handler.load_from_file(var.value) for var in self.keys}
|
|
47
|
+
self._creds_handler.set_credentials(self._file, file_creds)
|
|
48
|
+
|
|
31
49
|
def get_sql_conn_string(self, origin: str) -> str:
|
|
32
50
|
"""
|
|
33
51
|
Get the connection string from environment variables.
|
|
@@ -42,76 +60,10 @@ class SqlStoreConfigurator:
|
|
|
42
60
|
str
|
|
43
61
|
The connection string.
|
|
44
62
|
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
user = creds[SqlStoreEnv.USERNAME.value]
|
|
53
|
-
password = creds[SqlStoreEnv.PASSWORD.value]
|
|
54
|
-
host = creds[SqlStoreEnv.HOST.value]
|
|
55
|
-
port = creds[SqlStoreEnv.PORT.value]
|
|
56
|
-
database = creds[SqlStoreEnv.DATABASE.value]
|
|
63
|
+
creds = self.get_credentials(origin)
|
|
64
|
+
user = creds[CredsEnvVar.DB_USERNAME.value]
|
|
65
|
+
password = creds[CredsEnvVar.DB_PASSWORD.value]
|
|
66
|
+
host = creds[CredsEnvVar.DB_HOST.value]
|
|
67
|
+
port = creds[CredsEnvVar.DB_PORT.value]
|
|
68
|
+
database = creds[CredsEnvVar.DB_DATABASE.value]
|
|
57
69
|
return f"postgresql://{user}:{password}@{host}:{port}/{database}"
|
|
58
|
-
|
|
59
|
-
def _get_env_config(self) -> dict:
|
|
60
|
-
"""
|
|
61
|
-
Get the store configuration from environment variables.
|
|
62
|
-
|
|
63
|
-
Returns
|
|
64
|
-
-------
|
|
65
|
-
dict
|
|
66
|
-
The credentials.
|
|
67
|
-
"""
|
|
68
|
-
credentials = {var.value: configurator.load_from_env(var.value) for var in self.required_vars}
|
|
69
|
-
self._set_credentials(credentials)
|
|
70
|
-
return credentials
|
|
71
|
-
|
|
72
|
-
def _get_file_config(self) -> dict:
|
|
73
|
-
"""
|
|
74
|
-
Get the store configuration from file.
|
|
75
|
-
|
|
76
|
-
Returns
|
|
77
|
-
-------
|
|
78
|
-
dict
|
|
79
|
-
The credentials.
|
|
80
|
-
"""
|
|
81
|
-
credentials = {var.value: configurator.load_from_file(var.value) for var in self.required_vars}
|
|
82
|
-
self._set_credentials(credentials)
|
|
83
|
-
return credentials
|
|
84
|
-
|
|
85
|
-
def _check_credentials(self, credentials: dict) -> None:
|
|
86
|
-
"""
|
|
87
|
-
Check for missing credentials.
|
|
88
|
-
|
|
89
|
-
Parameters
|
|
90
|
-
----------
|
|
91
|
-
credentials : dict
|
|
92
|
-
The credentials.
|
|
93
|
-
|
|
94
|
-
Returns
|
|
95
|
-
-------
|
|
96
|
-
None
|
|
97
|
-
"""
|
|
98
|
-
missing_vars = [key for key, value in credentials.items() if value is None and key in self.required_vars]
|
|
99
|
-
if missing_vars:
|
|
100
|
-
raise StoreError(f"Missing credentials for SQL store: {', '.join(missing_vars)}")
|
|
101
|
-
|
|
102
|
-
def _set_credentials(self, credentials: dict) -> None:
|
|
103
|
-
"""
|
|
104
|
-
Set the store credentials into the configurator.
|
|
105
|
-
|
|
106
|
-
Parameters
|
|
107
|
-
----------
|
|
108
|
-
credentials : dict
|
|
109
|
-
The credentials.
|
|
110
|
-
|
|
111
|
-
Returns
|
|
112
|
-
-------
|
|
113
|
-
None
|
|
114
|
-
"""
|
|
115
|
-
# Set credentials
|
|
116
|
-
for key, value in credentials.items():
|
|
117
|
-
configurator.set_credential(key, value)
|
|
@@ -14,9 +14,8 @@ from sqlalchemy import MetaData, Table, create_engine, select
|
|
|
14
14
|
from sqlalchemy.engine import Engine
|
|
15
15
|
from sqlalchemy.exc import SQLAlchemyError
|
|
16
16
|
|
|
17
|
-
from digitalhub.stores.
|
|
17
|
+
from digitalhub.stores.credentials.enums import CredsOrigin
|
|
18
18
|
from digitalhub.stores.data._base.store import Store
|
|
19
|
-
from digitalhub.stores.data.sql.configurator import SqlStoreConfigurator
|
|
20
19
|
from digitalhub.stores.readers.data.api import get_reader_by_object
|
|
21
20
|
from digitalhub.utils.exceptions import StoreError
|
|
22
21
|
from digitalhub.utils.types import SourcesOrListOfSources
|
|
@@ -24,6 +23,9 @@ from digitalhub.utils.types import SourcesOrListOfSources
|
|
|
24
23
|
if typing.TYPE_CHECKING:
|
|
25
24
|
from sqlalchemy.engine.row import Row
|
|
26
25
|
|
|
26
|
+
from digitalhub.stores.credentials.configurator import Configurator
|
|
27
|
+
from digitalhub.stores.data.sql.configurator import SqlStoreConfigurator
|
|
28
|
+
|
|
27
29
|
|
|
28
30
|
class SqlStore(Store):
|
|
29
31
|
"""
|
|
@@ -31,8 +33,9 @@ class SqlStore(Store):
|
|
|
31
33
|
artifacts on SQL based storage.
|
|
32
34
|
"""
|
|
33
35
|
|
|
34
|
-
def __init__(self) -> None:
|
|
35
|
-
|
|
36
|
+
def __init__(self, configurator: Configurator | None = None) -> None:
|
|
37
|
+
super().__init__(configurator)
|
|
38
|
+
self._configurator: SqlStoreConfigurator
|
|
36
39
|
|
|
37
40
|
##############################
|
|
38
41
|
# I/O methods
|
|
@@ -40,9 +43,8 @@ class SqlStore(Store):
|
|
|
40
43
|
|
|
41
44
|
def download(
|
|
42
45
|
self,
|
|
43
|
-
|
|
46
|
+
src: str,
|
|
44
47
|
dst: Path,
|
|
45
|
-
src: list[str],
|
|
46
48
|
overwrite: bool = False,
|
|
47
49
|
) -> str:
|
|
48
50
|
"""
|
|
@@ -50,21 +52,19 @@ class SqlStore(Store):
|
|
|
50
52
|
|
|
51
53
|
Parameters
|
|
52
54
|
----------
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
src : str
|
|
56
|
+
Path of the material entity.
|
|
55
57
|
dst : str
|
|
56
|
-
The destination of the
|
|
57
|
-
src : list[str]
|
|
58
|
-
List of sources.
|
|
58
|
+
The destination of the material entity on local filesystem.
|
|
59
59
|
overwrite : bool
|
|
60
60
|
Specify if overwrite existing file(s).
|
|
61
61
|
|
|
62
62
|
Returns
|
|
63
63
|
-------
|
|
64
64
|
str
|
|
65
|
-
Destination path of the downloaded
|
|
65
|
+
Destination path of the downloaded files.
|
|
66
66
|
"""
|
|
67
|
-
table_name = self._get_table_name(
|
|
67
|
+
table_name = self._get_table_name(src) + ".parquet"
|
|
68
68
|
# Case where dst is not provided
|
|
69
69
|
if dst is None:
|
|
70
70
|
dst = Path(self._build_temp("sql")) / table_name
|
|
@@ -83,8 +83,8 @@ class SqlStore(Store):
|
|
|
83
83
|
self._check_overwrite(dst, overwrite)
|
|
84
84
|
self._build_path(dst)
|
|
85
85
|
|
|
86
|
-
schema = self._get_schema(
|
|
87
|
-
table = self._get_table_name(
|
|
86
|
+
schema = self._get_schema(src)
|
|
87
|
+
table = self._get_table_name(src)
|
|
88
88
|
return self._download_table(schema, table, str(dst))
|
|
89
89
|
|
|
90
90
|
def upload(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: digitalhub
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0b0
|
|
4
4
|
Summary: Python SDK for Digitalhub
|
|
5
5
|
Project-URL: Homepage, https://github.com/scc-digitalhub/digitalhub-sdk
|
|
6
6
|
Author-email: Fondazione Bruno Kessler <digitalhub@fbk.eu>, Matteo Martini <mmartini@fbk.eu>
|