From bae429650289cdfd96a52d826ad775c526dfdb1b Mon Sep 17 00:00:00 2001 From: Ryan Brady Date: Wed, 23 May 2018 11:03:36 -0400 Subject: [PATCH] Fixes ordering of environment files in a deployment plan Sometimes making changes in basic deployment breaks the order of passed env files and leads to a failed deployment. This patch orders the environment files according to the rules defined in the capabilities-map.yaml file. Change-Id: Idbb6f0f3adebebd429bcb3447e559838180f4b1c --- tripleo_common/actions/heat_capabilities.py | 17 ++++++++ .../tests/actions/test_heat_capabilities.py | 8 +++- tripleo_common/tests/utils/test_plan.py | 42 +++++++++++++++++++ tripleo_common/utils/plan.py | 39 +++++++++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) diff --git a/tripleo_common/actions/heat_capabilities.py b/tripleo_common/actions/heat_capabilities.py index 7cf93a480..71d1bfff9 100644 --- a/tripleo_common/actions/heat_capabilities.py +++ b/tripleo_common/actions/heat_capabilities.py @@ -193,6 +193,23 @@ class UpdateCapabilitiesAction(base.TripleOAction): self.cache_delete(context, self.container, "tripleo.parameters.get") + # get the capabilities-map content to perform the environment ordering + try: + swift = self.get_object_client(context) + map_file = swift.get_object( + self.container, 'capabilities-map.yaml') + capabilities = yaml.safe_load(map_file[1]) + except swiftexceptions.ClientException as err: + err_msg = ("Error retrieving capabilities-map.yaml for " + "plan %s: %s" % (self.container, err)) + LOG.exception(err_msg) + return actions.Result(error=err_msg) + + ordered_env = plan_utils.apply_environments_order( + capabilities, env.get('environments', [])) + + env['environments'] = ordered_env + try: plan_utils.put_env(swift, env) except swiftexceptions.ClientException as err: diff --git a/tripleo_common/tests/actions/test_heat_capabilities.py b/tripleo_common/tests/actions/test_heat_capabilities.py index 1a5e672ae..87d1b2b00 100644 --- a/tripleo_common/tests/actions/test_heat_capabilities.py +++ b/tripleo_common/tests/actions/test_heat_capabilities.py @@ -246,7 +246,9 @@ class UpdateCapabilitiesActionTest(base.TestCase): - path: /path/to/overcloud-default-env.yaml - path: /path/to/ceph-storage-env.yaml """ - swift.get_object.return_value = ({}, mocked_env) + swift.get_object.side_effect = ( + ({}, mocked_env), + ({}, MAPPING_YAML_CONTENTS)) get_object_client_mock.return_value = swift environments = { @@ -287,7 +289,9 @@ class UpdateCapabilitiesActionTest(base.TestCase): - path: /path/to/overcloud-default-env.yaml - path: /path/to/ceph-storage-env.yaml """ - swift.get_object.return_value = ({}, mocked_env) + swift.get_object.side_effect = ( + ({}, mocked_env), + ({}, MAPPING_YAML_CONTENTS)) get_object_client_mock.return_value = swift environments = { diff --git a/tripleo_common/tests/utils/test_plan.py b/tripleo_common/tests/utils/test_plan.py index 8ea0c4f80..1fc60316d 100644 --- a/tripleo_common/tests/utils/test_plan.py +++ b/tripleo_common/tests/utils/test_plan.py @@ -44,6 +44,36 @@ resource_registry: OS::TripleO::Foo: bar.yaml """ +UNORDERED_PLAN_ENV_LIST = [ + {'path': 'overcloud-resource-registry-puppet.yaml'}, + {'path': 'environments/docker-ha.yaml'}, + {'path': 'environments/containers-default-parameters.yaml'}, + {'path': 'environments/docker.yaml'} +] + +CAPABILITIES_DICT = { + 'topics': [{ + 'environment_groups': [{ + 'environments': [{ + 'file': 'overcloud-resource-registry-puppet.yaml'} + ]}, { + 'environments': [{ + 'file': 'environments/docker.yaml', + 'requires': ['overcloud-resource-registry-puppet.yaml'] + }, { + 'file': 'environments/containers-default-parameters.yaml', + 'requires': ['overcloud-resource-registry-puppet.yaml', + 'environments/docker.yaml'] + }]}, { + 'environments': [{ + 'file': 'environments/docker-ha.yaml', + 'requires': ['overcloud-resource-registry-puppet.yaml', + 'environments/docker.yaml'] + }]} + ] + }] +} + class PlanTest(base.TestCase): def setUp(self): @@ -247,3 +277,15 @@ class PlanTest(base.TestCase): for path in temp_env_paths: os.remove(path) + + def test_apply_env_order(self): + ordered_plan_env_list = [ + {'path': 'overcloud-resource-registry-puppet.yaml'}, + {'path': 'environments/docker.yaml'}, + {'path': 'environments/docker-ha.yaml'}, + {'path': 'environments/containers-default-parameters.yaml'} + ] + + ordered_env = plan_utils.apply_environments_order( + CAPABILITIES_DICT, UNORDERED_PLAN_ENV_LIST) + self.assertEqual(ordered_env, ordered_plan_env_list) diff --git a/tripleo_common/utils/plan.py b/tripleo_common/utils/plan.py index 1b4bb49eb..c52db2af3 100644 --- a/tripleo_common/utils/plan.py +++ b/tripleo_common/utils/plan.py @@ -160,3 +160,42 @@ def build_env_paths(swift, container, plan_env): env_paths.extend(temp_env_paths) return env_paths, temp_env_paths + + +def apply_environments_order(capabilities, environments): + """traverses the capabilities and orders the environment files + + by dependency rules defined in capabilities-map, so that parent + environments are first and children environments override these + parents + + :param capabilities: dict representing capabilities-map.yaml file + :param environments: list representing the environments section of the + plan-environments.yaml file + :return: list containing ordered environments + + """ + # get ordering rules from capabilities-map file + order_rules = {} + for topic in capabilities.get('topics', []): + for group in topic.get('environment_groups', []): + for environment in group.get('environments', []): + order_rules[environment['file']] = [] + if 'requires' in environment: + order_rules[environment['file']] \ + = environment.get('requires', []) + + # apply ordering rules + for e in environments: + path = e.get('path', '') + if path not in order_rules: + continue + path_pos = environments.index(e) + for requirement in order_rules[path]: + if {'path': requirement} in environments: + requirement_pos = environments.index({'path': requirement}) + if requirement_pos > path_pos: + item = environments.pop(requirement_pos) + environments.insert(path_pos, item) + + return environments