#!/usr/bin/env python3 """Состояние docker-приложений: app_status.py []. Сервисы берутся из compose-файла и сверяются с docker compose ps -a: описан, но контейнера нет - state missing. Контейнеры, которых нет в compose-файле (остались от прошлой версии), тоже попадают в список. Печатает JSON: {"apps": {app: {"services": {service: {"state", "health", "status"}}, "error": ""}}}. """ import json import os import subprocess import sys COMPOSE_FILE = 'docker-compose.yaml' def compose(app_dir, *args): result = subprocess.run( ['docker', 'compose', *args], cwd=app_dir, capture_output=True, text=True, timeout=120, ) if result.returncode != 0: raise RuntimeError((result.stderr or result.stdout).strip()[-500:]) return result.stdout def containers(output): """ps --format json: старый compose печатает массив, новый - объект на строку.""" output = output.strip() if not output: return [] if output.startswith('['): return json.loads(output) return [json.loads(line) for line in output.splitlines() if line.strip()] def app_status(app_dir): services = { name: {'state': 'missing', 'health': '', 'status': ''} for name in compose(app_dir, 'config', '--services').split() } for container in containers(compose(app_dir, 'ps', '-a', '--format', 'json')): state = { 'state': container.get('State', ''), 'health': container.get('Health', ''), 'status': container.get('Status', ''), } name = container.get('Service', '') # несколько реплик: показываем проблемную if services.get(name, {}).get('state') in (None, 'missing', 'running'): services[name] = state return services def main(apps_dir, only=''): apps = {} names = [only] if only else sorted(os.listdir(apps_dir)) if os.path.isdir(apps_dir) else [] for name in names: app_dir = os.path.join(apps_dir, name) if name.startswith('.') or not os.path.isfile(os.path.join(app_dir, COMPOSE_FILE)): continue try: apps[name] = {'services': app_status(app_dir), 'error': ''} except (RuntimeError, ValueError, subprocess.TimeoutExpired) as error: apps[name] = {'services': {}, 'error': str(error)} print(json.dumps({'apps': apps})) if __name__ == '__main__': main(*sys.argv[1:])