- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
735 lines
46 KiB
Markdown
735 lines
46 KiB
Markdown
# Детали работы веб-интерфейса: тестирование и 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 реализация:**
|
||
```html
|
||
<!-- 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:**
|
||
|
||
```python
|
||
# 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 реализация:**
|
||
```python
|
||
# 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'ов:**
|
||
|
||
```python
|
||
# 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'а
|
||
|
||
```python
|
||
# 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 команд
|
||
|
||
```python
|
||
# 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
|