From c9c7ff9bfe725cbd9357ac8739804e5e7755b2b7 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Thu, 9 Apr 2015 13:13:33 +1000 Subject: [PATCH] Move endpoint_policy migrations into keystone core This is the most basic way to move the endpoint_policy migrations into the keystone core migrations path. Check to see if the migration was performed already via the extension and if so skip performing it again. Change-Id: Id1cd0143951a8086803fe739ea5f143adb1ee6ba Implements: bp move-extensions --- .../versions/081_add_endpoint_policy_table.py | 54 +++++++++++++++++++ keystone/common/sql/migration_helpers.py | 6 ++- .../versions/001_add_endpoint_policy_table.py | 25 +-------- keystone/exception.py | 10 ++++ .../tests/unit/test_sql_migrate_extensions.py | 9 ++-- keystone/tests/unit/test_sql_upgrade.py | 23 ++++++++ 6 files changed, 98 insertions(+), 29 deletions(-) create mode 100644 keystone/common/sql/migrate_repo/versions/081_add_endpoint_policy_table.py diff --git a/keystone/common/sql/migrate_repo/versions/081_add_endpoint_policy_table.py b/keystone/common/sql/migrate_repo/versions/081_add_endpoint_policy_table.py new file mode 100644 index 0000000000..a0c307d08d --- /dev/null +++ b/keystone/common/sql/migrate_repo/versions/081_add_endpoint_policy_table.py @@ -0,0 +1,54 @@ +# Copyright 2014 IBM Corp. +# +# 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 sqlalchemy as sql + +from keystone.common.sql import migration_helpers + + +def upgrade(migrate_engine): + try: + extension_version = migration_helpers.get_db_version( + extension='endpoint_policy', + engine=migrate_engine) + except Exception: + extension_version = 0 + + # This migration corresponds to endpoint_policy extension migration 1. Only + # update if it has not been run. + if extension_version >= 1: + return + + # Upgrade operations go here. Don't create your own engine; bind + # migrate_engine to your metadata + meta = sql.MetaData() + meta.bind = migrate_engine + + endpoint_policy_table = sql.Table( + 'policy_association', + meta, + sql.Column('id', sql.String(64), primary_key=True), + sql.Column('policy_id', sql.String(64), + nullable=False), + sql.Column('endpoint_id', sql.String(64), + nullable=True), + sql.Column('service_id', sql.String(64), + nullable=True), + sql.Column('region_id', sql.String(64), + nullable=True), + sql.UniqueConstraint('endpoint_id', 'service_id', 'region_id'), + mysql_engine='InnoDB', + mysql_charset='utf8') + + endpoint_policy_table.create(migrate_engine, checkfirst=True) diff --git a/keystone/common/sql/migration_helpers.py b/keystone/common/sql/migration_helpers.py index aaa59f7042..9678fc30e6 100644 --- a/keystone/common/sql/migration_helpers.py +++ b/keystone/common/sql/migration_helpers.py @@ -35,12 +35,13 @@ from keystone.i18n import _ CONF = cfg.CONF DEFAULT_EXTENSIONS = ['endpoint_filter', - 'endpoint_policy', 'federation', 'oauth1', 'revoke', ] +MIGRATED_EXTENSIONS = ['endpoint_policy'] + def get_default_domain(): # Return the reference used for the default domain structure during @@ -161,6 +162,9 @@ def _assert_not_schema_downgrade(extension=None, version=None): def _sync_extension_repo(extension, version): + if extension in MIGRATED_EXTENSIONS: + raise exception.MigrationMovedFailure(extension=extension) + init_version = 0 engine = sql.get_engine() diff --git a/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py b/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py index 5c22f1699b..32bdabdd31 100644 --- a/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py +++ b/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py @@ -12,29 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. -import sqlalchemy as sql +from keystone import exception def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; bind - # migrate_engine to your metadata - meta = sql.MetaData() - meta.bind = migrate_engine - - endpoint_policy_table = sql.Table( - 'policy_association', - meta, - sql.Column('id', sql.String(64), primary_key=True), - sql.Column('policy_id', sql.String(64), - nullable=False), - sql.Column('endpoint_id', sql.String(64), - nullable=True), - sql.Column('service_id', sql.String(64), - nullable=True), - sql.Column('region_id', sql.String(64), - nullable=True), - sql.UniqueConstraint('endpoint_id', 'service_id', 'region_id'), - mysql_engine='InnoDB', - mysql_charset='utf8') - - endpoint_policy_table.create(migrate_engine, checkfirst=True) + raise exception.MigrationMovedFailure(extension='endpoint_policy') diff --git a/keystone/exception.py b/keystone/exception.py index eb449de281..6e4ea7bf0c 100644 --- a/keystone/exception.py +++ b/keystone/exception.py @@ -491,3 +491,13 @@ class TokenlessAuthConfigError(ValidationError): message_format = _('Could not determine Identity Provider ID. The ' 'configuration option %(issuer_attribute)s ' 'was not found in the request environment.') + + +class MigrationMovedFailure(RuntimeError): + def __init__(self, extension): + self.extension = extension + msg = _("The %s extension has been moved into keystone core and as " + "such its migrations are maintained by the main keystone " + "database control. Use the command: keystone-manage " + "db_sync") % self.extension + super(MigrationMovedFailure, self).__init__(msg) diff --git a/keystone/tests/unit/test_sql_migrate_extensions.py b/keystone/tests/unit/test_sql_migrate_extensions.py index f498fe9475..89173efe38 100644 --- a/keystone/tests/unit/test_sql_migrate_extensions.py +++ b/keystone/tests/unit/test_sql_migrate_extensions.py @@ -41,6 +41,7 @@ from keystone.contrib import example from keystone.contrib import federation from keystone.contrib import oauth1 from keystone.contrib import revoke +from keystone import exception from keystone.tests.unit import test_sql_upgrade @@ -164,11 +165,9 @@ class EndpointPolicyExtension(test_sql_upgrade.SqlMigrateBase): return endpoint_policy def test_upgrade(self): - self.assertTableDoesNotExist('policy_association') - self.upgrade(1, repository=self.repo_path) - self.assertTableColumns('policy_association', - ['id', 'policy_id', 'endpoint_id', - 'service_id', 'region_id']) + self.assertRaises(exception.MigrationMovedFailure, + self.upgrade, version=1, + repository=self.repo_path) class FederationExtension(test_sql_upgrade.SqlMigrateBase): diff --git a/keystone/tests/unit/test_sql_upgrade.py b/keystone/tests/unit/test_sql_upgrade.py index aac30e83d0..aea3250758 100644 --- a/keystone/tests/unit/test_sql_upgrade.py +++ b/keystone/tests/unit/test_sql_upgrade.py @@ -34,6 +34,7 @@ import json import uuid from migrate.versioning import api as versioning_api +import mock from oslo_config import cfg from oslo_db import exception as db_exception from oslo_db.sqlalchemy import migration @@ -543,6 +544,28 @@ class SqlUpgradeTests(SqlMigrateBase): self.assertTableColumns(sensitive_table, ['domain_id', 'group', 'option', 'value']) + def test_endpoint_policy_upgrade(self): + self.assertTableDoesNotExist('policy_association') + self.upgrade(81) + self.assertTableColumns('policy_association', + ['id', 'policy_id', 'endpoint_id', + 'service_id', 'region_id']) + + @mock.patch.object(migration_helpers, 'get_db_version', return_value=1) + def test_endpoint_policy_already_migrated(self, mock_ep): + + # By setting the return value to 1, the migration has already been + # run, and there's no need to create the table again + + self.upgrade(81) + + mock_ep.assert_called_once_with(extension='endpoint_policy', + engine=mock.ANY) + + # It won't exist because we are mocking it, but we can verify + # that 081 did not create the table + self.assertTableDoesNotExist('policy_association') + def test_fixup_service_name_value_upgrade(self): """Update service name data from `extra` to empty string.""" def add_service(**extra_data):