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