Netdata.cloud bot for Zulip
1"""Configuration management for the Netdata Zulip bot.""" 2 3import os 4from pathlib import Path 5from typing import Optional 6 7import structlog 8from dotenv import load_dotenv 9 10from .models import ZulipConfig, ServerConfig 11 12logger = structlog.get_logger() 13 14 15def load_config() -> tuple[ZulipConfig, ServerConfig]: 16 """Load configuration from environment variables and .env files.""" 17 18 # Load .env file if present 19 env_file = Path(".env") 20 if env_file.exists(): 21 load_dotenv(env_file) 22 logger.info("Loaded configuration from .env file") 23 24 # Load Zulip configuration 25 zulip_config = ZulipConfig( 26 site=os.getenv("ZULIP_SITE", ""), 27 email=os.getenv("ZULIP_EMAIL", ""), 28 api_key=os.getenv("ZULIP_API_KEY", ""), 29 stream=os.getenv("ZULIP_STREAM", "netdata-alerts"), 30 ) 31 32 # Validate required Zulip settings 33 if not all([zulip_config.site, zulip_config.email, zulip_config.api_key]): 34 raise ValueError( 35 "Missing required Zulip configuration. Please set ZULIP_SITE, " 36 "ZULIP_EMAIL, and ZULIP_API_KEY environment variables." 37 ) 38 39 # Load server configuration 40 server_config = ServerConfig( 41 host=os.getenv("SERVER_HOST", "0.0.0.0"), 42 port=int(os.getenv("SERVER_PORT", "8443")), 43 domain=os.getenv("SERVER_DOMAIN", ""), 44 cert_path=os.getenv("SERVER_CERT_PATH", "./certs"), 45 enable_mtls=os.getenv("SERVER_ENABLE_MTLS", "true").lower() == "true", 46 auto_cert=os.getenv("SERVER_AUTO_CERT", "false").lower() == "true", 47 cert_email=os.getenv("SERVER_CERT_EMAIL", ""), 48 cert_staging=os.getenv("SERVER_CERT_STAGING", "false").lower() == "true", 49 acme_port=int(os.getenv("SERVER_ACME_PORT", "80")), 50 ) 51 52 # Validate required server settings 53 if not server_config.domain: 54 raise ValueError( 55 "Missing required server configuration. Please set SERVER_DOMAIN " 56 "environment variable." 57 ) 58 59 # Validate auto-cert specific settings 60 if server_config.auto_cert and not server_config.cert_email: 61 raise ValueError( 62 "When SERVER_AUTO_CERT is enabled, SERVER_CERT_EMAIL must be set " 63 "for Let's Encrypt account registration." 64 ) 65 66 logger.info( 67 "Configuration loaded", 68 zulip_site=zulip_config.site, 69 zulip_email=zulip_config.email, 70 zulip_stream=zulip_config.stream, 71 server_host=server_config.host, 72 server_port=server_config.port, 73 server_domain=server_config.domain, 74 mtls_enabled=server_config.enable_mtls, 75 auto_cert=server_config.auto_cert, 76 cert_staging=server_config.cert_staging, 77 ) 78 79 return zulip_config, server_config 80 81 82def load_zuliprc_config(zuliprc_path: Optional[str] = None) -> ZulipConfig: 83 """Load Zulip configuration from a zuliprc file. 84 85 Args: 86 zuliprc_path: Path to zuliprc file. If None, looks for ~/.zuliprc 87 88 Returns: 89 ZulipConfig instance 90 """ 91 if zuliprc_path is None: 92 zuliprc_path = Path.home() / ".zuliprc" 93 else: 94 zuliprc_path = Path(zuliprc_path) 95 96 if not zuliprc_path.exists(): 97 raise FileNotFoundError(f"Zuliprc file not found: {zuliprc_path}") 98 99 config = {} 100 with open(zuliprc_path, 'r') as f: 101 for line in f: 102 line = line.strip() 103 if line and not line.startswith('#') and '=' in line: 104 key, value = line.split('=', 1) 105 config[key.strip()] = value.strip() 106 107 # Map zuliprc keys to our config 108 zulip_config = ZulipConfig( 109 site=config.get('site', ''), 110 email=config.get('email', ''), 111 api_key=config.get('key', ''), 112 stream=config.get('stream', 'netdata-alerts'), 113 ) 114 115 logger.info( 116 "Loaded Zulip configuration from zuliprc", 117 path=str(zuliprc_path), 118 site=zulip_config.site, 119 email=zulip_config.email, 120 stream=zulip_config.stream 121 ) 122 123 return zulip_config