From b4b126d41c4de71794ed3b601d511e15c9718af9 Mon Sep 17 00:00:00 2001 From: isethi Date: Mon, 28 Jan 2013 22:17:07 +0000 Subject: [PATCH] Add status column to image_members Related to bp glance-api-v2-image-sharing Change-Id: Id8896f9dcb717ab8a466829b3b1cd9fde4b67a36 --- .../versions/016_add_status_image_member.py | 37 +++++++ glance/db/sqlalchemy/models.py | 1 + glance/tests/unit/test_migrations.py | 98 +++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 glance/db/sqlalchemy/migrate_repo/versions/016_add_status_image_member.py diff --git a/glance/db/sqlalchemy/migrate_repo/versions/016_add_status_image_member.py b/glance/db/sqlalchemy/migrate_repo/versions/016_add_status_image_member.py new file mode 100644 index 0000000000..3d6e914672 --- /dev/null +++ b/glance/db/sqlalchemy/migrate_repo/versions/016_add_status_image_member.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import MetaData, Table, Column, String + + +meta = MetaData() + +status = Column('status', String(20), default="pending") + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + + image_members = Table('image_members', meta, autoload=True) + image_members.create_column(status) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + + image_members = Table('image_members', meta, autoload=True) + image_members.drop_column(status) diff --git a/glance/db/sqlalchemy/models.py b/glance/db/sqlalchemy/models.py index 9dc2423cf8..bd8c5e854e 100644 --- a/glance/db/sqlalchemy/models.py +++ b/glance/db/sqlalchemy/models.py @@ -151,6 +151,7 @@ class ImageMember(BASE, ModelBase): member = Column(String(255), nullable=False) can_share = Column(Boolean, nullable=False, default=False) + status = Column(String(20), nullable=False, default="pending") def register_models(engine): diff --git a/glance/tests/unit/test_migrations.py b/glance/tests/unit/test_migrations.py index 94369d616c..1571ca8592 100644 --- a/glance/tests/unit/test_migrations.py +++ b/glance/tests/unit/test_migrations.py @@ -383,3 +383,101 @@ class TestMigrations(utils.BaseTestCase): migration_api.downgrade(14) self.assertEqual(get_locations(), unquoted_locations) + + def test_no_data_loss_15_to_16_to_15(self): + """ + Here, we test that in the case when we moved a column "type" from the + base images table to be records in the image_properties table, that + we don't lose any data during the migration. Similarly, we test that + on downgrade, we don't lose any data, as the records are moved from + the image_properties table back into the base image table. + """ + for key, engine in self.engines.items(): + self.config(sql_connection=TestMigrations.TEST_DATABASES[key]) + self._no_data_loss_15_to_16_to_15(engine) + + def _no_data_loss_15_to_16_to_15(self, engine): + migration_api.version_control(version=0) + migration_api.upgrade(15) + + cur_version = migration_api.db_version() + self.assertEquals(15, cur_version) + + # We are now on version 15. + + image_members_table = Table('image_members', MetaData(), + autoload=True, autoload_with=engine) + + self.assertTrue('status' not in image_members_table.c, + "'status' not column found in image_members table " + "columns! image_members table columns: %s" + % image_members_table.c.keys()) + + conn = engine.connect() + sel = select([func.count("*")], from_obj=[image_members_table]) + orig_num_image_members = conn.execute(sel).scalar() + + now = datetime.datetime.now() + inserter = image_members_table.insert() + conn.execute(inserter, [ + {'deleted': False, 'created_at': now, 'member': 'fake-member', + 'updated_at': now, 'can_share': False, + 'image_id': 'fake-image-id1'}]) + + sel = select([func.count("*")], from_obj=[image_members_table]) + num_image_members = conn.execute(sel).scalar() + self.assertEqual(orig_num_image_members + 1, num_image_members) + conn.close() + + #Upgrade to version 16 + + migration_api.upgrade(16) + + cur_version = migration_api.db_version() + self.assertEquals(16, cur_version) + + image_members_table = Table('image_members', MetaData(), + autoload=True, autoload_with=engine) + + self.assertTrue('status' in image_members_table.c, + "'status' column found in image_members table " + "columns! image_members table columns: %s" + % image_members_table.c.keys()) + + conn = engine.connect() + sel = select([func.count("*")], from_obj=[image_members_table]) + num_image_members = conn.execute(sel).scalar() + self.assertEqual(orig_num_image_members + 1, num_image_members) + + now = datetime.datetime.now() + inserter = image_members_table.insert() + conn.execute(inserter, [ + {'deleted': False, 'created_at': now, 'member': 'fake-member', + 'updated_at': now, 'can_share': False, 'status': 'pending', + 'image_id': 'fake-image-id2'}]) + + sel = select([func.count("*")], from_obj=[image_members_table]) + num_image_members = conn.execute(sel).scalar() + self.assertEqual(orig_num_image_members + 2, num_image_members) + conn.close() + + #Downgrade to version 15 + + migration_api.downgrade(15) + + cur_version = migration_api.db_version() + self.assertEquals(15, cur_version) + + image_members_table = Table('image_members', MetaData(), + autoload=True, autoload_with=engine) + + self.assertTrue('status' not in image_members_table.c, + "'status' column not found in image_members table " + "columns! image_members table columns: %s" + % image_members_table.c.keys()) + + conn = engine.connect() + sel = select([func.count("*")], from_obj=[image_members_table]) + num_image_members = conn.execute(sel).scalar() + self.assertEqual(orig_num_image_members + 2, num_image_members) + conn.close()