pali-lili/src/tofu/models.py

108 lines
3.6 KiB
Python
Raw Normal View History

2025-12-14 17:47:51 +00:00
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()