| # 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 heat_integrationtests.common import test |
| from heatclient import exc |
| import six |
| |
| |
| class StackPreviewTest(test.HeatIntegrationTest): |
| template = ''' |
| heat_template_version: 2015-04-30 |
| parameters: |
| incomming: |
| type: string |
| resources: |
| one: |
| type: OS::Heat::TestResource |
| properties: |
| value: fred |
| two: |
| type: OS::Heat::TestResource |
| properties: |
| value: {get_param: incomming} |
| depends_on: one |
| outputs: |
| main_out: |
| value: {get_attr: [two, output]} |
| ''' |
| env = ''' |
| parameters: |
| incomming: abc |
| ''' |
| |
| def setUp(self): |
| super(StackPreviewTest, self).setUp() |
| self.client = self.orchestration_client |
| self.project_id = self.identity_client.project_id |
| |
| def _assert_resource(self, res, stack_name): |
| self.assertEqual(stack_name, res['stack_name']) |
| self.assertEqual('INIT', res['resource_action']) |
| self.assertEqual('COMPLETE', res['resource_status']) |
| for field in ('resource_status_reason', 'physical_resource_id', |
| 'description'): |
| self.assertIn(field, res) |
| self.assertEqual('', res[field]) |
| # 'creation_time' and 'updated_time' are None when preview |
| for field in ('creation_time', 'updated_time'): |
| self.assertIn(field, res) |
| self.assertIsNone(res[field]) |
| self.assertIn('output', res['attributes']) |
| |
| # resource_identity |
| self.assertEqual(stack_name, |
| res['resource_identity']['stack_name']) |
| self.assertEqual('None', res['resource_identity']['stack_id']) |
| self.assertEqual(self.project_id, |
| res['resource_identity']['tenant']) |
| self.assertEqual('/resources/%s' % res['resource_name'], |
| res['resource_identity']['path']) |
| # stack_identity |
| self.assertEqual(stack_name, |
| res['stack_identity']['stack_name']) |
| self.assertEqual('None', res['stack_identity']['stack_id']) |
| self.assertEqual(self.project_id, |
| res['stack_identity']['tenant']) |
| self.assertEqual('', res['stack_identity']['path']) |
| |
| def _assert_results(self, result, stack_name): |
| # global stuff. |
| self.assertEqual(stack_name, result['stack_name']) |
| self.assertTrue(result['disable_rollback']) |
| self.assertEqual('None', result['id']) |
| self.assertIsNone(result['parent']) |
| self.assertEqual('No description', result['template_description']) |
| |
| # parameters |
| self.assertEqual('None', result['parameters']['OS::stack_id']) |
| self.assertEqual(stack_name, result['parameters']['OS::stack_name']) |
| self.assertEqual('abc', result['parameters']['incomming']) |
| |
| def test_basic_pass(self): |
| stack_name = self._stack_rand_name() |
| result = self.client.stacks.preview( |
| template=self.template, |
| stack_name=stack_name, |
| disable_rollback=True, |
| environment=self.env).to_dict() |
| |
| self._assert_results(result, stack_name) |
| for res in result['resources']: |
| self._assert_resource(res, stack_name) |
| self.assertEqual('OS::Heat::TestResource', |
| res['resource_type']) |
| |
| # common properties |
| self.assertFalse(res['properties']['fail']) |
| self.assertEqual(0, res['properties']['wait_secs']) |
| self.assertFalse(res['properties']['update_replace']) |
| |
| if res['resource_name'] == 'one': |
| self.assertEqual('fred', res['properties']['value']) |
| self.assertEqual(['two'], res['required_by']) |
| if res['resource_name'] == 'two': |
| self.assertEqual('abc', res['properties']['value']) |
| self.assertEqual([], res['required_by']) |
| |
| def test_basic_fail(self): |
| stack_name = self._stack_rand_name() |
| |
| # break the template so it fails validation. |
| wont_work = self.template.replace('get_param: incomming', |
| 'get_param: missing') |
| excp = self.assertRaises(exc.HTTPBadRequest, |
| self.client.stacks.preview, |
| template=wont_work, |
| stack_name=stack_name, |
| disable_rollback=True, |
| environment=self.env) |
| |
| self.assertIn('Property error: : resources.two.properties.value: ' |
| ': The Parameter (missing) was not provided.', |
| six.text_type(excp)) |
| |
| def test_nested_pass(self): |
| """Nested stacks need to recurse down the stacks.""" |
| main_template = ''' |
| heat_template_version: 2015-04-30 |
| parameters: |
| incomming: |
| type: string |
| resources: |
| main: |
| type: nested.yaml |
| properties: |
| value: {get_param: incomming} |
| outputs: |
| main_out: |
| value: {get_attr: [main, output]} |
| ''' |
| nested_template = ''' |
| heat_template_version: 2015-04-30 |
| parameters: |
| value: |
| type: string |
| resources: |
| nested: |
| type: OS::Heat::TestResource |
| properties: |
| value: {get_param: value} |
| outputs: |
| output: |
| value: {get_attr: [nested, output]} |
| ''' |
| stack_name = self._stack_rand_name() |
| result = self.client.stacks.preview( |
| disable_rollback=True, |
| stack_name=stack_name, |
| template=main_template, |
| files={'nested.yaml': nested_template}, |
| environment=self.env).to_dict() |
| |
| self._assert_results(result, stack_name) |
| |
| # nested resources return a list of their resources. |
| res = result['resources'][0][0] |
| nested_stack_name = '%s-%s' % (stack_name, |
| res['parent_resource']) |
| |
| self._assert_resource(res, nested_stack_name) |
| self.assertEqual('OS::Heat::TestResource', |
| res['resource_type']) |
| |
| self.assertFalse(res['properties']['fail']) |
| self.assertEqual(0, res['properties']['wait_secs']) |
| self.assertFalse(res['properties']['update_replace']) |
| |
| self.assertEqual('abc', res['properties']['value']) |
| self.assertEqual([], res['required_by']) |
| |
| def test_res_group_with_nested_template(self): |
| main_template = ''' |
| heat_template_version: 2015-04-30 |
| resources: |
| fixed_network: |
| type: "OS::Neutron::Net" |
| rg: |
| type: "OS::Heat::ResourceGroup" |
| properties: |
| count: 1 |
| resource_def: |
| type: nested.yaml |
| properties: |
| fixed_network_id: {get_resource: fixed_network} |
| ''' |
| nested_template = ''' |
| heat_template_version: 2015-04-30 |
| |
| parameters: |
| fixed_network_id: |
| type: string |
| resources: |
| port: |
| type: "OS::Neutron::Port" |
| properties: |
| network_id: |
| get_param: fixed_network_id |
| |
| ''' |
| stack_name = self._stack_rand_name() |
| result = self.client.stacks.preview( |
| disable_rollback=True, |
| stack_name=stack_name, |
| template=main_template, |
| files={'nested.yaml': nested_template}).to_dict() |
| # ensure that fixed network and port here |
| self.assertEqual('fixed_network', |
| result['resources'][0]['resource_name']) |
| self.assertEqual('port', |
| result['resources'][1][0][0]['resource_name']) |