From 08f84e951ad042c645323f5510e790577f47d378 Mon Sep 17 00:00:00 2001 From: Bakery Admin Date: Sat, 24 Jan 2026 19:12:35 +0100 Subject: [PATCH] Fix redis ssl issues --- .../app/services/inventory_scheduler.py | 11 +++++++++-- services/inventory/test_dedup.py | 6 +++++- services/orchestrator/app/main.py | 11 +++++++++-- services/pos/app/scheduler.py | 9 +++++++-- .../app/services/delivery_tracking_service.py | 8 +++++++- .../app/services/production_scheduler.py | 8 +++++++- .../app/consumers/alert_event_consumer.py | 9 ++++++++- services/tenant/app/api/usage_forecast.py | 16 +++++++++++----- shared/leader_election/mixin.py | 11 +++++++++-- 9 files changed, 72 insertions(+), 17 deletions(-) diff --git a/services/inventory/app/services/inventory_scheduler.py b/services/inventory/app/services/inventory_scheduler.py index 8a378183..0445bc62 100644 --- a/services/inventory/app/services/inventory_scheduler.py +++ b/services/inventory/app/services/inventory_scheduler.py @@ -58,12 +58,19 @@ class InventoryScheduler: async def _start_with_leader_election(self): """Start with Redis-based leader election for horizontal scaling""" + import ssl import redis.asyncio as redis from shared.leader_election import LeaderElectionService try: - # Create Redis connection - self._redis_client = redis.from_url(self._redis_url, decode_responses=False) + # Create Redis connection with proper SSL handling for self-signed certificates + connection_kwargs = {"decode_responses": False} + + # Handle SSL/TLS for rediss:// URLs (self-signed certificates) + if self._redis_url and self._redis_url.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + self._redis_client = redis.from_url(self._redis_url, **connection_kwargs) await self._redis_client.ping() # Create scheduler (but don't start it yet) diff --git a/services/inventory/test_dedup.py b/services/inventory/test_dedup.py index 76a45a9c..3bd1c39f 100644 --- a/services/inventory/test_dedup.py +++ b/services/inventory/test_dedup.py @@ -62,7 +62,11 @@ async def test_deduplication_in_container(): async def start(self): # Connect to Redis for deduplication testing - self.redis = await aioredis.from_url(self.config.REDIS_URL) + import ssl + connection_kwargs = {} + if self.config.REDIS_URL and self.config.REDIS_URL.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + self.redis = await aioredis.from_url(self.config.REDIS_URL, **connection_kwargs) print(f"✅ Connected to Redis for testing") async def stop(self): diff --git a/services/orchestrator/app/main.py b/services/orchestrator/app/main.py index 94b5351c..abb09fe0 100644 --- a/services/orchestrator/app/main.py +++ b/services/orchestrator/app/main.py @@ -100,16 +100,23 @@ class OrchestratorService(StandardFastAPIService): Without leader election, each pod would run the same scheduled jobs, causing duplicate forecasts, production schedules, and database contention. """ + import ssl from shared.leader_election import LeaderElectionService import redis.asyncio as redis try: # Create Redis connection for leader election redis_url = f"redis://:{settings.REDIS_PASSWORD}@{settings.REDIS_HOST}:{settings.REDIS_PORT}/{settings.REDIS_DB}" - if settings.REDIS_TLS_ENABLED.lower() == "true": + use_tls = settings.REDIS_TLS_ENABLED.lower() == "true" + if use_tls: redis_url = redis_url.replace("redis://", "rediss://") - redis_client = redis.from_url(redis_url, decode_responses=False) + # Handle SSL/TLS for self-signed certificates + connection_kwargs = {"decode_responses": False} + if use_tls: + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + redis_client = redis.from_url(redis_url, **connection_kwargs) await redis_client.ping() # Use shared leader election service diff --git a/services/pos/app/scheduler.py b/services/pos/app/scheduler.py index 7741a0bc..f795172d 100644 --- a/services/pos/app/scheduler.py +++ b/services/pos/app/scheduler.py @@ -66,12 +66,17 @@ class POSScheduler: async def _start_with_leader_election(self): """Start with Redis-based leader election for horizontal scaling""" + import ssl import redis.asyncio as redis from shared.leader_election import LeaderElectionService try: - # Create Redis connection - self._redis_client = redis.from_url(self._redis_url, decode_responses=False) + # Create Redis connection with proper SSL handling for self-signed certificates + connection_kwargs = {"decode_responses": False} + if self._redis_url and self._redis_url.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + self._redis_client = redis.from_url(self._redis_url, **connection_kwargs) await self._redis_client.ping() # Create scheduler (but don't start it yet) diff --git a/services/procurement/app/services/delivery_tracking_service.py b/services/procurement/app/services/delivery_tracking_service.py index 9cdc0125..34fc7d5d 100644 --- a/services/procurement/app/services/delivery_tracking_service.py +++ b/services/procurement/app/services/delivery_tracking_service.py @@ -56,6 +56,7 @@ class DeliveryTrackingService: async def _setup_leader_election(self): """Setup Redis-based leader election for horizontal scaling""" + import ssl from shared.leader_election import LeaderElectionService import redis.asyncio as redis @@ -68,7 +69,12 @@ class DeliveryTrackingService: redis_db = getattr(self.config, 'REDIS_DB', 0) redis_url = f"redis://:{redis_password}@{redis_host}:{redis_port}/{redis_db}" - self._redis_client = redis.from_url(redis_url, decode_responses=False) + # Handle SSL/TLS for self-signed certificates + connection_kwargs = {"decode_responses": False} + if redis_url and redis_url.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + self._redis_client = redis.from_url(redis_url, **connection_kwargs) await self._redis_client.ping() # Create leader election service diff --git a/services/production/app/services/production_scheduler.py b/services/production/app/services/production_scheduler.py index c869f19a..d30b2702 100644 --- a/services/production/app/services/production_scheduler.py +++ b/services/production/app/services/production_scheduler.py @@ -65,10 +65,16 @@ class ProductionScheduler: async def _setup_leader_election(self): """Setup Redis-based leader election""" + import ssl from shared.leader_election import LeaderElectionService import redis.asyncio as redis - self._redis_client = redis.from_url(self.redis_url, decode_responses=False) + # Handle SSL/TLS for self-signed certificates + connection_kwargs = {"decode_responses": False} + if self.redis_url and self.redis_url.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + self._redis_client = redis.from_url(self.redis_url, **connection_kwargs) await self._redis_client.ping() self._leader_election = LeaderElectionService( diff --git a/services/suppliers/app/consumers/alert_event_consumer.py b/services/suppliers/app/consumers/alert_event_consumer.py index 26aa287a..6ca90eed 100644 --- a/services/suppliers/app/consumers/alert_event_consumer.py +++ b/services/suppliers/app/consumers/alert_event_consumer.py @@ -365,6 +365,7 @@ class AlertEventConsumer: # Redis-based rate limiting implementation try: + import ssl import redis.asyncio as redis from datetime import datetime, timedelta from app.core.config import Settings @@ -372,7 +373,13 @@ class AlertEventConsumer: # Connect to Redis using proper configuration with TLS and auth settings = Settings() redis_url = settings.REDIS_URL - redis_client = await redis.from_url(redis_url, decode_responses=True) + + # Handle SSL/TLS for self-signed certificates + connection_kwargs = {"decode_responses": True} + if redis_url and redis_url.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + redis_client = await redis.from_url(redis_url, **connection_kwargs) # Rate limit keys hour_key = f"alert_rate_limit:{tenant_id}:{alert_type}:hour:{datetime.utcnow().strftime('%Y%m%d%H')}" diff --git a/services/tenant/app/api/usage_forecast.py b/services/tenant/app/api/usage_forecast.py index a7db86f9..b0f216b7 100644 --- a/services/tenant/app/api/usage_forecast.py +++ b/services/tenant/app/api/usage_forecast.py @@ -49,11 +49,17 @@ class UsageForecastResponse(BaseModel): async def get_redis_client() -> redis.Redis: """Get Redis client for usage tracking""" - return redis.from_url( - settings.REDIS_URL, - encoding="utf-8", - decode_responses=True - ) + import ssl + + # Handle SSL/TLS for self-signed certificates + connection_kwargs = { + "encoding": "utf-8", + "decode_responses": True + } + if settings.REDIS_URL and settings.REDIS_URL.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + return redis.from_url(settings.REDIS_URL, **connection_kwargs) async def get_usage_history( diff --git a/shared/leader_election/mixin.py b/shared/leader_election/mixin.py index 52c1555d..639a0665 100644 --- a/shared/leader_election/mixin.py +++ b/shared/leader_election/mixin.py @@ -63,13 +63,20 @@ class SchedulerLeaderMixin: Only the leader will start the scheduler. """ + import ssl from apscheduler.schedulers.asyncio import AsyncIOScheduler from shared.leader_election.service import LeaderElectionService import redis.asyncio as redis try: - # Create Redis connection - self._redis_client = redis.from_url(self._redis_url, decode_responses=False) + # Create Redis connection with proper SSL handling for self-signed certificates + connection_kwargs = {"decode_responses": False} + + # Handle SSL/TLS for rediss:// URLs (self-signed certificates) + if self._redis_url and self._redis_url.startswith("rediss://"): + connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE + + self._redis_client = redis.from_url(self._redis_url, **connection_kwargs) await self._redis_client.ping() # Create scheduler (but don't start it yet)