Files
DevOpsLab/docs/WEB_INTERFACE_DETAILS.md
Сергей Антропов 1fbf9185a2 feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
2026-02-15 22:59:02 +03:00

46 KiB
Raw Blame History

Детали работы веб-интерфейса: тестирование и inventory

Автор: Сергей Антропов
Сайт: https://devops.org.ru

🎯 Обзор

Этот документ описывает, как пользователь будет работать с тестированием ролей, выбором Docker образов, настройкой inventory и Molecule через веб-интерфейс.


1. 🐳 Выбор Docker образов и количества контейнеров

1.1. Страница создания/редактирования preset'а

URL: /presets/create или /presets/{preset_name}/edit

Интерфейс:

┌─────────────────────────────────────────────────────────┐
│  Создание Preset'а: "my-custom-test"                   │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📋 Доступные образы:                                  │
│  ┌─────────────────────────────────────────────────┐   │
│  │ ☑ Ubuntu 22.04    [inecs/ansible-lab:ubuntu22] │   │
│  │ ☐ Ubuntu 20.04    [inecs/ansible-lab:ubuntu20] │   │
│  │ ☑ Debian 12       [inecs/ansible-lab:debian12] │   │
│  │ ☐ Debian 11       [inecs/ansible-lab:debian11] │   │
│  │ ☑ CentOS 9        [inecs/ansible-lab:centos9]  │   │
│  │ ☐ CentOS 8        [inecs/ansible-lab:centos8]  │   │
│  │ ☐ RHEL 8          [inecs/ansible-lab:rhel]      │   │
│  │ ☐ AlmaLinux 8     [inecs/ansible-lab:alma]      │   │
│  │ ☐ Rocky Linux 8   [inecs/ansible-lab:rocky]    │   │
│  │ ☐ ALT Linux 9     [inecs/ansible-lab:alt9]     │   │
│  │ ☐ Astra Linux     [inecs/ansible-lab:astra]    │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│   Добавить кастомный образ:                          │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Registry: [docker.io]                           │   │
│  │ Image:    [my-custom/image:tag]                  │   │
│  │ [Добавить]                                      │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  🖥️  Настройка хостов:                                 │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Хост 1                                          │   │
│  │ ├─ Имя: [web1]                                 │   │
│  │ ├─ Образ: [Ubuntu 22.04 ▼]                     │   │
│  │ ├─ Группы: [web, frontend] (можно добавить)    │   │
│  │ └─ [🗑️ Удалить]                                │   │
│  ├─────────────────────────────────────────────────┤   │
│  │ Хост 2                                          │   │
│  │ ├─ Имя: [web2]                                 │   │
│  │ ├─ Образ: [Debian 12 ▼]                        │   │
│  │ ├─ Группы: [web, backend]                      │   │
│  │ └─ [🗑️ Удалить]                                │   │
│  ├─────────────────────────────────────────────────┤   │
│  │ Хост 3                                          │   │
│  │ ├─ Имя: [db1]                                  │   │
│  │ ├─ Образ: [CentOS 9 ▼]                         │   │
│  │ ├─ Группы: [database, backend]                 │   │
│  │ └─ [🗑️ Удалить]                                │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [ Добавить хост]                                    │
│                                                         │
│  📊 Предпросмотр:                                      │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Всего хостов: 3                                │   │
│  │ Ubuntu 22.04: 1                                │   │
│  │ Debian 12: 1                                   │   │
│  │ CentOS 9: 1                                    │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [💾 Сохранить preset]  [👁️ Предпросмотр YAML]        │
└─────────────────────────────────────────────────────────┘

1.2. Быстрое создание preset'а при тестировании

URL: /roles/{role_name}/test

Интерфейс:

