e6data-python-connector 2.3.12rc31__tar.gz → 2.3.12rc32__tar.gz
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.
- {e6data_python_connector-2.3.12rc31/e6data_python_connector.egg-info → e6data_python_connector-2.3.12rc32}/PKG-INFO +69 -2
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/README.md +68 -1
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_manager.py +44 -2
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6data_grpc.py +47 -2
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32/e6data_python_connector.egg-info}/PKG-INFO +69 -2
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/setup.py +1 -1
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/LICENSE +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/MANIFEST.in +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_server/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/common.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/connection_pool.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/constants.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/datainputstream.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/date_time_utils.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/dialect.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/exceptions.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/QueryEngineService.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/constants.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/ttypes.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/strategy.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/typeId.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/SOURCES.txt +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/entry_points.txt +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/requires.txt +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/top_level.txt +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/e6x_vector/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/e6x_vector/constants.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/e6x_vector/ttypes.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/pyproject.toml +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/setup.cfg +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/__init__.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_38_nines.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_all_cases.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_binary.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_correct_value.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_fields.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/check_decimal_errors.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/cleanup_test_files.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/debug_38_nines.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/debug_binary.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/final_test.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/move_tests.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/quick_test.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_38_nines.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_all_decimal128_cases.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_cluster_manager_efficiency.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_cluster_manager_none_strategy.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_cluster_manager_strategy.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_comprehensive.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_connection_pool.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_connection_pool_e2e.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_current_implementation.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_decimal128_binary_parsing.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_decimal128_parsing.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_fix.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_improved_parsing.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_known_case.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_manual_analysis.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_mock_server.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_multiprocessing_fix.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_new_implementation.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_pool_concurrency_simple.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_pool_threading_multiprocessing.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_simple_connection_pool.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_specific_binary.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy_logic.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy_persistence_fix.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy_sharing_fix.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_user_binary_value.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/tests.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/tests_grpc.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/validate_decimal128.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/validate_implementation.py +0 -0
- {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/verify_decimal_fix.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.12rc32
|
|
4
4
|
Summary: Client for the e6data distributed SQL Engine.
|
|
5
5
|
Home-page: https://github.com/e6x-labs/e6data-python-connector
|
|
6
6
|
Author: e6data, Inc.
|
|
@@ -39,7 +39,7 @@ Dynamic: summary
|
|
|
39
39
|
|
|
40
40
|
# e6data Python Connector
|
|
41
41
|
|
|
42
|
-

|
|
43
43
|
|
|
44
44
|
## Introduction
|
|
45
45
|
|
|
@@ -122,6 +122,7 @@ The `Connection` class supports the following parameters:
|
|
|
122
122
|
| `catalog` | str | No | None | Catalog name |
|
|
123
123
|
| `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
|
|
124
124
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
125
|
+
| `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
|
|
125
126
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
126
127
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
127
128
|
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
@@ -159,6 +160,72 @@ conn = Connection(
|
|
|
159
160
|
)
|
|
160
161
|
```
|
|
161
162
|
|
|
163
|
+
#### HTTPS Connection with HAProxy
|
|
164
|
+
|
|
165
|
+
When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
|
|
166
|
+
|
|
167
|
+
**Using a CA certificate file path:**
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
conn = Connection(
|
|
171
|
+
host=host,
|
|
172
|
+
port=443,
|
|
173
|
+
username=username,
|
|
174
|
+
password=password,
|
|
175
|
+
database=database,
|
|
176
|
+
secure=True,
|
|
177
|
+
ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Reading certificate content as bytes:**
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
# Read certificate file and pass as bytes
|
|
185
|
+
with open('/path/to/ca-cert.pem', 'rb') as cert_file:
|
|
186
|
+
cert_data = cert_file.read()
|
|
187
|
+
|
|
188
|
+
conn = Connection(
|
|
189
|
+
host=host,
|
|
190
|
+
port=443,
|
|
191
|
+
username=username,
|
|
192
|
+
password=password,
|
|
193
|
+
database=database,
|
|
194
|
+
secure=True,
|
|
195
|
+
ssl_cert=cert_data # Certificate content as bytes
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Using system CA bundle for publicly signed certificates:**
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# When ssl_cert is None, system default CA bundle is used
|
|
203
|
+
conn = Connection(
|
|
204
|
+
host=host,
|
|
205
|
+
port=443,
|
|
206
|
+
username=username,
|
|
207
|
+
password=password,
|
|
208
|
+
database=database,
|
|
209
|
+
secure=True # Uses system CA bundle by default
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Connection pooling with custom CA certificate:**
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
pool = ConnectionPool(
|
|
217
|
+
min_size=2,
|
|
218
|
+
max_size=10,
|
|
219
|
+
host=host,
|
|
220
|
+
port=443,
|
|
221
|
+
username=username,
|
|
222
|
+
password=password,
|
|
223
|
+
database=database,
|
|
224
|
+
secure=True,
|
|
225
|
+
ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
162
229
|
#### Handling Missing System Dependencies
|
|
163
230
|
|
|
164
231
|
The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# e6data Python Connector
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
## Introduction
|
|
6
6
|
|
|
@@ -83,6 +83,7 @@ The `Connection` class supports the following parameters:
|
|
|
83
83
|
| `catalog` | str | No | None | Catalog name |
|
|
84
84
|
| `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
|
|
85
85
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
86
|
+
| `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
|
|
86
87
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
87
88
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
88
89
|
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
@@ -120,6 +121,72 @@ conn = Connection(
|
|
|
120
121
|
)
|
|
121
122
|
```
|
|
122
123
|
|
|
124
|
+
#### HTTPS Connection with HAProxy
|
|
125
|
+
|
|
126
|
+
When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
|
|
127
|
+
|
|
128
|
+
**Using a CA certificate file path:**
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
conn = Connection(
|
|
132
|
+
host=host,
|
|
133
|
+
port=443,
|
|
134
|
+
username=username,
|
|
135
|
+
password=password,
|
|
136
|
+
database=database,
|
|
137
|
+
secure=True,
|
|
138
|
+
ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Reading certificate content as bytes:**
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# Read certificate file and pass as bytes
|
|
146
|
+
with open('/path/to/ca-cert.pem', 'rb') as cert_file:
|
|
147
|
+
cert_data = cert_file.read()
|
|
148
|
+
|
|
149
|
+
conn = Connection(
|
|
150
|
+
host=host,
|
|
151
|
+
port=443,
|
|
152
|
+
username=username,
|
|
153
|
+
password=password,
|
|
154
|
+
database=database,
|
|
155
|
+
secure=True,
|
|
156
|
+
ssl_cert=cert_data # Certificate content as bytes
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Using system CA bundle for publicly signed certificates:**
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# When ssl_cert is None, system default CA bundle is used
|
|
164
|
+
conn = Connection(
|
|
165
|
+
host=host,
|
|
166
|
+
port=443,
|
|
167
|
+
username=username,
|
|
168
|
+
password=password,
|
|
169
|
+
database=database,
|
|
170
|
+
secure=True # Uses system CA bundle by default
|
|
171
|
+
)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Connection pooling with custom CA certificate:**
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
pool = ConnectionPool(
|
|
178
|
+
min_size=2,
|
|
179
|
+
max_size=10,
|
|
180
|
+
host=host,
|
|
181
|
+
port=443,
|
|
182
|
+
username=username,
|
|
183
|
+
password=password,
|
|
184
|
+
database=database,
|
|
185
|
+
secure=True,
|
|
186
|
+
ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
123
190
|
#### Handling Missing System Dependencies
|
|
124
191
|
|
|
125
192
|
The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
|
|
@@ -140,7 +140,7 @@ class ClusterManager:
|
|
|
140
140
|
"""
|
|
141
141
|
|
|
142
142
|
def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5,
|
|
143
|
-
cluster_uuid=None, grpc_options=None, debug=False):
|
|
143
|
+
cluster_uuid=None, grpc_options=None, debug=False, ssl_cert=None):
|
|
144
144
|
"""
|
|
145
145
|
Initializes a new instance of the ClusterManager class.
|
|
146
146
|
|
|
@@ -156,6 +156,8 @@ class ClusterManager:
|
|
|
156
156
|
cluster_uuid (str, optional): The unique identifier for the target cluster;
|
|
157
157
|
defaults to None.
|
|
158
158
|
debug (bool, optional): Enable debug logging; defaults to False.
|
|
159
|
+
ssl_cert (str or bytes, optional): Path to CA certificate file (PEM format) or
|
|
160
|
+
certificate content as bytes for secure connections; defaults to None.
|
|
159
161
|
"""
|
|
160
162
|
|
|
161
163
|
self._host = host
|
|
@@ -169,6 +171,46 @@ class ClusterManager:
|
|
|
169
171
|
if grpc_options is None:
|
|
170
172
|
self._grpc_options = dict()
|
|
171
173
|
self._debug = debug
|
|
174
|
+
self._ssl_cert = ssl_cert
|
|
175
|
+
|
|
176
|
+
def _get_ssl_credentials(self):
|
|
177
|
+
"""
|
|
178
|
+
Get SSL credentials for secure gRPC channel.
|
|
179
|
+
|
|
180
|
+
Handles three scenarios:
|
|
181
|
+
1. ssl_cert is a string (file path): Read the PEM certificate from the file
|
|
182
|
+
2. ssl_cert is bytes: Use the certificate content directly
|
|
183
|
+
3. ssl_cert is None: Use system default CA bundle
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
grpc.ChannelCredentials: SSL credentials for secure channel
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
FileNotFoundError: If ssl_cert is a file path but the file doesn't exist
|
|
190
|
+
IOError: If ssl_cert file cannot be read
|
|
191
|
+
"""
|
|
192
|
+
if self._ssl_cert is None:
|
|
193
|
+
# Use system default CA bundle
|
|
194
|
+
return grpc.ssl_channel_credentials()
|
|
195
|
+
elif isinstance(self._ssl_cert, str):
|
|
196
|
+
# ssl_cert is a file path - read the certificate from file
|
|
197
|
+
try:
|
|
198
|
+
with open(self._ssl_cert, 'rb') as cert_file:
|
|
199
|
+
root_ca_cert = cert_file.read()
|
|
200
|
+
return grpc.ssl_channel_credentials(root_certificates=root_ca_cert)
|
|
201
|
+
except FileNotFoundError:
|
|
202
|
+
logger.error(f"SSL certificate file not found: {self._ssl_cert}")
|
|
203
|
+
raise
|
|
204
|
+
except IOError as e:
|
|
205
|
+
logger.error(f"Failed to read SSL certificate file {self._ssl_cert}: {e}")
|
|
206
|
+
raise
|
|
207
|
+
elif isinstance(self._ssl_cert, bytes):
|
|
208
|
+
# ssl_cert is certificate content as bytes
|
|
209
|
+
return grpc.ssl_channel_credentials(root_certificates=self._ssl_cert)
|
|
210
|
+
else:
|
|
211
|
+
# Invalid type - log warning and use system default
|
|
212
|
+
logger.warning(f"Invalid ssl_cert type: {type(self._ssl_cert)}. Using system default CA bundle.")
|
|
213
|
+
return grpc.ssl_channel_credentials()
|
|
172
214
|
|
|
173
215
|
@property
|
|
174
216
|
def _get_connection(self):
|
|
@@ -184,7 +226,7 @@ class ClusterManager:
|
|
|
184
226
|
self._channel = grpc.secure_channel(
|
|
185
227
|
target='{}:{}'.format(self._host, self._port),
|
|
186
228
|
options=self._grpc_options,
|
|
187
|
-
credentials=
|
|
229
|
+
credentials=self._get_ssl_credentials()
|
|
188
230
|
)
|
|
189
231
|
else:
|
|
190
232
|
self._channel = grpc.insecure_channel(
|
|
@@ -331,6 +331,7 @@ class Connection(object):
|
|
|
331
331
|
database: str = None,
|
|
332
332
|
cluster_name: str = None,
|
|
333
333
|
secure: bool = False,
|
|
334
|
+
ssl_cert = None,
|
|
334
335
|
auto_resume: bool = True,
|
|
335
336
|
scheme: str = 'e6data',
|
|
336
337
|
grpc_options: dict = None,
|
|
@@ -356,6 +357,9 @@ class Connection(object):
|
|
|
356
357
|
Cluster's name
|
|
357
358
|
secure: bool, Optional
|
|
358
359
|
Flag to use a secure channel for data transfer
|
|
360
|
+
ssl_cert: str or bytes, Optional
|
|
361
|
+
Path to CA certificate file (PEM format) or certificate content as bytes for
|
|
362
|
+
secure connections. If None, system default CA bundle is used.
|
|
359
363
|
auto_resume: bool, Optional
|
|
360
364
|
Flag to enable auto resume of the cluster before the query execution
|
|
361
365
|
scheme: string, Optional
|
|
@@ -387,6 +391,7 @@ class Connection(object):
|
|
|
387
391
|
self._port = port
|
|
388
392
|
|
|
389
393
|
self._secure_channel = secure
|
|
394
|
+
self._ssl_cert = ssl_cert
|
|
390
395
|
|
|
391
396
|
self.catalog_name = catalog
|
|
392
397
|
|
|
@@ -511,6 +516,45 @@ class Connection(object):
|
|
|
511
516
|
|
|
512
517
|
return self._cached_grpc_options
|
|
513
518
|
|
|
519
|
+
def _get_ssl_credentials(self):
|
|
520
|
+
"""
|
|
521
|
+
Get SSL credentials for secure gRPC channel.
|
|
522
|
+
|
|
523
|
+
Handles three scenarios:
|
|
524
|
+
1. ssl_cert is a string (file path): Read the PEM certificate from the file
|
|
525
|
+
2. ssl_cert is bytes: Use the certificate content directly
|
|
526
|
+
3. ssl_cert is None: Use system default CA bundle
|
|
527
|
+
|
|
528
|
+
Returns:
|
|
529
|
+
grpc.ChannelCredentials: SSL credentials for secure channel
|
|
530
|
+
|
|
531
|
+
Raises:
|
|
532
|
+
FileNotFoundError: If ssl_cert is a file path but the file doesn't exist
|
|
533
|
+
IOError: If ssl_cert file cannot be read
|
|
534
|
+
"""
|
|
535
|
+
if self._ssl_cert is None:
|
|
536
|
+
# Use system default CA bundle
|
|
537
|
+
return grpc.ssl_channel_credentials()
|
|
538
|
+
elif isinstance(self._ssl_cert, str):
|
|
539
|
+
# ssl_cert is a file path - read the certificate from file
|
|
540
|
+
try:
|
|
541
|
+
with open(self._ssl_cert, 'rb') as cert_file:
|
|
542
|
+
root_ca_cert = cert_file.read()
|
|
543
|
+
return grpc.ssl_channel_credentials(root_certificates=root_ca_cert)
|
|
544
|
+
except FileNotFoundError:
|
|
545
|
+
logger.error(f"SSL certificate file not found: {self._ssl_cert}")
|
|
546
|
+
raise
|
|
547
|
+
except IOError as e:
|
|
548
|
+
logger.error(f"Failed to read SSL certificate file {self._ssl_cert}: {e}")
|
|
549
|
+
raise
|
|
550
|
+
elif isinstance(self._ssl_cert, bytes):
|
|
551
|
+
# ssl_cert is certificate content as bytes
|
|
552
|
+
return grpc.ssl_channel_credentials(root_certificates=self._ssl_cert)
|
|
553
|
+
else:
|
|
554
|
+
# Invalid type - log warning and use system default
|
|
555
|
+
logger.warning(f"Invalid ssl_cert type: {type(self._ssl_cert)}. Using system default CA bundle.")
|
|
556
|
+
return grpc.ssl_channel_credentials()
|
|
557
|
+
|
|
514
558
|
def _create_client(self):
|
|
515
559
|
"""
|
|
516
560
|
Creates a gRPC client for the connection.
|
|
@@ -531,7 +575,7 @@ class Connection(object):
|
|
|
531
575
|
self._channel = grpc.secure_channel(
|
|
532
576
|
target='{}:{}'.format(self._host, self._port),
|
|
533
577
|
options=self._get_grpc_options,
|
|
534
|
-
credentials=
|
|
578
|
+
credentials=self._get_ssl_credentials()
|
|
535
579
|
)
|
|
536
580
|
else:
|
|
537
581
|
self._channel = grpc.insecure_channel(
|
|
@@ -713,7 +757,8 @@ class Connection(object):
|
|
|
713
757
|
secure_channel=self._secure_channel,
|
|
714
758
|
cluster_uuid=self.cluster_name,
|
|
715
759
|
timeout=self.grpc_auto_resume_timeout_seconds,
|
|
716
|
-
debug=self._debug
|
|
760
|
+
debug=self._debug,
|
|
761
|
+
ssl_cert=self._ssl_cert
|
|
717
762
|
).resume()
|
|
718
763
|
return status # Return boolean status directly
|
|
719
764
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.12rc32
|
|
4
4
|
Summary: Client for the e6data distributed SQL Engine.
|
|
5
5
|
Home-page: https://github.com/e6x-labs/e6data-python-connector
|
|
6
6
|
Author: e6data, Inc.
|
|
@@ -39,7 +39,7 @@ Dynamic: summary
|
|
|
39
39
|
|
|
40
40
|
# e6data Python Connector
|
|
41
41
|
|
|
42
|
-

|
|
43
43
|
|
|
44
44
|
## Introduction
|
|
45
45
|
|
|
@@ -122,6 +122,7 @@ The `Connection` class supports the following parameters:
|
|
|
122
122
|
| `catalog` | str | No | None | Catalog name |
|
|
123
123
|
| `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
|
|
124
124
|
| `secure` | bool | No | False | Enable SSL/TLS for secure connections |
|
|
125
|
+
| `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
|
|
125
126
|
| `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
|
|
126
127
|
| `grpc_options` | dict | No | None | Additional gRPC configuration options |
|
|
127
128
|
| `debug` | bool | No | False | Enable debug logging for troubleshooting |
|
|
@@ -159,6 +160,72 @@ conn = Connection(
|
|
|
159
160
|
)
|
|
160
161
|
```
|
|
161
162
|
|
|
163
|
+
#### HTTPS Connection with HAProxy
|
|
164
|
+
|
|
165
|
+
When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
|
|
166
|
+
|
|
167
|
+
**Using a CA certificate file path:**
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
conn = Connection(
|
|
171
|
+
host=host,
|
|
172
|
+
port=443,
|
|
173
|
+
username=username,
|
|
174
|
+
password=password,
|
|
175
|
+
database=database,
|
|
176
|
+
secure=True,
|
|
177
|
+
ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Reading certificate content as bytes:**
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
# Read certificate file and pass as bytes
|
|
185
|
+
with open('/path/to/ca-cert.pem', 'rb') as cert_file:
|
|
186
|
+
cert_data = cert_file.read()
|
|
187
|
+
|
|
188
|
+
conn = Connection(
|
|
189
|
+
host=host,
|
|
190
|
+
port=443,
|
|
191
|
+
username=username,
|
|
192
|
+
password=password,
|
|
193
|
+
database=database,
|
|
194
|
+
secure=True,
|
|
195
|
+
ssl_cert=cert_data # Certificate content as bytes
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Using system CA bundle for publicly signed certificates:**
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# When ssl_cert is None, system default CA bundle is used
|
|
203
|
+
conn = Connection(
|
|
204
|
+
host=host,
|
|
205
|
+
port=443,
|
|
206
|
+
username=username,
|
|
207
|
+
password=password,
|
|
208
|
+
database=database,
|
|
209
|
+
secure=True # Uses system CA bundle by default
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Connection pooling with custom CA certificate:**
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
pool = ConnectionPool(
|
|
217
|
+
min_size=2,
|
|
218
|
+
max_size=10,
|
|
219
|
+
host=host,
|
|
220
|
+
port=443,
|
|
221
|
+
username=username,
|
|
222
|
+
password=password,
|
|
223
|
+
database=database,
|
|
224
|
+
secure=True,
|
|
225
|
+
ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
162
229
|
#### Handling Missing System Dependencies
|
|
163
230
|
|
|
164
231
|
The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_38_nines.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_all_cases.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_binary.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/cleanup_test_files.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/debug_38_nines.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/debug_binary.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/final_test.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/move_tests.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/quick_test.py
RENAMED
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_38_nines.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_comprehensive.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_known_case.py
RENAMED
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_mock_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/tests_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/verify_decimal_fix.py
RENAMED
|
File without changes
|