""" Скрипт для загрузки пресетов из файловой системы в базу данных Автор: Сергей Антропов Сайт: 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()