Files
ModelForge-Public/backend/alembic/versions/20260908_0026_project_onboarding.py
T

52 lines
2.2 KiB
Python

"""Project onboarding and bounded credential overlap.
Revision ID: 20260908_0026
Revises: 20260907_0025
"""
import sqlalchemy as sa
from alembic import op
revision = "20260908_0026"
down_revision = "20260907_0025"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"project_onboarding",
sa.Column("project_id", sa.Uuid(), sa.ForeignKey("projects.id", ondelete="RESTRICT"), primary_key=True),
sa.Column("owner", sa.String(128), nullable=False),
sa.Column("environment", sa.String(32), nullable=False),
sa.Column("labels", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
)
op.add_column("service_credentials", sa.Column("superseded_at", sa.DateTime(timezone=True)))
op.drop_index("uq_active_service_client_credential", table_name="service_credentials")
op.create_index(
"uq_active_service_client_credential", "service_credentials", ["service_client_id"],
unique=True,
postgresql_where=sa.text("revoked_at IS NULL AND superseded_at IS NULL"),
sqlite_where=sa.text("revoked_at IS NULL AND superseded_at IS NULL"),
)
if op.get_bind().dialect.name == "postgresql":
op.execute("GRANT SELECT, INSERT ON project_onboarding TO modelforge_runtime")
op.execute("REVOKE UPDATE, DELETE, TRUNCATE ON project_onboarding FROM modelforge_runtime")
def downgrade() -> None:
connection = op.get_bind()
if connection.execute(sa.text("SELECT count(*) FROM project_onboarding")).scalar_one() or connection.execute(
sa.text("SELECT count(*) FROM service_credentials WHERE superseded_at IS NOT NULL")
).scalar_one():
raise RuntimeError("Onboarding or rotation history exists; recover the pre-migration backup instead.")
op.drop_index("uq_active_service_client_credential", table_name="service_credentials")
op.create_index(
"uq_active_service_client_credential", "service_credentials", ["service_client_id"],
unique=True, postgresql_where=sa.text("revoked_at IS NULL"), sqlite_where=sa.text("revoked_at IS NULL"),
)
op.drop_column("service_credentials", "superseded_at")
op.drop_table("project_onboarding")