Skip to content

Commit

Permalink
Merge pull request #26 from Element84/migjob
Browse files Browse the repository at this point in the history
  • Loading branch information
hectormachin authored Aug 3, 2023
2 parents 84db9ce + f99b36a commit 9ffba64
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 4 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ ENV PATH=/opt/swoop/db/swoop-db-venv/bin:$PATH

RUN mkdir -p /opt/swoop/db/scripts
COPY bin/db-initialization.py /opt/swoop/db/scripts/db-initialization.py
COPY bin/run-migration-job.py /opt/swoop/db/scripts/run-migration-job.py
ENV PATH=/opt/swoop/db/scripts:$PATH
6 changes: 2 additions & 4 deletions bin/db-initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
swoop-caboose role username and password
- CONDUCTOR_ROLE_USER and CONDUCTOR_ROLE_PASS:
swoop-conductor role username and password
- MIGRATION_ROLE_USER and MIGRATION_ROLE_PASS:
username and password for migration role
- OWNER_ROLE_USER and OWNER_ROLE_PASS:
username and password for owner role
- Any additional libpq-supported connection parameters
(https://www.postgresql.org/docs/current/libpq-envars.html)
"""
Expand All @@ -24,8 +24,6 @@

from swoop.db import SwoopDB

OWNER_ROLE_NAME = "swoop"
OWNER_ROLE = "OWNER_ROLE"
APPLICATION_ROLES: list[str] = [
"API_ROLE",
"CABOOSE_ROLE",
Expand Down
93 changes: 93 additions & 0 deletions bin/run-migration-job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python
"""
This script automates applying migrations to a swoop database as created by
./db-initialization.py. By default it will migrate the database forward to
the most-recent schema version after waiting for all swoop application
connections to be closed, but a few environment variables can be used to
change that default behavior:
- ROLLBACK: boolean flag required when target version is less than current
- VERSION: the migration version to which to migrate/rollback the database
- NO_WAIT: override option to skip waiting for active connections to close
The script uses standard libpq-supported connection environment variables
(https://www.postgresql.org/docs/current/libpq-envars.html), so specify these
as required to connect to the database. Common vars include:
- PGHOST: hostname or IP address of the postgres cluster host
- PGPORT: port number of the postgres server
- PGUSER: name of the user (role) that will perform the migrations
- PGPASSWORD: password of the user (role)
- PGDATABASE: name of the database onto which to apply migrations
"""
import asyncio
import os
import sys
import time

from swoop.db import SwoopDB


def stderr(*args, **kwargs) -> None:
kwargs["file"] = sys.stderr
print(*args, **kwargs)


def strtobool(val) -> bool:
"""Convert a string representation of truth to true or false.
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
'val' is anything else.
"""
val = val.lower()
if val in ("y", "yes", "t", "true", "on", "1"):
return True
elif val in ("n", "no", "f", "false", "off", "0"):
return False
else:
raise ValueError(f"invalid boolean value {val!r}")


def int_or_none(val):
return int(val) if val else None


async def run_migrations() -> None:
rollback = strtobool(os.environ.get("ROLLBACK", "false"))
version = int_or_none(os.environ.get("VERSION"))
no_wait = strtobool(os.environ.get("NO_WAIT", "false"))

swoop_db = SwoopDB()

async with swoop_db.get_db_connection() as conn:
current_version = await swoop_db.get_current_version(conn=conn)
if current_version == version:
return
# Wait for all active connections from user roles to be closed
active_sessions = not no_wait
while active_sessions:
active_sessions = await conn.fetchval(
"""
SELECT EXISTS(
SELECT * FROM pg_stat_activity
WHERE
datname = current_database()
AND usename != current_user
)
""",
)
if active_sessions:
time.sleep(2)

if rollback:
stderr(f"Rolling back database to version {version}")
direction = "down"
else:
stderr(f"Migrating database to version {version}")
direction = "up"

await swoop_db.migrate(target=version, direction=direction, conn=conn)


if __name__ == "__main__":
asyncio.run(run_migrations())

0 comments on commit 9ffba64

Please sign in to comment.