summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--keystone/common/sql/migrate_repo/versions/088_domain_specific_roles.py31
-rw-r--r--keystone/common/sql/migrate_repo/versions/096_drop_role_name_constraint.py50
-rw-r--r--keystone/tests/unit/test_sql_upgrade.py93
3 files changed, 170 insertions, 4 deletions
diff --git a/keystone/common/sql/migrate_repo/versions/088_domain_specific_roles.py b/keystone/common/sql/migrate_repo/versions/088_domain_specific_roles.py
index 92f2b906d..8b792dfa7 100644
--- a/keystone/common/sql/migrate_repo/versions/088_domain_specific_roles.py
+++ b/keystone/common/sql/migrate_repo/versions/088_domain_specific_roles.py
@@ -13,9 +13,10 @@
import migrate
import sqlalchemy as sql
-_ROLE_NAME_OLD_CONSTRAINT = 'ixu_role_name'
+
_ROLE_NAME_NEW_CONSTRAINT = 'ixu_role_name_domain_id'
_ROLE_TABLE_NAME = 'role'
+_ROLE_NAME_COLUMN_NAME = 'name'
_DOMAIN_ID_COLUMN_NAME = 'domain_id'
_NULL_DOMAIN_ID = '<<null>>'
@@ -27,10 +28,32 @@ def upgrade(migrate_engine):
role_table = sql.Table(_ROLE_TABLE_NAME, meta, autoload=True)
domain_id = sql.Column(_DOMAIN_ID_COLUMN_NAME, sql.String(64),
nullable=False, server_default=_NULL_DOMAIN_ID)
- role_table.create_column(domain_id)
- migrate.UniqueConstraint(role_table.c.name,
- name=_ROLE_NAME_OLD_CONSTRAINT).drop()
+ # NOTE(morganfainberg): the `role_name` unique constraint is not
+ # guaranteed to be a fixed name, such as 'ixu_role_name`, so we need to
+ # search for the correct constraint that only affects role_table.c.name
+ # and drop that constraint.
+ to_drop = None
+ if migrate_engine.name == 'mysql':
+ for c in role_table.indexes:
+ if (c.unique and len(c.columns) == 1 and
+ _ROLE_NAME_COLUMN_NAME in c.columns):
+ to_drop = c
+ break
+ else:
+ for c in role_table.constraints:
+ if len(c.columns) == 1 and _ROLE_NAME_COLUMN_NAME in c.columns:
+ to_drop = c
+ break
+
+ if to_drop is not None:
+ migrate.UniqueConstraint(role_table.c.name,
+ name=to_drop.name).drop()
+
+ # perform changes after constraint is dropped.
+ if 'domain_id' not in role_table.columns:
+ # Only create the column if it doesn't already exist.
+ role_table.create_column(domain_id)
migrate.UniqueConstraint(role_table.c.name,
role_table.c.domain_id,
diff --git a/keystone/common/sql/migrate_repo/versions/096_drop_role_name_constraint.py b/keystone/common/sql/migrate_repo/versions/096_drop_role_name_constraint.py
new file mode 100644
index 000000000..0156de217
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/096_drop_role_name_constraint.py
@@ -0,0 +1,50 @@
+# 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.
+
+import migrate
+import sqlalchemy as sql
+
+_ROLE_TABLE_NAME = 'role'
+_ROLE_NAME_COLUMN_NAME = 'name'
+
+
+def upgrade(migrate_engine):
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ role_table = sql.Table(_ROLE_TABLE_NAME, meta, autoload=True)
+
+ # NOTE(morganfainberg): the `role_name` unique constraint is not
+ # guaranteed to be named 'ixu_role_name', so we need to search for the
+ # correct constraint that only affects role_table.c.name and drop
+ # that constraint.
+ #
+ # This is an idempotent change that reflects the fix to migration
+ # 88 if the role_name unique constraint was not named consistently and
+ # someone manually fixed the migrations / db without dropping the
+ # old constraint.
+ to_drop = None
+ if migrate_engine.name == 'mysql':
+ for c in role_table.indexes:
+ if (c.unique and len(c.columns) == 1 and
+ _ROLE_NAME_COLUMN_NAME in c.columns):
+ to_drop = c
+ break
+ else:
+ for c in role_table.constraints:
+ if len(c.columns) == 1 and _ROLE_NAME_COLUMN_NAME in c.columns:
+ to_drop = c
+ break
+
+ if to_drop is not None:
+ migrate.UniqueConstraint(role_table.c.name,
+ name=to_drop.name).drop()
diff --git a/keystone/tests/unit/test_sql_upgrade.py b/keystone/tests/unit/test_sql_upgrade.py
index 207122ef6..5ca12f66f 100644
--- a/keystone/tests/unit/test_sql_upgrade.py
+++ b/keystone/tests/unit/test_sql_upgrade.py
@@ -32,6 +32,7 @@ WARNING::
import json
import uuid
+import migrate
from migrate.versioning import api as versioning_api
from migrate.versioning import repository
import mock
@@ -494,6 +495,11 @@ class SqlUpgradeTests(SqlMigrateBase):
table = sqlalchemy.Table(table_name, meta, autoload=True)
return index_name in [idx.name for idx in table.indexes]
+ def does_constraint_exist(self, table_name, constraint_name):
+ meta = sqlalchemy.MetaData(bind=self.engine)
+ table = sqlalchemy.Table(table_name, meta, autoload=True)
+ return constraint_name in [con.name for con in table.constraints]
+
def test_endpoint_policy_upgrade(self):
self.assertTableDoesNotExist('policy_association')
self.upgrade(81)
@@ -1059,6 +1065,93 @@ class SqlUpgradeTests(SqlMigrateBase):
# assert id column is an integer (after)
self.assertEqual('INTEGER', str(revocation_event_table.c.id.type))
+ def _add_unique_constraint_to_role_name(self,
+ constraint_name='ixu_role_name'):
+ meta = sqlalchemy.MetaData()
+ meta.bind = self.engine
+ role_table = sqlalchemy.Table('role', meta, autoload=True)
+ migrate.UniqueConstraint(role_table.c.name,
+ name=constraint_name).create()
+
+ def _drop_unique_constraint_to_role_name(self,
+ constraint_name='ixu_role_name'):
+ role_table = sqlalchemy.Table('role', self.metadata, autoload=True)
+ migrate.UniqueConstraint(role_table.c.name,
+ name=constraint_name).drop()
+
+ def test_migration_88_drops_unique_constraint(self):
+ self.upgrade(87)
+ if self.engine.name == 'mysql':
+ self.assertTrue(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertTrue(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+ self.upgrade(88)
+ if self.engine.name == 'mysql':
+ self.assertFalse(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertFalse(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+
+ def test_migration_88_inconsistent_constraint_name(self):
+ self.upgrade(87)
+ self._drop_unique_constraint_to_role_name()
+
+ constraint_name = uuid.uuid4().hex
+ self._add_unique_constraint_to_role_name(
+ constraint_name=constraint_name)
+
+ if self.engine.name == 'mysql':
+ self.assertTrue(self.does_index_exist('role', constraint_name))
+ self.assertFalse(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertTrue(self.does_constraint_exist('role',
+ constraint_name))
+ self.assertFalse(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+
+ self.upgrade(88)
+ if self.engine.name == 'mysql':
+ self.assertFalse(self.does_index_exist('role', constraint_name))
+ self.assertFalse(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertFalse(self.does_constraint_exist('role',
+ constraint_name))
+ self.assertFalse(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+
+ def test_migration_96(self):
+ self.upgrade(95)
+ if self.engine.name == 'mysql':
+ self.assertFalse(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertFalse(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+
+ self.upgrade(96)
+ if self.engine.name == 'mysql':
+ self.assertFalse(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertFalse(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+
+ def test_migration_96_constraint_exists(self):
+ self.upgrade(95)
+ self._add_unique_constraint_to_role_name()
+
+ if self.engine.name == 'mysql':
+ self.assertTrue(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertTrue(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+
+ self.upgrade(96)
+ if self.engine.name == 'mysql':
+ self.assertFalse(self.does_index_exist('role', 'ixu_role_name'))
+ else:
+ self.assertFalse(self.does_constraint_exist('role',
+ 'ixu_role_name'))
+
class VersionTests(SqlMigrateBase):