- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
188 lines
6.3 KiB
Python
188 lines
6.3 KiB
Python
"""
|
||
Сервис для работы с playbook
|
||
Автор: Сергей Антропов
|
||
Сайт: https://devops.org.ru
|
||
"""
|
||
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from sqlalchemy import select, update, delete
|
||
from app.models.database import Playbook, PlaybookTestRun, PlaybookDeployment
|
||
from typing import Optional, List, Dict
|
||
import yaml
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class PlaybookService:
|
||
"""Сервис для работы с playbook"""
|
||
|
||
@staticmethod
|
||
async def create_playbook(
|
||
db: AsyncSession,
|
||
name: str,
|
||
roles: List[str],
|
||
description: Optional[str] = None,
|
||
variables: Optional[Dict] = None,
|
||
inventory: Optional[str] = None,
|
||
created_by: Optional[str] = None
|
||
) -> Playbook:
|
||
"""Создание нового playbook"""
|
||
# Генерация YAML содержимого playbook
|
||
playbook_content = PlaybookService._generate_playbook_yaml(roles, variables)
|
||
|
||
playbook = Playbook(
|
||
name=name,
|
||
description=description,
|
||
content=playbook_content,
|
||
roles=roles,
|
||
variables=variables or {},
|
||
inventory=inventory,
|
||
created_by=created_by
|
||
)
|
||
db.add(playbook)
|
||
await db.commit()
|
||
await db.refresh(playbook)
|
||
return playbook
|
||
|
||
@staticmethod
|
||
async def get_playbook(db: AsyncSession, playbook_id: int) -> Optional[Playbook]:
|
||
"""Получение playbook по ID"""
|
||
result = await db.execute(select(Playbook).where(Playbook.id == playbook_id))
|
||
return result.scalar_one_or_none()
|
||
|
||
@staticmethod
|
||
async def get_playbook_by_name(db: AsyncSession, name: str) -> Optional[Playbook]:
|
||
"""Получение playbook по имени"""
|
||
result = await db.execute(select(Playbook).where(Playbook.name == name))
|
||
return result.scalar_one_or_none()
|
||
|
||
@staticmethod
|
||
async def list_playbooks(db: AsyncSession, status: Optional[str] = None) -> List[Playbook]:
|
||
"""Список всех playbook"""
|
||
query = select(Playbook)
|
||
if status:
|
||
query = query.where(Playbook.status == status)
|
||
result = await db.execute(query.order_by(Playbook.created_at.desc()))
|
||
return result.scalars().all()
|
||
|
||
@staticmethod
|
||
async def update_playbook(
|
||
db: AsyncSession,
|
||
playbook_id: int,
|
||
name: Optional[str] = None,
|
||
description: Optional[str] = None,
|
||
roles: Optional[List[str]] = None,
|
||
variables: Optional[Dict] = None,
|
||
inventory: Optional[str] = None,
|
||
content: Optional[str] = None,
|
||
updated_by: Optional[str] = None
|
||
) -> Optional[Playbook]:
|
||
"""Обновление playbook"""
|
||
playbook = await PlaybookService.get_playbook(db, playbook_id)
|
||
if not playbook:
|
||
return None
|
||
|
||
if name:
|
||
playbook.name = name
|
||
if description is not None:
|
||
playbook.description = description
|
||
if roles is not None:
|
||
playbook.roles = roles
|
||
# Перегенерируем content если изменились роли
|
||
playbook.content = PlaybookService._generate_playbook_yaml(roles, variables or playbook.variables)
|
||
if variables is not None:
|
||
playbook.variables = variables
|
||
# Перегенерируем content если изменились переменные
|
||
playbook.content = PlaybookService._generate_playbook_yaml(playbook.roles, variables)
|
||
if inventory is not None:
|
||
playbook.inventory = inventory
|
||
if content is not None:
|
||
playbook.content = content
|
||
if updated_by:
|
||
playbook.updated_by = updated_by
|
||
|
||
await db.commit()
|
||
await db.refresh(playbook)
|
||
return playbook
|
||
|
||
@staticmethod
|
||
async def delete_playbook(db: AsyncSession, playbook_id: int) -> bool:
|
||
"""Удаление playbook"""
|
||
playbook = await PlaybookService.get_playbook(db, playbook_id)
|
||
if not playbook:
|
||
return False
|
||
|
||
await db.delete(playbook)
|
||
await db.commit()
|
||
return True
|
||
|
||
@staticmethod
|
||
def _generate_playbook_yaml(roles: List[str], variables: Optional[Dict] = None) -> str:
|
||
"""Генерация YAML содержимого playbook"""
|
||
playbook_data = {
|
||
'name': 'Playbook',
|
||
'hosts': 'all',
|
||
'become': True,
|
||
'roles': roles
|
||
}
|
||
|
||
if variables:
|
||
playbook_data['vars'] = variables
|
||
|
||
return yaml.dump([playbook_data], default_flow_style=False, allow_unicode=True)
|
||
|
||
@staticmethod
|
||
async def save_test_run(
|
||
db: AsyncSession,
|
||
playbook_id: int,
|
||
preset_name: Optional[str],
|
||
status: str,
|
||
user: Optional[str] = None,
|
||
output: Optional[str] = None,
|
||
error: Optional[str] = None,
|
||
returncode: Optional[int] = None
|
||
) -> PlaybookTestRun:
|
||
"""Сохранение результата тестирования playbook"""
|
||
test_run = PlaybookTestRun(
|
||
playbook_id=playbook_id,
|
||
preset_name=preset_name,
|
||
status=status,
|
||
output=output,
|
||
error=error,
|
||
returncode=returncode,
|
||
user=user
|
||
)
|
||
db.add(test_run)
|
||
await db.commit()
|
||
await db.refresh(test_run)
|
||
return test_run
|
||
|
||
@staticmethod
|
||
async def save_deployment(
|
||
db: AsyncSession,
|
||
playbook_id: int,
|
||
inventory: Optional[str],
|
||
hosts: Optional[List[str]],
|
||
status: str,
|
||
user: Optional[str] = None,
|
||
output: Optional[str] = None,
|
||
error: Optional[str] = None,
|
||
returncode: Optional[int] = None
|
||
) -> PlaybookDeployment:
|
||
"""Сохранение результата деплоя playbook"""
|
||
deployment = PlaybookDeployment(
|
||
playbook_id=playbook_id,
|
||
inventory=inventory,
|
||
hosts=hosts or [],
|
||
status=status,
|
||
output=output,
|
||
error=error,
|
||
returncode=returncode,
|
||
user=user
|
||
)
|
||
db.add(deployment)
|
||
await db.commit()
|
||
await db.refresh(deployment)
|
||
return deployment
|