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

169 lines
6.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Сервис для работы с Dockerfile
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.models.database import Dockerfile
from typing import Optional, List
from pathlib import Path
import logging
logger = logging.getLogger(__name__)
class DockerfileService:
"""Сервис для работы с Dockerfile"""
@staticmethod
async def get_dockerfile(db: AsyncSession, dockerfile_id: int) -> Optional[Dockerfile]:
"""Получение Dockerfile по ID"""
result = await db.execute(select(Dockerfile).where(Dockerfile.id == dockerfile_id))
return result.scalar_one_or_none()
@staticmethod
async def get_dockerfile_by_name(db: AsyncSession, name: str) -> Optional[Dockerfile]:
"""Получение Dockerfile по имени"""
result = await db.execute(select(Dockerfile).where(Dockerfile.name == name))
return result.scalar_one_or_none()
@staticmethod
async def list_dockerfiles(db: AsyncSession, status: Optional[str] = None) -> List[Dockerfile]:
"""Список всех Dockerfile"""
query = select(Dockerfile)
if status:
query = query.where(Dockerfile.status == status)
result = await db.execute(query.order_by(Dockerfile.name))
return result.scalars().all()
@staticmethod
async def create_dockerfile(
db: AsyncSession,
name: str,
content: str,
description: Optional[str] = None,
base_image: Optional[str] = None,
tags: Optional[List[str]] = None,
platforms: Optional[List[str]] = None,
created_by: Optional[str] = None
) -> Dockerfile:
"""Создание нового Dockerfile"""
# Платформы по умолчанию: linux/amd64 (x86_64), linux/386 (x86) и linux/arm64 (macOS M1)
if platforms is None:
platforms = ["linux/amd64", "linux/386", "linux/arm64"]
dockerfile = Dockerfile(
name=name,
description=description,
content=content,
base_image=base_image,
tags=tags or [],
platforms=platforms,
created_by=created_by
)
db.add(dockerfile)
await db.commit()
await db.refresh(dockerfile)
return dockerfile
@staticmethod
async def update_dockerfile(
db: AsyncSession,
dockerfile_id: int,
name: Optional[str] = None,
description: Optional[str] = None,
content: Optional[str] = None,
base_image: Optional[str] = None,
tags: Optional[List[str]] = None,
platforms: Optional[List[str]] = None,
updated_by: Optional[str] = None
) -> Optional[Dockerfile]:
"""Обновление Dockerfile"""
dockerfile = await DockerfileService.get_dockerfile(db, dockerfile_id)
if not dockerfile:
return None
if name:
dockerfile.name = name
if description is not None:
dockerfile.description = description
if content is not None:
dockerfile.content = content
if base_image is not None:
dockerfile.base_image = base_image
if tags is not None:
dockerfile.tags = tags
if platforms is not None:
dockerfile.platforms = platforms
if updated_by:
dockerfile.updated_by = updated_by
await db.commit()
await db.refresh(dockerfile)
return dockerfile
@staticmethod
async def delete_dockerfile(db: AsyncSession, dockerfile_id: int) -> bool:
"""Удаление Dockerfile"""
dockerfile = await DockerfileService.get_dockerfile(db, dockerfile_id)
if not dockerfile:
return False
await db.delete(dockerfile)
await db.commit()
return True
@staticmethod
async def load_from_filesystem(
db: AsyncSession,
project_root: Path,
created_by: Optional[str] = None
) -> List[Dockerfile]:
"""Загрузка Dockerfile из файловой системы в БД"""
# Dockerfiles теперь находятся в alembic/dockerfiles
alembic_dir = project_root / "app" / "alembic"
dockerfiles_dir = alembic_dir / "dockerfiles"
loaded = []
if not dockerfiles_dir.exists():
logger.warning(f"Dockerfiles directory not found: {dockerfiles_dir}")
return loaded
for dockerfile_path in dockerfiles_dir.rglob("Dockerfile*"):
if dockerfile_path.is_file():
# Имя из пути (например, ubuntu22/Dockerfile -> ubuntu22)
relative_path = dockerfile_path.relative_to(dockerfiles_dir)
name = str(relative_path.parent) if relative_path.parent != Path('.') else relative_path.stem
# Проверяем, существует ли уже в БД
existing = await DockerfileService.get_dockerfile_by_name(db, name)
if existing:
continue
content = dockerfile_path.read_text(encoding='utf-8')
# Определяем базовый образ из содержимого
base_image = None
for line in content.split('\n'):
if line.strip().startswith('FROM'):
base_image = line.strip().replace('FROM', '').strip().split()[0]
break
# Платформы по умолчанию: linux/amd64 (x86_64), linux/386 (x86) и linux/arm64 (macOS M1)
default_platforms = ["linux/amd64", "linux/386", "linux/arm64"]
dockerfile = await DockerfileService.create_dockerfile(
db=db,
name=name,
content=content,
base_image=base_image,
platforms=default_platforms,
created_by=created_by
)
loaded.append(dockerfile)
logger.info(f"Loaded Dockerfile: {name}")
return loaded