feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
This commit is contained in:
1
app/models/__init__.py
Normal file
1
app/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Models package
|
||||
261
app/models/database.py
Normal file
261
app/models/database.py
Normal 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)
|
||||
64
app/models/user.py
Normal file
64
app/models/user.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
Модель пользователя
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Text
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class User(Base):
|
||||
"""Модель пользователя"""
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
username = Column(String, unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String, nullable=False)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
is_superuser = Column(Boolean, default=False, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
# Связи
|
||||
profile = relationship("UserProfile", back_populates="user", uselist=False, cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User(username='{self.username}', is_active={self.is_active})>"
|
||||
|
||||
|
||||
class UserProfile(Base):
|
||||
"""Профиль пользователя с настройками"""
|
||||
__tablename__ = "user_profiles"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), unique=True, nullable=False, index=True)
|
||||
|
||||
# Docker Hub настройки
|
||||
dockerhub_username = Column(String)
|
||||
dockerhub_password = Column(Text) # Зашифрованный пароль
|
||||
dockerhub_repository = Column(String) # Имя репозитория по умолчанию
|
||||
|
||||
# Harbor настройки
|
||||
harbor_url = Column(String) # URL Harbor репозитория
|
||||
harbor_username = Column(String)
|
||||
harbor_password = Column(Text) # Зашифрованный пароль
|
||||
harbor_project = Column(String) # Имя проекта в Harbor
|
||||
|
||||
# Дополнительные настройки
|
||||
email = Column(String)
|
||||
full_name = Column(String)
|
||||
extra_data = Column(JSON) # Дополнительные настройки
|
||||
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
# Связи
|
||||
user = relationship("User", back_populates="profile")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<UserProfile(user_id={self.user_id})>"
|
||||
Reference in New Issue
Block a user