Files
DevOpsLab/app/scripts/load_presets.py
Сергей Антропов d4b0d6f848 Исправление синтаксической ошибки в molecule_executor.py и обновление k8s preset'ов
- Исправлена незакрытая скобка в _build_test_command (строка 745)
- Добавлена поддержка k8s preset'ов: выполнение create_k8s_cluster.py перед create.yml
- Обновлены образы в k8s preset'ах: заменен недоступный ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy на inecs/ansible-lab:ubuntu22-latest
- Обновлены preset'ы в базе данных через SQL
- Обновлены файлы: k8s-single.yml, k8s-multi.yml, k8s-istio-full.yml
2026-02-16 00:31:09 +03:00

216 lines
10 KiB
Python

"""
Скрипт для загрузки пресетов из файловой системы в базу данных
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import sys
import os
from pathlib import Path
# Добавляем путь к приложению
app_path = Path(__file__).parent.parent
sys.path.insert(0, str(app_path))
# Также добавляем родительскую директорию для импорта app
if str(app_path.parent) not in sys.path:
sys.path.insert(0, str(app_path.parent))
from sqlalchemy import create_engine, text
from sqlalchemy.dialects.postgresql import JSON
import yaml
import json
from datetime import datetime
# Импортируем настройки
try:
from app.core.config import settings
except ImportError:
# Если не получается импортировать, используем значения по умолчанию
class Settings:
PROJECT_ROOT = Path("/workspace")
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://devopslab:devopslab123@postgres:5432/devopslab")
settings = Settings()
def load_presets():
"""Загрузка пресетов из файловой системы в БД"""
# Создаем подключение к БД
db_url = str(settings.DATABASE_URL).replace("postgresql+asyncpg://", "postgresql://")
engine = create_engine(db_url)
connection = engine.connect()
try:
# Получаем путь к папке presets
# Пресеты теперь находятся в alembic/presets
script_dir = Path(__file__).parent
alembic_dir = script_dir.parent / "alembic"
presets_dir = alembic_dir / "presets"
# Если не найдено в alembic, пробуем старый путь (для обратной совместимости)
if not presets_dir.exists():
project_root = Path(settings.PROJECT_ROOT)
old_presets_dir = project_root / "molecule" / "presets"
if old_presets_dir.exists():
presets_dir = old_presets_dir
print(f"⚠️ Используется старый путь: {presets_dir}")
k8s_presets_dir = presets_dir / "k8s"
print(f"📁 Поиск пресетов в: {presets_dir}")
loaded_count = 0
skipped_count = 0
error_count = 0
# Функция для импорта одного пресета
def import_preset(preset_file, category='main'):
try:
with open(preset_file) as f:
content = f.read()
preset_data = yaml.safe_load(content) or {}
# Извлечение описания из комментария
description = None
for line in content.split('\n'):
if line.strip().startswith('#description:'):
description = line.split('#description:')[1].strip()
break
# Проверка существования в БД
result = connection.execute(
text("SELECT id FROM presets WHERE name = :name"),
{"name": preset_file.stem}
)
if result.fetchone():
print(f"⏭️ Пропущен (уже существует): {preset_file.stem}")
return 'skipped'
# Вставка в БД
# Преобразуем dict/list в JSON строки для PostgreSQL
hosts_json = json.dumps(preset_data.get('hosts', []))
images_json = json.dumps(preset_data.get('images', {}))
systemd_defaults_json = json.dumps(preset_data.get('systemd_defaults', {}))
kind_clusters_json = json.dumps(preset_data.get('kind_clusters', []))
connection.execute(
text("""
INSERT INTO presets (name, category, description, content, docker_network, hosts, images, systemd_defaults, kind_clusters, created_at, updated_at)
VALUES (:name, :category, :description, :content, :docker_network, CAST(:hosts AS jsonb), CAST(:images AS jsonb), CAST(:systemd_defaults AS jsonb), CAST(:kind_clusters AS jsonb), :created_at, :updated_at)
"""),
{
'name': preset_file.stem,
'category': category,
'description': description,
'content': content,
'docker_network': preset_data.get('docker_network'),
'hosts': hosts_json,
'images': images_json,
'systemd_defaults': systemd_defaults_json,
'kind_clusters': kind_clusters_json,
'created_at': datetime.utcnow(),
'updated_at': datetime.utcnow()
}
)
connection.commit()
print(f"✅ Загружен: {preset_file.stem}")
return 'loaded'
except Exception as e:
print(f"❌ Ошибка при загрузке {preset_file.name}: {e}")
return 'error'
# Основные preset'ы из корня папки presets
if presets_dir.exists():
for preset_file in presets_dir.glob("*.yml"):
if preset_file.name == "deploy.yml":
continue
result = import_preset(preset_file, category='main')
if result == 'loaded':
loaded_count += 1
elif result == 'skipped':
skipped_count += 1
elif result == 'error':
error_count += 1
# Пресеты из папки examples
examples_dir = presets_dir / "examples"
if examples_dir.exists():
print(f"📁 Поиск пресетов в examples: {examples_dir}")
for preset_file in examples_dir.glob("*.yml"):
result = import_preset(preset_file, category='main')
if result == 'loaded':
loaded_count += 1
elif result == 'skipped':
skipped_count += 1
elif result == 'error':
error_count += 1
# K8s preset'ы
if k8s_presets_dir.exists():
for preset_file in k8s_presets_dir.glob("*.yml"):
try:
with open(preset_file) as f:
content = f.read()
preset_data = yaml.safe_load(content) or {}
# Извлечение описания из комментария
description = None
for line in content.split('\n'):
if line.strip().startswith('#description:'):
description = line.split('#description:')[1].strip()
break
# Проверка существования в БД
result = connection.execute(
text("SELECT id FROM presets WHERE name = :name"),
{"name": preset_file.stem}
)
if result.fetchone():
print(f"⏭️ Пропущен (уже существует): {preset_file.stem}")
skipped_count += 1
continue
# Вставка в БД
# Преобразуем dict/list в JSON строки для PostgreSQL
hosts_json = json.dumps(preset_data.get('hosts', []))
images_json = json.dumps(preset_data.get('images', {}))
systemd_defaults_json = json.dumps(preset_data.get('systemd_defaults', {}))
kind_clusters_json = json.dumps(preset_data.get('kind_clusters', []))
connection.execute(
text("""
INSERT INTO presets (name, category, description, content, docker_network, hosts, images, systemd_defaults, kind_clusters, created_at, updated_at)
VALUES (:name, :category, :description, :content, :docker_network, CAST(:hosts AS jsonb), CAST(:images AS jsonb), CAST(:systemd_defaults AS jsonb), CAST(:kind_clusters AS jsonb), :created_at, :updated_at)
"""),
{
'name': preset_file.stem,
'category': 'k8s',
'description': description,
'content': content,
'docker_network': preset_data.get('docker_network'),
'hosts': hosts_json,
'images': images_json,
'systemd_defaults': systemd_defaults_json,
'kind_clusters': kind_clusters_json,
'created_at': datetime.utcnow(),
'updated_at': datetime.utcnow()
}
)
connection.commit()
print(f"✅ Загружен: {preset_file.stem} (k8s)")
loaded_count += 1
except Exception as e:
print(f"❌ Ошибка при загрузке k8s preset {preset_file.name}: {e}")
error_count += 1
print(f"\n📊 Итого:")
print(f" ✅ Загружено: {loaded_count}")
print(f" ⏭️ Пропущено: {skipped_count}")
print(f" ❌ Ошибок: {error_count}")
finally:
connection.close()
engine.dispose()
if __name__ == "__main__":
load_presets()