Fix 'project_share_type_quotas' DB table unique constraint

It should be possible to use share type quotas in each project/tenant.
But we get error trying to set share types quota in second+ project
for the same share type. It happens so due to the bug in DB schema,
where we don't have 'project_id' in unique constraint.
So, add new DB migration that fixes unique constraint and cover it with
DB and tempest tests.

Change-Id: I1405ced4e12b40904b7227c9f6285e2775005f8a
Closes-Bug: #1722707
This commit is contained in:
Valeriy Ponomaryov 2017-10-13 10:54:05 +03:00
parent 647147ba10
commit 7ba41320f3
3 changed files with 103 additions and 0 deletions

View File

@ -0,0 +1,45 @@
# 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.
"""Fix 'project_share_type_quotas' unique constraint
Revision ID: 829a09b0ddd4
Revises: b516de97bfee
Create Date: 2017-10-12 20:15:51.267488
"""
# revision identifiers, used by Alembic.
revision = '829a09b0ddd4'
down_revision = 'b516de97bfee'
from alembic import op
TABLE_NAME = 'project_share_type_quotas'
UNIQUE_CONSTRAINT_NAME = 'uc_quotas_per_share_types'
ST_FK_NAME = 'share_type_id_fk'
def upgrade():
op.drop_constraint(ST_FK_NAME, TABLE_NAME, type_='foreignkey')
op.drop_constraint(UNIQUE_CONSTRAINT_NAME, TABLE_NAME, type_='unique')
op.create_foreign_key(
ST_FK_NAME, TABLE_NAME, 'share_types', ['share_type_id'], ['id'])
op.create_unique_constraint(
UNIQUE_CONSTRAINT_NAME, TABLE_NAME,
['share_type_id', 'resource', 'deleted', 'project_id'])
def downgrade():
# NOTE(vponomaryov): no need to implement old behaviour as it was bug, and,
# moreover, not compatible with data from upgraded version.
pass

View File

@ -2466,3 +2466,37 @@ class ProjectShareTypesQuotasChecks(BaseMigrationChecks):
self.test_case.assertGreater(db_result.rowcount, 0) self.test_case.assertGreater(db_result.rowcount, 0)
for row in db_result: for row in db_result:
self.test_case.assertFalse(hasattr(row, 'share_type_id')) self.test_case.assertFalse(hasattr(row, 'share_type_id'))
@map_to_migration('829a09b0ddd4')
class FixProjectShareTypesQuotasUniqueConstraintChecks(BaseMigrationChecks):
st_record_id = uuidutils.generate_uuid()
def setup_upgrade_data(self, engine):
# Create share type
self.st_data = {
'id': self.st_record_id,
'name': uuidutils.generate_uuid(),
'deleted': "False",
}
st_table = utils.load_table('share_types', engine)
engine.execute(st_table.insert(self.st_data))
def check_upgrade(self, engine, data):
for project_id in ('x' * 255, 'x'):
# Create share type quota
self.quota_data = {
'project_id': project_id,
'resource': 'y' * 255,
'hard_limit': 987654321,
'created_at': datetime.datetime(2017, 4, 11, 18, 5, 58),
'updated_at': None,
'deleted_at': None,
'deleted': 0,
'share_type_id': self.st_record_id,
}
new_table = utils.load_table('project_share_type_quotas', engine)
engine.execute(new_table.insert(self.quota_data))
def check_downgrade(self, engine):
pass

View File

@ -247,6 +247,30 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest):
for q in ('shares', 'gigabytes', 'snapshots', 'snapshot_gigabytes'): for q in ('shares', 'gigabytes', 'snapshots', 'snapshot_gigabytes'):
self.assertEqual(int(quotas[q]) - 1, current_quotas[q]) self.assertEqual(int(quotas[q]) - 1, current_quotas[q])
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
@base.skip_if_microversion_lt("2.39")
def test_update_share_type_quota_in_two_projects(self):
"""Regression test for bug/1722707"""
share_type = self._create_share_type()
client1 = self.get_client_with_isolated_creds(client_version='2')
client2 = self.get_client_with_isolated_creds(client_version='2')
for client in (client1, client2):
# Update quotas
for q in ('shares', 'gigabytes', 'snapshots',
'snapshot_gigabytes'):
# Set new quota
updated = client.update_quotas(
client.tenant_id, share_type=share_type['id'], **{q: 0})
self.assertEqual(0, int(updated[q]))
current_quotas = client.show_quotas(
client.tenant_id, share_type=share_type['id'])
for q in ('shares', 'gigabytes', 'snapshots',
'snapshot_gigabytes'):
self.assertEqual(0, int(current_quotas[q]))
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_update_tenant_quota_snapshots(self): def test_update_tenant_quota_snapshots(self):
# get current quotas # get current quotas