diff --git a/heat/db/sqlalchemy/migrate_repo/versions/029_event_id_to_uuid.py b/heat/db/sqlalchemy/migrate_repo/versions/029_event_id_to_uuid.py new file mode 100644 index 0000000000..f8dff7f5cc --- /dev/null +++ b/heat/db/sqlalchemy/migrate_repo/versions/029_event_id_to_uuid.py @@ -0,0 +1,42 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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 +from migrate.versioning import util as migrate_util +from heat.openstack.common import uuidutils +from heat.openstack.common.gettextutils import _ + + +def upgrade(migrate_engine): + meta = sqlalchemy.MetaData(bind=migrate_engine) + + event = sqlalchemy.Table('event', meta, autoload=True) + event.c.id.alter(type=sqlalchemy.String(36), primary_key=True, + default=uuidutils.generate_uuid) + + +def downgrade(migrate_engine): + meta = sqlalchemy.MetaData(bind=migrate_engine) + + event = sqlalchemy.Table('event', meta, autoload=True) + + try: + event.c.id.alter(type=sqlalchemy.Integer, primary_key=True) + except: + # NOTE(pafuent): since there is no way to downgrade just passing + # The same is did in 018_resource_id_uuid.py + migrate_util.log.warning(_('If you really want to downgrade to this ' + 'version, you should drop all the records.' + )) + pass diff --git a/heat/db/sqlalchemy/models.py b/heat/db/sqlalchemy/models.py index e7de94b985..52b9c8c185 100644 --- a/heat/db/sqlalchemy/models.py +++ b/heat/db/sqlalchemy/models.py @@ -16,6 +16,7 @@ SQLAlchemy models for heat data. """ import sqlalchemy +import uuid from sqlalchemy.dialects import mysql from sqlalchemy.orm import relationship, backref @@ -167,7 +168,8 @@ class Event(BASE, HeatBase): __tablename__ = 'event' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) + id = sqlalchemy.Column(sqlalchemy.String(36), primary_key=True, + default=lambda: str(uuid.uuid4())) stack_id = sqlalchemy.Column(sqlalchemy.String(36), sqlalchemy.ForeignKey('stack.id'), nullable=False) diff --git a/heat/tests/test_api_cfn_v1.py b/heat/tests/test_api_cfn_v1.py index cdab08e1c5..3ecb6004d9 100644 --- a/heat/tests/test_api_cfn_v1.py +++ b/heat/tests/test_api_cfn_v1.py @@ -1190,7 +1190,13 @@ class CfnStackControllerTest(HeatTestCase): exception.HeatInvalidParameterValueError) self.m.VerifyAll() - def test_events_list(self): + def test_events_list_event_id_integer(self): + self._test_events_list('42') + + def test_events_list_event_id_uuid(self): + self._test_events_list('a3455d8c-9f88-404d-a85b-5315293e67de') + + def _test_events_list(self, event_id): # Format a dummy request stack_name = "wordpress" identity = dict(identifier.HeatIdentifier('t', stack_name, '6')) @@ -1210,7 +1216,8 @@ class CfnStackControllerTest(HeatTestCase): {u'tenant': u't', u'stack_name': u'wordpress', u'stack_id': u'6', - u'path': u'/resources/WikiDatabase/events/42'}, + u'path': u'/resources/WikiDatabase/events/{0}'.format( + event_id)}, u'resource_action': u'TEST', u'resource_status': u'IN_PROGRESS', u'physical_resource_id': None, @@ -1236,7 +1243,7 @@ class CfnStackControllerTest(HeatTestCase): expected = {'DescribeStackEventsResponse': {'DescribeStackEventsResult': {'StackEvents': - [{'EventId': u'42', + [{'EventId': unicode(event_id), 'StackId': u'arn:openstack:heat::t:stacks/wordpress/6', 'ResourceStatus': u'TEST_IN_PROGRESS', 'ResourceType': u'AWS::EC2::Instance', diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index 58f407012c..d89b3f3aa9 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -1787,8 +1787,13 @@ class EventControllerTest(ControllerTest, HeatTestCase): cfgopts = DummyConfig() self.controller = events.EventController(options=cfgopts) - def test_resource_index(self): - event_id = '42' + def test_resource_index_event_id_integer(self): + self._test_resource_index('42') + + def test_resource_index_event_id_uuid(self): + self._test_resource_index('a3455d8c-9f88-404d-a85b-5315293e67de') + + def _test_resource_index(self, event_id): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') @@ -1865,8 +1870,13 @@ class EventControllerTest(ControllerTest, HeatTestCase): self.assertEqual(result, expected) self.m.VerifyAll() - def test_stack_index(self): - event_id = '42' + def test_stack_index_event_id_integer(self): + self._test_stack_index('42') + + def test_stack_index_event_id_uuid(self): + self._test_stack_index('a3455d8c-9f88-404d-a85b-5315293e67de') + + def _test_stack_index(self, event_id): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') @@ -1998,8 +2008,13 @@ class EventControllerTest(ControllerTest, HeatTestCase): resource_name=res_name) self.m.VerifyAll() - def test_show(self): - event_id = '42' + def test_show_event_id_integer(self): + self._test_show('42') + + def test_show_event_id_uuid(self): + self._test_show('a3455d8c-9f88-404d-a85b-5315293e67de') + + def _test_show(self, event_id): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') @@ -2080,14 +2095,20 @@ class EventControllerTest(ControllerTest, HeatTestCase): self.assertEqual(result, expected) self.m.VerifyAll() - def test_show_nonexist(self): - event_id = '42' + def test_show_nonexist_event_id_integer(self): + self._test_show_nonexist('42', '41') + + def test_show_nonexist_event_id_uuid(self): + self._test_show_nonexist('a3455d8c-9f88-404d-a85b-5315293e67de', + 'x3455x8x-9x88-404x-x85x-5315293x67xx') + + def _test_show_nonexist(self, event_id, search_event_id): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') res_identity = identifier.ResourceIdentifier(resource_name=res_name, **stack_identity) - ev_identity = identifier.EventIdentifier(event_id='41', + ev_identity = identifier.EventIdentifier(event_id=search_event_id, **res_identity) req = self._get(stack_identity._tenant_path() + diff --git a/heat/tests/test_engine_api_utils.py b/heat/tests/test_engine_api_utils.py index 0ccf8c879f..3f1733fcf9 100644 --- a/heat/tests/test_engine_api_utils.py +++ b/heat/tests/test_engine_api_utils.py @@ -15,6 +15,8 @@ import heat.engine.api as api from heat.engine import parser from heat.engine import resource +from heat.engine.event import Event +from heat.common.identifier import EventIdentifier from heat.openstack.common import uuidutils from heat.rpc import api as rpc_api from heat.tests.common import HeatTestCase @@ -79,7 +81,6 @@ class EngineApiTest(HeatTestCase): class FormatTest(HeatTestCase): - def setUp(self): super(FormatTest, self).setUp() utils.setup_dummy_db() @@ -97,6 +98,13 @@ class FormatTest(HeatTestCase): self.stack = parser.Stack(utils.dummy_context(), 'test_stack', template, stack_id=uuidutils.generate_uuid()) + def _dummy_event(self, event_id): + resource = self.stack['generic1'] + return Event(utils.dummy_context(), self.stack, 'CREATE', 'COMPLETE', + 'state changed', 'z3455xyc-9f88-404d-a85b-5315293e67de', + resource.properties, resource.name, resource.type(), + id=event_id) + def test_format_stack_resource(self): res = self.stack['generic1'] @@ -128,3 +136,35 @@ class FormatTest(HeatTestCase): res2 = api.format_stack_resource(self.stack['generic2']) self.assertEqual(res1['required_by'], ['generic2']) self.assertEqual(res2['required_by'], []) + + def test_format_event_id_integer(self): + self._test_format_event('42') + + def test_format_event_id_uuid(self): + self._test_format_event('a3455d8c-9f88-404d-a85b-5315293e67de') + + def _test_format_event(self, event_id): + event = self._dummy_event(event_id) + + event_keys = set(( + rpc_api.EVENT_ID, + rpc_api.EVENT_STACK_ID, + rpc_api.EVENT_STACK_NAME, + rpc_api.EVENT_TIMESTAMP, + rpc_api.EVENT_RES_NAME, + rpc_api.EVENT_RES_PHYSICAL_ID, + rpc_api.EVENT_RES_ACTION, + rpc_api.EVENT_RES_STATUS, + rpc_api.EVENT_RES_STATUS_DATA, + rpc_api.EVENT_RES_TYPE, + rpc_api.EVENT_RES_PROPERTIES)) + + formatted = api.format_event(event) + self.assertEqual(event_keys, set(formatted.keys())) + + event_id_formatted = formatted[rpc_api.EVENT_ID] + event_identifier = EventIdentifier(event_id_formatted['tenant'], + event_id_formatted['stack_name'], + event_id_formatted['stack_id'], + event_id_formatted['path']) + self.assertEqual(event_id, event_identifier.event_id) diff --git a/heat/tests/test_identifier.py b/heat/tests/test_identifier.py index ec6959267a..1d7ea372f9 100644 --- a/heat/tests/test_identifier.py +++ b/heat/tests/test_identifier.py @@ -390,11 +390,17 @@ class ResourceIdentifierTest(testtools.TestCase): class EventIdentifierTest(testtools.TestCase): - def test_event_init(self): + def test_event_init_integer_id(self): + self._test_event_init('42') + + def test_event_init_uuid_id(self): + self._test_event_init('a3455d8c-9f88-404d-a85b-5315293e67de') + + def _test_event_init(self, event_id): si = identifier.HeatIdentifier('t', 's', 'i') pi = identifier.ResourceIdentifier(resource_name='p', **si) - ei = identifier.EventIdentifier(event_id='e', **pi) - self.assertEqual(ei.path, '/resources/p/events/e') + ei = identifier.EventIdentifier(event_id=event_id, **pi) + self.assertEqual(ei.path, '/resources/p/events/{0}'.format(event_id)) def test_event_init_from_dict(self): hi = identifier.HeatIdentifier('t', 's', 'i', '/resources/p/events/42') @@ -417,6 +423,13 @@ class EventIdentifierTest(testtools.TestCase): ei = identifier.EventIdentifier('t', 's', 'i', '/resources/p', 'e') self.assertEqual(ei.resource_name, 'p') - def test_event_id(self): - ei = identifier.EventIdentifier('t', 's', 'i', '/resources/p', 'e') - self.assertEqual(ei.event_id, 'e') + def test_event_id_integer(self): + self._test_event_id('42') + + def test_event_id_uuid(self): + self._test_event_id('a3455d8c-9f88-404d-a85b-5315293e67de') + + def _test_event_id(self, event_id): + ei = identifier.EventIdentifier('t', 's', 'i', '/resources/p', + event_id) + self.assertEqual(ei.event_id, event_id)