Архитектура

Документ описывает устройство фреймворка и решения, на которых держится его надёжность. Практический API — в DEVELOPER.md, поведение конкретных модулей — в MODULES.md.

Общая схема

                ┌────────────────────────────────────────────┐
                │              install.sh (оркестратор)      │
                │  preflight → выбор → опрос → план → цикл    │
                └───────┬──────────────┬──────────────┬──────┘
                        │              │              │
                 lib/preflight.sh   lib/tui.sh     lib/core.sh + lib/os.sh
                 (проверки среды)   (виджеты UI)   (логи/состояние/откат +
                                                    семейство ОС: пакеты,
                                                    файрвол, PG-раскладка)
                        │                             │
                        └──────────┬──────────────────┘
                                   ▼
                        modules/NN-*.sh  (subshell, set -Eeuo pipefail)
                                   │
                 /var/lib/mitdev/{state/, rollback.stack, install.env}
                                   ▲
                                   │
                ┌──────────────────┴─────────────────────────┐
                │        mitdev (администрирование)        │
                │ info doctor pg k8s app deploy backup …      │
                └────────────────────────────────────────────┘

Два исполняемых файла делят общий фундамент (config/ + lib/) и общаются между собой только через файлы состояния — никаких скрытых зависимостей.

Поток установки

   Оператор
      │  sudo ./install.sh
      ▼
 ┌──────────┐   среда ОК?    ┌─────────────┐   ответы     ┌──────────┐
 │ preflight │ ────────────▶ │ выбор + опрос │ ──────────▶ │   план   │
 └──────────┘   (иначе стоп)  └─────────────┘  (дефолты из  └────┬─────┘
                                                install.env)      │ подтверждение
                                                                  ▼
                                          ┌───────────────────────────────┐
                                          │  цикл по модулям (по порядку)  │
                                          │  ┌──────────────────────────┐  │
                                          │  │ module_install (subshell)│  │
                                          │  └────────────┬─────────────┘  │
                                          │        успех  │  сбой          │
                                          │           ┌───┴────┐           │
                                          │           ▼        ▼           │
                                          │     state_mark   rollback_to    │
                                          │                  (защищённый    │
                                          │                   при переуст.) │
                                          └───────────────┬────────────────┘
                                                          ▼
                                        install_admin_tool → apply (listen/HA)
                                                          ▼
                                                     mitdev doctor

Жизненный цикл модуля

                       ┌─────────────────┐
                       │   module_install │◀── run_step + rollback_register
                       └───────┬─────────┘     (каждое действие идемпотентно,
                        успех  │  сбой          мутация → регистрируется откат)
                    ┌──────────┴──────────┐
                    ▼                     ▼
             state_mark             module_rollback / rollback_to
             (маркер .done)         (обратные действия в обратном порядке;
                    │                деструктив пропускается при переустановке)
                    ▼
             module_verify  ── быстрая проверка «компонент работает»
                    │
                    ▼
             module_remove  ── удаление (mitdev remove <модуль> [--purge])

Топология: приватная сеть + HA (несколько серверов)

   Публичные IP (пользователи)                Приватная сеть WireGuard (mesh)
   185.x  91.x  46.x                          10.100.0.0/24 — каждый с каждым
      │     │     │                             app-1 ─── app-2
      ▼     ▼     ▼                               │  ╲   ╱  │
   ┌─────┐┌─────┐┌─────┐                          │   ╳    │   (нет центрального
   │app-1││app-2││db-1 │   ← nginx/приложения      │  ╱   ╲ │    хаба — SPOF нет)
   └─────┘└─────┘└─────┘                          db-1 ─────┘
      локальный IP                             10.100.0.1/.2/.3 + <узел>.internal

   Поверх приватной сети — HA-кластеры (cluster-init → token → join):
     PostgreSQL (репликация + watchdog)   Redis (Sentinel)   MongoDB (replica set)
     RabbitMQ (кластер)                   MinIO (distributed)
     Службы слушают только приватную сеть: mitdev net bind <служба> private

Ключевые решения

1. Оркестратор ничего не знает о содержимом модулей

install.sh оперирует реестром MODULE_REGISTRY (id|файл|обязательность|дефолт|метка) и единым контрактом модуля. Добавление компонента = новый файл + одна строка в реестре. Порядок выполнения задаётся номерами файлов, зависимость между модулями объявляется декларативно (add_dependency), а не зашивается в код модулей.

2. Модуль выполняется в строгом subshell

( set -Eeuo pipefail
  source config/defaults.conf
  source lib/core.sh
  source modules/NN-x.sh
  module_install
) &

Следствия:

3. Всё состояние — файлы, а не переменные

Subshell не может передать данные наверх через переменные, поэтому:

Бонус: состояние переживает обрыв SSH-сессии посреди установки.

4. Откат — по границе модуля, а не «всё или ничего»

Полный откат сервера после 40 минут установки почти никогда не нужен; нужен откат именно сломавшегося шага. Поэтому стек отката срезается до отметки, взятой перед запуском модуля, и пользователь сам решает: откатить модуль, продолжить остальные или прервать установку.

Отдельно каждый модуль обязан реализовать module_rollback() — полный детерминированный откат для ручного использования и тестов.

5. Весь пользовательский ввод — до начала установки

Модули никогда не разговаривают с пользователем: имя/пароль/ключ/порт/роль кластера собираются заранее и передаются через окружение. Это делает модули пригодными для неинтерактивного использования и упрощает их тестирование.

6. TUI — деградирующий слой, а не зависимость

lib/tui.sh выбирает лучший доступный бэкенд: gum (ставится автоматически из репозитория Charm или GitHub-релиза) → whiptail → dialog → plain-подсказки. Каждый виджет (tui_confirm, tui_input, tui_password, tui_choose, tui_checklist, tui_run, tui_progress, экраны успеха/ошибки) реализован для всех бэкендов. Отказ установки gum ничего не ломает.

7. Безопасность важнее удобства в трёх местах

Подробнее — SECURITY.md.

8. Логи — источник истины

Красивый интерфейс показывает статусы; полная правда — в /var/log/mitdev/install.log (каждый шаг с полным выводом команд) и errors.log (только сбои). run_step гарантирует, что ни одна команда не выполнится без записи в журнал и проверки кода возврата.

Поток данных установки

defaults.conf ──► env ──► install.sh ──┬─► TUI-опрос ──► env (DEPLOY_*, SSH_*, PG_*)
                                       │
                                       └─► цикл модулей:
                                             отметка стека отката
                                             subshell(module_install)
                                             ├─ ok  → state/<id>.done
                                             └─ fail→ rollback_to(отметка)? → продолжить/прервать
                                       ┌─► /opt/mitdev + симлинк
                                       └─► install.env, итоговая сводка

Инструмент mitdev

Тонкий слой над теми же библиотеками: каждая команда — функция cmd_*, диспетчеризация — один case. Команды только читают состояние (install.env, маркеры) и системные API (systemd, psql, kubectl, docker) — у инструмента нет собственной базы данных, поэтому он не может рассинхронизироваться с реальностью. Read-only команды работают без root (логирование при этом уводится в /dev/null).

Тестовая стратегия