Source code for credproxy.runner
# SPDX-License-Identifier: MPL-2.0
# Copyright 2025-present John Mille <john@ews-network.net>
"""Application runtime logic for CredProxy."""
from __future__ import annotations
import sys
import signal
import logging as logthings
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import argparse
from flask import Flask
from credproxy.app import init_app
from credproxy.config import Config
from credproxy.logger import LOG
# Global flag for graceful shutdown
shutdown_requested = False
[docs]
def setup_signal_handlers() -> None:
"""Setup signal handlers for graceful shutdown."""
def signal_handler(signum, frame):
"""Handle shutdown signals gracefully."""
global shutdown_requested
if not shutdown_requested:
shutdown_requested = True
LOG.info("Received signal %d, initiating graceful shutdown...", signum)
# Get access to the Flask app for cleanup
try:
from flask import current_app
if current_app and hasattr(current_app, "config"):
credentials_handler = current_app.config.get("credentials_handler")
if credentials_handler and hasattr(credentials_handler, "cleanup"):
credentials_handler.cleanup()
file_watcher = current_app.config.get("file_watcher")
if file_watcher and hasattr(file_watcher, "stop"):
file_watcher.stop()
except Exception:
# Ignore cleanup errors during shutdown
pass
LOG.info("Graceful shutdown completed")
sys.exit(0)
# Register signal handlers
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
[docs]
def validate_config_file(config_path: str) -> bool:
"""Validate configuration file."""
try:
_ = Config.from_file(config_path)
LOG.info("Configuration is valid")
return True
except Exception as error:
LOG.error("Configuration validation failed: %s", str(error))
return False
[docs]
def setup_cli_logging(log_level: str) -> None:
"""Setup logging level from CLI arguments."""
level = getattr(logthings, log_level.upper())
# Update the global logger level
LOG.setLevel(level)
# Also update all handlers
for handler in LOG.handlers:
handler.setLevel(level)
[docs]
def run_server(args: argparse.Namespace) -> int:
"""Run the CredProxy server with the given arguments."""
try:
# Setup signal handlers for graceful shutdown
setup_signal_handlers()
# Run the main application
LOG.info("CredProxy CLI")
LOG.info("Configuration file: %s", args.config)
LOG.info("Running CredProxy with Flask server")
# Load config and create Flask app
config = Config.from_file(args.config)
app: Flask = init_app(config)
# Override debug mode if --dev flag is set
debug_mode = config.server.debug or args.dev
# Start metrics server on separate port if enabled
if config.metrics.prometheus.enabled:
from prometheus_client import start_http_server
from credproxy.metrics import REGISTRY as CREDPROXY_REGISTRY
try:
start_http_server(
port=config.metrics.prometheus.port,
addr=config.metrics.prometheus.host,
registry=CREDPROXY_REGISTRY,
)
LOG.info(
"Prometheus metrics server started on %s:%d using CredProxy "
"registry",
config.metrics.prometheus.host,
config.metrics.prometheus.port,
)
except Exception as error:
LOG.error("Failed to start metrics server: %s", error)
app.run(host=config.server.host, port=config.server.port, debug=debug_mode)
except KeyboardInterrupt:
LOG.info("Shutting down gracefully...")
return 0
except Exception as error:
LOG.error("Fatal error: %s", str(error))
return 1
return 0