┌─────────────────────────────────────────────────────────┐
│  Тестирование роли: nginx                                │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📋 Выбор preset'а:                                      │
│  ┌─────────────────────────────────────────────────┐   │
│  │ ○ Использовать существующий preset              │   │
│  │   [default ▼] [minimal] [all-images]          │   │
│  │                                                 │   │
│  │ ● Создать временный preset для этого теста     │   │
│  │   ┌─────────────────────────────────────────┐  │   │
│  │   │ Быстрый выбор:                          │  │   │
│  │   │ ☑ Ubuntu 22.04  [Количество: 2]         │  │   │
│  │   │ ☑ Debian 12      [Количество: 2]         │  │   │
│  │   │ ☐ CentOS 9       [Количество: 0]         │  │   │
│  │   │ ☐ RHEL 8         [Количество: 0]         │  │   │
│  │   │                                             │  │   │
│  │   │ Итого: 4 хоста                             │  │   │
│  │   └─────────────────────────────────────────┘  │   │
│  │                                                 │   │
│  │   Или детальная настройка:                      │   │
│  │   [Открыть редактор preset'а]                  │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  ⚙️  Параметры теста:                                   │
│  ┌─────────────────────────────────────────────────┐   │
│  │ ☑ Параллельное выполнение [Количество: 2]       │   │
│  │ ☑ Проверка синтаксиса (lint)                    │   │
│  │ ☑ Проверка идемпотентности                      │   │
│  │ ☐ Verbose режим                                 │   │
│  │ ☐ Dry-run (только проверка)                     │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [🚀 Запустить тест]                                   │
└─────────────────────────────────────────────────────────┘

1.3. Визуальный редактор хостов (Drag & Drop)

Интерфейс с возможностью перетаскивания:

┌─────────────────────────────────────────────────────────┐
│  Редактор хостов                                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📦 Доступные образы (перетащите для добавления):     │
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐                   │
│  │Ubuntu│ │Debian│ │CentOS│ │RHEL  │                   │
│  │ 22.04│ │  12  │ │  9   │ │  8   │                   │
│  └──────┘ └──────┘ └──────┘ └──────┘                   │
│                                                         │
│  🖥️  Хосты (перетащите для изменения порядка):        │
│  ┌─────────────────────────────────────────────────┐   │
│  │ ═══ web1 (Ubuntu 22.04) [web, frontend] [✏️][🗑️]│   │
│  ├─────────────────────────────────────────────────┤   │
│  │ ═══ web2 (Debian 12) [web, backend] [✏️][🗑️]   │   │
│  ├─────────────────────────────────────────────────┤   │
│  │ ═══ db1 (CentOS 9) [database] [✏️][🗑️]        │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [ Добавить хост]  [🔄 Сбросить]  [💾 Сохранить]     │
└─────────────────────────────────────────────────────────┘

HTMX реализация:

<!-- Drag & Drop через HTMX и Alpine.js -->
<div 
  x-data="{ dragged: null }"
  @dragstart="dragged = $event.target.dataset.image"
  @drop.prevent="addHost(dragged)"
>
  <!-- Образы для перетаскивания -->
  <div 
    draggable="true"
    data-image="ubuntu22"
    class="draggable-image"
  >
    Ubuntu 22.04
  </div>
  
  <!-- Список хостов -->
  <div 
    @drop="addHostFromDrag($event)"
    class="hosts-list"
  >
    <!-- Хосты -->
  </div>
</div>

2. 📝 Заполнение Inventory

2.1. Визуальный редактор Inventory

URL: /inventory/edit или /presets/{preset_name}/inventory

Интерфейс:

┌─────────────────────────────────────────────────────────┐
│  Редактор Inventory                                     │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📋 Группы хостов:                                      │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ [web_servers]                                    │   │
│  │ ├─ web1                                         │   │
│  │ │  ├─ ansible_host: [172.17.0.2]               │   │
│  │ │  ├─ ansible_user: [ansible]                  │   │
│  │ │  ├─ ansible_ssh_private_key_file: [~/.ssh/id_rsa]│
│  │ │  └─ [ Добавить переменную]                  │   │
│  │ │                                                 │   │
│  │ └─ web2                                         │   │
│  │    ├─ ansible_host: [172.17.0.3]               │   │
│  │    └─ ...                                       │   │
│  │                                                 │   │
│  │ [ Добавить хост в группу]                     │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ [database_servers]                               │   │
│  │ └─ db1                                          │   │
│  │    └─ ...                                       │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ [all:vars] (глобальные переменные)              │   │
│  │ ├─ ansible_user: [ansible]                      │   │
│  │ ├─ ansible_ssh_private_key_file: [~/.ssh/id_rsa]│
│  │ ├─ ansible_python_interpreter: [/usr/bin/python3]│
│  │ └─ [ Добавить переменную]                      │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  📄 Предпросмотр INI формата:                          │
│  ┌─────────────────────────────────────────────────┐   │
│  │ [web_servers]                                    │   │
│  │ web1 ansible_host=172.17.0.2                    │   │
│  │ web2 ansible_host=172.17.0.3                    │   │
│  │                                                  │   │
│  │ [database_servers]                               │   │
│  │ db1 ansible_host=172.17.0.4                     │   │
│  │                                                  │   │
│  │ [all:vars]                                       │   │
│  │ ansible_user=ansible                             │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [💾 Сохранить]  [📥 Экспорт в файл]  [📤 Импорт]     │
└─────────────────────────────────────────────────────────┘

2.2. Автоматическая генерация Inventory из preset'а

При создании preset'а автоматически генерируется inventory:

# app/services/inventory_service.py
class InventoryService:
    def generate_from_preset(self, preset: dict) -> str:
        """Генерация inventory из preset'а"""
        inventory = []
        
        # Группировка хостов по группам
        groups = {}
        for host in preset['hosts']:
            for group in host.get('groups', []):
                if group not in groups:
                    groups[group] = []
                groups[group].append(host)
        
        # Формирование INI
        for group_name, hosts in groups.items():
            inventory.append(f"[{group_name}]")
            for host in hosts:
                # Получение IP из Docker контейнера
                ip = self.get_container_ip(host['name'])
                inventory.append(f"{host['name']} ansible_host={ip}")
            inventory.append("")
        
        # Глобальные переменные
        inventory.append("[all:vars]")
        inventory.append("ansible_user=ansible")
        inventory.append("ansible_ssh_private_key_file=~/.ssh/id_rsa")
        
        return "\n".join(inventory)

2.3. Редактирование Inventory для реальных серверов

URL: /inventory/production/edit

Интерфейс для продакшн inventory:

┌─────────────────────────────────────────────────────────┐
│  Production Inventory                                    │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ⚠️  Это inventory для реальных серверов!              │
│                                                         │
│  📋 Группы:                                            │
│  ┌─────────────────────────────────────────────────┐   │
│  │ [web_servers]                                    │   │
│  │ ┌─────────────────────────────────────────────┐ │   │
│  │ │ Хост: web1.example.com                     │ │   │
│  │ │ IP: [192.168.1.10]                         │ │   │
│  │ │ Пользователь: [deploy]                      │ │   │
│  │ │ SSH ключ: [~/.ssh/deploy_key]                │ │   │
│  │ │ Порт: [22]                                  │ │   │
│  │ │ Python: [/usr/bin/python3]                  │ │   │
│  │ │ [🔐 Тест подключения]                       │ │   │
│  │ └─────────────────────────────────────────────┘ │   │
│  │ [ Добавить хост]                               │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  🔐 Безопасность:                                       │
│  ☑ Использовать Vault для секретов                     │
│  ☑ Шифровать SSH ключи                                  │
│                                                         │
│  [💾 Сохранить]  [🔒 Зашифровать в Vault]             │
└─────────────────────────────────────────────────────────┘

3. 🧪 Тестирование в Molecule

3.1. Страница тестирования роли

URL: /roles/{role_name}/test

Полный интерфейс:

┌─────────────────────────────────────────────────────────┐
│  Тестирование роли: nginx                                │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📋 Шаг 1: Выбор окружения                              │
│  ┌─────────────────────────────────────────────────┐   │
│  │ ○ Использовать preset                           │   │
│  │   [default ▼] [minimal] [all-images]            │   │
│  │                                                 │   │
│  │ ● Создать кастомное окружение                   │   │
│  │   ┌─────────────────────────────────────────┐   │   │
│  │   │ Образы и хосты:                        │   │   │
│  │   │ [См. раздел 1.1]                       │   │   │
│  │   └─────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  📝 Шаг 2: Настройка переменных роли                   │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Переменные из defaults/main.yml:                │   │
│  │                                                 │   │
│  │ nginx_version: [1.25.0]                         │   │
│  │ nginx_worker_processes: [auto]                  │   │
│  │ nginx_worker_connections: [1024]                │   │
│  │ nginx_enabled: ☑                                │   │
│  │ nginx_sites:                                    │   │
│  │   ┌─────────────────────────────────────┐     │   │
│  │   │ [ Добавить сайт]                   │     │   │
│  │   │ - name: example.com                  │     │   │
│  │   │   root: /var/www/html                │     │   │
│  │   └─────────────────────────────────────┘     │   │
│  │                                                 │   │
│  │ [📥 Загрузить из файла]                        │   │
│  │ [💾 Сохранить как шаблон]                      │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  ⚙️  Шаг 3: Параметры Molecule                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Molecule сценарий: [default]                    │   │
│  │                                                 │   │
│  │ Этапы тестирования:                            │   │
│  │ ☑ create (создание контейнеров)                │   │
│  │ ☑ prepare (подготовка)                        │   │
│  │ ☑ converge (применение роли)                   │   │
│  │ ☑ idempotence (проверка идемпотентности)       │   │
│  │ ☑ verify (проверка через testinfra)            │   │
│  │ ☑ destroy (удаление контейнеров)                │   │
│  │                                                 │   │
│  │ Дополнительные опции:                          │   │
│  │ ☑ --destroy=never (не удалять после теста)     │   │
│  │ ☐ --scenario-name=custom (кастомный сценарий)  │   │
│  │ ☐ --driver-name=docker (драйвер)               │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  🔍 Шаг 4: Настройка проверок (verify)                  │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Использовать testinfra: ☑                      │   │
│  │                                                 │   │
│  │ Тесты для проверки:                            │   │
│  │ ☑ nginx установлен                             │   │
│  │ ☑ nginx запущен                                 │   │
│  │ ☑ nginx слушает на порту 80                    │   │
│  │ ☑ конфигурация валидна                         │   │
│  │                                                 │   │
│  │ [ Добавить кастомный тест]                    │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [🚀 Запустить тест]  [💾 Сохранить конфигурацию]     │
└─────────────────────────────────────────────────────────┘

3.2. Live мониторинг теста

После запуска теста открывается страница с live логами:

┌─────────────────────────────────────────────────────────┐
│  Тест роли: nginx | Статус: 🟢 Running                  │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📊 Прогресс:                                          │
│  ┌─────────────────────────────────────────────────┐   │
│  │ ████████████░░░░░░░░ 60%                        │   │
│  │ Этап: converge (применение роли)                 │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  📋 Этапы:                                              │
│  ✅ create    - Создание контейнеров (5s)             │
│  ✅ prepare   - Подготовка (2s)                        │
│  🟢 converge  - Применение роли (в процессе...)       │
│  ⏳ idempotence - Ожидание...                          │
│  ⏳ verify    - Ожидание...                            │
│  ⏳ destroy   - Ожидание...                            │
│                                                         │
│  📝 Логи (live):                                       │
│  ┌─────────────────────────────────────────────────┐   │
│  │ [2024-01-15 10:30:15] TASK [nginx : Install]    │   │
│  │ [2024-01-15 10:30:16] changed: [web1]          │   │
│  │ [2024-01-15 10:30:17] TASK [nginx : Configure] │   │
│  │ [2024-01-15 10:30:18] changed: [web1]          │   │
│  │ ...                                             │   │
│  │                                                  │   │
│  │ [Автоскролл: ☑] [Очистить] [📥 Скачать]        │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  🖥️  Статус контейнеров:                                │
│  ┌─────────────────────────────────────────────────┐   │
│  │ web1 (Ubuntu 22.04)  🟢 Running               │   │
│  │ web2 (Debian 12)      🟢 Running               │   │
│  │ db1 (CentOS 9)        🟢 Running               │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [⏸️ Пауза]  [⏹️ Остановить]  [🔄 Перезапустить]      │
└─────────────────────────────────────────────────────────┘

WebSocket реализация:

# app/api/v1/endpoints/websocket.py
@router.websocket("/ws/test/{test_id}/logs")
async def test_logs_websocket(websocket: WebSocket, test_id: str):
    await websocket.accept()
    
    # Запуск molecule test с потоковым выводом
    process = await asyncio.create_subprocess_exec(
        "molecule", "test", "-s", "default",
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.STDOUT,
        cwd=f"roles/{test_id}"
    )
    
    # Отправка логов в реальном времени
    async for line in process.stdout:
        await websocket.send_json({
            "type": "log",
            "stage": detect_stage(line),  # create, converge, etc.
            "data": line.decode(),
            "timestamp": datetime.now().isoformat()
        })
    
    # Результат
    await websocket.send_json({
        "type": "complete",
        "status": "success" if process.returncode == 0 else "failed"
    })

3.3. Редактирование molecule.yml через интерфейс

URL: /roles/{role_name}/molecule/edit

Интерфейс:

┌─────────────────────────────────────────────────────────┐
│  Редактор molecule.yml для роли: nginx                  │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📋 Вкладки:                                            │
│  [Driver] [Platforms] [Provisioner] [Verifier]        │
│                                                         │
│  🚗 Driver (Docker):                                    │
│  ┌─────────────────────────────────────────────────┐   │
│  │ driver:                                         │   │
│  │   name: [docker ▼]                              │   │
│  │   options:                                      │   │
│  │     privileged: ☑                               │   │
│  │     volumes:                                    │   │
│  │       - /sys/fs/cgroup:/sys/fs/cgroup:ro       │   │
│  │     capabilities:                               │   │
│  │       - SYS_ADMIN                               │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  🖥️  Platforms (контейнеры):                           │
│  ┌─────────────────────────────────────────────────┐   │
│  │ [ Добавить платформу]                         │   │
│  │                                                 │   │
│  │ Платформа 1:                                    │   │
│  │ ├─ name: [web1]                                 │   │
│  │ ├─ image: [inecs/ansible-lab:ubuntu22 ▼]       │   │
│  │ ├─ pre_build_image: ☑                           │   │
│  │ └─ [🗑️ Удалить]                                │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  📝 Предпросмотр YAML:                                 │
│  ┌─────────────────────────────────────────────────┐   │
│  │ driver:                                         │   │
│  │   name: docker                                  │   │
│  │ platforms:                                      │   │
│  │   - name: web1                                  │   │
│  │     image: inecs/ansible-lab:ubuntu22          │   │
│  │     pre_build_image: true                       │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [💾 Сохранить]  [✅ Валидация]  [🔄 Сбросить]        │
└─────────────────────────────────────────────────────────┘

4. 🔄 Интеграция с существующими preset'ами

4.1. Использование preset'ов из molecule/presets/

Автоматическое обнаружение preset'ов:

# app/services/preset_service.py
class PresetService:
    def list_presets(self) -> List[dict]:
        """Список всех preset'ов из molecule/presets/"""
        presets = []
        preset_dir = Path("molecule/presets")
        
        for preset_file in preset_dir.glob("*.yml"):
            with open(preset_file) as f:
                preset_data = yaml.safe_load(f)
                presets.append({
                    "name": preset_file.stem,
                    "description": preset_data.get("description", ""),
                    "hosts_count": len(preset_data.get("hosts", [])),
                    "images": self.extract_images(preset_data),
                    "file": str(preset_file)
                })
        
        return presets
    
    def load_preset(self, name: str) -> dict:
        """Загрузка preset'а"""
        preset_file = Path(f"molecule/presets/{name}.yml")
        with open(preset_file) as f:
            return yaml.safe_load(f)

4.2. Редактирование существующих preset'ов

Интерфейс редактирования:

┌─────────────────────────────────────────────────────────┐
│  Редактирование preset'а: default                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📋 Информация:                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Описание: [Стандартный preset для тестирования] │   │
│  │ Файл: molecule/presets/default.yml              │   │
│  │ Хостов: 2                                       │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  [См. интерфейс из раздела 1.1]                         │
│                                                         │
│  ⚠️  Внимание: Изменения сохраняются в файл!           │
│                                                         │
│  [💾 Сохранить]  [📋 Создать копию]  [🗑️ Удалить]    │
└─────────────────────────────────────────────────────────┘

5. 🎯 Примеры использования

5.1. Быстрый тест на одном образе

Сценарий: Пользователь хочет быстро протестировать роль на Ubuntu 22.04

  1. Переходит на /roles/nginx/test
  2. Выбирает "Создать временный preset"
  3. Ставит галочку только на "Ubuntu 22.04", количество: 1
  4. Нажимает "Запустить тест"
  5. Смотрит live логи

5.2. Тест на нескольких ОС

Сценарий: Пользователь хочет протестировать на Ubuntu, Debian и CentOS

  1. Переходит на /roles/nginx/test
  2. Выбирает "Создать временный preset"
  3. Ставит галочки:
    • Ubuntu 22.04 (2 хоста)
    • Debian 12 (2 хоста)
    • CentOS 9 (1 хост)
  4. Настраивает переменные роли
  5. Запускает тест
  6. Смотрит результаты по каждому хосту

5.3. Создание и сохранение кастомного preset'а

Сценарий: Пользователь создает preset для тестирования веб-серверов

  1. Переходит на /presets/create
  2. Называет preset: "web-servers-test"
  3. Добавляет хосты:
    • web1 (Ubuntu 22.04) в группы [web, frontend]
    • web2 (Debian 12) в группы [web, backend]
    • lb1 (CentOS 9) в группы [web, loadbalancer]
  4. Сохраняет preset
  5. Использует его для тестирования: /roles/nginx/test?preset=web-servers-test

5.4. Редактирование inventory для продакшн

Сценарий: Пользователь настраивает inventory для реальных серверов

  1. Переходит на /inventory/production/edit
  2. Добавляет группу [web_servers]
  3. Добавляет хосты с реальными IP
  4. Настраивает SSH ключи
  5. Тестирует подключение к каждому хосту
  6. Сохраняет в Vault для безопасности

6. 🔧 Технические детали реализации

6.1. Генерация molecule.yml из preset'а

# app/services/molecule_service.py
class MoleculeService:
    def generate_molecule_yml(self, preset: dict, role_name: str) -> str:
        """Генерация molecule.yml из preset'а"""
        molecule_config = {
            "driver": {
                "name": "docker"
            },
            "platforms": [],
            "provisioner": {
                "name": "ansible",
                "inventory": {
                    "hosts": self.generate_inventory_hosts(preset)
                }
            },
            "verifier": {
                "name": "ansible"
            }
        }
        
        # Преобразование хостов из preset'а в platforms
        for host in preset['hosts']:
            molecule_config['platforms'].append({
                "name": host['name'],
                "image": preset['images'][host['family']],
                "pre_build_image": True,
                **preset.get('systemd_defaults', {})
            })
        
        return yaml.dump(molecule_config)

6.2. Выполнение molecule команд

# app/core/molecule_executor.py
class MoleculeExecutor:
    async def test(
        self, 
        role_name: str, 
        scenario: str = "default",
        stream: bool = False
    ):
        """Запуск molecule test"""
        cmd = [
            "molecule", "test",
            "-s", scenario,
            "--destroy", "never"  # Не удалять для просмотра
        ]
        
        if stream:
            # Для WebSocket
            process = await asyncio.create_subprocess_exec(
                *cmd,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.STDOUT,
                cwd=f"roles/{role_name}"
            )
            return process
        else:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                cwd=f"roles/{role_name}"
            )
            return {
                "success": result.returncode == 0,
                "stdout": result.stdout,
                "stderr": result.stderr
            }

📝 Резюме

Все действия по настройке тестирования выполняются через веб-интерфейс:

  1. Выбор образов и хостов: Визуальный редактор с drag & drop
  2. Настройка inventory: Автоматическая генерация + ручное редактирование
  3. Molecule тестирование: Полный контроль через интерфейс с live логами
  4. Preset'ы: Создание, редактирование, использование через UI
  5. Переменные ролей: Формы для заполнения с валидацией

Всё интуитивно, визуально и без необходимости знать команды make или структуру файлов!


Автор: Сергей Антропов
Сайт: https://devops.org.ru