opensecureconf-client 1.0.2__py3-none-any.whl → 2.0.2__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.
- opensecureconf_client-2.0.2.dist-info/METADATA +1289 -0
- opensecureconf_client-2.0.2.dist-info/RECORD +6 -0
- opensecureconf_client.py +477 -22
- opensecureconf_client-1.0.2.dist-info/METADATA +0 -229
- opensecureconf_client-1.0.2.dist-info/RECORD +0 -6
- {opensecureconf_client-1.0.2.dist-info → opensecureconf_client-2.0.2.dist-info}/WHEEL +0 -0
- {opensecureconf_client-1.0.2.dist-info → opensecureconf_client-2.0.2.dist-info}/licenses/LICENSE +0 -0
- {opensecureconf_client-1.0.2.dist-info → opensecureconf_client-2.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1289 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opensecureconf-client
|
|
3
|
+
Version: 2.0.2
|
|
4
|
+
Summary: Python client library for OpenSecureConf encrypted configuration management API with clustering support
|
|
5
|
+
Author-email: Alessandro Pioli <alessandro.pioli+apioli-pypi@gmail.com>
|
|
6
|
+
Maintainer-email: Alessandro Pioli <alessandro.pioli+apioli-pypi@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/lordraw77/OpenSecureConf
|
|
9
|
+
Project-URL: Documentation, https://github.com/lordraw77/OpenSecureConf/blob/main/client/README.md
|
|
10
|
+
Project-URL: Repository, https://github.com/lordraw77/OpenSecureConf/tree/main/client
|
|
11
|
+
Project-URL: Issues, https://github.com/lordraw77/OpenSecureConf/issues
|
|
12
|
+
Keywords: configuration,encryption,api-client,secure,config-management,clustering,distributed-config,secrets-management
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: Security :: Cryptography
|
|
25
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: requests>=2.28.0
|
|
30
|
+
Requires-Dist: urllib3>=1.26.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pylint>=2.17.0; extra == "dev"
|
|
38
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
39
|
+
Provides-Extra: enhanced
|
|
40
|
+
Requires-Dist: urllib3>=2.0.0; extra == "enhanced"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# OpenSecureConf Python Client
|
|
44
|
+
|
|
45
|
+
[](https://badge.fury.io/py/opensecureconf-client)
|
|
46
|
+
[](https://pypi.org/project/opensecureconf-client/)
|
|
47
|
+
[](https://opensource.org/licenses/MIT)
|
|
48
|
+
|
|
49
|
+
A powerful Python client library for interacting with the [OpenSecureConf API](https://github.com/lordraw77/OpenSecureConf), providing encrypted configuration management with clustering support, automatic retry logic, and comprehensive monitoring capabilities.
|
|
50
|
+
|
|
51
|
+
## 📋 Table of Contents
|
|
52
|
+
|
|
53
|
+
- [Features](#features)
|
|
54
|
+
- [Installation](#installation)
|
|
55
|
+
- [Quick Start](#quick-start)
|
|
56
|
+
- [Core Features](#core-features)
|
|
57
|
+
- [Basic CRUD Operations](#basic-crud-operations)
|
|
58
|
+
- [Cluster Awareness](#cluster-awareness)
|
|
59
|
+
- [Batch Operations](#batch-operations)
|
|
60
|
+
- [Retry Logic](#retry-logic)
|
|
61
|
+
- [Health Checks](#health-checks)
|
|
62
|
+
- [Utility Methods](#utility-methods)
|
|
63
|
+
- [Advanced Usage](#advanced-usage)
|
|
64
|
+
- [API Reference](#api-reference)
|
|
65
|
+
- [Error Handling](#error-handling)
|
|
66
|
+
- [Configuration Options](#configuration-options)
|
|
67
|
+
- [Best Practices](#best-practices)
|
|
68
|
+
- [Development](#development)
|
|
69
|
+
- [Contributing](#contributing)
|
|
70
|
+
- [License](#license)
|
|
71
|
+
|
|
72
|
+
## ✨ Features
|
|
73
|
+
|
|
74
|
+
### Core Capabilities
|
|
75
|
+
- 🔐 **Encrypted Configuration Management**: Securely store and retrieve encrypted configurations using PBKDF2 + Fernet
|
|
76
|
+
- 🚀 **Simple & Intuitive API**: Clean interface for CRUD operations
|
|
77
|
+
- 🛡️ **Type-Safe**: Fully typed with comprehensive error handling
|
|
78
|
+
- 📦 **Lightweight**: Minimal dependencies (only `requests` and `urllib3`)
|
|
79
|
+
|
|
80
|
+
### Enhanced Features
|
|
81
|
+
- 🔄 **Automatic Retry Logic**: Exponential backoff for transient failures
|
|
82
|
+
- 🌐 **Cluster Awareness**: Monitor and interact with clustered deployments
|
|
83
|
+
- ⚡ **Connection Pooling**: Optimized HTTP connection management
|
|
84
|
+
- 📊 **Structured Logging**: Built-in logging for debugging and monitoring
|
|
85
|
+
- 🔢 **Batch Operations**: Efficient bulk create, read, and delete operations
|
|
86
|
+
- 🏥 **Health Checks**: Built-in connectivity and cluster health monitoring
|
|
87
|
+
- 🎯 **Utility Methods**: Convenient helpers for common operations
|
|
88
|
+
- 🔌 **Context Manager**: Automatic resource cleanup
|
|
89
|
+
|
|
90
|
+
## 📦 Installation
|
|
91
|
+
|
|
92
|
+
### Standard Installation
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install opensecureconf-client
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Development Installation
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install opensecureconf-client[dev]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### From Source
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
git clone https://github.com/lordraw77/OpenSecureConf.git
|
|
108
|
+
cd OpenSecureConf/client
|
|
109
|
+
pip install -e .
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 🚀 Quick Start
|
|
113
|
+
|
|
114
|
+
### Basic Usage
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from opensecureconf_client import OpenSecureConfClient
|
|
118
|
+
|
|
119
|
+
# Initialize the client
|
|
120
|
+
client = OpenSecureConfClient(
|
|
121
|
+
base_url="http://localhost:9000",
|
|
122
|
+
user_key="my-secure-key-min-8-chars",
|
|
123
|
+
api_key="cluster-secret-key-123" # Optional: if API key authentication is enabled
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Create a configuration
|
|
127
|
+
config = client.create(
|
|
128
|
+
key="database",
|
|
129
|
+
value={"host": "localhost", "port": 5432, "username": "admin", "password": "secret"},
|
|
130
|
+
category="production"
|
|
131
|
+
)
|
|
132
|
+
print(f"Created: {config['key']}")
|
|
133
|
+
|
|
134
|
+
# Read a configuration
|
|
135
|
+
db_config = client.read("database")
|
|
136
|
+
print(f"Host: {db_config['value']['host']}")
|
|
137
|
+
|
|
138
|
+
# Update a configuration
|
|
139
|
+
updated = client.update(
|
|
140
|
+
key="database",
|
|
141
|
+
value={"host": "db.example.com", "port": 5432, "username": "admin", "password": "secret"}
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# List all configurations
|
|
145
|
+
configs = client.list_all(category="production")
|
|
146
|
+
for cfg in configs:
|
|
147
|
+
print(f"- {cfg['key']}: {cfg['category']}")
|
|
148
|
+
|
|
149
|
+
# Delete a configuration
|
|
150
|
+
client.delete("database")
|
|
151
|
+
|
|
152
|
+
# Close the client
|
|
153
|
+
client.close()
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Using Context Manager (Recommended)
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from opensecureconf_client import OpenSecureConfClient
|
|
160
|
+
|
|
161
|
+
with OpenSecureConfClient(
|
|
162
|
+
base_url="http://localhost:9000",
|
|
163
|
+
user_key="my-secure-key-min-8-chars"
|
|
164
|
+
) as client:
|
|
165
|
+
# Create and use configurations
|
|
166
|
+
config = client.create("app", {"version": "1.0.0", "debug": False})
|
|
167
|
+
print(config)
|
|
168
|
+
# Session automatically closed when exiting context
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Enhanced Client with All Features
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
175
|
+
|
|
176
|
+
# Initialize with advanced features
|
|
177
|
+
client = OpenSecureConfClient(
|
|
178
|
+
base_url="http://localhost:9000",
|
|
179
|
+
user_key="my-secure-key-min-8-chars",
|
|
180
|
+
api_key="cluster-secret-key-123",
|
|
181
|
+
enable_retry=True, # Enable automatic retry
|
|
182
|
+
max_retries=3, # Max retry attempts
|
|
183
|
+
backoff_factor=1.0, # Exponential backoff factor
|
|
184
|
+
pool_connections=20, # Connection pool size
|
|
185
|
+
pool_maxsize=50, # Max pool size
|
|
186
|
+
log_level="INFO" # Logging level
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Use all the enhanced features...
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 🎯 Core Features
|
|
193
|
+
|
|
194
|
+
### Basic CRUD Operations
|
|
195
|
+
|
|
196
|
+
#### Create Configuration
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
# Create a simple configuration
|
|
200
|
+
config = client.create(
|
|
201
|
+
key="api_settings",
|
|
202
|
+
value={"base_url": "https://api.example.com", "timeout": 30, "retries": 3},
|
|
203
|
+
category="production"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Create with validation
|
|
207
|
+
if not client.exists("api_settings"):
|
|
208
|
+
config = client.create("api_settings", {"base_url": "https://api.example.com"})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Read Configuration
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
# Read a specific configuration
|
|
215
|
+
config = client.read("api_settings")
|
|
216
|
+
print(f"API URL: {config['value']['base_url']}")
|
|
217
|
+
|
|
218
|
+
# Safe read with default value
|
|
219
|
+
config = client.get_or_default(
|
|
220
|
+
"optional_setting",
|
|
221
|
+
default={"enabled": False, "timeout": 30}
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### Update Configuration
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
# Update existing configuration
|
|
229
|
+
updated = client.update(
|
|
230
|
+
key="api_settings",
|
|
231
|
+
value={"base_url": "https://api.example.com", "timeout": 60, "retries": 5},
|
|
232
|
+
category="production"
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### Delete Configuration
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
# Delete a single configuration
|
|
240
|
+
result = client.delete("api_settings")
|
|
241
|
+
print(result["message"])
|
|
242
|
+
|
|
243
|
+
# Conditional delete
|
|
244
|
+
if client.exists("temporary_config"):
|
|
245
|
+
client.delete("temporary_config")
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### List Configurations
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
# List all configurations
|
|
252
|
+
all_configs = client.list_all()
|
|
253
|
+
print(f"Total configurations: {len(all_configs)}")
|
|
254
|
+
|
|
255
|
+
# List by category
|
|
256
|
+
prod_configs = client.list_all(category="production")
|
|
257
|
+
for config in prod_configs:
|
|
258
|
+
print(f"- {config['key']}: {config['value']}")
|
|
259
|
+
|
|
260
|
+
# Get count
|
|
261
|
+
total = client.count()
|
|
262
|
+
prod_count = client.count(category="production")
|
|
263
|
+
print(f"Production configs: {prod_count}/{total}")
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Cluster Awareness
|
|
267
|
+
|
|
268
|
+
Monitor and interact with OpenSecureConf clusters:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
# Check cluster status
|
|
272
|
+
status = client.get_cluster_status()
|
|
273
|
+
if status['enabled']:
|
|
274
|
+
print(f"Cluster Mode: {status['mode']}") # REPLICA or FEDERATED
|
|
275
|
+
print(f"Node ID: {status['node_id']}")
|
|
276
|
+
print(f"Healthy Nodes: {status['healthy_nodes']}/{status['total_nodes']}")
|
|
277
|
+
else:
|
|
278
|
+
print("Clustering is disabled")
|
|
279
|
+
|
|
280
|
+
# Check node health
|
|
281
|
+
health = client.get_cluster_health()
|
|
282
|
+
print(f"Node Status: {health['status']}")
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Cluster Modes:**
|
|
286
|
+
- **REPLICA**: Active-active replication with automatic synchronization
|
|
287
|
+
- **FEDERATED**: Distributed storage with cross-node queries
|
|
288
|
+
|
|
289
|
+
### Batch Operations
|
|
290
|
+
|
|
291
|
+
Perform multiple operations efficiently:
|
|
292
|
+
|
|
293
|
+
#### Bulk Create
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
# Create multiple configurations at once
|
|
297
|
+
configs_to_create = [
|
|
298
|
+
{
|
|
299
|
+
"key": "service1",
|
|
300
|
+
"value": {"url": "http://service1.local", "timeout": 30},
|
|
301
|
+
"category": "microservices"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"key": "service2",
|
|
305
|
+
"value": {"url": "http://service2.local", "timeout": 60},
|
|
306
|
+
"category": "microservices"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"key": "service3",
|
|
310
|
+
"value": {"url": "http://service3.local", "timeout": 45},
|
|
311
|
+
"category": "microservices"
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
# Create all configurations (stop on first error)
|
|
316
|
+
results = client.bulk_create(configs_to_create)
|
|
317
|
+
print(f"Created {len(results)} configurations")
|
|
318
|
+
|
|
319
|
+
# Create all configurations (continue on errors)
|
|
320
|
+
results = client.bulk_create(configs_to_create, ignore_errors=True)
|
|
321
|
+
print(f"Created {len(results)} configurations")
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Bulk Read
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
# Read multiple configurations at once
|
|
328
|
+
keys = ["service1", "service2", "service3", "api_settings"]
|
|
329
|
+
|
|
330
|
+
# Read all (stop on first error)
|
|
331
|
+
configs = client.bulk_read(keys)
|
|
332
|
+
|
|
333
|
+
# Read all (skip missing keys)
|
|
334
|
+
configs = client.bulk_read(keys, ignore_errors=True)
|
|
335
|
+
for config in configs:
|
|
336
|
+
print(f"{config['key']}: {config['value']['url']}")
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### Bulk Delete
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
# Delete multiple configurations
|
|
343
|
+
keys_to_delete = ["temp1", "temp2", "temp3"]
|
|
344
|
+
|
|
345
|
+
# Delete all (stop on first error)
|
|
346
|
+
result = client.bulk_delete(keys_to_delete)
|
|
347
|
+
|
|
348
|
+
# Delete all (continue on errors)
|
|
349
|
+
result = client.bulk_delete(keys_to_delete, ignore_errors=True)
|
|
350
|
+
print(f"Deleted: {len(result['deleted'])}")
|
|
351
|
+
print(f"Failed: {len(result['failed'])}")
|
|
352
|
+
|
|
353
|
+
# Inspect failures
|
|
354
|
+
for failure in result['failed']:
|
|
355
|
+
print(f"Failed to delete '{failure['key']}': {failure['error']}")
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Retry Logic
|
|
359
|
+
|
|
360
|
+
The enhanced client includes automatic retry with exponential backoff:
|
|
361
|
+
|
|
362
|
+
```python
|
|
363
|
+
# Configure retry behavior
|
|
364
|
+
client = OpenSecureConfClient(
|
|
365
|
+
base_url="http://localhost:9000",
|
|
366
|
+
user_key="my-key",
|
|
367
|
+
enable_retry=True,
|
|
368
|
+
max_retries=5, # Retry up to 5 times
|
|
369
|
+
backoff_factor=2.0, # 2^n seconds between retries
|
|
370
|
+
timeout=30
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Automatic retry on transient failures (429, 500, 502, 503, 504)
|
|
374
|
+
try:
|
|
375
|
+
config = client.read("my_config")
|
|
376
|
+
except Exception as e:
|
|
377
|
+
print(f"Failed after {client.max_retries} retries: {e}")
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Retry Strategy:**
|
|
381
|
+
- Status codes: `429, 500, 502, 503, 504`
|
|
382
|
+
- HTTP methods: All methods including POST
|
|
383
|
+
- Backoff: `backoff_factor * (2 ^ retry_count)` seconds
|
|
384
|
+
|
|
385
|
+
### Health Checks
|
|
386
|
+
|
|
387
|
+
Built-in health monitoring:
|
|
388
|
+
|
|
389
|
+
```python
|
|
390
|
+
# Simple ping check
|
|
391
|
+
if client.ping():
|
|
392
|
+
print("✓ Server is healthy and reachable")
|
|
393
|
+
else:
|
|
394
|
+
print("✗ Server is not reachable")
|
|
395
|
+
|
|
396
|
+
# Get detailed service information
|
|
397
|
+
info = client.get_service_info()
|
|
398
|
+
print(f"Service: {info['service']}")
|
|
399
|
+
print(f"Version: {info['version']}")
|
|
400
|
+
print(f"Features: {', '.join(info['features'])}")
|
|
401
|
+
print(f"Cluster Enabled: {info['cluster_enabled']}")
|
|
402
|
+
|
|
403
|
+
# Monitor cluster health
|
|
404
|
+
if info['cluster_enabled']:
|
|
405
|
+
health = client.get_cluster_health()
|
|
406
|
+
print(f"Cluster Health: {health['status']}")
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Utility Methods
|
|
410
|
+
|
|
411
|
+
Convenient helper methods:
|
|
412
|
+
|
|
413
|
+
#### Check Existence
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
# Check if a key exists
|
|
417
|
+
if client.exists("database"):
|
|
418
|
+
print("Configuration exists")
|
|
419
|
+
config = client.read("database")
|
|
420
|
+
else:
|
|
421
|
+
print("Configuration does not exist")
|
|
422
|
+
config = client.create("database", {"host": "localhost"})
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
#### Get with Default
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
# Get configuration or return default
|
|
429
|
+
config = client.get_or_default(
|
|
430
|
+
"optional_feature",
|
|
431
|
+
default={"enabled": False, "timeout": 30}
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Use the configuration
|
|
435
|
+
if config['value']['enabled']:
|
|
436
|
+
timeout = config['value']['timeout']
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### Count Configurations
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
# Count all configurations
|
|
443
|
+
total = client.count()
|
|
444
|
+
print(f"Total configurations: {total}")
|
|
445
|
+
|
|
446
|
+
# Count by category
|
|
447
|
+
prod_count = client.count(category="production")
|
|
448
|
+
dev_count = client.count(category="development")
|
|
449
|
+
print(f"Production: {prod_count}, Development: {dev_count}")
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### List Categories
|
|
453
|
+
|
|
454
|
+
```python
|
|
455
|
+
# Get all unique categories
|
|
456
|
+
categories = client.list_categories()
|
|
457
|
+
print(f"Available categories: {', '.join(categories)}")
|
|
458
|
+
|
|
459
|
+
# Process by category
|
|
460
|
+
for category in categories:
|
|
461
|
+
count = client.count(category=category)
|
|
462
|
+
print(f"{category}: {count} configurations")
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## 🔧 Advanced Usage
|
|
466
|
+
|
|
467
|
+
### Custom Connection Pooling
|
|
468
|
+
|
|
469
|
+
```python
|
|
470
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
471
|
+
|
|
472
|
+
# Configure connection pooling for high-traffic scenarios
|
|
473
|
+
client = OpenSecureConfClient(
|
|
474
|
+
base_url="http://localhost:9000",
|
|
475
|
+
user_key="my-key",
|
|
476
|
+
pool_connections=50, # Number of connection pools
|
|
477
|
+
pool_maxsize=100, # Maximum pool size
|
|
478
|
+
timeout=60 # Request timeout
|
|
479
|
+
)
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Structured Logging
|
|
483
|
+
|
|
484
|
+
```python
|
|
485
|
+
import logging
|
|
486
|
+
|
|
487
|
+
# Enable detailed logging
|
|
488
|
+
logging.basicConfig(
|
|
489
|
+
level=logging.DEBUG,
|
|
490
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
client = OpenSecureConfClient(
|
|
494
|
+
base_url="http://localhost:9000",
|
|
495
|
+
user_key="my-key",
|
|
496
|
+
log_level="DEBUG" # DEBUG, INFO, WARNING, ERROR
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
# All operations will be logged
|
|
500
|
+
config = client.create("test", {"data": "value"})
|
|
501
|
+
# Output: 2026-01-14 17:00:00 - opensecureconf_client - INFO - POST /configs - Status: 201 - Duration: 0.045s
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### SSL Configuration
|
|
505
|
+
|
|
506
|
+
```python
|
|
507
|
+
# Disable SSL verification (not recommended for production)
|
|
508
|
+
client = OpenSecureConfClient(
|
|
509
|
+
base_url="https://localhost:9000",
|
|
510
|
+
user_key="my-key",
|
|
511
|
+
verify_ssl=False
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
# Use custom SSL certificate
|
|
515
|
+
import requests
|
|
516
|
+
session = requests.Session()
|
|
517
|
+
session.verify = '/path/to/ca-bundle.crt'
|
|
518
|
+
|
|
519
|
+
client = OpenSecureConfClient(
|
|
520
|
+
base_url="https://localhost:9000",
|
|
521
|
+
user_key="my-key",
|
|
522
|
+
verify_ssl=True
|
|
523
|
+
)
|
|
524
|
+
client._session = session
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Environment-Based Configuration
|
|
528
|
+
|
|
529
|
+
```python
|
|
530
|
+
import os
|
|
531
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
532
|
+
|
|
533
|
+
# Load from environment variables
|
|
534
|
+
client = OpenSecureConfClient(
|
|
535
|
+
base_url=os.getenv("OSC_URL", "http://localhost:9000"),
|
|
536
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
537
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
538
|
+
enable_retry=os.getenv("OSC_RETRY", "true").lower() == "true",
|
|
539
|
+
max_retries=int(os.getenv("OSC_MAX_RETRIES", "3")),
|
|
540
|
+
log_level=os.getenv("OSC_LOG_LEVEL", "INFO")
|
|
541
|
+
)
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Working with Clusters
|
|
545
|
+
|
|
546
|
+
```python
|
|
547
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
548
|
+
|
|
549
|
+
# Connect to any node in the cluster
|
|
550
|
+
client = OpenSecureConfClient(
|
|
551
|
+
base_url="http://node1.example.com:9000", # Can be any node
|
|
552
|
+
user_key="my-key",
|
|
553
|
+
api_key="cluster-secret-key"
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
# Check cluster topology
|
|
557
|
+
status = client.get_cluster_status()
|
|
558
|
+
print(f"Connected to: {status['node_id']}")
|
|
559
|
+
print(f"Cluster mode: {status['mode']}")
|
|
560
|
+
|
|
561
|
+
# In REPLICA mode: writes are automatically replicated
|
|
562
|
+
config = client.create("shared_config", {"data": "value"})
|
|
563
|
+
# This configuration is now available on all nodes
|
|
564
|
+
|
|
565
|
+
# In FEDERATED mode: reads check all nodes
|
|
566
|
+
config = client.read("distributed_config")
|
|
567
|
+
# This searches across all federated nodes
|
|
568
|
+
|
|
569
|
+
# Monitor cluster health
|
|
570
|
+
if status['healthy_nodes'] < status['total_nodes']:
|
|
571
|
+
print(f"Warning: {status['total_nodes'] - status['healthy_nodes']} nodes are down")
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
## 📚 API Reference
|
|
575
|
+
|
|
576
|
+
### OpenSecureConfClient
|
|
577
|
+
|
|
578
|
+
Main client class for interacting with OpenSecureConf API.
|
|
579
|
+
|
|
580
|
+
#### Constructor
|
|
581
|
+
|
|
582
|
+
```python
|
|
583
|
+
OpenSecureConfClient(
|
|
584
|
+
base_url: str,
|
|
585
|
+
user_key: str,
|
|
586
|
+
api_key: Optional[str] = None,
|
|
587
|
+
timeout: int = 30,
|
|
588
|
+
verify_ssl: bool = True,
|
|
589
|
+
enable_retry: bool = True,
|
|
590
|
+
max_retries: int = 3,
|
|
591
|
+
backoff_factor: float = 1.0,
|
|
592
|
+
pool_connections: int = 10,
|
|
593
|
+
pool_maxsize: int = 20,
|
|
594
|
+
log_level: str = "WARNING"
|
|
595
|
+
)
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Parameters:**
|
|
599
|
+
|
|
600
|
+
| Parameter | Type | Default | Description |
|
|
601
|
+
|-----------|------|---------|-------------|
|
|
602
|
+
| `base_url` | `str` | Required | Base URL of the OpenSecureConf API server |
|
|
603
|
+
| `user_key` | `str` | Required | User encryption key (minimum 8 characters) |
|
|
604
|
+
| `api_key` | `Optional[str]` | `None` | API key for authentication (if enabled on server) |
|
|
605
|
+
| `timeout` | `int` | `30` | Request timeout in seconds |
|
|
606
|
+
| `verify_ssl` | `bool` | `True` | Verify SSL certificates |
|
|
607
|
+
| `enable_retry` | `bool` | `True` | Enable automatic retry with exponential backoff |
|
|
608
|
+
| `max_retries` | `int` | `3` | Maximum number of retry attempts |
|
|
609
|
+
| `backoff_factor` | `float` | `1.0` | Exponential backoff multiplier |
|
|
610
|
+
| `pool_connections` | `int` | `10` | Number of connection pools to cache |
|
|
611
|
+
| `pool_maxsize` | `int` | `20` | Maximum size of the connection pool |
|
|
612
|
+
| `log_level` | `str` | `"WARNING"` | Logging level (DEBUG, INFO, WARNING, ERROR) |
|
|
613
|
+
|
|
614
|
+
#### Methods
|
|
615
|
+
|
|
616
|
+
##### Health & Status
|
|
617
|
+
|
|
618
|
+
###### `ping() -> bool`
|
|
619
|
+
|
|
620
|
+
Check if the API server is reachable.
|
|
621
|
+
|
|
622
|
+
**Returns:** `True` if server is healthy, `False` otherwise
|
|
623
|
+
|
|
624
|
+
**Example:**
|
|
625
|
+
```python
|
|
626
|
+
if client.ping():
|
|
627
|
+
print("Server is online")
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
###### `get_service_info() -> Dict[str, Any]`
|
|
631
|
+
|
|
632
|
+
Get information about the OpenSecureConf service.
|
|
633
|
+
|
|
634
|
+
**Returns:** Dictionary with service metadata
|
|
635
|
+
- `service`: Service name
|
|
636
|
+
- `version`: API version
|
|
637
|
+
- `features`: List of enabled features
|
|
638
|
+
- `cluster_enabled`: Whether clustering is enabled
|
|
639
|
+
- `cluster_mode`: Cluster mode (if enabled)
|
|
640
|
+
|
|
641
|
+
**Example:**
|
|
642
|
+
```python
|
|
643
|
+
info = client.get_service_info()
|
|
644
|
+
print(f"Version: {info['version']}")
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
##### Cluster Operations
|
|
648
|
+
|
|
649
|
+
###### `get_cluster_status() -> Dict[str, Any]`
|
|
650
|
+
|
|
651
|
+
Get cluster status and node information.
|
|
652
|
+
|
|
653
|
+
**Returns:** Dictionary with cluster status
|
|
654
|
+
- `enabled`: Whether clustering is enabled
|
|
655
|
+
- `mode`: Cluster mode (replica or federated)
|
|
656
|
+
- `node_id`: Current node identifier
|
|
657
|
+
- `total_nodes`: Total number of nodes
|
|
658
|
+
- `healthy_nodes`: Number of healthy nodes
|
|
659
|
+
|
|
660
|
+
**Raises:** `ClusterError` if cluster status cannot be retrieved
|
|
661
|
+
|
|
662
|
+
**Example:**
|
|
663
|
+
```python
|
|
664
|
+
status = client.get_cluster_status()
|
|
665
|
+
print(f"Healthy: {status['healthy_nodes']}/{status['total_nodes']}")
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
###### `get_cluster_health() -> Dict[str, Any]`
|
|
669
|
+
|
|
670
|
+
Check cluster node health.
|
|
671
|
+
|
|
672
|
+
**Returns:** Dictionary with health status
|
|
673
|
+
|
|
674
|
+
**Example:**
|
|
675
|
+
```python
|
|
676
|
+
health = client.get_cluster_health()
|
|
677
|
+
print(f"Status: {health['status']}")
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
##### Configuration CRUD
|
|
681
|
+
|
|
682
|
+
###### `create(key: str, value: Dict[str, Any], category: Optional[str] = None) -> Dict[str, Any]`
|
|
683
|
+
|
|
684
|
+
Create a new encrypted configuration entry.
|
|
685
|
+
|
|
686
|
+
**Parameters:**
|
|
687
|
+
- `key`: Unique configuration key (1-255 characters)
|
|
688
|
+
- `value`: Configuration data as dictionary
|
|
689
|
+
- `category`: Optional category for grouping (max 100 characters)
|
|
690
|
+
|
|
691
|
+
**Returns:** Dictionary with created configuration
|
|
692
|
+
|
|
693
|
+
**Raises:**
|
|
694
|
+
- `ConfigurationExistsError`: If key already exists
|
|
695
|
+
- `ValueError`: If parameters are invalid
|
|
696
|
+
|
|
697
|
+
**Example:**
|
|
698
|
+
```python
|
|
699
|
+
config = client.create(
|
|
700
|
+
"database",
|
|
701
|
+
{"host": "localhost", "port": 5432},
|
|
702
|
+
category="production"
|
|
703
|
+
)
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
###### `read(key: str) -> Dict[str, Any]`
|
|
707
|
+
|
|
708
|
+
Read and decrypt a configuration entry.
|
|
709
|
+
|
|
710
|
+
**Parameters:**
|
|
711
|
+
- `key`: Configuration key to retrieve
|
|
712
|
+
|
|
713
|
+
**Returns:** Dictionary with configuration
|
|
714
|
+
|
|
715
|
+
**Raises:**
|
|
716
|
+
- `ConfigurationNotFoundError`: If key does not exist
|
|
717
|
+
- `ValueError`: If key is invalid
|
|
718
|
+
|
|
719
|
+
**Example:**
|
|
720
|
+
```python
|
|
721
|
+
config = client.read("database")
|
|
722
|
+
print(config['value'])
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
###### `update(key: str, value: Dict[str, Any], category: Optional[str] = None) -> Dict[str, Any]`
|
|
726
|
+
|
|
727
|
+
Update an existing configuration entry.
|
|
728
|
+
|
|
729
|
+
**Parameters:**
|
|
730
|
+
- `key`: Configuration key to update
|
|
731
|
+
- `value`: New configuration data
|
|
732
|
+
- `category`: Optional new category
|
|
733
|
+
|
|
734
|
+
**Returns:** Dictionary with updated configuration
|
|
735
|
+
|
|
736
|
+
**Raises:**
|
|
737
|
+
- `ConfigurationNotFoundError`: If key does not exist
|
|
738
|
+
- `ValueError`: If parameters are invalid
|
|
739
|
+
|
|
740
|
+
**Example:**
|
|
741
|
+
```python
|
|
742
|
+
updated = client.update("database", {"host": "db.example.com", "port": 5432})
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
###### `delete(key: str) -> Dict[str, str]`
|
|
746
|
+
|
|
747
|
+
Delete a configuration entry permanently.
|
|
748
|
+
|
|
749
|
+
**Parameters:**
|
|
750
|
+
- `key`: Configuration key to delete
|
|
751
|
+
|
|
752
|
+
**Returns:** Dictionary with success message
|
|
753
|
+
|
|
754
|
+
**Raises:**
|
|
755
|
+
- `ConfigurationNotFoundError`: If key does not exist
|
|
756
|
+
- `ValueError`: If key is invalid
|
|
757
|
+
|
|
758
|
+
**Example:**
|
|
759
|
+
```python
|
|
760
|
+
result = client.delete("database")
|
|
761
|
+
print(result['message'])
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
###### `list_all(category: Optional[str] = None) -> List[Dict[str, Any]]`
|
|
765
|
+
|
|
766
|
+
List all configurations with optional category filter.
|
|
767
|
+
|
|
768
|
+
**Parameters:**
|
|
769
|
+
- `category`: Optional category filter
|
|
770
|
+
|
|
771
|
+
**Returns:** List of configuration dictionaries
|
|
772
|
+
|
|
773
|
+
**Example:**
|
|
774
|
+
```python
|
|
775
|
+
configs = client.list_all(category="production")
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
##### Batch Operations
|
|
779
|
+
|
|
780
|
+
###### `bulk_create(configs: List[Dict[str, Any]], ignore_errors: bool = False) -> List[Dict[str, Any]]`
|
|
781
|
+
|
|
782
|
+
Create multiple configurations in batch.
|
|
783
|
+
|
|
784
|
+
**Parameters:**
|
|
785
|
+
- `configs`: List of configuration dictionaries
|
|
786
|
+
- `ignore_errors`: Continue on errors and return partial results
|
|
787
|
+
|
|
788
|
+
**Returns:** List of created configurations
|
|
789
|
+
|
|
790
|
+
**Raises:**
|
|
791
|
+
- `ValueError`: If configs format is invalid
|
|
792
|
+
- `OpenSecureConfError`: If creation fails and ignore_errors is False
|
|
793
|
+
|
|
794
|
+
**Example:**
|
|
795
|
+
```python
|
|
796
|
+
configs = [
|
|
797
|
+
{"key": "db1", "value": {"host": "localhost"}, "category": "prod"},
|
|
798
|
+
{"key": "db2", "value": {"host": "remote"}, "category": "prod"}
|
|
799
|
+
]
|
|
800
|
+
results = client.bulk_create(configs)
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
###### `bulk_read(keys: List[str], ignore_errors: bool = False) -> List[Dict[str, Any]]`
|
|
804
|
+
|
|
805
|
+
Read multiple configurations in batch.
|
|
806
|
+
|
|
807
|
+
**Parameters:**
|
|
808
|
+
- `keys`: List of configuration keys
|
|
809
|
+
- `ignore_errors`: Skip missing keys and return partial results
|
|
810
|
+
|
|
811
|
+
**Returns:** List of configuration dictionaries
|
|
812
|
+
|
|
813
|
+
**Example:**
|
|
814
|
+
```python
|
|
815
|
+
configs = client.bulk_read(["db1", "db2", "api"])
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
###### `bulk_delete(keys: List[str], ignore_errors: bool = False) -> Dict[str, Any]`
|
|
819
|
+
|
|
820
|
+
Delete multiple configurations in batch.
|
|
821
|
+
|
|
822
|
+
**Parameters:**
|
|
823
|
+
- `keys`: List of configuration keys
|
|
824
|
+
- `ignore_errors`: Continue on errors
|
|
825
|
+
|
|
826
|
+
**Returns:** Dictionary with summary: `{"deleted": [...], "failed": [...]}`
|
|
827
|
+
|
|
828
|
+
**Example:**
|
|
829
|
+
```python
|
|
830
|
+
result = client.bulk_delete(["temp1", "temp2", "temp3"])
|
|
831
|
+
print(f"Deleted: {len(result['deleted'])}")
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
##### Utility Methods
|
|
835
|
+
|
|
836
|
+
###### `exists(key: str) -> bool`
|
|
837
|
+
|
|
838
|
+
Check if a configuration key exists.
|
|
839
|
+
|
|
840
|
+
**Parameters:**
|
|
841
|
+
- `key`: Configuration key to check
|
|
842
|
+
|
|
843
|
+
**Returns:** `True` if key exists, `False` otherwise
|
|
844
|
+
|
|
845
|
+
**Example:**
|
|
846
|
+
```python
|
|
847
|
+
if client.exists("database"):
|
|
848
|
+
config = client.read("database")
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
###### `get_or_default(key: str, default: Dict[str, Any]) -> Dict[str, Any]`
|
|
852
|
+
|
|
853
|
+
Get configuration or return default if not found.
|
|
854
|
+
|
|
855
|
+
**Parameters:**
|
|
856
|
+
- `key`: Configuration key to retrieve
|
|
857
|
+
- `default`: Default value to return if not found
|
|
858
|
+
|
|
859
|
+
**Returns:** Configuration dictionary or default
|
|
860
|
+
|
|
861
|
+
**Example:**
|
|
862
|
+
```python
|
|
863
|
+
config = client.get_or_default("optional", {"enabled": False})
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
###### `count(category: Optional[str] = None) -> int`
|
|
867
|
+
|
|
868
|
+
Count configurations, optionally filtered by category.
|
|
869
|
+
|
|
870
|
+
**Parameters:**
|
|
871
|
+
- `category`: Optional category filter
|
|
872
|
+
|
|
873
|
+
**Returns:** Number of configurations
|
|
874
|
+
|
|
875
|
+
**Example:**
|
|
876
|
+
```python
|
|
877
|
+
total = client.count()
|
|
878
|
+
prod_count = client.count(category="production")
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
###### `list_categories() -> List[str]`
|
|
882
|
+
|
|
883
|
+
Get list of all unique categories.
|
|
884
|
+
|
|
885
|
+
**Returns:** Sorted list of category names
|
|
886
|
+
|
|
887
|
+
**Example:**
|
|
888
|
+
```python
|
|
889
|
+
categories = client.list_categories()
|
|
890
|
+
print(f"Categories: {', '.join(categories)}")
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
##### Session Management
|
|
894
|
+
|
|
895
|
+
###### `close()`
|
|
896
|
+
|
|
897
|
+
Close the underlying HTTP session.
|
|
898
|
+
|
|
899
|
+
**Example:**
|
|
900
|
+
```python
|
|
901
|
+
client.close()
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
###### Context Manager
|
|
905
|
+
|
|
906
|
+
The client supports context manager protocol for automatic cleanup.
|
|
907
|
+
|
|
908
|
+
**Example:**
|
|
909
|
+
```python
|
|
910
|
+
with OpenSecureConfClient(base_url="...", user_key="...") as client:
|
|
911
|
+
config = client.create("key", {"value": "data"})
|
|
912
|
+
# Session automatically closed
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
## ⚠️ Error Handling
|
|
916
|
+
|
|
917
|
+
### Exception Hierarchy
|
|
918
|
+
|
|
919
|
+
```
|
|
920
|
+
OpenSecureConfError (base exception)
|
|
921
|
+
├── AuthenticationError # Invalid or missing credentials
|
|
922
|
+
├── ConfigurationNotFoundError # Configuration key does not exist
|
|
923
|
+
├── ConfigurationExistsError # Configuration key already exists
|
|
924
|
+
└── ClusterError # Cluster operation failed
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
### Handling Errors
|
|
928
|
+
|
|
929
|
+
```python
|
|
930
|
+
from opensecureconf_client import (
|
|
931
|
+
OpenSecureConfClient,
|
|
932
|
+
AuthenticationError,
|
|
933
|
+
ConfigurationNotFoundError,
|
|
934
|
+
ConfigurationExistsError,
|
|
935
|
+
ClusterError,
|
|
936
|
+
OpenSecureConfError
|
|
937
|
+
)
|
|
938
|
+
|
|
939
|
+
try:
|
|
940
|
+
config = client.create("mykey", {"data": "value"})
|
|
941
|
+
except AuthenticationError:
|
|
942
|
+
print("Authentication failed - check your user_key and api_key")
|
|
943
|
+
except ConfigurationExistsError:
|
|
944
|
+
print("Configuration already exists - use update() instead")
|
|
945
|
+
config = client.update("mykey", {"data": "new_value"})
|
|
946
|
+
except ConfigurationNotFoundError:
|
|
947
|
+
print("Configuration not found")
|
|
948
|
+
except ClusterError as e:
|
|
949
|
+
print(f"Cluster error: {e}")
|
|
950
|
+
except OpenSecureConfError as e:
|
|
951
|
+
print(f"API error: {e}")
|
|
952
|
+
except ConnectionError as e:
|
|
953
|
+
print(f"Connection error: {e}")
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
### Best Practices for Error Handling
|
|
957
|
+
|
|
958
|
+
```python
|
|
959
|
+
# 1. Use specific exceptions first
|
|
960
|
+
try:
|
|
961
|
+
config = client.read("important_config")
|
|
962
|
+
except ConfigurationNotFoundError:
|
|
963
|
+
# Create with defaults if not found
|
|
964
|
+
config = client.create("important_config", {"default": "value"})
|
|
965
|
+
|
|
966
|
+
# 2. Use exists() to avoid exceptions
|
|
967
|
+
if not client.exists("optional_config"):
|
|
968
|
+
client.create("optional_config", {"data": "value"})
|
|
969
|
+
|
|
970
|
+
# 3. Use get_or_default() for optional configurations
|
|
971
|
+
config = client.get_or_default("optional", {"enabled": False})
|
|
972
|
+
|
|
973
|
+
# 4. Handle bulk operation failures
|
|
974
|
+
result = client.bulk_delete(keys, ignore_errors=True)
|
|
975
|
+
if result['failed']:
|
|
976
|
+
print(f"Some deletions failed: {result['failed']}")
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
## ⚙️ Configuration Options
|
|
980
|
+
|
|
981
|
+
### Production Configuration
|
|
982
|
+
|
|
983
|
+
```python
|
|
984
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
985
|
+
import os
|
|
986
|
+
|
|
987
|
+
# Production-ready configuration
|
|
988
|
+
client = OpenSecureConfClient(
|
|
989
|
+
base_url=os.getenv("OSC_URL"),
|
|
990
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
991
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
992
|
+
timeout=60, # Longer timeout for production
|
|
993
|
+
verify_ssl=True, # Always verify SSL in production
|
|
994
|
+
enable_retry=True,
|
|
995
|
+
max_retries=5, # More retries for reliability
|
|
996
|
+
backoff_factor=2.0, # Aggressive backoff
|
|
997
|
+
pool_connections=50, # Large pool for high traffic
|
|
998
|
+
pool_maxsize=100,
|
|
999
|
+
log_level="INFO" # Moderate logging
|
|
1000
|
+
)
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
### Development Configuration
|
|
1004
|
+
|
|
1005
|
+
```python
|
|
1006
|
+
# Development-friendly configuration
|
|
1007
|
+
client = OpenSecureConfClient(
|
|
1008
|
+
base_url="http://localhost:9000",
|
|
1009
|
+
user_key="dev-key-12345678",
|
|
1010
|
+
api_key="dev-api-key",
|
|
1011
|
+
timeout=30,
|
|
1012
|
+
verify_ssl=False, # Self-signed certs in dev
|
|
1013
|
+
enable_retry=False, # Fail fast in development
|
|
1014
|
+
pool_connections=5, # Smaller pool
|
|
1015
|
+
pool_maxsize=10,
|
|
1016
|
+
log_level="DEBUG" # Verbose logging for debugging
|
|
1017
|
+
)
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
### Environment Variables
|
|
1021
|
+
|
|
1022
|
+
Recommended environment variables:
|
|
1023
|
+
|
|
1024
|
+
```bash
|
|
1025
|
+
# Required
|
|
1026
|
+
export OSC_URL="http://localhost:9000"
|
|
1027
|
+
export OSC_USER_KEY="your-secure-key-min-8-chars"
|
|
1028
|
+
|
|
1029
|
+
# Optional
|
|
1030
|
+
export OSC_API_KEY="cluster-secret-key-123"
|
|
1031
|
+
export OSC_TIMEOUT="30"
|
|
1032
|
+
export OSC_VERIFY_SSL="true"
|
|
1033
|
+
export OSC_RETRY="true"
|
|
1034
|
+
export OSC_MAX_RETRIES="3"
|
|
1035
|
+
export OSC_BACKOFF_FACTOR="1.0"
|
|
1036
|
+
export OSC_POOL_CONNECTIONS="10"
|
|
1037
|
+
export OSC_POOL_MAXSIZE="20"
|
|
1038
|
+
export OSC_LOG_LEVEL="INFO"
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
## 💡 Best Practices
|
|
1042
|
+
|
|
1043
|
+
### 1. Use Context Managers
|
|
1044
|
+
|
|
1045
|
+
```python
|
|
1046
|
+
# ✅ Good: Automatic cleanup
|
|
1047
|
+
with OpenSecureConfClient(base_url="...", user_key="...") as client:
|
|
1048
|
+
config = client.create("key", {"data": "value"})
|
|
1049
|
+
|
|
1050
|
+
# ❌ Avoid: Manual cleanup required
|
|
1051
|
+
client = OpenSecureConfClient(base_url="...", user_key="...")
|
|
1052
|
+
config = client.create("key", {"data": "value"})
|
|
1053
|
+
client.close() # Easy to forget
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
### 2. Check Existence Before Operations
|
|
1057
|
+
|
|
1058
|
+
```python
|
|
1059
|
+
# ✅ Good: Avoid exceptions
|
|
1060
|
+
if not client.exists("config"):
|
|
1061
|
+
client.create("config", {"data": "value"})
|
|
1062
|
+
|
|
1063
|
+
# ❌ Avoid: Exception handling for flow control
|
|
1064
|
+
try:
|
|
1065
|
+
client.create("config", {"data": "value"})
|
|
1066
|
+
except ConfigurationExistsError:
|
|
1067
|
+
pass
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
### 3. Use Batch Operations for Multiple Items
|
|
1071
|
+
|
|
1072
|
+
```python
|
|
1073
|
+
# ✅ Good: Single batch operation
|
|
1074
|
+
keys = ["config1", "config2", "config3"]
|
|
1075
|
+
configs = client.bulk_read(keys, ignore_errors=True)
|
|
1076
|
+
|
|
1077
|
+
# ❌ Avoid: Multiple individual requests
|
|
1078
|
+
configs = []
|
|
1079
|
+
for key in keys:
|
|
1080
|
+
try:
|
|
1081
|
+
configs.append(client.read(key))
|
|
1082
|
+
except:
|
|
1083
|
+
pass
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
### 4. Enable Retry for Production
|
|
1087
|
+
|
|
1088
|
+
```python
|
|
1089
|
+
# ✅ Good: Resilient to transient failures
|
|
1090
|
+
client = OpenSecureConfClient(
|
|
1091
|
+
base_url="...",
|
|
1092
|
+
user_key="...",
|
|
1093
|
+
enable_retry=True,
|
|
1094
|
+
max_retries=3
|
|
1095
|
+
)
|
|
1096
|
+
|
|
1097
|
+
# ❌ Avoid: No retry, fails on transient errors
|
|
1098
|
+
client = OpenSecureConfClient(
|
|
1099
|
+
base_url="...",
|
|
1100
|
+
user_key="...",
|
|
1101
|
+
enable_retry=False
|
|
1102
|
+
)
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
### 5. Use Structured Logging
|
|
1106
|
+
|
|
1107
|
+
```python
|
|
1108
|
+
# ✅ Good: Enable logging for production monitoring
|
|
1109
|
+
client = OpenSecureConfClient(
|
|
1110
|
+
base_url="...",
|
|
1111
|
+
user_key="...",
|
|
1112
|
+
log_level="INFO"
|
|
1113
|
+
)
|
|
1114
|
+
|
|
1115
|
+
# ❌ Avoid: Silent failures in production
|
|
1116
|
+
client = OpenSecureConfClient(
|
|
1117
|
+
base_url="...",
|
|
1118
|
+
user_key="...",
|
|
1119
|
+
log_level="ERROR" # Misses important info
|
|
1120
|
+
)
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
### 6. Secure Credential Management
|
|
1124
|
+
|
|
1125
|
+
```python
|
|
1126
|
+
# ✅ Good: Load from environment or secrets manager
|
|
1127
|
+
import os
|
|
1128
|
+
from opensecureconf_client import OpenSecureConfClient
|
|
1129
|
+
|
|
1130
|
+
client = OpenSecureConfClient(
|
|
1131
|
+
base_url=os.getenv("OSC_URL"),
|
|
1132
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
1133
|
+
api_key=os.getenv("OSC_API_KEY")
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
# ❌ Avoid: Hardcoded credentials
|
|
1137
|
+
client = OpenSecureConfClient(
|
|
1138
|
+
base_url="http://localhost:9000",
|
|
1139
|
+
user_key="hardcoded-key-12345", # Never do this!
|
|
1140
|
+
api_key="hardcoded-api-key"
|
|
1141
|
+
)
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
### 7. Monitor Cluster Health
|
|
1145
|
+
|
|
1146
|
+
```python
|
|
1147
|
+
# ✅ Good: Regular health checks
|
|
1148
|
+
if client.ping():
|
|
1149
|
+
status = client.get_cluster_status()
|
|
1150
|
+
if status['enabled']:
|
|
1151
|
+
if status['healthy_nodes'] < status['total_nodes']:
|
|
1152
|
+
logger.warning(f"Cluster degraded: {status['healthy_nodes']}/{status['total_nodes']}")
|
|
1153
|
+
|
|
1154
|
+
# ❌ Avoid: No health monitoring
|
|
1155
|
+
config = client.read("config") # Might fail silently in degraded cluster
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
## 🔨 Development
|
|
1159
|
+
|
|
1160
|
+
### Setting Up Development Environment
|
|
1161
|
+
|
|
1162
|
+
```bash
|
|
1163
|
+
# Clone the repository
|
|
1164
|
+
git clone https://github.com/lordraw77/OpenSecureConf.git
|
|
1165
|
+
cd OpenSecureConf/client
|
|
1166
|
+
|
|
1167
|
+
# Create virtual environment
|
|
1168
|
+
python -m venv venv
|
|
1169
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
1170
|
+
|
|
1171
|
+
# Install in development mode with all dependencies
|
|
1172
|
+
pip install -e ".[dev]"
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
### Running Tests
|
|
1176
|
+
|
|
1177
|
+
```bash
|
|
1178
|
+
# Run all tests
|
|
1179
|
+
pytest
|
|
1180
|
+
|
|
1181
|
+
# Run with coverage
|
|
1182
|
+
pytest --cov=opensecureconf_client --cov-report=html
|
|
1183
|
+
|
|
1184
|
+
# Run specific test file
|
|
1185
|
+
pytest tests/test_client.py
|
|
1186
|
+
|
|
1187
|
+
# Run with verbose output
|
|
1188
|
+
pytest -v
|
|
1189
|
+
|
|
1190
|
+
# Run with logging
|
|
1191
|
+
pytest -s
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
### Code Quality
|
|
1195
|
+
|
|
1196
|
+
```bash
|
|
1197
|
+
# Format code
|
|
1198
|
+
black opensecureconf_client.py
|
|
1199
|
+
black tests/
|
|
1200
|
+
|
|
1201
|
+
# Sort imports
|
|
1202
|
+
isort opensecureconf_client.py tests/
|
|
1203
|
+
|
|
1204
|
+
# Lint code
|
|
1205
|
+
flake8 opensecureconf_client.py
|
|
1206
|
+
pylint opensecureconf_client.py
|
|
1207
|
+
|
|
1208
|
+
# Type checking
|
|
1209
|
+
mypy opensecureconf_client.py
|
|
1210
|
+
|
|
1211
|
+
# Security check
|
|
1212
|
+
bandit -r opensecureconf_client.py
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
### Building Distribution
|
|
1216
|
+
|
|
1217
|
+
```bash
|
|
1218
|
+
# Build package
|
|
1219
|
+
python -m build
|
|
1220
|
+
|
|
1221
|
+
# Check distribution
|
|
1222
|
+
twine check dist/*
|
|
1223
|
+
|
|
1224
|
+
# Upload to TestPyPI
|
|
1225
|
+
twine upload --repository testpypi dist/*
|
|
1226
|
+
|
|
1227
|
+
# Upload to PyPI
|
|
1228
|
+
twine upload dist/*
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
## 🤝 Contributing
|
|
1232
|
+
|
|
1233
|
+
Contributions are welcome! Please follow these guidelines:
|
|
1234
|
+
|
|
1235
|
+
### Reporting Issues
|
|
1236
|
+
|
|
1237
|
+
- Use the [GitHub issue tracker](https://github.com/lordraw77/OpenSecureConf/issues)
|
|
1238
|
+
- Include minimal reproducible example
|
|
1239
|
+
- Specify Python version and client version
|
|
1240
|
+
- Include relevant logs (with sensitive data removed)
|
|
1241
|
+
|
|
1242
|
+
### Pull Requests
|
|
1243
|
+
|
|
1244
|
+
1. Fork the repository
|
|
1245
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
1246
|
+
3. Make your changes
|
|
1247
|
+
4. Add tests for new functionality
|
|
1248
|
+
5. Ensure all tests pass (`pytest`)
|
|
1249
|
+
6. Format code (`black`, `isort`)
|
|
1250
|
+
7. Lint code (`flake8`, `pylint`)
|
|
1251
|
+
8. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
1252
|
+
9. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1253
|
+
10. Open a Pull Request
|
|
1254
|
+
|
|
1255
|
+
### Code Style
|
|
1256
|
+
|
|
1257
|
+
- Follow [PEP 8](https://peps.python.org/pep-0008/)
|
|
1258
|
+
- Use type hints
|
|
1259
|
+
- Write docstrings for all public methods
|
|
1260
|
+
- Maintain test coverage above 90%
|
|
1261
|
+
- Add examples for new features
|
|
1262
|
+
|
|
1263
|
+
## 📄 License
|
|
1264
|
+
|
|
1265
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
1266
|
+
|
|
1267
|
+
## 🔗 Links
|
|
1268
|
+
|
|
1269
|
+
- [OpenSecureConf Server](https://github.com/lordraw77/OpenSecureConf)
|
|
1270
|
+
- [PyPI Package](https://pypi.org/project/opensecureconf-client/)
|
|
1271
|
+
- [Issue Tracker](https://github.com/lordraw77/OpenSecureConf/issues)
|
|
1272
|
+
- [Documentation](https://github.com/lordraw77/OpenSecureConf/tree/main/docs)
|
|
1273
|
+
- [Changelog](https://github.com/lordraw77/OpenSecureConf/blob/main/CHANGELOG.md)
|
|
1274
|
+
|
|
1275
|
+
## 📮 Support
|
|
1276
|
+
|
|
1277
|
+
- 📧 Email: support@opensecureconf.dev
|
|
1278
|
+
- 💬 GitHub Discussions: [opensecureconf/discussions](https://github.com/lordraw77/OpenSecureConf/discussions)
|
|
1279
|
+
- 🐛 Bug Reports: [opensecureconf/issues](https://github.com/lordraw77/OpenSecureConf/issues)
|
|
1280
|
+
|
|
1281
|
+
## 🙏 Acknowledgments
|
|
1282
|
+
|
|
1283
|
+
- Built with [FastAPI](https://fastapi.tiangolo.com/)
|
|
1284
|
+
- Encryption powered by [cryptography](https://cryptography.io/)
|
|
1285
|
+
- HTTP client using [requests](https://requests.readthedocs.io/)
|
|
1286
|
+
|
|
1287
|
+
---
|
|
1288
|
+
|
|
1289
|
+
**Made with ❤️ by the OpenSecureConf Team**
|