Files
mypointvpn/src/serverus/playbooks/files/app_status.py
T
protokeyandClaude Opus 5.5 a8d1f286d1 feat: docker-серверы - приложения в ~/apps под управлением бота
serverus: DockerHost - установка Docker и ingress Caddy, deploy приложения
с проверкой compose и caddy.conf до изменений и пересозданием только
изменившихся сервисов, status, control, logs, remove. nftables
перезапускается только при изменении конфига, Docker следом
восстанавливает свои правила; release восстанавливает конфиг Debian
вместо остановки nftables.

Telegram: прием документов и отправка файлов, раздача файлов своего
Bot API сервера через nginx (telegram-files).

App: общая модель Managed для Point и Platform, DockerApp и версии
файлов AppVersion, разбор архивов (uploads), сценарии платформы
(platforms), задачи и меню docker-серверов в боте.

Проверено на стенде Debian 13 с local_certs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:27:05 +04:00

69 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Состояние docker-приложений: app_status.py <apps_dir> [<app>].
Сервисы берутся из 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:])