Skip to content

Commit 661bd59

Browse files
authored
#245-adding-backend-inference-for-the-path-api (#246)
#245-adding-backend-inference-for-the-path-api
1 parent ae8378a commit 661bd59

File tree

9 files changed

+492
-17
lines changed

9 files changed

+492
-17
lines changed

doc/changes/unreleased.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Unreleased
22

33
## Refactorings
4-
* #186: Integration test for correctness of UDF path generation, using as_udf_path and pathlike
4+
* #186: Integration test for correctness of UDF path generation, using as_udf_path and pathlike
5+
* #245: Add backend inference for the Path-API

exasol/bucketfs/_buckets.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ def __str__(self):
177177
def name(self) -> str:
178178
return self._name
179179

180+
@property
181+
def service(self) -> str:
182+
return self._service
183+
184+
@property
185+
def service_name(self) -> str:
186+
return self._service_name
187+
180188
@property
181189
def udf_path(self) -> str:
182190
if self._service_name is None:
@@ -271,6 +279,22 @@ def __init__(self, url: str, account_id: str, database_id: str, pat: str) -> Non
271279
self._database_id = database_id
272280
self._pat = pat
273281

282+
@property
283+
def url(self) -> str:
284+
return self._url
285+
286+
@property
287+
def account_id(self) -> str:
288+
return self._account_id
289+
290+
@property
291+
def database_id(self) -> str:
292+
return self._database_id
293+
294+
@property
295+
def pat(self) -> str:
296+
return self._pat
297+
274298
@property
275299
def name(self) -> str:
276300
return "default"

exasol/bucketfs/_error.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ class BucketFsError(Exception):
66

77
def __init__(self, *args, **kwargs):
88
super().__init__(*args, **kwargs)
9+
10+
11+
class InferBfsPathError(BucketFsError):
12+
"""Error occurred while inferring bfs path - Missing required details."""

exasol/bucketfs/_path.py

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,17 @@
2222
Protocol,
2323
)
2424

25+
from exasol.saas.client.api_access import get_database_id
26+
2527
from exasol.bucketfs._buckets import (
2628
BucketLike,
2729
MountedBucket,
2830
SaaSBucket,
2931
)
30-
from exasol.bucketfs._error import BucketFsError
32+
from exasol.bucketfs._error import (
33+
BucketFsError,
34+
InferBfsPathError,
35+
)
3136
from exasol.bucketfs._service import Service
3237

3338
ARCHIVE_SUFFIXES = [".tar", ".gz", ".tgz", ".zip", ".tar"]
@@ -268,6 +273,14 @@ def __init__(self, path: str | PurePath, bucket_api: BucketLike):
268273
self._path = PurePath(path)
269274
self._bucket_api = bucket_api
270275

276+
@property
277+
def bucket_api(self):
278+
return self._bucket_api
279+
280+
@property
281+
def path(self):
282+
return self._path
283+
271284
def _get_relative_posix(self) -> str:
272285
"""
273286
Returns the pure path of this object as a string, in the format of a bucket
@@ -566,7 +579,6 @@ def build_path(**kwargs) -> PathLike:
566579
Explicitly specified root path in a file system. This is an alternative to
567580
providing the service_name and the bucket_name.
568581
"""
569-
570582
backend = kwargs.pop("backend", StorageBackend.onprem)
571583
path = kwargs.pop("path") if "path" in kwargs else ""
572584

