from typing import Dict, List import requests from config import AppConfig from .base import BaseCollector, CollectionResult class NginxFromAgentCollector(BaseCollector): source_name = "nginx" def __init__(self, config: AppConfig): self.config = config def collect(self) -> CollectionResult: if not self.config.docker_agent_endpoints: return CollectionResult(source=self.source_name, assets=[], status="skipped", error="No DOCKER_AGENT_ENDPOINTS configured") headers = {"Accept": "application/json"} if self.config.docker_agent_token: headers["Authorization"] = f"Bearer {self.config.docker_agent_token}" assets: List[Dict] = [] errors: List[str] = [] for endpoint in self.config.docker_agent_endpoints: base = endpoint.rstrip("/") try: resp = requests.get( f"{base}/api/v1/nginx-configs", headers=headers, timeout=self.config.request_timeout_seconds, ) resp.raise_for_status() payload = resp.json() configs = payload.get("configs", []) if isinstance(payload, dict) else [] for config in configs: path = config.get("path", "unknown.conf") server_names = config.get("server_names", []) or [] listens = config.get("listens", []) or [] proxy_pass = config.get("proxy_pass", []) or [] proxy_pass_resolved = config.get("proxy_pass_resolved", []) or [] upstreams = config.get("upstreams", []) or [] upstream_servers = config.get("upstream_servers", []) or [] inferred_targets = config.get("inferred_targets", []) or [] if not server_names: server_names = [path] for server_name in server_names: assets.append( { "asset_type": "nginx_site", "external_id": f"{endpoint}:{path}:{server_name}", "name": server_name, "hostname": server_name, "status": "configured", "ip_addresses": [], "node": endpoint, "metadata": { "path": path, "listens": listens, "proxy_pass": proxy_pass, "proxy_pass_resolved": proxy_pass_resolved, "upstreams": upstreams, "upstream_servers": upstream_servers, "inferred_targets": inferred_targets, "collected_via": "docker-agent-nginx", }, } ) except Exception as exc: errors.append(f"{endpoint}: {exc}") if errors and not assets: return CollectionResult(source=self.source_name, assets=[], status="error", error=" | ".join(errors)) if errors: return CollectionResult(source=self.source_name, assets=assets, status="degraded", error=" | ".join(errors)) return CollectionResult(source=self.source_name, assets=assets, status="ok")