Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Alembic migrations using flask-alembic #15

Merged
merged 1 commit into from
Jun 27, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -15,10 +15,10 @@ our code management microservice ecosystem.

##### Running the development server

To create a database schema:
To create a database:

```bash
$ invoke create_db
$ invoke upgrade
```

To build and start the development services' containers:
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -8,8 +8,6 @@ services:
build:
context: ./
dockerfile: ./docker/Dockerfile-dev
volumes:
- ./:/app
ports:
- "8888:80"
environment:
@@ -20,7 +18,9 @@ services:
- TRANSPLANT_URL=https://stub.transplant.example.com
- DATABASE_URL=sqlite:////db/sqlite.db
volumes:
- ./:/app
- ./.db/:/db/
- ./migrations/:/migrations/
py3-linter:
build:
context: ./
2 changes: 2 additions & 0 deletions docker/Dockerfile-prod
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ RUN apk --update --no-cache add \
RUN mkdir /db
RUN chown app:app /db

COPY migrations /migrations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this also need to be applied to Dockerfile-dev?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is linked in docker-compose


COPY requirements.txt /requirements.txt
RUN pip install --no-cache -r /requirements.txt

Binary file removed docker/db/schema.db
Binary file not shown.
10 changes: 9 additions & 1 deletion landoapi/app.py
Original file line number Diff line number Diff line change
@@ -5,9 +5,10 @@

import click
import connexion

from connexion.resolver import RestyResolver
from landoapi.dockerflow import dockerflow
from landoapi.models.storage import db
from landoapi.models.storage import alembic, db


def create_app(version_path):
@@ -22,9 +23,16 @@ def create_app(version_path):
'SQLALCHEMY_DATABASE_URI', os.environ.get('DATABASE_URL', 'sqlite://')
)
flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
flask_app.config['ALEMBIC'] = {'script_location': '/migrations/'}

flask_app.register_blueprint(dockerflow)

# Initialize database
db.init_app(flask_app)

# Intialize the alembic extension
alembic.init_app(app.app)

return app


15 changes: 11 additions & 4 deletions landoapi/manage.py
Original file line number Diff line number Diff line change
@@ -3,18 +3,25 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from landoapi.app import create_app
from landoapi.models.storage import db
from landoapi.models.storage import alembic

from flask_script import Manager

app = create_app('/version.json')

manager = Manager(app.app)


@manager.command
def create_db():
"""Creates SQLAlchemy database schema."""
return db.create_all()
def revision(message):
"""Generate a new migration revision."""
return alembic.revision(message)


@manager.command
def upgrade():
"""Run all available migration upgrades."""
return alembic.upgrade()


if __name__ == "__main__":
2 changes: 2 additions & 0 deletions landoapi/models/storage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from flask_alembic import Alembic
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
alembic = Alembic()
10 changes: 9 additions & 1 deletion landoapi/transplant_client.py
Original file line number Diff line number Diff line change
@@ -6,6 +6,9 @@
import requests
import requests_mock

from sqlalchemy import text
from landoapi.models.storage import db


class TransplantClient:
""" A class to interface with Transplant's API. """
@@ -19,10 +22,15 @@ def land(self, ldap_username, tree, request):

Returns request_id received from Transplant API.
"""
# get the number of Landing objects to create the unique request_id
sql = text('SELECT COUNT(*) FROM landings')
result = db.session.execute(sql).fetchone()
request_id = result[0] + 1

# Connect to stubbed Transplant service
request.post(
self.api_url + '/autoland',
json={'request_id': 1},
json={'request_id': request_id},
status_code=200
)

34 changes: 34 additions & 0 deletions migrations/ae9dd729e66c_add_landings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""add landings

Revision ID: ae9dd729e66c
Revises:
Create Date: 2017-06-20 17:13:22.173811

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'ae9dd729e66c'
down_revision = None
branch_labels = ('default', )
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
'landings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('request_id', sa.Integer(), nullable=True),
sa.Column('revision_id', sa.String(length=30), nullable=True),
sa.Column('status', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('request_id')
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('landings')
# ### end Alembic commands ###
24 changes: 24 additions & 0 deletions migrations/script.py.mako
Original file line number Diff line number Diff line change
@@ -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"}
13 changes: 13 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -57,3 +57,16 @@ Flask-SQLAlchemy==2.2 \
--hash=sha256:fa3bd6343de231b2b3376a276df6d331c63c766449b660fc04d375a1c21312ac
Flask-Script==2.0.5 \
--hash=sha256:cef76eac751396355429a14c38967bb14d4973c53e07dec94af5cc8fb017107f
alembic==0.9.2 \
--hash=sha256:a2b6b599f48b39ebf33b7dd069463924ca59c94aae7ae6f3661e0e54b5e78647
Mako==1.0.6 \
--hash=sha256:48559ebd872a8e77f92005884b3d88ffae552812cdf17db6768e5c3be5ebbe0d
python-editor==1.0.3 \
--hash=sha256:a3c066acee22a1c94f63938341d4fb374e3fdd69366ed6603d7b24bed1efc565
python-dateutil==2.6.0 \
--hash=sha256:62a2f8df3d66f878373fd0072eacf4ee52194ba302e00082828e0d263b0418d2 \
--hash=sha256:3acbef017340600e9ff8f2994d8f7afd6eacb295383f286466a6df3961e486f0 \
--hash=sha256:537bf2a8f8ce6f6862ad705cd68f9e405c0b5db014aa40fa29eab4335d4b1716
Flask-Alembic==2.0.1 \
--hash=sha256:05a1e6f4148dbfcc9280a393373bfbd250af6f9f4f0ca9f744ef8f7376a3deec \
--hash=sha256:7e67740b0b08d58dcae0c701d56b56e60f5fa4af907bb82b4cb0469229ba94ff
21 changes: 15 additions & 6 deletions tasks.py
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ def version(ctx):
print(json.dumps(version))


@task(name='build')
@task
def build(ctx):
"""Build the production docker image."""
ctx.run(
@@ -89,12 +89,21 @@ def imageid(ctx):
)


@task(name='create_db')
def create_db(ctx):
"""Call `create_all` on a SQLAlchemy database."""
@task(name='add-migration')
def add_migration(ctx, msg):
"""Call Alembic to create a migration revision"""
ctx.run(
"docker-compose run --rm lando-api "
"python landoapi/manage.py revision '%s'" % msg
)


@task
def upgrade(ctx):
"""Call Alembic to run all available migration upgrades."""
ctx.run(
"docker-compose run --rm lando-api "
"python landoapi/manage.py create_db"
"python landoapi/manage.py upgrade"
)


@@ -104,5 +113,5 @@ def create_db(ctx):
lint_all,
lint_flake8,
lint_yapf,
), build, create_db, format, imageid, test, version
), add_migration, build, format, imageid, test, upgrade, version
)
20 changes: 20 additions & 0 deletions tests/test_landings.py
Original file line number Diff line number Diff line change
@@ -47,6 +47,26 @@ def test_landing_revision(db, client, phabfactory):
'status': TRANSPLANT_JOB_STARTED
}

response = client.post(
'/landings?api_key=api-key',
data=json.dumps({
'revision_id': 'D1'
}),
content_type='application/json'
)
assert response.status_code == 202
assert response.content_type == 'application/json'
assert response.json == {'id': 2}

# test saved data
landing = Landing.query.get(2)
assert landing.serialize() == {
'id': 2,
'request_id': 2,
'revision_id': 'D1',
'status': TRANSPLANT_JOB_STARTED
}


def test_get_transplant_status(db, client):
Landing(1, 'D1', 'started').save(True)