92 lines
2.4 KiB
Python
92 lines
2.4 KiB
Python
"""
|
|
PyServe Daemon Process
|
|
|
|
Runs pyserve services in background mode.
|
|
"""
|
|
|
|
import argparse
|
|
import asyncio
|
|
import logging
|
|
import os
|
|
import signal
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="PyServe Daemon")
|
|
parser.add_argument("--config", required=True, help="Configuration file path")
|
|
parser.add_argument("--state-dir", required=True, help="State directory path")
|
|
parser.add_argument("--services", default=None, help="Comma-separated list of services")
|
|
parser.add_argument("--scale", action="append", default=[], help="Scale overrides (name=workers)")
|
|
parser.add_argument("--force-recreate", action="store_true", help="Force recreate services")
|
|
|
|
args = parser.parse_args()
|
|
|
|
config_path = Path(args.config)
|
|
state_dir = Path(args.state_dir)
|
|
|
|
services = args.services.split(",") if args.services else None
|
|
|
|
scale_map = {}
|
|
for scale in args.scale:
|
|
name, workers = scale.split("=")
|
|
scale_map[name] = int(workers)
|
|
|
|
from ..config import Config
|
|
|
|
config = Config.from_yaml(str(config_path))
|
|
|
|
from .state import StateManager
|
|
|
|
state_manager = StateManager(state_dir)
|
|
|
|
log_file = state_dir / "logs" / "daemon.log"
|
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
handlers=[
|
|
logging.FileHandler(log_file),
|
|
],
|
|
)
|
|
|
|
logger = logging.getLogger("pyserve.daemon")
|
|
|
|
pid_file = state_dir / "pyserve.pid"
|
|
pid_file.write_text(str(os.getpid()))
|
|
|
|
logger.info(f"Starting daemon with PID {os.getpid()}")
|
|
|
|
from ._runner import ServiceRunner
|
|
|
|
runner = ServiceRunner(config, state_manager)
|
|
|
|
def signal_handler(signum, frame):
|
|
logger.info(f"Received signal {signum}, shutting down...")
|
|
runner.stop()
|
|
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
try:
|
|
asyncio.run(
|
|
runner.start(
|
|
services=services,
|
|
scale_map=scale_map,
|
|
force_recreate=args.force_recreate,
|
|
)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Daemon error: {e}")
|
|
sys.exit(1)
|
|
finally:
|
|
if pid_file.exists():
|
|
pid_file.unlink()
|
|
logger.info("Daemon stopped")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|