From 83f18928516637c0efc5d05631f51b5f0538e742 Mon Sep 17 00:00:00 2001 From: Giulia Naponiello Date: Feb 12 2018 17:05:26 +0000 Subject: Changes on schema migration for backward compatibility In changing waiverdb to record waivers against a subject+testcase, rather than a result_id, we wrote a schema migration which just drops the old column. Now that result_id it is used we needed to make some changes for backward compatibility. --- diff --git a/waiverdb/migrations/versions/f2772c2c64a6_waive_absence_of_result.py b/waiverdb/migrations/versions/f2772c2c64a6_waive_absence_of_result.py index 7d3537f..8238b1d 100644 --- a/waiverdb/migrations/versions/f2772c2c64a6_waive_absence_of_result.py +++ b/waiverdb/migrations/versions/f2772c2c64a6_waive_absence_of_result.py @@ -8,6 +8,10 @@ Create Date: 2017-12-04 10:03:54.792758 from alembic import op import sqlalchemy as sa +import requests + +from waiverdb.api_v1 import get_resultsdb_result +from waiverdb.models import db, Waiver # revision identifiers, used by Alembic. @@ -15,23 +19,54 @@ revision = 'f2772c2c64a6' down_revision = '0a74cdab732a' +def convert_id_to_subject_and_testcase(result_id): + try: + result = get_resultsdb_result(result_id) + except requests.HTTPError as e: + if e.response.status_code == 404: + raise RuntimeError('Result id %s not found in Resultsdb' % (result_id)) + else: + raise RuntimeError('Failed looking up result in Resultsdb: %s' % e) + except Exception as e: + raise RuntimeError('Failed looking up result in Resultsdb: %s' % e) + if 'original_spec_nvr' in result['data']: + subject = {'original_spec_nvr': result['data']['original_spec_nvr'][0]} + else: + if result['data']['type'][0] == 'koji_build' or \ + result['data']['type'][0] == 'bodhi_update': + SUBJECT_KEYS = ['item', 'type'] + subject = dict([(k, v[0]) for k, v in result['data'].items() + if k in SUBJECT_KEYS]) + else: + raise RuntimeError('Unable to determine subject for result id %s' % (result_id)) + testcase = result['testcase']['name'] + return (subject, testcase) + + def upgrade(): op.add_column('waiver', sa.Column('subject', sa.Text(), nullable=True, index=True)) op.add_column('waiver', sa.Column('testcase', sa.Text(), nullable=True, index=True)) + # querying resultsdb for the corresponding subject/testcase for each result_id + waivers = Waiver.query.all() + for waiver in waivers: + subject, testcase = convert_id_to_subject_and_testcase(waiver.result_id) + waiver.subject = subject + waiver.testcase = testcase + db.session.commit() + # SQLite has some problem in dropping/altering columns. # So in this way Alembic should do some behind the scenes # with: make new table - copy data - drop old table - rename new table with op.batch_alter_table('waiver') as batch_op: batch_op.alter_column('subject', nullable=False) batch_op.alter_column('testcase', nullable=False) - batch_op.drop_column('result_id') + batch_op.alter_column('result_id', nullable=True) def downgrade(): - op.add_column('waiver', sa.Column('result_id', sa.INTEGER(), nullable=True)) - - with op.batch_alter_table('waiver') as batch_op: - batch_op.drop_column('testcase') - batch_op.drop_column('subject') - batch_op.alter_column('result_id', nullable=False) + # It shouldn't be possible to downgrade this change. + # Because the result_id field will not be populated with data anymore. + # If the user tries to downgrade "result_id" should be not null once again + # like in the old version of the schema, but the value is not longer available + raise RuntimeError('Irreversible migration') diff --git a/waiverdb/models/waivers.py b/waiverdb/models/waivers.py index c45c2a5..094994f 100644 --- a/waiverdb/models/waivers.py +++ b/waiverdb/models/waivers.py @@ -8,6 +8,7 @@ from sqlalchemy_utils import JSONType class Waiver(db.Model): id = db.Column(db.Integer, primary_key=True) + result_id = db.Column(db.Integer, nullable=True) subject = db.Column(JSONType, nullable=False, index=True) testcase = db.Column(db.Text, nullable=False, index=True) username = db.Column(db.String(255), nullable=False) @@ -28,9 +29,9 @@ class Waiver(db.Model): self.proxied_by = proxied_by def __repr__(self): - return '%s(subject=%r, testcase=%r, username=%r, product_version=%r, waived=%r)' % \ - (self.__class__.__name__, self.subject, self.testcase, self.username, - self.product_version, self.waived) + return '%s(result_id=%r, subject=%r, testcase=%r, username=%r, product_version=%r,\ + waived=%r)' % (self.__class__.__name__, self.result_id, self.subject, self.testcase, + self.username, self.product_version, self.waived) @classmethod def by_results(cls, query, results):