src-connectors 0.1.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.
- common/.gitkeep +0 -0
- common/__init__.py +0 -0
- src_connectors/__init__.py +18 -0
- src_connectors/src_base/__init__.py +3 -0
- src_connectors/src_base/connector.py +31 -0
- src_connectors/src_spark/__init__.py +6 -0
- src_connectors/src_spark/configs/__init__.py +6 -0
- src_connectors/src_spark/configs/base.py +30 -0
- src_connectors/src_spark/configs/core_config.py +138 -0
- src_connectors/src_spark/configs/iceberg.py +205 -0
- src_connectors/src_spark/configs/jdbc.py +81 -0
- src_connectors/src_spark/connector.py +235 -0
- src_connectors/src_sqlserver/__init__.py +3 -0
- src_connectors/src_sqlserver/connector.py +167 -0
- src_connectors-0.1.0.dist-info/METADATA +184 -0
- src_connectors-0.1.0.dist-info/RECORD +21 -0
- src_connectors-0.1.0.dist-info/WHEEL +4 -0
- variables/__init__.py +0 -0
- variables/helper.py +120 -0
- variables/spark.py +113 -0
- variables/sqlserver.py +31 -0
common/.gitkeep
ADDED
|
File without changes
|
common/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
__all__ = ["SQLServerConnector", "SparkConnector"]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def __getattr__(name: str) -> Any:
|
|
8
|
+
if name == "SQLServerConnector":
|
|
9
|
+
from src_connectors.src_sqlserver.connector import SQLServerConnector
|
|
10
|
+
|
|
11
|
+
return SQLServerConnector
|
|
12
|
+
|
|
13
|
+
if name == "SparkConnector":
|
|
14
|
+
from src_connectors.src_spark.connector import SparkConnector
|
|
15
|
+
|
|
16
|
+
return SparkConnector
|
|
17
|
+
|
|
18
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any, Optional
|
|
3
|
+
|
|
4
|
+
class BaseConnector(ABC):
|
|
5
|
+
"""
|
|
6
|
+
Abstract base class for database connectors.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def connect(self) -> Any:
|
|
11
|
+
"""Establish a connection to the database."""
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def close(self) -> None:
|
|
16
|
+
"""Close the database connection."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def execute_query(
|
|
21
|
+
self,
|
|
22
|
+
query: str,
|
|
23
|
+
params: Optional[tuple] = None,
|
|
24
|
+
stream: bool = False,
|
|
25
|
+
batch_size: int = 1000,
|
|
26
|
+
output_type: str = "list",
|
|
27
|
+
**kwargs: Any
|
|
28
|
+
) -> Any:
|
|
29
|
+
"""Execute a query and return the results."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from src_connectors.src_spark.connector import SparkConnector
|
|
2
|
+
from src_connectors.src_spark.configs.core_config import SparkCoreConfig
|
|
3
|
+
from src_connectors.src_spark.configs.iceberg import SparkIcebergConfig
|
|
4
|
+
from src_connectors.src_spark.configs.jdbc import SparkJdbcConfig
|
|
5
|
+
|
|
6
|
+
__all__ = ["SparkConnector", "SparkCoreConfig", "SparkIcebergConfig", "SparkJdbcConfig"]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from src_connectors.src_spark.configs.core_config import SparkCoreConfig
|
|
2
|
+
from src_connectors.src_spark.configs.iceberg import SparkIcebergConfig
|
|
3
|
+
from src_connectors.src_spark.configs.jdbc import SparkJdbcConfig
|
|
4
|
+
from src_connectors.src_spark.configs.base import SparkBaseComponent
|
|
5
|
+
|
|
6
|
+
__all__ = ["SparkCoreConfig", "SparkIcebergConfig", "SparkJdbcConfig", "SparkBaseComponent"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import List, Dict
|
|
3
|
+
|
|
4
|
+
class SparkBaseComponent(ABC):
|
|
5
|
+
"""
|
|
6
|
+
Abstract base class for Spark components.
|
|
7
|
+
|
|
8
|
+
This class defines the interface for components that require
|
|
9
|
+
specific Spark configurations and external packages.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def get_spark_config(self) -> Dict:
|
|
14
|
+
"""
|
|
15
|
+
Return Spark configuration settings.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
dict: A dictionary of Spark configuration keys and values.
|
|
19
|
+
"""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def get_required_spark_packages(self) -> List:
|
|
24
|
+
"""
|
|
25
|
+
Return a list of required Spark packages.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
list: A list of Maven coordinates or package names.
|
|
29
|
+
"""
|
|
30
|
+
pass
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from typing import Dict, List, Any, Optional
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
from src_connectors.src_spark.configs.base import SparkBaseComponent
|
|
5
|
+
from variables.spark import SparkVariables
|
|
6
|
+
|
|
7
|
+
# Load environment-based defaults
|
|
8
|
+
_spark_defaults = SparkVariables.get_spark_base_config()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SparkCoreConfig(BaseModel, SparkBaseComponent):
|
|
12
|
+
"""
|
|
13
|
+
Configuration model for core Apache Spark engine settings.
|
|
14
|
+
|
|
15
|
+
This class manages Spark core parameters including resource allocation (memory, cores),
|
|
16
|
+
cluster connectivity, and runtime dependencies. Defaults are initialized from
|
|
17
|
+
environment variables.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Cluster & Application Settings
|
|
21
|
+
spark_master: str = Field(
|
|
22
|
+
default=_spark_defaults.get("SPARK_MASTER") or "local[*]",
|
|
23
|
+
description="The Spark master URL to connect to."
|
|
24
|
+
)
|
|
25
|
+
spark_driver_host: str = Field(
|
|
26
|
+
default=_spark_defaults.get("SPARK_DRIVER_HOST") or "127.0.0.1",
|
|
27
|
+
description="Hostname or IP address for the driver to listen on."
|
|
28
|
+
)
|
|
29
|
+
spark_driver_bind_address: str = Field(
|
|
30
|
+
default=_spark_defaults.get("SPARK_DRIVER_BIND_ADDRESS") or "127.0.0.1",
|
|
31
|
+
description="Address to bind to for the driver."
|
|
32
|
+
)
|
|
33
|
+
spark_app_name: str = Field(
|
|
34
|
+
default=_spark_defaults.get("SPARK_APP_NAME") or "SparkApp",
|
|
35
|
+
description="The name of the Spark application."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Resource Management
|
|
39
|
+
spark_executor_memory: str = Field(
|
|
40
|
+
default=_spark_defaults.get("SPARK_EXECUTOR_MEMORY") or "2g",
|
|
41
|
+
description="Amount of memory to use per executor process."
|
|
42
|
+
)
|
|
43
|
+
spark_executor_cores: int = Field(
|
|
44
|
+
default=int(_spark_defaults.get("SPARK_EXECUTOR_CORES") or 2),
|
|
45
|
+
description="The number of cores to use on each executor."
|
|
46
|
+
)
|
|
47
|
+
spark_driver_memory: str = Field(
|
|
48
|
+
default=_spark_defaults.get("SPARK_DRIVER_MEMORY") or "4g",
|
|
49
|
+
description="Amount of memory to use for the driver process."
|
|
50
|
+
)
|
|
51
|
+
spark_driver_cores: int = Field(
|
|
52
|
+
default=int(_spark_defaults.get("SPARK_DRIVER_CORES") or 2),
|
|
53
|
+
description="Number of cores to use for the driver process."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Dependencies & Runtime
|
|
57
|
+
spark_jars: Optional[str] = Field(
|
|
58
|
+
default=_spark_defaults.get("SPARK_LOCAL_JARS") or "",
|
|
59
|
+
description="Comma-separated local jar paths."
|
|
60
|
+
)
|
|
61
|
+
spark_jars_packages: Optional[str] = Field(
|
|
62
|
+
default=_spark_defaults.get("SPARK_JARS_PACKAGES") or "",
|
|
63
|
+
description="Comma-separated Maven coordinates of jars to include."
|
|
64
|
+
)
|
|
65
|
+
spark_jars_ivy: str = Field(
|
|
66
|
+
default=_spark_defaults.get("SPARK_JARS_IVY") or "/tmp/.ivy2",
|
|
67
|
+
description="Path to local Ivy repository for dependency resolution."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Environment & Versioning
|
|
71
|
+
spark_version: str = Field(
|
|
72
|
+
default=_spark_defaults.get("SPARK_VERSION") or "3.0.0",
|
|
73
|
+
description="Target Spark version."
|
|
74
|
+
)
|
|
75
|
+
spark_minor_version: str = Field(
|
|
76
|
+
default=_spark_defaults.get("SPARK_MINOR_VERSION") or "3.0",
|
|
77
|
+
description="Target Spark minor version."
|
|
78
|
+
)
|
|
79
|
+
spark_local_dir: str = Field(
|
|
80
|
+
default=_spark_defaults.get("SPARK_LOCAL_DIR") or "/tmp/spark",
|
|
81
|
+
description="Directory for Spark 'scratch' space (map outputs, RDD spills)."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get_spark_config(self) -> Dict[str, Any]:
|
|
85
|
+
"""
|
|
86
|
+
Generates a dictionary of Spark configuration properties.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Dict[str, Any]: Flat dictionary of 'spark.*' configuration keys and values.
|
|
90
|
+
"""
|
|
91
|
+
config = {
|
|
92
|
+
"spark.master": self.spark_master,
|
|
93
|
+
"spark.app.name": self.spark_app_name,
|
|
94
|
+
"spark.executor.memory": self.spark_executor_memory,
|
|
95
|
+
"spark.executor.cores": self.spark_executor_cores,
|
|
96
|
+
"spark.driver.memory": self.spark_driver_memory,
|
|
97
|
+
"spark.driver.cores": self.spark_driver_cores,
|
|
98
|
+
"spark.jars.ivy": self.spark_jars_ivy,
|
|
99
|
+
"spark.local.dir": self.spark_local_dir,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Handle Driver Host logic:
|
|
103
|
+
# Force 127.0.0.1 ONLY if running in local mode to avoid RPC errors on dev machines.
|
|
104
|
+
# In cluster mode (K8s/YARN), we let Spark/Cluster Manager handle it or use provided host.
|
|
105
|
+
is_local = self.spark_master.startswith("local")
|
|
106
|
+
if is_local:
|
|
107
|
+
config["spark.driver.host"] = self.spark_driver_host
|
|
108
|
+
config["spark.driver.bindAddress"] = self.spark_driver_bind_address
|
|
109
|
+
else:
|
|
110
|
+
# In cluster mode, only apply if explicitly provided (not default 127.0.0.1)
|
|
111
|
+
if self.spark_driver_host != "127.0.0.1":
|
|
112
|
+
config["spark.driver.host"] = self.spark_driver_host
|
|
113
|
+
if self.spark_driver_bind_address != "127.0.0.1":
|
|
114
|
+
config["spark.driver.bindAddress"] = self.spark_driver_bind_address
|
|
115
|
+
|
|
116
|
+
# Handle Jars and Packages logic priority
|
|
117
|
+
if self.spark_jars:
|
|
118
|
+
config["spark.jars"] = self.spark_jars
|
|
119
|
+
elif self.spark_jars_packages:
|
|
120
|
+
config["spark.jars.packages"] = self.spark_jars_packages
|
|
121
|
+
|
|
122
|
+
return config
|
|
123
|
+
|
|
124
|
+
def get_required_spark_packages(self) -> List[str]:
|
|
125
|
+
"""
|
|
126
|
+
Returns a list of required Spark packages.
|
|
127
|
+
If local jars are provided, packages are usually skipped to avoid conflicts.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
List[str]: A list of cleaned Maven package coordinates.
|
|
131
|
+
"""
|
|
132
|
+
if self.spark_jars:
|
|
133
|
+
return []
|
|
134
|
+
|
|
135
|
+
if not self.spark_jars_packages:
|
|
136
|
+
return []
|
|
137
|
+
|
|
138
|
+
return [pkg.strip() for pkg in self.spark_jars_packages.split(",") if pkg.strip()]
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional, List, Union
|
|
2
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
3
|
+
|
|
4
|
+
from src_connectors.src_spark.configs.base import SparkBaseComponent
|
|
5
|
+
from variables.spark import SparkVariables
|
|
6
|
+
|
|
7
|
+
# Load environment-based defaults from centralized variables
|
|
8
|
+
_iceberg_defaults = SparkVariables.get_iceberg_config()
|
|
9
|
+
_minio_defaults = SparkVariables.get_minio_config()
|
|
10
|
+
_spark_defaults = SparkVariables.get_spark_base_config()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SparkIcebergConfig(BaseModel, SparkBaseComponent):
|
|
14
|
+
"""
|
|
15
|
+
Configuration model for Apache Iceberg integration with Spark.
|
|
16
|
+
|
|
17
|
+
This class manages Iceberg catalog settings, including connection URIs,
|
|
18
|
+
authentication credentials, and warehouse locations. It constructs the
|
|
19
|
+
necessary 'spark.sql.catalog' properties required by the Iceberg Spark runtime.
|
|
20
|
+
"""
|
|
21
|
+
model_config = ConfigDict(extra='allow')
|
|
22
|
+
|
|
23
|
+
iceberg_catalog_uri: str = Field(
|
|
24
|
+
default=_iceberg_defaults.get("SPARK_ICEBERG_CATALOG_URI") or "",
|
|
25
|
+
description="The URI for the Iceberg catalog (REST/Hive/Glue endpoint)."
|
|
26
|
+
)
|
|
27
|
+
iceberg_catalog_type: str = Field(
|
|
28
|
+
default=_iceberg_defaults.get("SPARK_ICEBERG_CATALOG_TYPE") or "hadoop",
|
|
29
|
+
description="The type of Iceberg catalog (e.g., 'rest', 'hive', 'hadoop')."
|
|
30
|
+
)
|
|
31
|
+
iceberg_oauth2_server_uri: str = Field(
|
|
32
|
+
default=_iceberg_defaults.get("SPARK_ICEBERG_OAUTH2_SERVER_URI") or "",
|
|
33
|
+
description="OAuth2 server URI for REST catalog authentication."
|
|
34
|
+
)
|
|
35
|
+
iceberg_credential: str = Field(
|
|
36
|
+
default=_iceberg_defaults.get("SPARK_ICEBERG_CREDENTIAL") or "",
|
|
37
|
+
description="Credentials for Iceberg catalog (e.g., client_id:client_secret)."
|
|
38
|
+
)
|
|
39
|
+
iceberg_scope: str = Field(
|
|
40
|
+
default=_iceberg_defaults.get("SPARK_ICEBERG_SCOPE") or "",
|
|
41
|
+
description="OAuth2 scope for catalog authentication."
|
|
42
|
+
)
|
|
43
|
+
iceberg_warehouse: Union[str, List[str]] = Field(
|
|
44
|
+
default=_iceberg_defaults.get("SPARK_ICEBERG_WAREHOUSE") or "",
|
|
45
|
+
description="The warehouse location/name used for the Spark SQL catalog prefix."
|
|
46
|
+
)
|
|
47
|
+
iceberg_version: str = Field(
|
|
48
|
+
default=_iceberg_defaults.get("SPARK_ICEBERG_VERSION") or "1.4.2",
|
|
49
|
+
description="The Version of Iceberg runtime packages to load."
|
|
50
|
+
)
|
|
51
|
+
spark_jars: Optional[str] = Field(
|
|
52
|
+
default=_spark_defaults.get("SPARK_LOCAL_JARS") or "",
|
|
53
|
+
description="Local jar file paths to include in the Spark session."
|
|
54
|
+
)
|
|
55
|
+
spark_jars_packages: Optional[str] = Field(
|
|
56
|
+
default=_spark_defaults.get("SPARK_JARS_PACKAGES") or "",
|
|
57
|
+
description="Override for Spark dependency packages."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Storage Settings (MinIO / S3)
|
|
61
|
+
spark_minio_access_key: str = Field(
|
|
62
|
+
default=_minio_defaults.get("SPARK_MINIO_ACCESS_KEY_ID") or "",
|
|
63
|
+
description="Access key for MinIO or S3 storage."
|
|
64
|
+
)
|
|
65
|
+
spark_minio_secret_key: str = Field(
|
|
66
|
+
default=_minio_defaults.get("SPARK_MINIO_SECRET_ACCESS_KEY") or "",
|
|
67
|
+
description="Secret key for MinIO or S3 storage."
|
|
68
|
+
)
|
|
69
|
+
spark_minio_endpoint: str = Field(
|
|
70
|
+
default=_minio_defaults.get("SPARK_MINIO_ENDPOINT_URL") or "",
|
|
71
|
+
description="Endpoint URL for MinIO or S3 storage (e.g., http://localhost:9000)."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def get_spark_config(self) -> Dict[str, Any]:
|
|
75
|
+
"""
|
|
76
|
+
Generates Spark configuration for the Iceberg catalog.
|
|
77
|
+
|
|
78
|
+
Includes SQL extensions, catalog-specific properties, and Hadoop S3A settings
|
|
79
|
+
for cloud storage connectivity.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Dict[str, Any]: A flat dictionary of Spark configuration properties.
|
|
83
|
+
"""
|
|
84
|
+
config = {
|
|
85
|
+
"spark.sql.extensions": "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Determine warehouses to configure (handles single string or list)
|
|
89
|
+
warehouses = [self.iceberg_warehouse] if isinstance(self.iceberg_warehouse, str) else self.iceberg_warehouse
|
|
90
|
+
|
|
91
|
+
for wh in warehouses:
|
|
92
|
+
# Using warehouse name as the catalog identifier prefix
|
|
93
|
+
prefix = f"spark.sql.catalog.{wh}"
|
|
94
|
+
config[prefix] = "org.apache.iceberg.spark.SparkCatalog"
|
|
95
|
+
|
|
96
|
+
# For non-REST catalogs, type can be used as shorthand (hive, hadoop, etc.)
|
|
97
|
+
# For REST catalogs, catalog-impl is preferred and setting both causes conflicts.
|
|
98
|
+
if self.iceberg_catalog_type != "rest":
|
|
99
|
+
config[f"{prefix}.type"] = self.iceberg_catalog_type
|
|
100
|
+
|
|
101
|
+
# Catalog URI and basic I/O settings
|
|
102
|
+
if self.iceberg_catalog_uri:
|
|
103
|
+
config[f"{prefix}.uri"] = self.iceberg_catalog_uri
|
|
104
|
+
config[f"{prefix}.classloader.isolation.enabled"] = "false"
|
|
105
|
+
|
|
106
|
+
# Use S3FileIO if S3/MinIO is likely being used
|
|
107
|
+
if any(s in wh.lower() or s in self.iceberg_catalog_uri.lower() for s in ["s3", "minio"]):
|
|
108
|
+
config[f"{prefix}.io-impl"] = "org.apache.iceberg.aws.s3.S3FileIO"
|
|
109
|
+
|
|
110
|
+
if wh:
|
|
111
|
+
config[f"{prefix}.warehouse"] = wh
|
|
112
|
+
|
|
113
|
+
# REST Catalog specialized authentication settings
|
|
114
|
+
if self.iceberg_catalog_type == "rest":
|
|
115
|
+
self._apply_rest_catalog_config(config, prefix)
|
|
116
|
+
|
|
117
|
+
# Handle Spark Jars and Packages logic: Priority to local jars
|
|
118
|
+
self._apply_dependency_config(config)
|
|
119
|
+
|
|
120
|
+
# Apply Hadoop S3A storage configurations if credentials are provided
|
|
121
|
+
if self.spark_minio_access_key and self.spark_minio_secret_key:
|
|
122
|
+
config.update(self._get_s3_hadoop_config())
|
|
123
|
+
|
|
124
|
+
return config
|
|
125
|
+
|
|
126
|
+
def _apply_rest_catalog_config(self, config: Dict[str, Any], prefix: str) -> None:
|
|
127
|
+
"""Applies REST catalog specific authentication and refresh settings."""
|
|
128
|
+
config[f"{prefix}.catalog-impl"] = "org.apache.iceberg.rest.RESTCatalog"
|
|
129
|
+
if self.iceberg_oauth2_server_uri:
|
|
130
|
+
config[f"{prefix}.oauth2-server-uri"] = self.iceberg_oauth2_server_uri
|
|
131
|
+
if self.iceberg_credential:
|
|
132
|
+
config[f"{prefix}.credential"] = self.iceberg_credential
|
|
133
|
+
if self.iceberg_scope:
|
|
134
|
+
config[f"{prefix}.scope"] = self.iceberg_scope
|
|
135
|
+
|
|
136
|
+
config[f"{prefix}.rest.auth.type"] = "oauth2"
|
|
137
|
+
config[f"{prefix}.token-refresh-enabled"] = "true"
|
|
138
|
+
config[f"{prefix}.token-refresh-min-validity-time"] = "60s"
|
|
139
|
+
|
|
140
|
+
def _apply_dependency_config(self, config: Dict[str, Any]) -> None:
|
|
141
|
+
"""Handles the logic for Spark JARs and Packages with local priority."""
|
|
142
|
+
if self.spark_jars:
|
|
143
|
+
config["spark.jars"] = self.spark_jars
|
|
144
|
+
else:
|
|
145
|
+
if self.spark_jars_packages:
|
|
146
|
+
config["spark.jars.packages"] = self.spark_jars_packages
|
|
147
|
+
else:
|
|
148
|
+
spark_minor_version = _spark_defaults.get("SPARK_MINOR_VERSION", "3.0")
|
|
149
|
+
config["spark.jars.packages"] = (
|
|
150
|
+
f"org.apache.iceberg:iceberg-spark-runtime-{spark_minor_version}_2.12:{self.iceberg_version},"
|
|
151
|
+
f"org.apache.iceberg:iceberg-azure-bundle:{self.iceberg_version},"
|
|
152
|
+
f"org.apache.iceberg:iceberg-aws-bundle:{self.iceberg_version},"
|
|
153
|
+
f"org.apache.iceberg:iceberg-gcp-bundle:{self.iceberg_version}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def _get_s3_hadoop_config(self) -> Dict[str, str]:
|
|
157
|
+
"""
|
|
158
|
+
Generates Hadoop S3A configurations for MinIO/S3 compatible storage.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Dict[str, str]: Hadoop filesystem properties.
|
|
162
|
+
"""
|
|
163
|
+
hadoop_conf = {
|
|
164
|
+
"spark.hadoop.fs.s3a.access.key": self.spark_minio_access_key,
|
|
165
|
+
"spark.hadoop.fs.s3a.secret.key": self.spark_minio_secret_key,
|
|
166
|
+
"spark.hadoop.fs.s3a.impl": "org.apache.hadoop.fs.s3a.S3AFileSystem",
|
|
167
|
+
"spark.hadoop.fs.s3a.path.style.access": "true",
|
|
168
|
+
"spark.hadoop.fs.s3a.fast.upload": "true",
|
|
169
|
+
"spark.hadoop.fs.s3a.connection.ssl.enabled": "false" if "http://" in self.spark_minio_endpoint.lower() else "true"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if self.spark_minio_endpoint:
|
|
173
|
+
hadoop_conf["spark.hadoop.fs.s3a.endpoint"] = self.spark_minio_endpoint
|
|
174
|
+
|
|
175
|
+
# When keys are provided, we use the SimpleAWSCredentialsProvider
|
|
176
|
+
hadoop_conf["spark.hadoop.fs.s3a.aws.credentials.provider"] = (
|
|
177
|
+
"org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Compatibility with libraries using 's3' instead of 's3a'
|
|
181
|
+
hadoop_conf["spark.hadoop.fs.s3.impl"] = "org.apache.hadoop.fs.s3a.S3AFileSystem"
|
|
182
|
+
|
|
183
|
+
return hadoop_conf
|
|
184
|
+
|
|
185
|
+
def get_required_spark_packages(self) -> List[str]:
|
|
186
|
+
"""
|
|
187
|
+
Returns a list of required Spark packages for Iceberg.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
List[str]: A list of Maven coordinates for Iceberg and related bundles.
|
|
191
|
+
"""
|
|
192
|
+
if self.spark_jars:
|
|
193
|
+
return []
|
|
194
|
+
|
|
195
|
+
if self.spark_jars_packages:
|
|
196
|
+
return [pkg.strip() for pkg in self.spark_jars_packages.split(",") if pkg.strip()]
|
|
197
|
+
|
|
198
|
+
# Default Iceberg packages if nothing is provided
|
|
199
|
+
spark_minor_version = _spark_defaults.get("SPARK_MINOR_VERSION", "3.0")
|
|
200
|
+
return [
|
|
201
|
+
f"org.apache.iceberg:iceberg-spark-runtime-{spark_minor_version}_2.12:{self.iceberg_version}",
|
|
202
|
+
f"org.apache.iceberg:iceberg-azure-bundle:{self.iceberg_version}",
|
|
203
|
+
f"org.apache.iceberg:iceberg-aws-bundle:{self.iceberg_version}",
|
|
204
|
+
f"org.apache.iceberg:iceberg-gcp-bundle:{self.iceberg_version}"
|
|
205
|
+
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import Dict, Any, List
|
|
2
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
3
|
+
|
|
4
|
+
from src_connectors.src_spark.configs.base import SparkBaseComponent
|
|
5
|
+
from variables.spark import SparkVariables
|
|
6
|
+
|
|
7
|
+
# Load environment-based defaults from centralized variables
|
|
8
|
+
_jdbc_defaults = SparkVariables.get_jdbc_config()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SparkJdbcConfig(BaseModel, SparkBaseComponent):
|
|
12
|
+
"""
|
|
13
|
+
Configuration model for Spark JDBC connections.
|
|
14
|
+
|
|
15
|
+
This class manages JDBC connectivity parameters, including driver details,
|
|
16
|
+
connection URIs, and authentication credentials. It provides both descriptive
|
|
17
|
+
metadata and functional options for Spark data source operations.
|
|
18
|
+
"""
|
|
19
|
+
model_config = ConfigDict(extra='allow')
|
|
20
|
+
|
|
21
|
+
jdbc_url: str = Field(
|
|
22
|
+
default=_jdbc_defaults.get("SPARK_JDBC_URL") or "",
|
|
23
|
+
description="The JDBC connection URL (e.g., 'jdbc:postgresql://localhost:5432/db')."
|
|
24
|
+
)
|
|
25
|
+
jdbc_driver: str = Field(
|
|
26
|
+
default=_jdbc_defaults.get("SPARK_JDBC_DRIVER") or "",
|
|
27
|
+
description="The fully qualified class name of the JDBC driver (e.g., 'org.postgresql.Driver')."
|
|
28
|
+
)
|
|
29
|
+
jdbc_username: str = Field(
|
|
30
|
+
default=_jdbc_defaults.get("SPARK_JDBC_USERNAME") or "",
|
|
31
|
+
description="The database username for the JDBC connection."
|
|
32
|
+
)
|
|
33
|
+
jdbc_password: str = Field(
|
|
34
|
+
default=_jdbc_defaults.get("SPARK_JDBC_PASSWORD") or "",
|
|
35
|
+
description="The database password for the JDBC connection."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def get_spark_config(self) -> Dict[str, Any]:
|
|
39
|
+
"""
|
|
40
|
+
Returns a descriptive overview of the JDBC configuration.
|
|
41
|
+
|
|
42
|
+
Security Note: Sensitive credentials (password) are masked in the output.
|
|
43
|
+
Note: JDBC parameters are typically utilized in .read.format("jdbc").options(...)
|
|
44
|
+
rather than being part of the global SparkConf.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Dict[str, Any]: A dictionary containing JDBC connection metadata.
|
|
48
|
+
"""
|
|
49
|
+
return {
|
|
50
|
+
"spark_jdbc_url": self.jdbc_url,
|
|
51
|
+
"spark_jdbc_driver": self.jdbc_driver,
|
|
52
|
+
"spark_jdbc_user": self.jdbc_username,
|
|
53
|
+
"spark_jdbc_password_provided": bool(self.jdbc_password)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def get_jdbc_options(self) -> Dict[str, str]:
|
|
57
|
+
"""
|
|
58
|
+
Generates connection options for Spark's JDBC data source.
|
|
59
|
+
|
|
60
|
+
Suitable for use with spark.read.format("jdbc").options(**config.get_jdbc_options()).load().
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Dict[str, str]: A dictionary of key-value pairs representing JDBC options.
|
|
64
|
+
"""
|
|
65
|
+
options = {
|
|
66
|
+
"url": self.jdbc_url,
|
|
67
|
+
"driver": self.jdbc_driver,
|
|
68
|
+
"user": self.jdbc_username,
|
|
69
|
+
"password": self.jdbc_password,
|
|
70
|
+
}
|
|
71
|
+
# Filter out empty options and ensure all values are strings for Spark compatibility
|
|
72
|
+
return {k: str(v) for k, v in options.items() if v}
|
|
73
|
+
|
|
74
|
+
def get_required_spark_packages(self) -> List[str]:
|
|
75
|
+
"""
|
|
76
|
+
Returns a list of required Spark packages for JDBC.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
List[str]: Usually an empty list as JDBC drivers are managed via JARs.
|
|
80
|
+
"""
|
|
81
|
+
return []
|