# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. # # 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 heatclient.v1 import stacks import mock from oslo_config import cfg from murano.dsl import constants from murano.dsl import helpers from murano.dsl import murano_class from murano.dsl import object_store from murano.engine import client_manager from murano.engine import environment from murano.engine.system import heat_stack from murano.tests.unit import base MOD_NAME = 'murano.engine.system.heat_stack' CONF = cfg.CONF class TestHeatStack(base.MuranoTestCase): def setUp(self): super(TestHeatStack, self).setUp() self.mock_murano_class = mock.Mock(spec=murano_class.MuranoClass) self.mock_murano_class.name = 'io.murano.system.HeatStack' self.mock_murano_class.declared_parents = [] self.heat_client_mock = mock.MagicMock() self.heat_client_mock.stacks = mock.MagicMock(spec=stacks.StackManager) self.mock_object_store = mock.Mock(spec=object_store.ObjectStore) self.environment_mock = mock.Mock( spec=environment.Environment) client_manager_mock = mock.Mock(spec=client_manager.ClientManager) client_manager_mock.get_heat_client.return_value = \ self.heat_client_mock self.environment_mock.clients = client_manager_mock CONF.set_override('stack_tags', ['test-murano'], 'heat', enforce_type=True) self.mock_tag = ','.join(CONF.heat.stack_tags) @mock.patch(MOD_NAME + '.HeatStack._wait_state') @mock.patch(MOD_NAME + '.HeatStack._get_status') def test_push_adds_version(self, status_get, wait_st): """Assert that if heat_template_version is omitted, it's added.""" status_get.return_value = 'NOT_FOUND' wait_st.return_value = {} context = {constants.CTX_ENVIRONMENT: self.environment_mock} with helpers.contextual(context): hs = heat_stack.HeatStack( 'test-stack', 'Generated by TestHeatStack') hs._template = {'resources': {'test': 1}} hs._files = {} hs._hot_environment = '' hs._parameters = {} hs._applied = False hs.push() with helpers.contextual(context): hs = heat_stack.HeatStack( 'test-stack', 'Generated by TestHeatStack') hs._template = {'resources': {'test': 1}} hs._files = {} hs._parameters = {} hs._applied = False hs.push() expected_template = { 'heat_template_version': '2013-05-23', 'description': 'Generated by TestHeatStack', 'resources': {'test': 1} } self.heat_client_mock.stacks.create.assert_called_with( stack_name='test-stack', disable_rollback=True, parameters={}, template=expected_template, files={}, environment='', tags=self.mock_tag ) self.assertTrue(hs._applied) @mock.patch(MOD_NAME + '.HeatStack._wait_state') @mock.patch(MOD_NAME + '.HeatStack._get_status') def test_description_is_optional(self, status_get, wait_st): """Assert that if heat_template_version is omitted, it's added.""" status_get.return_value = 'NOT_FOUND' wait_st.return_value = {} context = {constants.CTX_ENVIRONMENT: self.environment_mock} with helpers.contextual(context): hs = heat_stack.HeatStack('test-stack', None) hs._template = {'resources': {'test': 1}} hs._files = {} hs._hot_environment = '' hs._parameters = {} hs._applied = False hs.push() expected_template = { 'heat_template_version': '2013-05-23', 'resources': {'test': 1} } self.heat_client_mock.stacks.create.assert_called_with( stack_name='test-stack', disable_rollback=True, parameters={}, template=expected_template, files={}, environment='', tags=self.mock_tag ) self.assertTrue(hs._applied) @mock.patch(MOD_NAME + '.HeatStack._wait_state') @mock.patch(MOD_NAME + '.HeatStack._get_status') def test_heat_files_are_sent(self, status_get, wait_st): """Assert that if heat_template_version is omitted, it's added.""" status_get.return_value = 'NOT_FOUND' wait_st.return_value = {} context = {constants.CTX_ENVIRONMENT: self.environment_mock} with helpers.contextual(context): hs = heat_stack.HeatStack('test-stack', None) hs._description = None hs._template = {'resources': {'test': 1}} hs._files = {"heatFile": "file"} hs._hot_environment = '' hs._parameters = {} hs._applied = False hs.push() expected_template = { 'heat_template_version': '2013-05-23', 'resources': {'test': 1} } self.heat_client_mock.stacks.create.assert_called_with( stack_name='test-stack', disable_rollback=True, parameters={}, template=expected_template, files={"heatFile": "file"}, environment='', tags=self.mock_tag ) self.assertTrue(hs._applied) @mock.patch(MOD_NAME + '.HeatStack._wait_state') @mock.patch(MOD_NAME + '.HeatStack._get_status') def test_heat_environments_are_sent(self, status_get, wait_st): """Assert that if heat_template_version is omitted, it's added.""" status_get.return_value = 'NOT_FOUND' wait_st.return_value = {} context = {constants.CTX_ENVIRONMENT: self.environment_mock} with helpers.contextual(context): hs = heat_stack.HeatStack('test-stack', None) hs._description = None hs._template = {'resources': {'test': 1}} hs._files = {"heatFile": "file"} hs._hot_environment = 'environments' hs._parameters = {} hs._applied = False hs.push() expected_template = { 'heat_template_version': '2013-05-23', 'resources': {'test': 1} } self.heat_client_mock.stacks.create.assert_called_with( stack_name='test-stack', disable_rollback=True, parameters={}, template=expected_template, files={"heatFile": "file"}, environment='environments', tags=self.mock_tag ) self.assertTrue(hs._applied) @mock.patch(MOD_NAME + '.HeatStack.current') def test_update_wrong_template_version(self, current): """Template version other than expected should cause error.""" context = {constants.CTX_ENVIRONMENT: self.environment_mock} with helpers.contextual(context): hs = heat_stack.HeatStack( 'test-stack', 'Generated by TestHeatStack') hs._template = {'resources': {'test': 1}} invalid_template = { 'heat_template_version': 'something else' } current.return_value = {} e = self.assertRaises(heat_stack.HeatStackError, hs.update_template, invalid_template) err_msg = "Currently only heat_template_version 2013-05-23 "\ "is supported." self.assertEqual(err_msg, str(e)) # Check it's ok without a version hs.update_template({}) expected = {'resources': {'test': 1}} self.assertEqual(expected, hs._template) # .. or with a good version hs.update_template({'heat_template_version': '2013-05-23'}) expected['heat_template_version'] = '2013-05-23' self.assertEqual(expected, hs._template) @mock.patch(MOD_NAME + '.HeatStack._wait_state') @mock.patch(MOD_NAME + '.HeatStack._get_status') def test_heat_stack_tags_are_sent(self, status_get, wait_st): """Assert that heat_stack `tags` parameter get push & with value from config parameter `stack_tags`. """ status_get.return_value = 'NOT_FOUND' wait_st.return_value = {} CONF.set_override('stack_tags', ['test-murano', 'murano-tag'], 'heat', enforce_type=True) context = {constants.CTX_ENVIRONMENT: self.environment_mock} with helpers.contextual(context): hs = heat_stack.HeatStack('test-stack', None) hs._description = None hs._template = {'resources': {'test': 1}} hs._files = {} hs._hot_environment = '' hs._parameters = {} hs._applied = False hs._tags = ','.join(CONF.heat.stack_tags) hs.push() expected_template = { 'heat_template_version': '2013-05-23', 'resources': {'test': 1} } self.heat_client_mock.stacks.create.assert_called_with( stack_name='test-stack', disable_rollback=True, parameters={}, template=expected_template, files={}, environment='', tags=','.join(CONF.heat.stack_tags) ) self.assertTrue(hs._applied)