Add database migration for project tags

This change adds the SQL database migration for adding a new
project_tag table that consists of an project_id and name. The
project_id is the id of a project that the tag is mapped to and
the name is the string value of the tag.

Partially-Implements: bp project-tags
Co-Authored-By: Pilla, Samuel (sp516w) <sp516w@att.com>
Co-Authored-By: Tin Lam <tin@irrational.io>

Change-Id: Ib3db74cb08e7fdec01d55d8988e0906948d80a32
This commit is contained in:
Gage Hugo 2017-07-17 13:47:49 -05:00
parent 400259f58a
commit 99ad40ef94
5 changed files with 143 additions and 0 deletions

View File

@ -0,0 +1,15 @@
# 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.
def upgrade(migrate_engine):
pass

View File

@ -71,6 +71,7 @@ PrimaryKeyConstraint = sql.PrimaryKeyConstraint
joinedload = sql.orm.joinedload
# Suppress flake8's unused import warning for flag_modified:
flag_modified = flag_modified
Unicode = sql.Unicode
def initialize():

View File

@ -0,0 +1,15 @@
# 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.
def upgrade(migrate_engine):
pass

View File

@ -0,0 +1,44 @@
# 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
def upgrade(migrate_engine):
meta = sql.MetaData()
meta.bind = migrate_engine
project_table = sql.Table('project', meta, autoload=True)
# NOTE(lamt) To allow tag name to be case sensitive for MySQL, the 'name'
# column needs to use collation, which is incompatible with Postgresql.
# Using unicode to mirror nova's server tag:
# https://github.com/openstack/nova/blob/master/nova/db/sqlalchemy/models.py
project_tags_table = sql.Table(
'project_tag',
meta,
sql.Column('project_id',
sql.String(64),
sql.ForeignKey(project_table.c.id, ondelete='CASCADE'),
nullable=False,
primary_key=True),
sql.Column('name',
sql.Unicode(255),
nullable=False,
primary_key=True),
sql.UniqueConstraint('project_id', 'name'),
mysql_engine='InnoDB',
mysql_charset='utf8'
)
project_tags_table.create(migrate_engine, checkfirst=True)

View File

@ -2421,6 +2421,74 @@ class FullMigration(SqlMigrateBase, unit.TestCase):
pw_table = sqlalchemy.Table('password', meta, autoload=True)
self.assertFalse(pw_table.c.created_at_int.nullable)
def test_migration_30_expand_add_project_tags_table(self):
self.expand(29)
self.migrate(29)
self.contract(29)
table_name = 'project_tag'
self.assertTableDoesNotExist(table_name)
self.expand(30)
self.migrate(30)
self.contract(30)
self.assertTableExists(table_name)
self.assertTableColumns(
table_name,
['project_id', 'name'])
def test_migration_030_project_tags_works_correctly_after_migration(self):
if self.engine.name == 'sqlite':
self.skipTest('sqlite backend does not support foreign keys')
self.expand(30)
self.migrate(30)
self.contract(30)
project_table = sqlalchemy.Table(
'project', self.metadata, autoload=True)
tag_table = sqlalchemy.Table(
'project_tag', self.metadata, autoload=True)
session = self.sessionmaker()
project_id = uuid.uuid4().hex
project = {
'id': project_id,
'name': uuid.uuid4().hex,
'enabled': True,
'domain_id': resource_base.NULL_DOMAIN_ID,
'is_domain': False
}
tag = {
'project_id': project_id,
'name': uuid.uuid4().hex
}
self.insert_dict(session, 'project', project)
self.insert_dict(session, 'project_tag', tag)
tags_query = session.query(tag_table).filter_by(
project_id=project_id).all()
self.assertThat(tags_query, matchers.HasLength(1))
# Adding duplicate tags should cause error.
self.assertRaises(db_exception.DBDuplicateEntry,
self.insert_dict,
session, 'project_tag', tag)
session.execute(
project_table.delete().where(project_table.c.id == project_id)
)
tags_query = session.query(tag_table).filter_by(
project_id=project_id).all()
self.assertThat(tags_query, matchers.HasLength(0))
session.close()
class MySQLOpportunisticFullMigration(FullMigration):
FIXTURE = test_base.MySQLOpportunisticFixture