1
0
mirror of https://github.com/quay/quay.git synced 2026-01-26 06:21:37 +03:00
Files
quay/data/migrations/env.py
Harish Govindarajulu 074f35ee99 chore: Update alembic migration script to support sqlite db (PROJQUAY-7097) (#2839)
* Add sqlite db support on quay start up

* Add batchmode to migration scripts to support sqlite db

* Add sqlite db to config-tool validator +  alembic migration

* Fix migration script to prevent db row locking

Added commit statement to ensure previous transaction is
completed before the next one within the same table

* Clean up unused sqlite volume

* Apply black formatting to migration scripts

* Address review comments

* Ensure py39-unit test runs the alembic migration on Sqlite

* Add static type checking for alembic config file name

* alembic remove commit and invalidate during migration

When disconnecting from db, alembic tries to rollback
causing PendingRollbackError

* Bump go version in config-tool Dockerfile

* Explicitly commit transaction to prevent db table locking

* Clean up + remove debug statements

* Undo database secret key change

* Add TEST_DATABASE_URI to py39-unit to run unit test with sqlite db

* Drop index before dropping column to prevent sqlite error

* Add test coverage + address last set of reviews

---------

Signed-off-by: harishsurf <hgovinda@redhat.com>
2024-06-12 13:57:32 -04:00

138 lines
4.3 KiB
Python

import logging
import logging.config
import os
from urllib.parse import unquote
from alembic import context
from alembic import op as alembic_op
from alembic.script.revision import ResolutionError
from alembic.util import CommandError
from peewee import SqliteDatabase
from sqlalchemy import create_engine
from app import app
from data.database import LEGACY_INDEX_MAP, all_models, db
from data.migrations.tester import NoopTester, PopulateTestDataTester
from data.model.sqlalchemybridge import gen_sqlalchemy_metadata
from release import GIT_HEAD, REGION, SERVICE
from util.morecollections import AttrDict
logger = logging.getLogger(__name__)
# Alembic's configuration
config = context.config
assert config.config_file_name is not None, "Alembic config file name must be set"
logging.config.fileConfig(config.config_file_name)
# Alembic is designed to be used with SQL Alchemy. These steps convert the schema as defined by the
# Peewee models to a format usable by Alembic.
target_metadata = gen_sqlalchemy_metadata(all_models, LEGACY_INDEX_MAP)
tables = AttrDict(target_metadata.tables)
def get_db_url():
"""
Return the Database URI. This is typically set in config.yaml but may be overridden using
an environment variable or expected to default with a SQLite database for testing purposes.
"""
db_url = app.config.get("DB_URI", "sqlite:///test/data/test.db")
return db_url
def get_tester():
"""
Returns the tester to use.
We only return the tester that populates data if the TEST_MIGRATE env var is set to `true` AND
we make sure we're not connecting to a production database.
"""
db_url = get_db_url()
if os.environ.get("TEST_MIGRATE", "") == "true":
if db_url.find("amazonaws.com") < 0:
return PopulateTestDataTester()
return NoopTester()
def get_engine():
"""
Return a SQL Alchemy engine object which Alembic uses to connect to the database.
"""
db_url = get_db_url()
peewee_connection_args = app.config.get("DB_CONNECTION_ARGS", {})
sa_connection_args = {}
# Include MySQL/MariaDB SSL configuration
if "ssl" in peewee_connection_args:
sa_connection_args["ssl"] = peewee_connection_args["ssl"]
engine = create_engine(db_url, connect_args=sa_connection_args)
return engine
def run_migrations_offline():
"""
Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
db_url = get_db_url()
config.set_main_option("sqlalchemy.url", db_url) # TODO: Is this required?
context.configure(url=db_url, target_metadata=target_metadata, transactional_ddl=True)
with context.begin_transaction():
context.run_migrations(op=alembic_op, tables=tables, tester=get_tester())
def run_migrations_online():
"""
Run migrations in 'online' mode.
In this scenario we need to create an Engine and associate a connection with the context.
"""
engine = get_engine()
connection = engine.connect()
context.configure(
connection=connection,
target_metadata=target_metadata,
transactional_ddl=False,
render_as_batch=True,
)
try:
with context.begin_transaction():
try:
context.run_migrations(op=alembic_op, tables=tables, tester=get_tester())
except (CommandError, ResolutionError) as ex:
if "No such revision" not in str(ex):
raise
if not REGION or not GIT_HEAD:
raise
from data.model.release import get_recent_releases
# ignore revision error if we're running the previous release
releases = list(get_recent_releases(SERVICE, REGION).offset(1).limit(1))
if releases and releases[0].version == GIT_HEAD:
logger.warn("Skipping database migration because revision not found")
else:
raise
finally:
connection.close()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()