Initial commit - production deployment
This commit is contained in:
0
services/orchestrator/app/core/__init__.py
Normal file
0
services/orchestrator/app/core/__init__.py
Normal file
133
services/orchestrator/app/core/config.py
Normal file
133
services/orchestrator/app/core/config.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# ================================================================
|
||||
# services/orchestrator/app/core/config.py
|
||||
# ================================================================
|
||||
"""
|
||||
Orchestrator Service Configuration
|
||||
"""
|
||||
|
||||
import os
|
||||
from pydantic import Field
|
||||
from shared.config.base import BaseServiceSettings
|
||||
|
||||
|
||||
class OrchestratorSettings(BaseServiceSettings):
|
||||
"""Orchestrator service specific settings"""
|
||||
|
||||
# Service Identity
|
||||
APP_NAME: str = "Orchestrator Service"
|
||||
SERVICE_NAME: str = "orchestrator-service"
|
||||
VERSION: str = "1.0.0"
|
||||
DESCRIPTION: str = "Automated orchestration of forecasting, production, and procurement workflows"
|
||||
|
||||
# Database configuration (minimal - only for audit logs)
|
||||
@property
|
||||
def DATABASE_URL(self) -> str:
|
||||
"""Build database URL from secure components"""
|
||||
# Try complete URL first (for backward compatibility)
|
||||
complete_url = os.getenv("ORCHESTRATOR_DATABASE_URL")
|
||||
if complete_url:
|
||||
return complete_url
|
||||
|
||||
# Build from components (secure approach)
|
||||
user = os.getenv("ORCHESTRATOR_DB_USER", "orchestrator_user")
|
||||
password = os.getenv("ORCHESTRATOR_DB_PASSWORD", "orchestrator_pass123")
|
||||
host = os.getenv("ORCHESTRATOR_DB_HOST", "localhost")
|
||||
port = os.getenv("ORCHESTRATOR_DB_PORT", "5432")
|
||||
name = os.getenv("ORCHESTRATOR_DB_NAME", "orchestrator_db")
|
||||
|
||||
return f"postgresql+asyncpg://{user}:{password}@{host}:{port}/{name}"
|
||||
|
||||
# Orchestration Settings
|
||||
ORCHESTRATION_ENABLED: bool = os.getenv("ORCHESTRATION_ENABLED", "true").lower() == "true"
|
||||
ORCHESTRATION_SCHEDULE: str = os.getenv("ORCHESTRATION_SCHEDULE", "30 5 * * *") # 5:30 AM daily (cron format)
|
||||
ORCHESTRATION_HOUR: int = int(os.getenv("ORCHESTRATION_HOUR", "2")) # Hour to run daily orchestration (default: 2 AM)
|
||||
ORCHESTRATION_MINUTE: int = int(os.getenv("ORCHESTRATION_MINUTE", "0")) # Minute to run (default: :00)
|
||||
ORCHESTRATION_TIMEOUT_SECONDS: int = int(os.getenv("ORCHESTRATION_TIMEOUT_SECONDS", "600")) # 10 minutes
|
||||
|
||||
# Tenant Processing
|
||||
MAX_CONCURRENT_TENANTS: int = int(os.getenv("MAX_CONCURRENT_TENANTS", "5"))
|
||||
TENANT_TIMEOUT_SECONDS: int = int(os.getenv("TENANT_TIMEOUT_SECONDS", "180")) # 3 minutes per tenant
|
||||
|
||||
# Retry Configuration
|
||||
MAX_RETRIES: int = int(os.getenv("MAX_RETRIES", "3"))
|
||||
RETRY_DELAY_SECONDS: int = int(os.getenv("RETRY_DELAY_SECONDS", "30"))
|
||||
ENABLE_EXPONENTIAL_BACKOFF: bool = os.getenv("ENABLE_EXPONENTIAL_BACKOFF", "true").lower() == "true"
|
||||
|
||||
# Circuit Breaker
|
||||
CIRCUIT_BREAKER_ENABLED: bool = os.getenv("CIRCUIT_BREAKER_ENABLED", "true").lower() == "true"
|
||||
CIRCUIT_BREAKER_FAILURE_THRESHOLD: int = int(os.getenv("CIRCUIT_BREAKER_FAILURE_THRESHOLD", "5"))
|
||||
CIRCUIT_BREAKER_RESET_TIMEOUT: int = int(os.getenv("CIRCUIT_BREAKER_RESET_TIMEOUT", "300")) # 5 minutes
|
||||
|
||||
# ================================================================
|
||||
# CIRCUIT BREAKER SETTINGS - Enhanced with Pydantic validation
|
||||
# ================================================================
|
||||
|
||||
CIRCUIT_BREAKER_TIMEOUT_DURATION: int = Field(
|
||||
default=60,
|
||||
description="Seconds to wait before attempting recovery"
|
||||
)
|
||||
CIRCUIT_BREAKER_SUCCESS_THRESHOLD: int = Field(
|
||||
default=2,
|
||||
description="Successful calls needed to close circuit"
|
||||
)
|
||||
|
||||
# ================================================================
|
||||
# SAGA PATTERN SETTINGS
|
||||
# ================================================================
|
||||
|
||||
SAGA_TIMEOUT_SECONDS: int = Field(
|
||||
default=600,
|
||||
description="Timeout for saga execution (10 minutes)"
|
||||
)
|
||||
SAGA_ENABLE_COMPENSATION: bool = Field(
|
||||
default=True,
|
||||
description="Enable saga compensation on failure"
|
||||
)
|
||||
|
||||
# Service Integration URLs
|
||||
FORECASTING_SERVICE_URL: str = os.getenv("FORECASTING_SERVICE_URL", "http://forecasting-service:8000")
|
||||
PRODUCTION_SERVICE_URL: str = os.getenv("PRODUCTION_SERVICE_URL", "http://production-service:8000")
|
||||
PROCUREMENT_SERVICE_URL: str = os.getenv("PROCUREMENT_SERVICE_URL", "http://procurement-service:8000")
|
||||
NOTIFICATION_SERVICE_URL: str = os.getenv("NOTIFICATION_SERVICE_URL", "http://notification-service:8000")
|
||||
TENANT_SERVICE_URL: str = os.getenv("TENANT_SERVICE_URL", "http://tenant-service:8000")
|
||||
|
||||
# Notification Settings
|
||||
SEND_NOTIFICATIONS: bool = os.getenv("SEND_NOTIFICATIONS", "true").lower() == "true"
|
||||
NOTIFY_ON_SUCCESS: bool = os.getenv("NOTIFY_ON_SUCCESS", "true").lower() == "true"
|
||||
NOTIFY_ON_FAILURE: bool = os.getenv("NOTIFY_ON_FAILURE", "true").lower() == "true"
|
||||
|
||||
# Audit and Logging
|
||||
AUDIT_ORCHESTRATION_RUNS: bool = os.getenv("AUDIT_ORCHESTRATION_RUNS", "true").lower() == "true"
|
||||
DETAILED_LOGGING: bool = os.getenv("DETAILED_LOGGING", "true").lower() == "true"
|
||||
|
||||
# AI Enhancement Settings
|
||||
ORCHESTRATION_USE_AI_INSIGHTS: bool = os.getenv("ORCHESTRATION_USE_AI_INSIGHTS", "true").lower() == "true"
|
||||
AI_INSIGHTS_SERVICE_URL: str = os.getenv("AI_INSIGHTS_SERVICE_URL", "http://ai-insights-service:8000")
|
||||
AI_INSIGHTS_MIN_CONFIDENCE: int = int(os.getenv("AI_INSIGHTS_MIN_CONFIDENCE", "70"))
|
||||
|
||||
# Redis Cache Settings (for dashboard performance)
|
||||
REDIS_HOST: str = os.getenv("REDIS_HOST", "localhost")
|
||||
REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379"))
|
||||
REDIS_DB: int = int(os.getenv("REDIS_DB", "0"))
|
||||
REDIS_PASSWORD: str = os.getenv("REDIS_PASSWORD", "")
|
||||
REDIS_TLS_ENABLED: str = os.getenv("REDIS_TLS_ENABLED", "false")
|
||||
CACHE_ENABLED: bool = os.getenv("CACHE_ENABLED", "true").lower() == "true"
|
||||
CACHE_TTL_HEALTH: int = int(os.getenv("CACHE_TTL_HEALTH", "30")) # 30 seconds
|
||||
CACHE_TTL_INSIGHTS: int = int(os.getenv("CACHE_TTL_INSIGHTS", "60")) # 1 minute (reduced for faster metrics updates)
|
||||
CACHE_TTL_SUMMARY: int = int(os.getenv("CACHE_TTL_SUMMARY", "60")) # 1 minute
|
||||
|
||||
# Enterprise dashboard cache TTLs
|
||||
CACHE_TTL_ENTERPRISE_SUMMARY: int = int(os.getenv("CACHE_TTL_ENTERPRISE_SUMMARY", "60")) # 1 minute
|
||||
CACHE_TTL_ENTERPRISE_PERFORMANCE: int = int(os.getenv("CACHE_TTL_ENTERPRISE_PERFORMANCE", "60")) # 1 minute
|
||||
CACHE_TTL_ENTERPRISE_DISTRIBUTION: int = int(os.getenv("CACHE_TTL_ENTERPRISE_DISTRIBUTION", "30")) # 30 seconds
|
||||
CACHE_TTL_ENTERPRISE_FORECAST: int = int(os.getenv("CACHE_TTL_ENTERPRISE_FORECAST", "120")) # 2 minutes
|
||||
CACHE_TTL_ENTERPRISE_NETWORK: int = int(os.getenv("CACHE_TTL_ENTERPRISE_NETWORK", "60")) # 1 minute
|
||||
|
||||
|
||||
# Global settings instance
|
||||
settings = OrchestratorSettings()
|
||||
|
||||
|
||||
def get_settings():
|
||||
"""Get the global settings instance"""
|
||||
return settings
|
||||
48
services/orchestrator/app/core/database.py
Normal file
48
services/orchestrator/app/core/database.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# ================================================================
|
||||
# services/orchestrator/app/core/database.py
|
||||
# ================================================================
|
||||
"""
|
||||
Database connection and session management for Orchestrator Service
|
||||
Minimal database - only for audit trail
|
||||
"""
|
||||
|
||||
from shared.database.base import DatabaseManager
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
from .config import settings
|
||||
|
||||
# Initialize database manager
|
||||
database_manager = DatabaseManager(
|
||||
database_url=settings.DATABASE_URL,
|
||||
echo=settings.DEBUG
|
||||
)
|
||||
|
||||
# Create async session factory
|
||||
AsyncSessionLocal = async_sessionmaker(
|
||||
database_manager.async_engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
autocommit=False,
|
||||
autoflush=False,
|
||||
)
|
||||
|
||||
|
||||
async def get_db() -> AsyncSession:
|
||||
"""
|
||||
Dependency to get database session.
|
||||
Used in FastAPI endpoints via Depends(get_db).
|
||||
"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
|
||||
async def init_db():
|
||||
"""Initialize database (create tables if needed)"""
|
||||
await database_manager.create_all()
|
||||
|
||||
|
||||
async def close_db():
|
||||
"""Close database connections"""
|
||||
await database_manager.close()
|
||||
Reference in New Issue
Block a user