Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 2 | # not use this file except in compliance with the License. You may obtain |
| 3 | # a copy of the License at |
| 4 | # |
| 5 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 6 | # |
| 7 | # Unless required by applicable law or agreed to in writing, software |
| 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 10 | # License for the specific language governing permissions and limitations |
| 11 | # under the License. |
| 12 | |
Stefan Nica | 4750ea4 | 2017-06-20 14:45:14 +0200 | [diff] [blame] | 13 | from heat_integrationtests.functional import functional_base |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 14 | from heatclient import exc |
| 15 | import six |
| 16 | |
| 17 | |
Stefan Nica | 4750ea4 | 2017-06-20 14:45:14 +0200 | [diff] [blame] | 18 | class StackPreviewTest(functional_base.FunctionalTestsBase): |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 19 | template = ''' |
| 20 | heat_template_version: 2015-04-30 |
| 21 | parameters: |
| 22 | incomming: |
| 23 | type: string |
| 24 | resources: |
| 25 | one: |
| 26 | type: OS::Heat::TestResource |
| 27 | properties: |
| 28 | value: fred |
| 29 | two: |
| 30 | type: OS::Heat::TestResource |
| 31 | properties: |
| 32 | value: {get_param: incomming} |
| 33 | depends_on: one |
| 34 | outputs: |
| 35 | main_out: |
| 36 | value: {get_attr: [two, output]} |
| 37 | ''' |
| 38 | env = ''' |
| 39 | parameters: |
| 40 | incomming: abc |
| 41 | ''' |
| 42 | |
| 43 | def setUp(self): |
| 44 | super(StackPreviewTest, self).setUp() |
| 45 | self.client = self.orchestration_client |
Rabi Mishra | 65493fb | 2016-01-29 22:23:21 +0530 | [diff] [blame] | 46 | self.project_id = self.identity_client.project_id |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 47 | |
| 48 | def _assert_resource(self, res, stack_name): |
| 49 | self.assertEqual(stack_name, res['stack_name']) |
| 50 | self.assertEqual('INIT', res['resource_action']) |
| 51 | self.assertEqual('COMPLETE', res['resource_status']) |
| 52 | for field in ('resource_status_reason', 'physical_resource_id', |
| 53 | 'description'): |
| 54 | self.assertIn(field, res) |
| 55 | self.assertEqual('', res[field]) |
huangtianhua | cce999a | 2015-08-03 16:13:25 +0800 | [diff] [blame] | 56 | # 'creation_time' and 'updated_time' are None when preview |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 57 | for field in ('creation_time', 'updated_time'): |
| 58 | self.assertIn(field, res) |
huangtianhua | cce999a | 2015-08-03 16:13:25 +0800 | [diff] [blame] | 59 | self.assertIsNone(res[field]) |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 60 | self.assertIn('output', res['attributes']) |
| 61 | |
| 62 | # resource_identity |
| 63 | self.assertEqual(stack_name, |
| 64 | res['resource_identity']['stack_name']) |
| 65 | self.assertEqual('None', res['resource_identity']['stack_id']) |
| 66 | self.assertEqual(self.project_id, |
| 67 | res['resource_identity']['tenant']) |
| 68 | self.assertEqual('/resources/%s' % res['resource_name'], |
| 69 | res['resource_identity']['path']) |
| 70 | # stack_identity |
| 71 | self.assertEqual(stack_name, |
| 72 | res['stack_identity']['stack_name']) |
| 73 | self.assertEqual('None', res['stack_identity']['stack_id']) |
| 74 | self.assertEqual(self.project_id, |
| 75 | res['stack_identity']['tenant']) |
| 76 | self.assertEqual('', res['stack_identity']['path']) |
| 77 | |
| 78 | def _assert_results(self, result, stack_name): |
| 79 | # global stuff. |
| 80 | self.assertEqual(stack_name, result['stack_name']) |
| 81 | self.assertTrue(result['disable_rollback']) |
| 82 | self.assertEqual('None', result['id']) |
| 83 | self.assertIsNone(result['parent']) |
| 84 | self.assertEqual('No description', result['template_description']) |
| 85 | |
| 86 | # parameters |
| 87 | self.assertEqual('None', result['parameters']['OS::stack_id']) |
| 88 | self.assertEqual(stack_name, result['parameters']['OS::stack_name']) |
| 89 | self.assertEqual('abc', result['parameters']['incomming']) |
| 90 | |
| 91 | def test_basic_pass(self): |
| 92 | stack_name = self._stack_rand_name() |
| 93 | result = self.client.stacks.preview( |
| 94 | template=self.template, |
| 95 | stack_name=stack_name, |
| 96 | disable_rollback=True, |
| 97 | environment=self.env).to_dict() |
| 98 | |
| 99 | self._assert_results(result, stack_name) |
| 100 | for res in result['resources']: |
| 101 | self._assert_resource(res, stack_name) |
| 102 | self.assertEqual('OS::Heat::TestResource', |
| 103 | res['resource_type']) |
| 104 | |
| 105 | # common properties |
Bertrand Lallau | 855d1c0 | 2015-10-26 16:37:37 +0100 | [diff] [blame] | 106 | self.assertFalse(res['properties']['fail']) |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 107 | self.assertEqual(0, res['properties']['wait_secs']) |
Bertrand Lallau | 855d1c0 | 2015-10-26 16:37:37 +0100 | [diff] [blame] | 108 | self.assertFalse(res['properties']['update_replace']) |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 109 | |
| 110 | if res['resource_name'] == 'one': |
| 111 | self.assertEqual('fred', res['properties']['value']) |
| 112 | self.assertEqual(['two'], res['required_by']) |
| 113 | if res['resource_name'] == 'two': |
| 114 | self.assertEqual('abc', res['properties']['value']) |
| 115 | self.assertEqual([], res['required_by']) |
| 116 | |
| 117 | def test_basic_fail(self): |
| 118 | stack_name = self._stack_rand_name() |
| 119 | |
| 120 | # break the template so it fails validation. |
| 121 | wont_work = self.template.replace('get_param: incomming', |
| 122 | 'get_param: missing') |
| 123 | excp = self.assertRaises(exc.HTTPBadRequest, |
| 124 | self.client.stacks.preview, |
| 125 | template=wont_work, |
| 126 | stack_name=stack_name, |
| 127 | disable_rollback=True, |
| 128 | environment=self.env) |
| 129 | |
| 130 | self.assertIn('Property error: : resources.two.properties.value: ' |
| 131 | ': The Parameter (missing) was not provided.', |
| 132 | six.text_type(excp)) |
| 133 | |
| 134 | def test_nested_pass(self): |
| 135 | """Nested stacks need to recurse down the stacks.""" |
| 136 | main_template = ''' |
| 137 | heat_template_version: 2015-04-30 |
| 138 | parameters: |
| 139 | incomming: |
| 140 | type: string |
| 141 | resources: |
| 142 | main: |
| 143 | type: nested.yaml |
| 144 | properties: |
| 145 | value: {get_param: incomming} |
| 146 | outputs: |
| 147 | main_out: |
| 148 | value: {get_attr: [main, output]} |
| 149 | ''' |
| 150 | nested_template = ''' |
| 151 | heat_template_version: 2015-04-30 |
| 152 | parameters: |
| 153 | value: |
| 154 | type: string |
| 155 | resources: |
| 156 | nested: |
| 157 | type: OS::Heat::TestResource |
| 158 | properties: |
| 159 | value: {get_param: value} |
| 160 | outputs: |
| 161 | output: |
| 162 | value: {get_attr: [nested, output]} |
| 163 | ''' |
| 164 | stack_name = self._stack_rand_name() |
| 165 | result = self.client.stacks.preview( |
| 166 | disable_rollback=True, |
| 167 | stack_name=stack_name, |
| 168 | template=main_template, |
| 169 | files={'nested.yaml': nested_template}, |
| 170 | environment=self.env).to_dict() |
| 171 | |
| 172 | self._assert_results(result, stack_name) |
| 173 | |
| 174 | # nested resources return a list of their resources. |
| 175 | res = result['resources'][0][0] |
| 176 | nested_stack_name = '%s-%s' % (stack_name, |
| 177 | res['parent_resource']) |
| 178 | |
| 179 | self._assert_resource(res, nested_stack_name) |
| 180 | self.assertEqual('OS::Heat::TestResource', |
| 181 | res['resource_type']) |
| 182 | |
Bertrand Lallau | 855d1c0 | 2015-10-26 16:37:37 +0100 | [diff] [blame] | 183 | self.assertFalse(res['properties']['fail']) |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 184 | self.assertEqual(0, res['properties']['wait_secs']) |
Bertrand Lallau | 855d1c0 | 2015-10-26 16:37:37 +0100 | [diff] [blame] | 185 | self.assertFalse(res['properties']['update_replace']) |
Angus Salkeld | cecb8ed | 2015-08-05 13:19:45 +1000 | [diff] [blame] | 186 | |
| 187 | self.assertEqual('abc', res['properties']['value']) |
| 188 | self.assertEqual([], res['required_by']) |
Oleksii Chuprykov | 793c23e | 2016-03-04 19:42:20 +0200 | [diff] [blame] | 189 | |
| 190 | def test_res_group_with_nested_template(self): |
| 191 | main_template = ''' |
| 192 | heat_template_version: 2015-04-30 |
| 193 | resources: |
| 194 | fixed_network: |
| 195 | type: "OS::Neutron::Net" |
| 196 | rg: |
| 197 | type: "OS::Heat::ResourceGroup" |
| 198 | properties: |
| 199 | count: 1 |
| 200 | resource_def: |
| 201 | type: nested.yaml |
| 202 | properties: |
| 203 | fixed_network_id: {get_resource: fixed_network} |
| 204 | ''' |
| 205 | nested_template = ''' |
| 206 | heat_template_version: 2015-04-30 |
| 207 | |
| 208 | parameters: |
| 209 | fixed_network_id: |
| 210 | type: string |
| 211 | resources: |
| 212 | port: |
| 213 | type: "OS::Neutron::Port" |
| 214 | properties: |
| 215 | network_id: |
| 216 | get_param: fixed_network_id |
| 217 | |
| 218 | ''' |
| 219 | stack_name = self._stack_rand_name() |
| 220 | result = self.client.stacks.preview( |
| 221 | disable_rollback=True, |
| 222 | stack_name=stack_name, |
| 223 | template=main_template, |
| 224 | files={'nested.yaml': nested_template}).to_dict() |
rabi | ffdd10f | 2017-03-29 09:29:56 +0530 | [diff] [blame] | 225 | |
| 226 | resource_names = [] |
| 227 | |
| 228 | def get_resource_names(resources): |
| 229 | for item in resources: |
| 230 | if isinstance(item, dict): |
| 231 | resource_names.append(item['resource_name']) |
| 232 | else: |
| 233 | get_resource_names(item) |
| 234 | get_resource_names(result['resources']) |
Oleksii Chuprykov | 793c23e | 2016-03-04 19:42:20 +0200 | [diff] [blame] | 235 | # ensure that fixed network and port here |
rabi | ffdd10f | 2017-03-29 09:29:56 +0530 | [diff] [blame] | 236 | self.assertIn('fixed_network', resource_names) |
| 237 | self.assertIn('port', resource_names) |