52 lines
2.2 KiB
Python
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")
|