From f8e2feef333d299dc1ac65d6928a27c5dbdb21e5 Mon Sep 17 00:00:00 2001 From: ho1ow Date: Tue, 24 Dec 2024 11:14:32 +0700 Subject: [PATCH 1/2] mysql to psql --- app/__init__.py | 6 +- app/env.py | 6 +- example.env | 6 +- migrations/README | 1 - migrations/alembic.ini | 50 -------- migrations/env.py | 113 ------------------ migrations/script.py.mako | 24 ---- .../versions/0b4fe7004833_update_schema.py | 101 ---------------- .../ffcc21ab6b1b_initial_migration.py | 35 ------ requirements.txt | 4 +- 10 files changed, 5 insertions(+), 341 deletions(-) delete mode 100644 migrations/README delete mode 100644 migrations/alembic.ini delete mode 100644 migrations/env.py delete mode 100644 migrations/script.py.mako delete mode 100644 migrations/versions/0b4fe7004833_update_schema.py delete mode 100644 migrations/versions/ffcc21ab6b1b_initial_migration.py diff --git a/app/__init__.py b/app/__init__.py index fe5e31e..9dc9b8a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -8,9 +8,7 @@ from app.env import ( - MYSQL_HOSTNAME, MYSQL_HOSTPORT, - MYSQL_USERNAME, MYSQL_PASSWORD, - MYSQL_DATABASE, SECRET_KEY, + POSTGRES_URL, SECRET_KEY, ) def create_app(): @@ -19,7 +17,7 @@ def create_app(): CORS(app) app.config["SECRET_KEY"] = SECRET_KEY - app.config["SQLALCHEMY_DATABASE_URI"] = f"mysql://{MYSQL_USERNAME}:{MYSQL_PASSWORD}@{MYSQL_HOSTNAME}:{MYSQL_HOSTPORT}/{MYSQL_DATABASE}" + app.config["SQLALCHEMY_DATABASE_URI"] = POSTGRES_URL db.init_app(app) migrate = Migrate(app, db) # type: ignore diff --git a/app/env.py b/app/env.py index 04b06c9..e66ad91 100644 --- a/app/env.py +++ b/app/env.py @@ -28,11 +28,7 @@ def readIntEnv(key: str) -> int: SECRET_KEY = readNonEmptyStringEnv("SECRET_KEY") -MYSQL_HOSTNAME = readUriComponentEnv("MYSQL_HOSTNAME") -MYSQL_HOSTPORT = readIntEnv("MYSQL_HOSTPORT") -MYSQL_USERNAME = readNonEmptyStringEnv("MYSQL_USERNAME") -MYSQL_PASSWORD = readNonEmptyStringEnv("MYSQL_PASSWORD") -MYSQL_DATABASE = readNonEmptyStringEnv("MYSQL_DATABASE") +POSTGRES_URL=readNonEmptyStringEnv("POSTGRES_URL") NOVA_VM_FLAVOR_ID=readNonEmptyStringEnv("NOVA_VM_FLAVOR_ID") NOVA_VM_IMAGE_ID=readNonEmptyStringEnv("NOVA_VM_IMAGE_ID") NOVA_VM_NETWORK_ID=readNonEmptyStringEnv("NOVA_VM_NETWORK_ID") diff --git a/example.env b/example.env index 930238c..507a05b 100644 --- a/example.env +++ b/example.env @@ -2,11 +2,7 @@ FLASK_ENV="required, can be 'development' or 'production'" SECRET_KEY="required, please go to and copy code from the box '63 random alpha-numeric characters (a-z, A-Z, 0-9)'" -MYSQL_HOSTNAME="required, localhost or some IP, domain name" -MYSQL_HOSTPORT="required, 3306 or something else" -MYSQL_USERNAME="required" -MYSQL_PASSWORD="required" -MYSQL_DATABASE="required" +POSTGRES_URL="required, example: postgresql+psycopg2://{username}:{password}@{hostname}:{hostport}/{database}" NOVA_VM_FLAVOR_ID="required" NOVA_VM_IMAGE_ID="required" NOVA_VM_NETWORK_ID="required" diff --git a/migrations/README b/migrations/README deleted file mode 100644 index 0e04844..0000000 --- a/migrations/README +++ /dev/null @@ -1 +0,0 @@ -Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini deleted file mode 100644 index ec9d45c..0000000 --- a/migrations/alembic.ini +++ /dev/null @@ -1,50 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic,flask_migrate - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[logger_flask_migrate] -level = INFO -handlers = -qualname = flask_migrate - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py deleted file mode 100644 index 4c97092..0000000 --- a/migrations/env.py +++ /dev/null @@ -1,113 +0,0 @@ -import logging -from logging.config import fileConfig - -from flask import current_app - -from alembic import context - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -fileConfig(config.config_file_name) -logger = logging.getLogger('alembic.env') - - -def get_engine(): - try: - # this works with Flask-SQLAlchemy<3 and Alchemical - return current_app.extensions['migrate'].db.get_engine() - except (TypeError, AttributeError): - # this works with Flask-SQLAlchemy>=3 - return current_app.extensions['migrate'].db.engine - - -def get_engine_url(): - try: - return get_engine().url.render_as_string(hide_password=False).replace( - '%', '%%') - except AttributeError: - return str(get_engine().url).replace('%', '%%') - - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -config.set_main_option('sqlalchemy.url', get_engine_url()) -target_db = current_app.extensions['migrate'].db - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def get_metadata(): - if hasattr(target_db, 'metadatas'): - return target_db.metadatas[None] - return target_db.metadata - - -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. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, target_metadata=get_metadata(), literal_binds=True - ) - - with context.begin_transaction(): - context.run_migrations() - - -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. - - """ - - # this callback is used to prevent an auto-migration from being generated - # when there are no changes to the schema - # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html - def process_revision_directives(context, revision, directives): - if getattr(config.cmd_opts, 'autogenerate', False): - script = directives[0] - if script.upgrade_ops.is_empty(): - directives[:] = [] - logger.info('No changes in schema detected.') - - conf_args = current_app.extensions['migrate'].configure_args - if conf_args.get("process_revision_directives") is None: - conf_args["process_revision_directives"] = process_revision_directives - - connectable = get_engine() - - with connectable.connect() as connection: - context.configure( - connection=connection, - target_metadata=get_metadata(), - **conf_args - ) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako deleted file mode 100644 index 2c01563..0000000 --- a/migrations/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/0b4fe7004833_update_schema.py b/migrations/versions/0b4fe7004833_update_schema.py deleted file mode 100644 index 240f670..0000000 --- a/migrations/versions/0b4fe7004833_update_schema.py +++ /dev/null @@ -1,101 +0,0 @@ -"""update schema - -Revision ID: 0b4fe7004833 -Revises: ffcc21ab6b1b -Create Date: 2024-12-14 10:46:08.607623 - -""" -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision = '0b4fe7004833' -down_revision = 'ffcc21ab6b1b' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('cinder_volumes', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('openstack_cinder_volume_id', sa.String(length=255), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('openstack_cinder_volume_id') - ) - op.create_table('nova_vms', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('status', sa.String(length=50), nullable=False), - sa.Column('floating_ip', sa.String(length=100), nullable=True), - sa.Column('openstack_nova_vm_id', sa.String(length=255), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('openstack_nova_vm_id') - ) - op.create_table('plans', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('type', sa.String(length=150), nullable=False), - sa.Column('name', sa.String(length=255), nullable=False), - sa.Column('storage_in_gb', sa.Float(), nullable=False), - sa.Column('ram_in_gb', sa.Float(), nullable=False), - sa.Column('cpu_cores', sa.Integer(), nullable=False), - sa.Column('has_redis', sa.Boolean(), nullable=False), - sa.Column('has_mysql', sa.Boolean(), nullable=False), - sa.Column('monthly_fee_in_usd', sa.Float(), nullable=False), - sa.Column('image_reference', sa.String(length=255), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('image_reference') - ) - op.create_table('websites', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(length=255), nullable=False), - sa.Column('user_id', sa.Integer(), nullable=False), - sa.Column('plan_id', sa.Integer(), nullable=False), - sa.Column('status', sa.String(length=50), nullable=False), - sa.Column('public_port', sa.Integer(), nullable=False), - sa.Column('nova_vm_port', sa.Integer(), nullable=False), - sa.Column('nova_vm_id', sa.Integer(), nullable=False), - sa.Column('cinder_volume_id', sa.Integer(), nullable=False), - sa.Column('created_at', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['cinder_volume_id'], ['cinder_volumes.id'], onupdate='RESTRICT', ondelete='RESTRICT'), - sa.ForeignKeyConstraint(['nova_vm_id'], ['nova_vms.id'], onupdate='RESTRICT', ondelete='RESTRICT'), - sa.ForeignKeyConstraint(['plan_id'], ['plans.id'], onupdate='RESTRICT', ondelete='RESTRICT'), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], onupdate='RESTRICT', ondelete='RESTRICT'), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('nova_vm_port', 'nova_vm_id', name='unique_nova_vm_port_id'), - sa.UniqueConstraint('public_port') - ) - with op.batch_alter_table('users', schema=None) as batch_op: - batch_op.alter_column('email', - existing_type=mysql.VARCHAR(length=150), - nullable=False) - batch_op.alter_column('password', - existing_type=mysql.VARCHAR(length=150), - type_=sa.Text(), - nullable=False) - batch_op.alter_column('name', - existing_type=mysql.VARCHAR(length=150), - nullable=False) - - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('users', schema=None) as batch_op: - batch_op.alter_column('name', - existing_type=mysql.VARCHAR(length=150), - nullable=True) - batch_op.alter_column('password', - existing_type=sa.Text(), - type_=mysql.VARCHAR(length=150), - nullable=True) - batch_op.alter_column('email', - existing_type=mysql.VARCHAR(length=150), - nullable=True) - - op.drop_table('websites') - op.drop_table('plans') - op.drop_table('nova_vms') - op.drop_table('cinder_volumes') - # ### end Alembic commands ### diff --git a/migrations/versions/ffcc21ab6b1b_initial_migration.py b/migrations/versions/ffcc21ab6b1b_initial_migration.py deleted file mode 100644 index 0294d31..0000000 --- a/migrations/versions/ffcc21ab6b1b_initial_migration.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Initial migration - -Revision ID: ffcc21ab6b1b -Revises: -Create Date: 2024-12-04 22:20:13.912834 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'ffcc21ab6b1b' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('users', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('email', sa.String(length=150), nullable=True), - sa.Column('password', sa.String(length=150), nullable=True), - sa.Column('name', sa.String(length=150), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('email') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('users') - # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index af10e25..03c25ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,17 +25,15 @@ jsonpointer==3.0.0 keystoneauth1==5.9.1 Mako==1.3.7 MarkupSafe==3.0.2 -mysql-connector-python==9.1.0 -mysqlclient==2.2.6 openstacksdk==4.2.0 os-service-types==1.7.0 paramiko==3.5.0 pbr==6.1.0 platformdirs==4.3.6 psutil==6.1.0 +psycopg2==2.9.10 pycparser==2.22 PyJWT==2.10.1 -PyMySQL==1.1.1 PyNaCl==1.5.0 python-dotenv==1.0.1 PyYAML==6.0.2 From 495af245b365ba6afee7e4c835e23c9191589fa4 Mon Sep 17 00:00:00 2001 From: ho1ow Date: Tue, 24 Dec 2024 11:38:11 +0700 Subject: [PATCH 2/2] Revert "mysql to psql" This reverts commit f8e2feef333d299dc1ac65d6928a27c5dbdb21e5. --- app/__init__.py | 6 +- app/env.py | 6 +- example.env | 6 +- migrations/README | 1 + migrations/alembic.ini | 50 ++++++++ migrations/env.py | 113 ++++++++++++++++++ migrations/script.py.mako | 24 ++++ .../versions/0b4fe7004833_update_schema.py | 101 ++++++++++++++++ .../ffcc21ab6b1b_initial_migration.py | 35 ++++++ requirements.txt | 4 +- 10 files changed, 341 insertions(+), 5 deletions(-) create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/0b4fe7004833_update_schema.py create mode 100644 migrations/versions/ffcc21ab6b1b_initial_migration.py diff --git a/app/__init__.py b/app/__init__.py index 9dc9b8a..fe5e31e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -8,7 +8,9 @@ from app.env import ( - POSTGRES_URL, SECRET_KEY, + MYSQL_HOSTNAME, MYSQL_HOSTPORT, + MYSQL_USERNAME, MYSQL_PASSWORD, + MYSQL_DATABASE, SECRET_KEY, ) def create_app(): @@ -17,7 +19,7 @@ def create_app(): CORS(app) app.config["SECRET_KEY"] = SECRET_KEY - app.config["SQLALCHEMY_DATABASE_URI"] = POSTGRES_URL + app.config["SQLALCHEMY_DATABASE_URI"] = f"mysql://{MYSQL_USERNAME}:{MYSQL_PASSWORD}@{MYSQL_HOSTNAME}:{MYSQL_HOSTPORT}/{MYSQL_DATABASE}" db.init_app(app) migrate = Migrate(app, db) # type: ignore diff --git a/app/env.py b/app/env.py index e66ad91..04b06c9 100644 --- a/app/env.py +++ b/app/env.py @@ -28,7 +28,11 @@ def readIntEnv(key: str) -> int: SECRET_KEY = readNonEmptyStringEnv("SECRET_KEY") -POSTGRES_URL=readNonEmptyStringEnv("POSTGRES_URL") +MYSQL_HOSTNAME = readUriComponentEnv("MYSQL_HOSTNAME") +MYSQL_HOSTPORT = readIntEnv("MYSQL_HOSTPORT") +MYSQL_USERNAME = readNonEmptyStringEnv("MYSQL_USERNAME") +MYSQL_PASSWORD = readNonEmptyStringEnv("MYSQL_PASSWORD") +MYSQL_DATABASE = readNonEmptyStringEnv("MYSQL_DATABASE") NOVA_VM_FLAVOR_ID=readNonEmptyStringEnv("NOVA_VM_FLAVOR_ID") NOVA_VM_IMAGE_ID=readNonEmptyStringEnv("NOVA_VM_IMAGE_ID") NOVA_VM_NETWORK_ID=readNonEmptyStringEnv("NOVA_VM_NETWORK_ID") diff --git a/example.env b/example.env index 507a05b..930238c 100644 --- a/example.env +++ b/example.env @@ -2,7 +2,11 @@ FLASK_ENV="required, can be 'development' or 'production'" SECRET_KEY="required, please go to and copy code from the box '63 random alpha-numeric characters (a-z, A-Z, 0-9)'" -POSTGRES_URL="required, example: postgresql+psycopg2://{username}:{password}@{hostname}:{hostport}/{database}" +MYSQL_HOSTNAME="required, localhost or some IP, domain name" +MYSQL_HOSTPORT="required, 3306 or something else" +MYSQL_USERNAME="required" +MYSQL_PASSWORD="required" +MYSQL_DATABASE="required" NOVA_VM_FLAVOR_ID="required" NOVA_VM_IMAGE_ID="required" NOVA_VM_NETWORK_ID="required" diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000..0e04844 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 0000000..ec9d45c --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,50 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic,flask_migrate + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000..4c97092 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,113 @@ +import logging +from logging.config import fileConfig + +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + + +def get_engine(): + try: + # this works with Flask-SQLAlchemy<3 and Alchemical + return current_app.extensions['migrate'].db.get_engine() + except (TypeError, AttributeError): + # this works with Flask-SQLAlchemy>=3 + return current_app.extensions['migrate'].db.engine + + +def get_engine_url(): + try: + return get_engine().url.render_as_string(hide_password=False).replace( + '%', '%%') + except AttributeError: + return str(get_engine().url).replace('%', '%%') + + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option('sqlalchemy.url', get_engine_url()) +target_db = current_app.extensions['migrate'].db + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def get_metadata(): + if hasattr(target_db, 'metadatas'): + return target_db.metadatas[None] + return target_db.metadata + + +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. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=get_metadata(), literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +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. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + conf_args = current_app.extensions['migrate'].configure_args + if conf_args.get("process_revision_directives") is None: + conf_args["process_revision_directives"] = process_revision_directives + + connectable = get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=get_metadata(), + **conf_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/0b4fe7004833_update_schema.py b/migrations/versions/0b4fe7004833_update_schema.py new file mode 100644 index 0000000..240f670 --- /dev/null +++ b/migrations/versions/0b4fe7004833_update_schema.py @@ -0,0 +1,101 @@ +"""update schema + +Revision ID: 0b4fe7004833 +Revises: ffcc21ab6b1b +Create Date: 2024-12-14 10:46:08.607623 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '0b4fe7004833' +down_revision = 'ffcc21ab6b1b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('cinder_volumes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('openstack_cinder_volume_id', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('openstack_cinder_volume_id') + ) + op.create_table('nova_vms', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('status', sa.String(length=50), nullable=False), + sa.Column('floating_ip', sa.String(length=100), nullable=True), + sa.Column('openstack_nova_vm_id', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('openstack_nova_vm_id') + ) + op.create_table('plans', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('type', sa.String(length=150), nullable=False), + sa.Column('name', sa.String(length=255), nullable=False), + sa.Column('storage_in_gb', sa.Float(), nullable=False), + sa.Column('ram_in_gb', sa.Float(), nullable=False), + sa.Column('cpu_cores', sa.Integer(), nullable=False), + sa.Column('has_redis', sa.Boolean(), nullable=False), + sa.Column('has_mysql', sa.Boolean(), nullable=False), + sa.Column('monthly_fee_in_usd', sa.Float(), nullable=False), + sa.Column('image_reference', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('image_reference') + ) + op.create_table('websites', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=255), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('plan_id', sa.Integer(), nullable=False), + sa.Column('status', sa.String(length=50), nullable=False), + sa.Column('public_port', sa.Integer(), nullable=False), + sa.Column('nova_vm_port', sa.Integer(), nullable=False), + sa.Column('nova_vm_id', sa.Integer(), nullable=False), + sa.Column('cinder_volume_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint(['cinder_volume_id'], ['cinder_volumes.id'], onupdate='RESTRICT', ondelete='RESTRICT'), + sa.ForeignKeyConstraint(['nova_vm_id'], ['nova_vms.id'], onupdate='RESTRICT', ondelete='RESTRICT'), + sa.ForeignKeyConstraint(['plan_id'], ['plans.id'], onupdate='RESTRICT', ondelete='RESTRICT'), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], onupdate='RESTRICT', ondelete='RESTRICT'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('nova_vm_port', 'nova_vm_id', name='unique_nova_vm_port_id'), + sa.UniqueConstraint('public_port') + ) + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.alter_column('email', + existing_type=mysql.VARCHAR(length=150), + nullable=False) + batch_op.alter_column('password', + existing_type=mysql.VARCHAR(length=150), + type_=sa.Text(), + nullable=False) + batch_op.alter_column('name', + existing_type=mysql.VARCHAR(length=150), + nullable=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.alter_column('name', + existing_type=mysql.VARCHAR(length=150), + nullable=True) + batch_op.alter_column('password', + existing_type=sa.Text(), + type_=mysql.VARCHAR(length=150), + nullable=True) + batch_op.alter_column('email', + existing_type=mysql.VARCHAR(length=150), + nullable=True) + + op.drop_table('websites') + op.drop_table('plans') + op.drop_table('nova_vms') + op.drop_table('cinder_volumes') + # ### end Alembic commands ### diff --git a/migrations/versions/ffcc21ab6b1b_initial_migration.py b/migrations/versions/ffcc21ab6b1b_initial_migration.py new file mode 100644 index 0000000..0294d31 --- /dev/null +++ b/migrations/versions/ffcc21ab6b1b_initial_migration.py @@ -0,0 +1,35 @@ +"""Initial migration + +Revision ID: ffcc21ab6b1b +Revises: +Create Date: 2024-12-04 22:20:13.912834 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ffcc21ab6b1b' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('email', sa.String(length=150), nullable=True), + sa.Column('password', sa.String(length=150), nullable=True), + sa.Column('name', sa.String(length=150), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index 03c25ad..af10e25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,15 +25,17 @@ jsonpointer==3.0.0 keystoneauth1==5.9.1 Mako==1.3.7 MarkupSafe==3.0.2 +mysql-connector-python==9.1.0 +mysqlclient==2.2.6 openstacksdk==4.2.0 os-service-types==1.7.0 paramiko==3.5.0 pbr==6.1.0 platformdirs==4.3.6 psutil==6.1.0 -psycopg2==2.9.10 pycparser==2.22 PyJWT==2.10.1 +PyMySQL==1.1.1 PyNaCl==1.5.0 python-dotenv==1.0.1 PyYAML==6.0.2