fraiseql-confiture 0.3.4__cp311-cp311-win_amd64.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.
- confiture/__init__.py +48 -0
- confiture/_core.cp311-win_amd64.pyd +0 -0
- confiture/cli/__init__.py +0 -0
- confiture/cli/dry_run.py +116 -0
- confiture/cli/lint_formatter.py +193 -0
- confiture/cli/main.py +1656 -0
- confiture/config/__init__.py +0 -0
- confiture/config/environment.py +263 -0
- confiture/core/__init__.py +51 -0
- confiture/core/anonymization/__init__.py +0 -0
- confiture/core/anonymization/audit.py +485 -0
- confiture/core/anonymization/benchmarking.py +372 -0
- confiture/core/anonymization/breach_notification.py +652 -0
- confiture/core/anonymization/compliance.py +617 -0
- confiture/core/anonymization/composer.py +298 -0
- confiture/core/anonymization/data_subject_rights.py +669 -0
- confiture/core/anonymization/factory.py +319 -0
- confiture/core/anonymization/governance.py +737 -0
- confiture/core/anonymization/performance.py +1092 -0
- confiture/core/anonymization/profile.py +284 -0
- confiture/core/anonymization/registry.py +195 -0
- confiture/core/anonymization/security/kms_manager.py +547 -0
- confiture/core/anonymization/security/lineage.py +888 -0
- confiture/core/anonymization/security/token_store.py +686 -0
- confiture/core/anonymization/strategies/__init__.py +41 -0
- confiture/core/anonymization/strategies/address.py +359 -0
- confiture/core/anonymization/strategies/credit_card.py +374 -0
- confiture/core/anonymization/strategies/custom.py +161 -0
- confiture/core/anonymization/strategies/date.py +218 -0
- confiture/core/anonymization/strategies/differential_privacy.py +398 -0
- confiture/core/anonymization/strategies/email.py +141 -0
- confiture/core/anonymization/strategies/format_preserving_encryption.py +310 -0
- confiture/core/anonymization/strategies/hash.py +150 -0
- confiture/core/anonymization/strategies/ip_address.py +235 -0
- confiture/core/anonymization/strategies/masking_retention.py +252 -0
- confiture/core/anonymization/strategies/name.py +298 -0
- confiture/core/anonymization/strategies/phone.py +119 -0
- confiture/core/anonymization/strategies/preserve.py +85 -0
- confiture/core/anonymization/strategies/redact.py +101 -0
- confiture/core/anonymization/strategies/salted_hashing.py +322 -0
- confiture/core/anonymization/strategies/text_redaction.py +183 -0
- confiture/core/anonymization/strategies/tokenization.py +334 -0
- confiture/core/anonymization/strategy.py +241 -0
- confiture/core/anonymization/syncer_audit.py +357 -0
- confiture/core/blue_green.py +683 -0
- confiture/core/builder.py +500 -0
- confiture/core/checksum.py +358 -0
- confiture/core/connection.py +132 -0
- confiture/core/differ.py +522 -0
- confiture/core/drift.py +564 -0
- confiture/core/dry_run.py +182 -0
- confiture/core/health.py +313 -0
- confiture/core/hooks/__init__.py +87 -0
- confiture/core/hooks/base.py +232 -0
- confiture/core/hooks/context.py +146 -0
- confiture/core/hooks/execution_strategies.py +57 -0
- confiture/core/hooks/observability.py +220 -0
- confiture/core/hooks/phases.py +53 -0
- confiture/core/hooks/registry.py +295 -0
- confiture/core/large_tables.py +775 -0
- confiture/core/linting/__init__.py +70 -0
- confiture/core/linting/composer.py +192 -0
- confiture/core/linting/libraries/__init__.py +17 -0
- confiture/core/linting/libraries/gdpr.py +168 -0
- confiture/core/linting/libraries/general.py +184 -0
- confiture/core/linting/libraries/hipaa.py +144 -0
- confiture/core/linting/libraries/pci_dss.py +104 -0
- confiture/core/linting/libraries/sox.py +120 -0
- confiture/core/linting/schema_linter.py +491 -0
- confiture/core/linting/versioning.py +151 -0
- confiture/core/locking.py +389 -0
- confiture/core/migration_generator.py +298 -0
- confiture/core/migrator.py +793 -0
- confiture/core/observability/__init__.py +44 -0
- confiture/core/observability/audit.py +323 -0
- confiture/core/observability/logging.py +187 -0
- confiture/core/observability/metrics.py +174 -0
- confiture/core/observability/tracing.py +192 -0
- confiture/core/pg_version.py +418 -0
- confiture/core/pool.py +406 -0
- confiture/core/risk/__init__.py +39 -0
- confiture/core/risk/predictor.py +188 -0
- confiture/core/risk/scoring.py +248 -0
- confiture/core/rollback_generator.py +388 -0
- confiture/core/schema_analyzer.py +769 -0
- confiture/core/schema_to_schema.py +590 -0
- confiture/core/security/__init__.py +32 -0
- confiture/core/security/logging.py +201 -0
- confiture/core/security/validation.py +416 -0
- confiture/core/signals.py +371 -0
- confiture/core/syncer.py +540 -0
- confiture/exceptions.py +192 -0
- confiture/integrations/__init__.py +0 -0
- confiture/models/__init__.py +0 -0
- confiture/models/lint.py +193 -0
- confiture/models/migration.py +180 -0
- confiture/models/schema.py +203 -0
- confiture/scenarios/__init__.py +36 -0
- confiture/scenarios/compliance.py +586 -0
- confiture/scenarios/ecommerce.py +199 -0
- confiture/scenarios/financial.py +253 -0
- confiture/scenarios/healthcare.py +315 -0
- confiture/scenarios/multi_tenant.py +340 -0
- confiture/scenarios/saas.py +295 -0
- confiture/testing/FRAMEWORK_API.md +722 -0
- confiture/testing/__init__.py +38 -0
- confiture/testing/fixtures/__init__.py +11 -0
- confiture/testing/fixtures/data_validator.py +229 -0
- confiture/testing/fixtures/migration_runner.py +167 -0
- confiture/testing/fixtures/schema_snapshotter.py +352 -0
- confiture/testing/frameworks/__init__.py +10 -0
- confiture/testing/frameworks/mutation.py +587 -0
- confiture/testing/frameworks/performance.py +479 -0
- confiture/testing/utils/__init__.py +0 -0
- fraiseql_confiture-0.3.4.dist-info/METADATA +438 -0
- fraiseql_confiture-0.3.4.dist-info/RECORD +119 -0
- fraiseql_confiture-0.3.4.dist-info/WHEEL +4 -0
- fraiseql_confiture-0.3.4.dist-info/entry_points.txt +2 -0
- fraiseql_confiture-0.3.4.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Prometheus metrics for migrations.
|
|
2
|
+
|
|
3
|
+
Provides optional metrics integration for migration monitoring.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class MetricsConfig:
|
|
15
|
+
"""Configuration for Prometheus metrics."""
|
|
16
|
+
|
|
17
|
+
enabled: bool = False
|
|
18
|
+
port: int = 9090
|
|
19
|
+
path: str = "/metrics"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MigrationMetrics:
|
|
23
|
+
"""Prometheus metrics for migrations.
|
|
24
|
+
|
|
25
|
+
Exposes metrics when prometheus_client is installed:
|
|
26
|
+
- confiture_migrations_total (counter)
|
|
27
|
+
- confiture_migration_duration_seconds (histogram)
|
|
28
|
+
- confiture_migration_errors_total (counter)
|
|
29
|
+
- confiture_migration_last_success_timestamp (gauge)
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> metrics = MigrationMetrics(MetricsConfig(enabled=True))
|
|
33
|
+
>>> metrics.start_server()
|
|
34
|
+
>>> metrics.record_migration("001", "create_users", 2.5, success=True)
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, config: MetricsConfig):
|
|
38
|
+
"""Initialize migration metrics.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
config: Metrics configuration
|
|
42
|
+
"""
|
|
43
|
+
self.config = config
|
|
44
|
+
self._registry: Any = None
|
|
45
|
+
self._migrations_total: Any = None
|
|
46
|
+
self._migration_duration: Any = None
|
|
47
|
+
self._migration_errors: Any = None
|
|
48
|
+
self._last_success: Any = None
|
|
49
|
+
self._initialized = False
|
|
50
|
+
|
|
51
|
+
if config.enabled:
|
|
52
|
+
self._initialize_metrics()
|
|
53
|
+
|
|
54
|
+
def _initialize_metrics(self) -> None:
|
|
55
|
+
"""Initialize Prometheus metrics."""
|
|
56
|
+
try:
|
|
57
|
+
from prometheus_client import REGISTRY, Counter, Gauge, Histogram
|
|
58
|
+
|
|
59
|
+
self._registry = REGISTRY
|
|
60
|
+
|
|
61
|
+
# Define metrics
|
|
62
|
+
self._migrations_total = Counter(
|
|
63
|
+
"confiture_migrations_total",
|
|
64
|
+
"Total number of migrations executed",
|
|
65
|
+
["version", "name", "status"],
|
|
66
|
+
registry=self._registry,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
self._migration_duration = Histogram(
|
|
70
|
+
"confiture_migration_duration_seconds",
|
|
71
|
+
"Migration execution duration",
|
|
72
|
+
["version"],
|
|
73
|
+
buckets=(0.1, 0.5, 1, 2, 5, 10, 30, 60, 120, 300),
|
|
74
|
+
registry=self._registry,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
self._migration_errors = Counter(
|
|
78
|
+
"confiture_migration_errors_total",
|
|
79
|
+
"Total number of migration errors",
|
|
80
|
+
["version", "error_type"],
|
|
81
|
+
registry=self._registry,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
self._last_success = Gauge(
|
|
85
|
+
"confiture_migration_last_success_timestamp",
|
|
86
|
+
"Timestamp of last successful migration",
|
|
87
|
+
registry=self._registry,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
self._initialized = True
|
|
91
|
+
logger.info("Prometheus metrics initialized")
|
|
92
|
+
|
|
93
|
+
except ImportError:
|
|
94
|
+
logger.warning(
|
|
95
|
+
"prometheus_client not installed. Install with: "
|
|
96
|
+
"pip install confiture[observability]"
|
|
97
|
+
)
|
|
98
|
+
self.config.enabled = False
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def is_enabled(self) -> bool:
|
|
102
|
+
"""Check if metrics are enabled and initialized."""
|
|
103
|
+
return self.config.enabled and self._initialized
|
|
104
|
+
|
|
105
|
+
def start_server(self) -> None:
|
|
106
|
+
"""Start Prometheus metrics HTTP server."""
|
|
107
|
+
if not self.is_enabled:
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
from prometheus_client import start_http_server
|
|
112
|
+
|
|
113
|
+
start_http_server(self.config.port)
|
|
114
|
+
logger.info(f"Prometheus metrics server started on port {self.config.port}")
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.error(f"Failed to start metrics server: {e}")
|
|
118
|
+
|
|
119
|
+
def record_migration(
|
|
120
|
+
self,
|
|
121
|
+
version: str,
|
|
122
|
+
name: str,
|
|
123
|
+
duration_seconds: float,
|
|
124
|
+
success: bool,
|
|
125
|
+
error: Exception | None = None,
|
|
126
|
+
) -> None:
|
|
127
|
+
"""Record migration execution metrics.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
version: Migration version
|
|
131
|
+
name: Migration name
|
|
132
|
+
duration_seconds: Execution duration in seconds
|
|
133
|
+
success: Whether migration succeeded
|
|
134
|
+
error: Exception if failed (optional)
|
|
135
|
+
"""
|
|
136
|
+
if not self.is_enabled:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
import time
|
|
140
|
+
|
|
141
|
+
status = "success" if success else "error"
|
|
142
|
+
self._migrations_total.labels(version=version, name=name, status=status).inc()
|
|
143
|
+
self._migration_duration.labels(version=version).observe(duration_seconds)
|
|
144
|
+
|
|
145
|
+
if success:
|
|
146
|
+
self._last_success.set(time.time())
|
|
147
|
+
elif error:
|
|
148
|
+
error_type = type(error).__name__
|
|
149
|
+
self._migration_errors.labels(version=version, error_type=error_type).inc()
|
|
150
|
+
|
|
151
|
+
def record_error(self, version: str, error: Exception) -> None:
|
|
152
|
+
"""Record a migration error.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
version: Migration version
|
|
156
|
+
error: Exception that occurred
|
|
157
|
+
"""
|
|
158
|
+
if not self.is_enabled:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
error_type = type(error).__name__
|
|
162
|
+
self._migration_errors.labels(version=version, error_type=error_type).inc()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def create_metrics(config: MetricsConfig | None = None) -> MigrationMetrics:
|
|
166
|
+
"""Factory function to create metrics.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
config: Metrics configuration (optional)
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Configured MigrationMetrics
|
|
173
|
+
"""
|
|
174
|
+
return MigrationMetrics(config or MetricsConfig())
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""OpenTelemetry tracing for migrations.
|
|
2
|
+
|
|
3
|
+
Provides optional tracing integration for migration execution.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from collections.abc import Generator
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class TracingConfig:
|
|
17
|
+
"""Configuration for OpenTelemetry tracing."""
|
|
18
|
+
|
|
19
|
+
enabled: bool = False
|
|
20
|
+
service_name: str = "confiture"
|
|
21
|
+
endpoint: str | None = None # OTLP endpoint
|
|
22
|
+
sample_rate: float = 1.0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MigrationTracer:
|
|
26
|
+
"""OpenTelemetry tracer for migrations.
|
|
27
|
+
|
|
28
|
+
Provides tracing spans for migration operations when OpenTelemetry
|
|
29
|
+
is installed and configured.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> tracer = MigrationTracer(TracingConfig(enabled=True))
|
|
33
|
+
>>> with tracer.span("migration.apply", migration_id="001"):
|
|
34
|
+
... # Migration code
|
|
35
|
+
... pass
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, config: TracingConfig):
|
|
39
|
+
"""Initialize migration tracer.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
config: Tracing configuration
|
|
43
|
+
"""
|
|
44
|
+
self.config = config
|
|
45
|
+
self._tracer: Any = None
|
|
46
|
+
self._initialized = False
|
|
47
|
+
|
|
48
|
+
if config.enabled:
|
|
49
|
+
self._initialize_tracer()
|
|
50
|
+
|
|
51
|
+
def _initialize_tracer(self) -> None:
|
|
52
|
+
"""Initialize OpenTelemetry tracer."""
|
|
53
|
+
try:
|
|
54
|
+
from opentelemetry import trace
|
|
55
|
+
from opentelemetry.sdk.resources import Resource
|
|
56
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
57
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
58
|
+
|
|
59
|
+
# Create resource
|
|
60
|
+
resource = Resource.create(
|
|
61
|
+
{
|
|
62
|
+
"service.name": self.config.service_name,
|
|
63
|
+
"service.version": "0.5.0",
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Create provider
|
|
68
|
+
provider = TracerProvider(resource=resource)
|
|
69
|
+
|
|
70
|
+
# Add exporter if endpoint configured
|
|
71
|
+
if self.config.endpoint:
|
|
72
|
+
try:
|
|
73
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
|
|
74
|
+
OTLPSpanExporter,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
exporter = OTLPSpanExporter(endpoint=self.config.endpoint)
|
|
78
|
+
provider.add_span_processor(BatchSpanProcessor(exporter))
|
|
79
|
+
except ImportError:
|
|
80
|
+
logger.warning(
|
|
81
|
+
"OTLP exporter not installed. Install with: "
|
|
82
|
+
"pip install opentelemetry-exporter-otlp"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
trace.set_tracer_provider(provider)
|
|
86
|
+
self._tracer = trace.get_tracer("confiture")
|
|
87
|
+
self._initialized = True
|
|
88
|
+
|
|
89
|
+
logger.info("OpenTelemetry tracing initialized")
|
|
90
|
+
|
|
91
|
+
except ImportError:
|
|
92
|
+
logger.warning(
|
|
93
|
+
"OpenTelemetry not installed. Install with: pip install confiture[observability]"
|
|
94
|
+
)
|
|
95
|
+
self.config.enabled = False
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def is_enabled(self) -> bool:
|
|
99
|
+
"""Check if tracing is enabled and initialized."""
|
|
100
|
+
return self.config.enabled and self._initialized
|
|
101
|
+
|
|
102
|
+
@contextmanager
|
|
103
|
+
def span(
|
|
104
|
+
self,
|
|
105
|
+
name: str,
|
|
106
|
+
**attributes: Any,
|
|
107
|
+
) -> Generator[Any, None, None]:
|
|
108
|
+
"""Create a tracing span.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
name: Span name
|
|
112
|
+
**attributes: Span attributes
|
|
113
|
+
|
|
114
|
+
Yields:
|
|
115
|
+
Span context (or None if disabled)
|
|
116
|
+
"""
|
|
117
|
+
if not self.is_enabled or self._tracer is None:
|
|
118
|
+
yield None
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
with self._tracer.start_as_current_span(name) as span:
|
|
122
|
+
for key, value in attributes.items():
|
|
123
|
+
span.set_attribute(key, str(value))
|
|
124
|
+
yield span
|
|
125
|
+
|
|
126
|
+
def record_migration_start(
|
|
127
|
+
self,
|
|
128
|
+
migration_version: str,
|
|
129
|
+
migration_name: str,
|
|
130
|
+
) -> Any:
|
|
131
|
+
"""Create a span for migration start.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
migration_version: Migration version
|
|
135
|
+
migration_name: Migration name
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Span context manager
|
|
139
|
+
"""
|
|
140
|
+
return self.span(
|
|
141
|
+
"confiture.migration.execute",
|
|
142
|
+
migration_version=migration_version,
|
|
143
|
+
migration_name=migration_name,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def record_error(self, span: Any, error: Exception) -> None:
|
|
147
|
+
"""Record error on span.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
span: Active span
|
|
151
|
+
error: Exception that occurred
|
|
152
|
+
"""
|
|
153
|
+
if span is None or not self.is_enabled:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
from opentelemetry import trace
|
|
158
|
+
|
|
159
|
+
span.set_status(trace.Status(trace.StatusCode.ERROR))
|
|
160
|
+
span.record_exception(error)
|
|
161
|
+
except ImportError:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
def record_success(self, span: Any, duration_ms: int) -> None:
|
|
165
|
+
"""Record successful completion on span.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
span: Active span
|
|
169
|
+
duration_ms: Duration in milliseconds
|
|
170
|
+
"""
|
|
171
|
+
if span is None or not self.is_enabled:
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
from opentelemetry import trace
|
|
176
|
+
|
|
177
|
+
span.set_attribute("duration_ms", duration_ms)
|
|
178
|
+
span.set_status(trace.Status(trace.StatusCode.OK))
|
|
179
|
+
except ImportError:
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def create_tracer(config: TracingConfig | None = None) -> MigrationTracer:
|
|
184
|
+
"""Factory function to create tracer.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
config: Tracing configuration (optional)
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Configured MigrationTracer
|
|
191
|
+
"""
|
|
192
|
+
return MigrationTracer(config or TracingConfig())
|