From 4ad3cae6c3ef026a7ac91d4e78a059938956f746 Mon Sep 17 00:00:00 2001 From: Rabi Mishra Date: Mon, 21 Sep 2015 18:15:04 +0530 Subject: [PATCH] Add functional test for ResourceGroup rolling_update Change-Id: I77456afde1a425aacce5d9778825861179ac89aa --- heat_integrationtests/common/test.py | 21 ++ .../functional/test_resource_group.py | 245 ++++++++++++++++-- 2 files changed, 239 insertions(+), 27 deletions(-) diff --git a/heat_integrationtests/common/test.py b/heat_integrationtests/common/test.py index 8d8fa15c39..a451fcb248 100644 --- a/heat_integrationtests/common/test.py +++ b/heat_integrationtests/common/test.py @@ -432,6 +432,27 @@ class HeatIntegrationTest(testscenarios.WithScenarios, self.assertEqual(parent_id, nested_stack.parent) return nested_identifier + def group_nested_identifier(self, stack_identifier, + group_name): + # Get the nested stack identifier from a group resource + rsrc = self.client.resources.get(stack_identifier, group_name) + physical_resource_id = rsrc.physical_resource_id + + nested_stack = self.client.stacks.get(physical_resource_id) + nested_identifier = '%s/%s' % (nested_stack.stack_name, + nested_stack.id) + parent_id = stack_identifier.split("/")[-1] + self.assertEqual(parent_id, nested_stack.parent) + return nested_identifier + + def list_group_resources(self, stack_identifier, + group_name, minimal=True): + nested_identifier = self.group_nested_identifier(stack_identifier, + group_name) + if minimal: + return self.list_resources(nested_identifier) + return self.client.resources.list(nested_identifier) + def list_resources(self, stack_identifier): resources = self.client.resources.list(stack_identifier) return dict((r.resource_name, r.resource_type) for r in resources) diff --git a/heat_integrationtests/functional/test_resource_group.py b/heat_integrationtests/functional/test_resource_group.py index 4fd70af614..e40376c4ad 100644 --- a/heat_integrationtests/functional/test_resource_group.py +++ b/heat_integrationtests/functional/test_resource_group.py @@ -45,19 +45,6 @@ outputs: def setUp(self): super(ResourceGroupTest, self).setUp() - def _group_nested_identifier(self, stack_identifier, - group_name='random_group'): - # Get the nested stack identifier from the group - rsrc = self.client.resources.get(stack_identifier, group_name) - physical_resource_id = rsrc.physical_resource_id - - nested_stack = self.client.stacks.get(physical_resource_id) - nested_identifier = '%s/%s' % (nested_stack.stack_name, - nested_stack.id) - parent_id = stack_identifier.split("/")[-1] - self.assertEqual(parent_id, nested_stack.parent) - return nested_identifier - def test_resource_group_zero_novalidate(self): # Nested resources should be validated only when size > 0 # This allows features to be disabled via size=0 without @@ -92,7 +79,8 @@ resources: self.list_resources(stack_identifier)) # Check we created an empty nested stack - nested_identifier = self._group_nested_identifier(stack_identifier) + nested_identifier = self.group_nested_identifier(stack_identifier, + 'random_group') self.assertEqual({}, self.list_resources(nested_identifier)) # Prove validation works for non-zero create/update @@ -108,12 +96,9 @@ resources: environment=env, files=files) self.assertIn(expected_err, six.text_type(ex)) - def _list_group_resources(self, stack_identifier): - nested_identifier = self._group_nested_identifier(stack_identifier) - return self.list_resources(nested_identifier) - def _validate_resources(self, stack_identifier, expected_count): - resources = self._list_group_resources(stack_identifier) + resources = self.list_group_resources(stack_identifier, + 'random_group') self.assertEqual(expected_count, len(resources)) expected_resources = dict( (str(idx), 'My::RandomString') @@ -187,7 +172,8 @@ resources: stack_identifier = self.stack_create(template=rp_template) self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'}, self.list_resources(stack_identifier)) - group_resources = self._list_group_resources(stack_identifier) + group_resources = self.list_group_resources(stack_identifier, + 'random_group') expected_resources = {u'0': u'OS::Heat::RandomString', u'1': u'OS::Heat::RandomString', u'2': u'OS::Heat::RandomString', @@ -200,7 +186,8 @@ resources: 'removal_policies: []', 'removal_policies: [{resource_list: [\'1\', \'2\', \'3\']}]') self.update_stack(stack_identifier, update_template) - group_resources = self._list_group_resources(stack_identifier) + group_resources = self.list_group_resources(stack_identifier, + 'random_group') expected_resources = {u'0': u'OS::Heat::RandomString', u'4': u'OS::Heat::RandomString', u'5': u'OS::Heat::RandomString', @@ -219,7 +206,8 @@ resources: self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'}, self.list_resources(stack_identifier)) - initial_nested_ident = self._group_nested_identifier(stack_identifier) + initial_nested_ident = self.group_nested_identifier(stack_identifier, + 'random_group') self.assertEqual({'0': 'My::RandomString'}, self.list_resources(initial_nested_ident)) # get the resource id @@ -230,7 +218,8 @@ resources: # not the nested stack or resource group. template_salt = template_one.replace("salt: initial", "salt: more") self.update_stack(stack_identifier, template_salt, environment=env) - updated_nested_ident = self._group_nested_identifier(stack_identifier) + updated_nested_ident = self.group_nested_identifier(stack_identifier, + 'random_group') self.assertEqual(initial_nested_ident, updated_nested_ident) # compare the resource id, we expect a change. @@ -249,7 +238,8 @@ resources: self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'}, self.list_resources(stack_identifier)) - initial_nested_ident = self._group_nested_identifier(stack_identifier) + initial_nested_ident = self.group_nested_identifier(stack_identifier, + 'random_group') self.assertEqual({'0': 'My::RandomString', '1': 'My::RandomString'}, self.list_resources(initial_nested_ident)) # get the output @@ -258,7 +248,8 @@ resources: template_copy = copy.deepcopy(template_one) self.update_stack(stack_identifier, template_copy, environment=env) - updated_nested_ident = self._group_nested_identifier(stack_identifier) + updated_nested_ident = self.group_nested_identifier(stack_identifier, + 'random_group') self.assertEqual(initial_nested_ident, updated_nested_ident) # compare the random number, we expect no change. @@ -309,7 +300,8 @@ outputs: self.assertEqual({u'random_group': u'OS::Heat::ResourceGroup'}, self.list_resources(stack_identifier)) - initial_nested_ident = self._group_nested_identifier(stack_identifier) + initial_nested_ident = self.group_nested_identifier(stack_identifier, + 'random_group') self.assertEqual({'0': 'My::RandomString', '1': 'My::RandomString'}, self.list_resources(initial_nested_ident)) # get the output @@ -320,7 +312,8 @@ outputs: # note "files2". self.update_stack(stack_identifier, template_one, environment=env, files=files2) - updated_nested_ident = self._group_nested_identifier(stack_identifier) + updated_nested_ident = self.group_nested_identifier(stack_identifier, + 'random_group') self.assertEqual(initial_nested_ident, updated_nested_ident) # compare the output, we expect a change. @@ -510,3 +503,201 @@ resources: self._wait_for_stack_status( stack_identifier, 'DELETE_COMPLETE', success_on_not_found=True) + + +class ResourceGroupUpdatePolicyTest(functional_base.FunctionalTestsBase): + + template = ''' +heat_template_version: '2015-04-30' +resources: + random_group: + type: OS::Heat::ResourceGroup + update_policy: + rolling_update: + min_in_service: 1 + max_batch_size: 2 + pause_time: 1 + properties: + count: 10 + resource_def: + type: OS::Heat::TestResource + properties: + value: initial + update_replace: False +''' + + def update_resource_group(self, update_template, + updated, created, deleted): + stack_identifier = self.stack_create(template=self.template) + group_resources = self.list_group_resources(stack_identifier, + 'random_group', + minimal=False) + + init_names = [res.physical_resource_id for res in group_resources] + + self.update_stack(stack_identifier, update_template) + group_resources = self.list_group_resources(stack_identifier, + 'random_group', + minimal=False) + + updt_names = [res.physical_resource_id for res in group_resources] + + matched_names = set(updt_names) & set(init_names) + + self.assertEqual(updated, len(matched_names)) + + self.assertEqual(created, len(set(updt_names) - set(init_names))) + + self.assertEqual(deleted, len(set(init_names) - set(updt_names))) + + def test_resource_group_update(self): + """Test rolling update with no conflict. + + Simple rolling update with no conflict in batch size + and minimum instances in service. + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '1' + policy['max_batch_size'] = '3' + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + + self.update_resource_group(updt_template, + updated=10, + created=0, + deleted=0) + + def test_resource_group_update_replace(self): + """Test rolling update(replace)with no conflict. + + Simple rolling update replace with no conflict in batch size + and minimum instances in service. + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '1' + policy['max_batch_size'] = '3' + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + res_def['properties']['update_replace'] = True + + self.update_resource_group(updt_template, + updated=0, + created=10, + deleted=10) + + def test_resource_group_update_scaledown(self): + """Test rolling update with scaledown. + + Simple rolling update with reduced size. + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '1' + policy['max_batch_size'] = '3' + grp['properties']['count'] = 6 + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + + self.update_resource_group(updt_template, + updated=6, + created=0, + deleted=4) + + def test_resource_group_update_scaleup(self): + """Test rolling update with scaleup. + + Simple rolling update with increased size. + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '1' + policy['max_batch_size'] = '3' + grp['properties']['count'] = 12 + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + + self.update_resource_group(updt_template, + updated=10, + created=2, + deleted=0) + + def test_resource_group_update_adjusted(self): + """Test rolling update with enough available resources + + Update with capacity adjustment with enough resources. + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '8' + policy['max_batch_size'] = '4' + grp['properties']['count'] = 6 + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + + self.update_resource_group(updt_template, + updated=6, + created=0, + deleted=4) + + def test_resource_group_update_with_adjusted_capacity(self): + """Test rolling update with capacity adjustment. + + Rolling update with capacity adjustment due to conflict in + batch size and minimum instances in service. + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '8' + policy['max_batch_size'] = '4' + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + + self.update_resource_group(updt_template, + updated=10, + created=0, + deleted=0) + + def test_resource_group_update_huge_batch_size(self): + """Test rolling update with huge batch size. + + Rolling Update with a huge batch size(more than + current size). + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '0' + policy['max_batch_size'] = '20' + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + self.update_resource_group(updt_template, + updated=10, + created=0, + deleted=0) + + def test_resource_group_update_huge_min_in_service(self): + """Test rolling update with huge minimum capacity. + + Rolling Update with a huge number of minimum instances + in service. + """ + updt_template = yaml.load(copy.deepcopy(self.template)) + grp = updt_template['resources']['random_group'] + policy = grp['update_policy']['rolling_update'] + policy['min_in_service'] = '20' + policy['max_batch_size'] = '1' + res_def = grp['properties']['resource_def'] + res_def['properties']['value'] = 'updated' + + self.update_resource_group(updt_template, + updated=10, + created=0, + deleted=0)