@@ -578,5 +590,132 @@ def build_path(**kwargs) -> PathLike:
578590
bucket = _create_saas_bucket(**kwargs)
579591
else:
580592
bucket = _create_mounted_bucket(**kwargs)
581-
582593
return BucketPath(path, bucket)
594+
595+
596+
def infer_backend(
597+
bucketfs_host: str | None = None,
598+
bucketfs_port: int | None = None,
599+
bucketfs_name: str | None = None,
600+
bucket: str | None = None,
601+
bucketfs_user: str | None = None,
602+
bucketfs_password: str | None = None,
603+
saas_url: str | None = None,
604+
saas_account_id: str | None = None,
605+
saas_database_id: str | None = None,
606+
saas_database_name: str | None = None,
607+
saas_token: str | None = None,
608+
base_path: str | None = None,
609+
) -> StorageBackend:
610+
"""Infer the backend from the provided parameters: returns 'onprem' or 'saas',
611+
or raises an InferBfsPathError if the list of parameters is insufficient for either of the backends.
612+
"""
613+
# On-prem required fields
614+
required_onprem_fields = [
615+
bucketfs_host,
616+
bucketfs_port,
617+
bucketfs_name,
618+
bucket,
619+
bucketfs_user,
620+
bucketfs_password,
621+
]
622+
# SaaS required fields
623+
required_saas_fields = [saas_url, saas_account_id, saas_token]
624+
if all(required_onprem_fields):
625+
return StorageBackend.onprem
626+
elif all(required_saas_fields) and (saas_database_id or saas_database_name):
627+
return StorageBackend.saas
628+
elif (bucketfs_name and bucket) or base_path:
629+
return StorageBackend.mounted
630+
else:
631+
raise InferBfsPathError("Insufficient parameters to infer backend")
632+
633+
634+
def get_database_id_by_name(
635+
host: str, account_id: str, pat: str, database_name: str
636+
) -> str:
637+
database_id = get_database_id(
638+
host=host, account_id=account_id, pat=pat, database_name=database_name
639+
)
640+
if not database_id:
641+
raise InferBfsPathError(f"Could not find database_id for name {database_name}")
642+
return database_id
643+
644+
645+
def infer_path(
646+
bucketfs_host: str | None = None,
647+
bucketfs_port: int | None = None,
648+
bucketfs_name: str | None = None,
649+
bucket: str | None = None,
650+
bucketfs_user: str | None = None,
651+
bucketfs_password: str | None = None,
652+
bucketfs_use_https: bool = True,
653+
saas_url: str | None = None,
654+
saas_account_id: str | None = None,
655+
saas_database_id: str | None = None,
656+
saas_database_name: str | None = None,
657+
saas_token: str | None = None,
658+
path_in_bucket: str = "",
659+
use_ssl_cert_validation: bool = True,
660+
ssl_trusted_ca: str | None = None,
661+
base_path: str | None = None,
662+
) -> PathLike:
663+
"""
664+
Infers the correct storage backend (on-premises BucketFS or SaaS) from the provided parameters
665+
and returns a PathLike object for accessing the specified resource.
666+
667+
Raises:
668+
InferBfsPathError: If the parameters are insufficient or inconsistent and the backend cannot be determined.
669+
"""
670+
backend = infer_backend(
671+
bucketfs_host,
672+
bucketfs_port,
673+
bucketfs_name,
674+
bucket,
675+
bucketfs_user,
676+
bucketfs_password,
677+
saas_url,
678+
saas_account_id,
679+
saas_database_id,
680+
saas_database_name,
681+
saas_token,
682+
base_path,
683+
)
684+
if backend == StorageBackend.onprem:
685+
bfs_url = f"{'https' if bucketfs_use_https else 'http'}://{bucketfs_host}:{bucketfs_port}"
686+
verify = ssl_trusted_ca or use_ssl_cert_validation
687+
return build_path(
688+
backend=StorageBackend.onprem,
689+
url=bfs_url,
690+
username=bucketfs_user,
691+
password=bucketfs_password,
692+
service_name=bucketfs_name,
693+
bucket_name=bucket,
694+
verify=verify,
695+
path=path_in_bucket,
696+
)
697+
elif backend == StorageBackend.saas:
698+
if not saas_database_id and saas_database_name:
699+
saas_database_id = get_database_id_by_name(
700+
host=saas_url,
701+
account_id=saas_account_id,
702+
pat=saas_token,
703+
database_name=saas_database_name,
704+
)
705+
return build_path(
706+
backend=StorageBackend.saas,
707+
url=saas_url,
708+
account_id=saas_account_id,
709+
database_id=saas_database_id,
710+
pat=saas_token,
711+
path=path_in_bucket,
712+
)
713+
elif backend == StorageBackend.mounted:
714+
return build_path(
715+
backend=StorageBackend.mounted,
716+
service_name=bucketfs_name,
717+
bucket_name=bucket,
718+
base_path=base_path,
719+
)
720+
else:
721+
raise InferBfsPathError(f"Unsupported backend: {backend}.")

exasol/bucketfs/version.py

Lines changed: 10 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

poetry.lock

Lines changed: 21 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)