Fix DB migration script
Problems we had: 1. If the collation of the DB and table is utf8 and the columns we want to index are 512 chars, it is too big for the index since the max key length is 767 bytes. Following error message observed: (OperationalError) (1071, 'Specified key was too long; max key length is 767 bytes') 2. During migration to Alembic we missed the unique field for the 'fully_qualified_name' column of the table 'package'. This change fixes that issue. Fixes: 1. Reduce length of indexed columns to fix problem. 128 chars should be probably enough 2. Add uniqe paramater for column fully_qualified_name of table package Additional changes: * Test that column is unique in DB migration tests (on real databases) * Introduced base for DB-related unit-tests. These tests use in-memory instance of SQLite * Test that column is unique in DB-related unit-tests Closes-Bug: #1339201 Closes-Bug: #1339728 Change-Id: I4816790e11f225c5dbb130747535094fdf06733e
This commit is contained in:
parent
1f68419391
commit
2174fc8a84
|
@ -0,0 +1,25 @@
|
|||
# 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 murano.db import models
|
||||
from murano.db import session
|
||||
|
||||
|
||||
def setup_db():
|
||||
engine = session.get_engine()
|
||||
models.register_models(engine)
|
||||
|
||||
|
||||
def drop_db():
|
||||
engine = session.get_engine()
|
||||
models.unregister_models(engine)
|
|
@ -15,6 +15,7 @@
|
|||
from oslo.config import cfg
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm import attributes
|
||||
# TODO(ruhe) use exception declared in openstack/common/db
|
||||
from webob import exc
|
||||
|
||||
from murano.db import models
|
||||
|
|
|
@ -90,12 +90,14 @@ def upgrade():
|
|||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=False, index=True),
|
||||
sa.Column('name', sa.String(length=80), nullable=False, unique=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine=MYSQL_ENGINE,
|
||||
mysql_charset=MYSQL_CHARSET
|
||||
)
|
||||
|
||||
op.create_index('ix_category_name', 'category', ['name'])
|
||||
|
||||
op.create_table(
|
||||
'apistats',
|
||||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
|
@ -137,8 +139,10 @@ def upgrade():
|
|||
sa.Column('updated', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('archive', st.LargeBinary(), nullable=True),
|
||||
sa.Column('fully_qualified_name', sa.String(length=512),
|
||||
nullable=False, index=True),
|
||||
sa.Column('fully_qualified_name',
|
||||
sa.String(length=128),
|
||||
nullable=False,
|
||||
unique=True),
|
||||
sa.Column('type', sa.String(length=20), nullable=False),
|
||||
sa.Column('author', sa.String(length=80), nullable=True),
|
||||
sa.Column('name', sa.String(length=80), nullable=False),
|
||||
|
@ -153,6 +157,10 @@ def upgrade():
|
|||
mysql_charset=MYSQL_CHARSET
|
||||
)
|
||||
|
||||
op.create_index('ix_package_fqn',
|
||||
'package',
|
||||
['fully_qualified_name'])
|
||||
|
||||
op.create_table(
|
||||
'session',
|
||||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
|
@ -189,7 +197,10 @@ def upgrade():
|
|||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=512), nullable=False, index=True),
|
||||
sa.Column('name',
|
||||
sa.String(length=128),
|
||||
nullable=False,
|
||||
unique=True),
|
||||
sa.Column('package_id', sa.String(length=36), nullable=True),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
|
@ -197,6 +208,10 @@ def upgrade():
|
|||
mysql_charset=MYSQL_CHARSET
|
||||
)
|
||||
|
||||
op.create_index('ix_class_definition_name',
|
||||
'class_definition',
|
||||
['name'])
|
||||
|
||||
op.create_table(
|
||||
'status',
|
||||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
|
@ -241,6 +256,10 @@ def upgrade():
|
|||
|
||||
|
||||
def downgrade():
|
||||
op.drop_index('ix_category_name', table_name='category')
|
||||
op.drop_index('ix_package_fqn', table_name='package')
|
||||
op.drop_index('ix_class_definition_name', table_name='class_definition')
|
||||
|
||||
op.drop_table('status')
|
||||
op.drop_table('package_to_category')
|
||||
op.drop_table('class_definition')
|
||||
|
|
|
@ -11,10 +11,35 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import fixtures
|
||||
from oslo.config import cfg
|
||||
import testtools
|
||||
|
||||
from murano.db import api as db_api
|
||||
from murano.openstack.common import log
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('connection',
|
||||
'murano.openstack.common.db.options',
|
||||
group='database')
|
||||
log.setup('murano')
|
||||
|
||||
|
||||
class MuranoTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(MuranoTestCase, self).setUp()
|
||||
self.useFixture(fixtures.FakeLogger('murano'))
|
||||
|
||||
def override_config(self, name, override, group=None):
|
||||
CONF.set_override(name, override, group)
|
||||
self.addCleanup(CONF.clear_override, name, group)
|
||||
|
||||
|
||||
class MuranoWithDBTestCase(MuranoTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(MuranoWithDBTestCase, self).setUp()
|
||||
self.override_config('connection', "sqlite://", group='database')
|
||||
db_api.setup_db()
|
||||
self.addCleanup(db_api.drop_db)
|
||||
|
|
|
@ -11,13 +11,16 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from oslo.config import cfg
|
||||
from sqlalchemy import exc
|
||||
|
||||
from murano.db import models # noqa
|
||||
from murano.openstack.common.db.sqlalchemy import utils as db_utils
|
||||
from murano.tests.db.migration import test_migrations_base as base
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
|
@ -69,6 +72,43 @@ class TestMigrations(base.BaseWalkMigrationTestCase, base.CommonTestsMixIn):
|
|||
def _check_001(self, engine, data):
|
||||
self.assertColumnExists(engine, 'category', 'id')
|
||||
self.assertColumnExists(engine, 'environment', 'tenant_id')
|
||||
|
||||
# make sure indexes are in place
|
||||
self.assertIndexExists(engine,
|
||||
'class_definition',
|
||||
'ix_class_definition_name')
|
||||
|
||||
self.assertIndexExists(engine,
|
||||
'package',
|
||||
'ix_package_fqn')
|
||||
self.assertIndexExists(engine,
|
||||
'category',
|
||||
'ix_category_name')
|
||||
|
||||
self._test_package_fqn_is_uniq(engine)
|
||||
|
||||
def _test_package_fqn_is_uniq(self, engine):
|
||||
package_table = db_utils.get_table(engine, 'package')
|
||||
|
||||
package = {
|
||||
'id': str(uuid.uuid4()),
|
||||
'archive': "archive blob here",
|
||||
'fully_qualified_name': 'com.example.package',
|
||||
'type': 'class',
|
||||
'author': 'OpenStack',
|
||||
'name': 'package',
|
||||
'enabled': True,
|
||||
'description': 'some text',
|
||||
'is_public': False,
|
||||
'tags': ['tag1', 'tag2'],
|
||||
'logo': "logo blob here",
|
||||
'ui_definition': '{}',
|
||||
'owner_id': '123',
|
||||
'created': datetime.datetime.now(),
|
||||
'updated': datetime.datetime.now()
|
||||
}
|
||||
package_table.insert().execute(package)
|
||||
|
||||
package['id'] = str(uuid.uuid4())
|
||||
self.assertRaises(exc.IntegrityError,
|
||||
package_table.insert().execute, package)
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
# 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 uuid
|
||||
|
||||
from webob import exc
|
||||
|
||||
from murano.db.catalog import api
|
||||
from murano.openstack.common.db import exception as db_exception
|
||||
from murano.tests import base
|
||||
from murano.tests import utils
|
||||
|
||||
|
||||
class CatalogDBTestCase(base.MuranoWithDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(CatalogDBTestCase, self).setUp()
|
||||
self.tenant_id = str(uuid.uuid4())
|
||||
self.context = utils.dummy_context(tenant_id=self.tenant_id)
|
||||
|
||||
def _create_categories(self):
|
||||
api.category_add('cat1')
|
||||
api.category_add('cat2')
|
||||
|
||||
def _stub_package(self):
|
||||
return {
|
||||
'archive': "archive blob here",
|
||||
'fully_qualified_name': 'com.example.package',
|
||||
'type': 'class',
|
||||
'author': 'OpenStack',
|
||||
'name': 'package',
|
||||
'enabled': True,
|
||||
'description': 'some text',
|
||||
'is_public': False,
|
||||
'tags': ['tag1', 'tag2'],
|
||||
'logo': "logo blob here",
|
||||
'ui_definition': '{}',
|
||||
}
|
||||
|
||||
def test_list_empty_categories(self):
|
||||
res = api.category_get_names()
|
||||
self.assertEqual(0, len(res))
|
||||
|
||||
def test_add_list_categories(self):
|
||||
self._create_categories()
|
||||
|
||||
res = api.categories_list()
|
||||
self.assertEqual(2, len(res))
|
||||
|
||||
for cat in res:
|
||||
self.assertTrue(cat.id is not None)
|
||||
self.assertTrue(cat.name.startswith('cat'))
|
||||
|
||||
def test_package_upload(self):
|
||||
self._create_categories()
|
||||
values = self._stub_package()
|
||||
|
||||
package = api.package_upload(values, self.tenant_id)
|
||||
|
||||
self.assertIsNotNone(package.id)
|
||||
for k in values.keys():
|
||||
self.assertEqual(values[k], package[k])
|
||||
|
||||
def test_package_fqn_is_unique(self):
|
||||
self._create_categories()
|
||||
values = self._stub_package()
|
||||
|
||||
api.package_upload(values, self.tenant_id)
|
||||
self.assertRaises(db_exception.DBDuplicateEntry,
|
||||
api.package_upload, values, self.tenant_id)
|
||||
|
||||
def test_package_delete(self):
|
||||
values = self._stub_package()
|
||||
package = api.package_upload(values, self.tenant_id)
|
||||
|
||||
api.package_delete(package.id, self.context)
|
||||
|
||||
self.assertRaises(exc.HTTPNotFound,
|
||||
api.package_get, package.id, self.context)
|
Loading…
Reference in New Issue