diff --git a/heat/tests/engine/test_stack_events.py b/heat/tests/engine/test_stack_events.py new file mode 100644 index 000000000..bfb082dda --- /dev/null +++ b/heat/tests/engine/test_stack_events.py @@ -0,0 +1,238 @@ +# +# 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 mock + +from heat.engine import resource as res +from heat.engine.resources.aws.ec2 import instance as instances +from heat.engine import service +from heat.engine import stack as parser +from heat.objects import event as event_object +from heat.objects import stack as stack_object +from heat.tests import common +from heat.tests.engine import tools +from heat.tests import generic_resource as generic_rsrc +from heat.tests import utils + + +class StackEventTest(common.HeatTestCase): + + def setUp(self): + super(StackEventTest, self).setUp() + + self.ctx = utils.dummy_context(tenant_id='stack_event_test_tenant') + self.eng = service.EngineService('a-host', 'a-topic') + self.eng.create_periodic_tasks() + + @tools.stack_context('service_event_list_test_stack') + @mock.patch.object(service.EngineService, '_get_stack') + def test_event_list(self, mock_get): + mock_get.return_value = stack_object.Stack.get_by_id(self.ctx, + self.stack.id) + events = self.eng.list_events(self.ctx, self.stack.identifier()) + + self.assertEqual(4, len(events)) + for ev in events: + self.assertIn('event_identity', ev) + self.assertIsInstance(ev['event_identity'], dict) + self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1]) + + self.assertIn('resource_name', ev) + self.assertIn(ev['resource_name'], + ('service_event_list_test_stack', 'WebServer')) + + self.assertIn('physical_resource_id', ev) + + self.assertIn('resource_properties', ev) + # Big long user data field.. it mentions 'wordpress' + # a few times so this should work. + if ev.get('resource_properties'): + user_data = ev['resource_properties']['UserData'] + self.assertIn('wordpress', user_data) + self.assertEqual('F17-x86_64-gold', + ev['resource_properties']['ImageId']) + self.assertEqual('m1.large', + ev['resource_properties']['InstanceType']) + + self.assertEqual('CREATE', ev['resource_action']) + self.assertIn(ev['resource_status'], ('IN_PROGRESS', 'COMPLETE')) + + self.assertIn('resource_status_reason', ev) + self.assertIn(ev['resource_status_reason'], + ('state changed', + 'Stack CREATE started', + 'Stack CREATE completed successfully')) + + self.assertIn('resource_type', ev) + self.assertIn(ev['resource_type'], + ('AWS::EC2::Instance', 'OS::Heat::Stack')) + + self.assertIn('stack_identity', ev) + + self.assertIn('stack_name', ev) + self.assertEqual(self.stack.name, ev['stack_name']) + + self.assertIn('event_time', ev) + + mock_get.assert_called_once_with(self.ctx, self.stack.identifier(), + show_deleted=True) + + @tools.stack_context('service_event_list_deleted_resource') + @mock.patch.object(instances.Instance, 'handle_delete') + def test_event_list_deleted_resource(self, mock_delete): + mock_delete.return_value = None + + res._register_class('GenericResourceType', + generic_rsrc.GenericResource) + + thread = mock.Mock() + thread.link = mock.Mock(return_value=None) + + def run(stack_id, func, *args, **kwargs): + func(*args) + return thread + self.eng.thread_group_mgr.start = run + + new_tmpl = {'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': {'AResource': {'Type': + 'GenericResourceType'}}} + + result = self.eng.update_stack(self.ctx, self.stack.identifier(), + new_tmpl, None, None, {}) + + # The self.stack reference needs to be updated. Since the underlying + # stack is updated in update_stack, the original reference is now + # pointing to an orphaned stack object. + self.stack = parser.Stack.load(self.ctx, stack_id=result['stack_id']) + + self.assertEqual(result, self.stack.identifier()) + self.assertIsInstance(result, dict) + self.assertTrue(result['stack_id']) + events = self.eng.list_events(self.ctx, self.stack.identifier()) + + self.assertEqual(10, len(events)) + + for ev in events: + self.assertIn('event_identity', ev) + self.assertIsInstance(ev['event_identity'], dict) + self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1]) + + self.assertIn('resource_name', ev) + self.assertIn('physical_resource_id', ev) + self.assertIn('resource_properties', ev) + self.assertIn('resource_status_reason', ev) + + self.assertIn(ev['resource_action'], + ('CREATE', 'UPDATE', 'DELETE')) + self.assertIn(ev['resource_status'], ('IN_PROGRESS', 'COMPLETE')) + + self.assertIn('resource_type', ev) + self.assertIn(ev['resource_type'], ('AWS::EC2::Instance', + 'GenericResourceType', + 'OS::Heat::Stack')) + + self.assertIn('stack_identity', ev) + + self.assertIn('stack_name', ev) + self.assertEqual(self.stack.name, ev['stack_name']) + + self.assertIn('event_time', ev) + + mock_delete.assert_called_once_with() + expected = [ + mock.call(mock.ANY), + mock.call(mock.ANY, self.stack.id, mock.ANY) + ] + self.assertEqual(expected, thread.link.call_args_list) + + @tools.stack_context('service_event_list_by_tenant') + def test_event_list_by_tenant(self): + events = self.eng.list_events(self.ctx, None) + + self.assertEqual(4, len(events)) + for ev in events: + self.assertIn('event_identity', ev) + self.assertIsInstance(ev['event_identity'], dict) + self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1]) + + self.assertIn('resource_name', ev) + self.assertIn(ev['resource_name'], + ('WebServer', 'service_event_list_by_tenant')) + + self.assertIn('physical_resource_id', ev) + + self.assertIn('resource_properties', ev) + # Big long user data field.. it mentions 'wordpress' + # a few times so this should work. + if ev.get('resource_properties'): + user_data = ev['resource_properties']['UserData'] + self.assertIn('wordpress', user_data) + self.assertEqual('F17-x86_64-gold', + ev['resource_properties']['ImageId']) + self.assertEqual('m1.large', + ev['resource_properties']['InstanceType']) + + self.assertEqual('CREATE', ev['resource_action']) + self.assertIn(ev['resource_status'], ('IN_PROGRESS', 'COMPLETE')) + + self.assertIn('resource_status_reason', ev) + self.assertIn(ev['resource_status_reason'], + ('state changed', + 'Stack CREATE started', + 'Stack CREATE completed successfully')) + + self.assertIn('resource_type', ev) + self.assertIn(ev['resource_type'], + ('AWS::EC2::Instance', 'OS::Heat::Stack')) + + self.assertIn('stack_identity', ev) + + self.assertIn('stack_name', ev) + self.assertEqual(self.stack.name, ev['stack_name']) + + self.assertIn('event_time', ev) + + @mock.patch.object(event_object.Event, 'get_all_by_stack') + @mock.patch.object(service.EngineService, '_get_stack') + def test_event_list_with_marker_and_filters(self, mock_get, mock_get_all): + limit = object() + marker = object() + sort_keys = object() + sort_dir = object() + filters = object() + mock_get.return_value = mock.Mock(id=1) + self.eng.list_events(self.ctx, 1, limit=limit, marker=marker, + sort_keys=sort_keys, sort_dir=sort_dir, + filters=filters) + + mock_get_all.assert_called_once_with(self.ctx, 1, limit=limit, + sort_keys=sort_keys, + marker=marker, sort_dir=sort_dir, + filters=filters) + + @mock.patch.object(event_object.Event, 'get_all_by_tenant') + def test_tenant_events_list_with_marker_and_filters(self, mock_get_all): + limit = object() + marker = object() + sort_keys = object() + sort_dir = object() + filters = object() + + self.eng.list_events(self.ctx, None, limit=limit, marker=marker, + sort_keys=sort_keys, sort_dir=sort_dir, + filters=filters) + mock_get_all.assert_called_once_with(self.ctx, limit=limit, + sort_keys=sort_keys, + marker=marker, + sort_dir=sort_dir, + filters=filters) diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index 9e8e38d5e..274b074bf 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -32,14 +32,12 @@ from heat.engine import dependencies from heat.engine import environment from heat.engine.hot import template as hottemplate from heat.engine import resource as res -from heat.engine.resources.aws.ec2 import instance as instances from heat.engine import service from heat.engine import service_stack_watch from heat.engine import stack as parser from heat.engine import stack_lock from heat.engine import template as templatem from heat.engine import watchrule -from heat.objects import event as event_object from heat.objects import raw_template as raw_template_object from heat.objects import resource as resource_objects from heat.objects import stack as stack_object @@ -1524,218 +1522,6 @@ class StackServiceTest(common.HeatTestCase): ctx2, self.stack.name)) - @tools.stack_context('service_event_list_test_stack') - def test_stack_event_list(self): - self.m.StubOutWithMock(service.EngineService, '_get_stack') - s = stack_object.Stack.get_by_id(self.ctx, self.stack.id) - service.EngineService._get_stack(self.ctx, - self.stack.identifier(), - show_deleted=True).AndReturn(s) - self.m.ReplayAll() - - events = self.eng.list_events(self.ctx, self.stack.identifier()) - - self.assertEqual(4, len(events)) - for ev in events: - self.assertIn('event_identity', ev) - self.assertIsInstance(ev['event_identity'], dict) - self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1]) - - self.assertIn('resource_name', ev) - self.assertIn(ev['resource_name'], - ('service_event_list_test_stack', 'WebServer')) - - self.assertIn('physical_resource_id', ev) - - self.assertIn('resource_properties', ev) - # Big long user data field.. it mentions 'wordpress' - # a few times so this should work. - if ev.get('resource_properties'): - user_data = ev['resource_properties']['UserData'] - self.assertIn('wordpress', user_data) - self.assertEqual('F17-x86_64-gold', - ev['resource_properties']['ImageId']) - self.assertEqual('m1.large', - ev['resource_properties']['InstanceType']) - - self.assertEqual('CREATE', ev['resource_action']) - self.assertIn(ev['resource_status'], ('IN_PROGRESS', 'COMPLETE')) - - self.assertIn('resource_status_reason', ev) - self.assertIn(ev['resource_status_reason'], - ('state changed', - 'Stack CREATE started', - 'Stack CREATE completed successfully')) - - self.assertIn('resource_type', ev) - self.assertIn(ev['resource_type'], - ('AWS::EC2::Instance', 'OS::Heat::Stack')) - - self.assertIn('stack_identity', ev) - - self.assertIn('stack_name', ev) - self.assertEqual(self.stack.name, ev['stack_name']) - - self.assertIn('event_time', ev) - - self.m.VerifyAll() - - @tools.stack_context('event_list_deleted_stack') - def test_stack_event_list_deleted_resource(self): - - thread = self.m.CreateMockAnything() - thread.link(mox.IgnoreArg()).AndReturn(None) - thread.link(mox.IgnoreArg(), self.stack.id, - mox.IgnoreArg()).AndReturn(None) - - def run(stack_id, func, *args, **kwargs): - func(*args) - return thread - self.eng.thread_group_mgr.start = run - - new_tmpl = {'HeatTemplateFormatVersion': '2012-12-12', - 'Resources': {'AResource': {'Type': - 'GenericResourceType'}}} - - self.m.StubOutWithMock(instances.Instance, 'handle_delete') - instances.Instance.handle_delete() - - self.m.ReplayAll() - - result = self.eng.update_stack(self.ctx, self.stack.identifier(), - new_tmpl, None, None, {}) - - # The self.stack reference needs to be updated. Since the underlying - # stack is updated in update_stack, the original reference is now - # pointing to an orphaned stack object. - self.stack = parser.Stack.load(self.ctx, stack_id=result['stack_id']) - - self.assertEqual(result, self.stack.identifier()) - self.assertIsInstance(result, dict) - self.assertTrue(result['stack_id']) - events = self.eng.list_events(self.ctx, self.stack.identifier()) - - self.assertEqual(10, len(events)) - - for ev in events: - self.assertIn('event_identity', ev) - self.assertIsInstance(ev['event_identity'], dict) - self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1]) - - self.assertIn('resource_name', ev) - self.assertIn('physical_resource_id', ev) - self.assertIn('resource_properties', ev) - self.assertIn('resource_status_reason', ev) - - self.assertIn(ev['resource_action'], - ('CREATE', 'UPDATE', 'DELETE')) - self.assertIn(ev['resource_status'], ('IN_PROGRESS', 'COMPLETE')) - - self.assertIn('resource_type', ev) - self.assertIn(ev['resource_type'], ('AWS::EC2::Instance', - 'GenericResourceType', - 'OS::Heat::Stack')) - - self.assertIn('stack_identity', ev) - - self.assertIn('stack_name', ev) - self.assertEqual(self.stack.name, ev['stack_name']) - - self.assertIn('event_time', ev) - - self.m.VerifyAll() - - @tools.stack_context('service_event_list_test_stack') - def test_stack_event_list_by_tenant(self): - events = self.eng.list_events(self.ctx, None) - - self.assertEqual(4, len(events)) - for ev in events: - self.assertIn('event_identity', ev) - self.assertIsInstance(ev['event_identity'], dict) - self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1]) - - self.assertIn('resource_name', ev) - self.assertIn(ev['resource_name'], - ('WebServer', 'service_event_list_test_stack')) - - self.assertIn('physical_resource_id', ev) - - self.assertIn('resource_properties', ev) - # Big long user data field.. it mentions 'wordpress' - # a few times so this should work. - if ev.get('resource_properties'): - user_data = ev['resource_properties']['UserData'] - self.assertIn('wordpress', user_data) - self.assertEqual('F17-x86_64-gold', - ev['resource_properties']['ImageId']) - self.assertEqual('m1.large', - ev['resource_properties']['InstanceType']) - - self.assertEqual('CREATE', ev['resource_action']) - self.assertIn(ev['resource_status'], ('IN_PROGRESS', 'COMPLETE')) - - self.assertIn('resource_status_reason', ev) - self.assertIn(ev['resource_status_reason'], - ('state changed', - 'Stack CREATE started', - 'Stack CREATE completed successfully')) - - self.assertIn('resource_type', ev) - self.assertIn(ev['resource_type'], - ('AWS::EC2::Instance', 'OS::Heat::Stack')) - - self.assertIn('stack_identity', ev) - - self.assertIn('stack_name', ev) - self.assertEqual(self.stack.name, ev['stack_name']) - - self.assertIn('event_time', ev) - - self.m.VerifyAll() - - @mock.patch.object(event_object.Event, 'get_all_by_stack') - @mock.patch.object(service.EngineService, '_get_stack') - def test_stack_events_list_passes_marker_and_filters(self, - mock_get_stack, - mock_events_get_all): - limit = object() - marker = object() - sort_keys = object() - sort_dir = object() - filters = object() - s = mock.Mock(id=1) - mock_get_stack.return_value = s - self.eng.list_events(self.ctx, 1, limit=limit, - marker=marker, sort_keys=sort_keys, - sort_dir=sort_dir, filters=filters) - mock_events_get_all.assert_called_once_with(self.ctx, - 1, - limit=limit, - sort_keys=sort_keys, - marker=marker, - sort_dir=sort_dir, - filters=filters) - - @mock.patch.object(event_object.Event, 'get_all_by_tenant') - def test_tenant_events_list_passes_marker_and_filters( - self, mock_tenant_events_get_all): - limit = object() - marker = object() - sort_keys = object() - sort_dir = object() - filters = object() - - self.eng.list_events(self.ctx, None, limit=limit, - marker=marker, sort_keys=sort_keys, - sort_dir=sort_dir, filters=filters) - mock_tenant_events_get_all.assert_called_once_with(self.ctx, - limit=limit, - sort_keys=sort_keys, - marker=marker, - sort_dir=sort_dir, - filters=filters) - @tools.stack_context('service_list_all_test_stack') def test_stack_list_all(self): self.m.StubOutWithMock(parser.Stack, '_from_db')