pali-lili/src/tofu/models.py
2025-12-14 17:47:51 +00:00

107 lines
3.6 KiB
Python

from datetime import datetime
from enum import Enum
from typing import Any
from sqlalchemy import ForeignKey, func, text
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Mapped, mapped_column, relationship
from src.models import CustomBase, TimestampMixin, IdMixin, DeletedTimestampMixin
class TofuInstanceStatus(Enum):
ACTIVE = "ACTIVE"
DEPLOYING = "DEPLOYING"
DESTROYED = "DESTROYED"
DESTROYING = "DESTROYING"
DRIFTED = "DRIFTED"
FAILED = "FAILED"
FAILED_DESTROY = "FAILED_DESTROY"
PENDING = "PENDING"
PENDING_DESTROY = "PENDING_DESTROY"
PENDING_DRIFT_CHECK = "PENDING_DRIFT_CHECK"
class TofuInstance(CustomBase, IdMixin, TimestampMixin, DeletedTimestampMixin):
__tablename__ = "tofu_instance"
status: Mapped[TofuInstanceStatus] = mapped_column(default=TofuInstanceStatus.PENDING)
configuration: Mapped[dict[str, Any]]
outputs: Mapped[dict[str, Any] | None]
plan: Mapped[dict[str, Any] | None]
state: Mapped[dict[str, Any] | None]
state_password: Mapped[bytes | None]
state_lock: Mapped[dict[str, Any] | None]
status_changed_at: Mapped[datetime] = mapped_column(default=func.now())
drift_checked_at: Mapped[datetime | None]
tasks = relationship("TofuInstanceTask", back_populates="instance")
status_changes = relationship("TofuInstanceStatusChange", back_populates="instance")
class TofuInstanceStatusChange(CustomBase, IdMixin):
__tablename__ = "tofu_instance_status_change"
instance_id: Mapped[int] = mapped_column(ForeignKey("tofu_instance.id"))
instance_task_id: Mapped[int] = mapped_column(ForeignKey("tofu_instance_task.id"))
timestamp: Mapped[datetime] = mapped_column(default=func.now())
old_status: Mapped[TofuInstanceStatus]
new_status: Mapped[TofuInstanceStatus]
instance = relationship("TofuInstance", back_populates="status_changes")
class TofuInstanceTaskType(Enum):
CHECK_DRIFT = "CHECK_DRIFT"
DEPLOY = "DEPLOY"
DESTROY = "DESTROY"
class TofuInstanceTaskStatus(Enum):
CANCELED = "CANCELED"
COMPLETED = "COMPLETED"
FAILED = "FAILED"
PENDING = "PENDING"
RUNNING = "RUNNING"
class TofuInstanceTask(CustomBase, IdMixin, TimestampMixin):
__tablename__ = "tofu_instance_task"
instance_id: Mapped[int] = mapped_column(ForeignKey("tofu_instance.id"))
task: Mapped[TofuInstanceTaskType]
status: Mapped[TofuInstanceTaskStatus] = mapped_column(
default=TofuInstanceTaskStatus.PENDING
)
start_time: Mapped[datetime | None]
end_time: Mapped[datetime | None]
instance = relationship("TofuInstance", back_populates="tasks")
class TofuInstanceTaskLog(CustomBase, IdMixin):
__tablename__ = "tofu_instance_task_log"
instance_task_id: Mapped[int] = mapped_column(ForeignKey("tofu_instance_task.id"))
timestamp: Mapped[datetime] = mapped_column(default=func.now())
log: Mapped[dict[str, Any]]
class TofuBruteForce(CustomBase, IdMixin, TimestampMixin):
__tablename__ = "tofu_brute_force"
host: Mapped[str]
expiry: Mapped[datetime] = mapped_column(default=func.now() + text("INTERVAL '1 hour'"))
def update_tofu_instance_status(
db: AsyncSession, instance: TofuInstance, task_id: int, new_status: TofuInstanceStatus
) -> None:
status_change = TofuInstanceStatusChange(
instance_id=instance.id,
instance_task_id=task_id,
old_status=instance.status,
new_status=new_status,
)
db.add(status_change)
instance.status = new_status
instance.status_changed_at = func.now()
if new_status == TofuInstanceStatus.DESTROYED:
instance.deleted_at = func.now()