feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile

- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
This commit is contained in:
Сергей Антропов
2026-02-15 22:59:02 +03:00
parent 23e1a6037b
commit 1fbf9185a2
232 changed files with 38075 additions and 5 deletions

261
app/models/database.py Normal file
View File

@@ -0,0 +1,261 @@
"""
Модели базы данных для истории команд и тестов
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, JSON, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
# Импортируем Base из user.py для единообразия
from app.models.user import Base
class CommandHistory(Base):
"""История выполнения команд"""
__tablename__ = "command_history"
id = Column(Integer, primary_key=True, index=True)
command = Column(String, nullable=False, index=True)
command_type = Column(String, nullable=False, index=True) # test, deploy, export, import
role_name = Column(String, index=True)
preset_name = Column(String)
status = Column(String, nullable=False) # success, failed, running
stdout = Column(Text)
stderr = Column(Text)
returncode = Column(Integer)
started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
finished_at = Column(DateTime)
duration = Column(Integer) # в секундах
user = Column(String)
extra_data = Column(JSON) # Дополнительные данные
# Связи
test_results = relationship("TestResult", back_populates="command")
class TestResult(Base):
"""Результаты тестирования ролей"""
__tablename__ = "test_results"
id = Column(Integer, primary_key=True, index=True)
command_id = Column(Integer, ForeignKey("command_history.id"), nullable=False)
role_name = Column(String, nullable=False, index=True)
preset_name = Column(String, index=True)
test_type = Column(String) # molecule, lint, syntax
status = Column(String, nullable=False) # passed, failed, skipped
duration = Column(Integer) # в секундах
output = Column(Text)
error = Column(Text)
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
# Связи
command = relationship("CommandHistory", back_populates="test_results")
class DeploymentHistory(Base):
"""История деплоев"""
__tablename__ = "deployment_history"
id = Column(Integer, primary_key=True, index=True)
role_name = Column(String, nullable=False, index=True)
inventory = Column(String)
hosts = Column(JSON) # Список хостов
status = Column(String, nullable=False) # success, failed, running
started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
finished_at = Column(DateTime)
duration = Column(Integer)
output = Column(Text)
error = Column(Text)
user = Column(String)
extra_data = Column(JSON)
class ExportHistory(Base):
"""История экспорта ролей"""
__tablename__ = "export_history"
id = Column(Integer, primary_key=True, index=True)
role_name = Column(String, nullable=False, index=True)
repo_url = Column(String, nullable=False)
branch = Column(String)
version = Column(String)
commit_hash = Column(String)
status = Column(String, nullable=False) # success, failed
started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
finished_at = Column(DateTime)
user = Column(String)
extra_data = Column(JSON)
class ImportHistory(Base):
"""История импорта ролей"""
__tablename__ = "import_history"
id = Column(Integer, primary_key=True, index=True)
role_name = Column(String, nullable=False, index=True)
source_type = Column(String, nullable=False) # git, galaxy
source_url = Column(String)
status = Column(String, nullable=False) # success, failed
started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
finished_at = Column(DateTime)
user = Column(String)
extra_data = Column(JSON)
class Playbook(Base):
"""Playbook - объединение ролей для тестирования и деплоя"""
__tablename__ = "playbooks"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, nullable=False, index=True)
description = Column(Text)
content = Column(Text, nullable=False) # YAML содержимое playbook
roles = Column(JSON, nullable=False) # Список ролей в playbook
variables = Column(JSON) # Переменные для playbook
inventory = Column(Text) # Инвентарь для playbook
status = Column(String, default="active") # active, archived
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
created_by = Column(String)
updated_by = Column(String)
extra_data = Column(JSON)
# Связи
test_runs = relationship("PlaybookTestRun", back_populates="playbook")
deployments = relationship("PlaybookDeployment", back_populates="playbook")
class PlaybookTestRun(Base):
"""Результаты тестирования playbook"""
__tablename__ = "playbook_test_runs"
id = Column(Integer, primary_key=True, index=True)
playbook_id = Column(Integer, ForeignKey("playbooks.id"), nullable=False)
preset_name = Column(String, index=True)
status = Column(String, nullable=False) # running, success, failed, cancelled
started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
finished_at = Column(DateTime)
duration = Column(Integer) # в секундах
output = Column(Text)
error = Column(Text)
returncode = Column(Integer)
user = Column(String)
extra_data = Column(JSON)
# Связи
playbook = relationship("Playbook", back_populates="test_runs")
class PlaybookDeployment(Base):
"""Результаты деплоя playbook"""
__tablename__ = "playbook_deployments"
id = Column(Integer, primary_key=True, index=True)
playbook_id = Column(Integer, ForeignKey("playbooks.id"), nullable=False)
inventory = Column(Text)
hosts = Column(JSON) # Список хостов
status = Column(String, nullable=False) # running, success, failed, cancelled
started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
finished_at = Column(DateTime)
duration = Column(Integer) # в секундах
output = Column(Text)
error = Column(Text)
returncode = Column(Integer)
user = Column(String)
extra_data = Column(JSON)
# Связи
playbook = relationship("Playbook", back_populates="deployments")
class Dockerfile(Base):
"""Dockerfile для образов тестирования"""
__tablename__ = "dockerfiles"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, nullable=False, index=True) # ubuntu22, centos8, etc.
description = Column(Text)
content = Column(Text, nullable=False) # Содержимое Dockerfile
base_image = Column(String) # Базовый образ
tags = Column(JSON) # Теги для образа
platforms = Column(JSON) # Платформы для сборки (по умолчанию: ["linux/amd64", "linux/386", "linux/arm64"])
status = Column(String, default="active") # active, archived
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
created_by = Column(String)
updated_by = Column(String)
extra_data = Column(JSON)
class DockerfileBuildLog(Base):
"""Логи сборки Dockerfile"""
__tablename__ = "dockerfile_build_logs"
id = Column(Integer, primary_key=True, index=True)
dockerfile_id = Column(Integer, ForeignKey("dockerfiles.id"), nullable=False, index=True)
image_name = Column(String, nullable=False, index=True)
tag = Column(String, default="latest")
platforms = Column(JSON) # Список платформ
status = Column(String, nullable=False, default="running") # running, success, failed
logs = Column(Text) # Полные логи сборки
started_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
finished_at = Column(DateTime)
duration = Column(Integer) # в секундах
returncode = Column(Integer)
user = Column(String)
extra_data = Column(JSON)
# Связи
dockerfile = relationship("Dockerfile", backref="build_logs")
class Role(Base):
"""Ansible роли"""
__tablename__ = "roles"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, nullable=False, index=True) # Имя роли
description = Column(Text) # Описание роли
content = Column(JSON, nullable=False) # Содержимое роли в виде JSON структуры {file_path: content}
# Флаги доступа
is_global = Column(Boolean, default=False, nullable=False, index=True) # Доступно для всех пользователей
is_personal = Column(Boolean, default=False, nullable=False, index=True) # Личная роль пользователя
groups = Column(JSON) # Список групп, которым доступна роль (если is_personal=False и is_global=False)
user_id = Column(Integer, ForeignKey("users.id"), nullable=True, index=True) # Владелец роли (если is_personal=True)
# Метаданные
author = Column(String) # Автор роли
platforms = Column(JSON) # Поддерживаемые платформы
galaxy_info = Column(JSON) # Информация для Ansible Galaxy
status = Column(String, default="active") # active, archived
created_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
created_by = Column(String)
updated_by = Column(String)
extra_data = Column(JSON)
# Связи
user = relationship("User", backref="roles")
class Preset(Base):
"""Preset для Molecule тестирования"""
__tablename__ = "presets"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, nullable=False, index=True) # minimal, default, etc.
category = Column(String, default="main", index=True) # main, k8s
description = Column(Text) # Описание из комментария #description:
content = Column(Text, nullable=False) # YAML содержимое preset'а
docker_network = Column(String) # Имя Docker сети
hosts = Column(JSON) # Список хостов
images = Column(JSON) # Словарь образов
systemd_defaults = Column(JSON) # Настройки systemd
kind_clusters = Column(JSON) # Kubernetes кластеры (для k8s preset'ов)
status = Column(String, default="active") # active, archived
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
created_by = Column(String)
updated_by = Column(String)
extra_data = Column(JSON)