summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-02-26 05:43:15 +0000
committerGerrit Code Review <review@openstack.org>2016-02-26 05:43:15 +0000
commitfdc1ee7deeff894fc37ceb5cf6791910af3ed3af (patch)
treeab2e3bf34171a68330d7ba135b4b4d9c072a0f55
parent9336c5bc87681639b99e369cedcd6a64e794b55c (diff)
parent7627e1bae53747e1471caf632dfc1b6edf2e26cf (diff)
downloadpython-heatclient-fdc1ee7deeff894fc37ceb5cf6791910af3ed3af.tar.gz
Merge "Changes to support server-side environment resolution"
-rw-r--r--heatclient/common/template_utils.py85
-rw-r--r--heatclient/tests/unit/test_template_utils.py36
-rw-r--r--heatclient/v1/shell.py30
3 files changed, 137 insertions, 14 deletions
diff --git a/heatclient/common/template_utils.py b/heatclient/common/template_utils.py
index b80878d..fae54ef 100644
--- a/heatclient/common/template_utils.py
+++ b/heatclient/common/template_utils.py
@@ -189,17 +189,51 @@ def deep_update(old, new):
def process_multiple_environments_and_files(env_paths=None, template=None,
template_url=None,
env_path_is_object=None,
- object_request=None):
-
+ object_request=None,
+ env_list_tracker=None):
+ """Reads one or more environment files.
+
+ Reads in each specified environment file and returns a dictionary
+ of the filenames->contents (suitable for the files dict)
+ and the consolidated environment (after having applied the correct
+ overrides based on order).
+
+ If a list is provided in the env_list_tracker parameter, the behavior
+ is altered to take advantage of server-side environment resolution.
+ Specifically, this means:
+
+ * Populating env_list_tracker with an ordered list of environment file
+ URLs to be passed to the server
+ * Including the contents of each environment file in the returned
+ files dict, keyed by one of the URLs in env_list_tracker
+
+ :param env_paths: list of paths to the environment files to load; if
+ None, empty results will be returned
+ :type env_paths: list or None
+ :param template: unused; only included for API compatibility
+ :param template_url: unused; only included for API compatibility
+ :param env_list_tracker: if specified, environment filenames will be
+ stored within
+ :type env_list_tracker: list or None
+ :return: tuple of files dict and a dict of the consolidated environment
+ :rtype: tuple
+ """
merged_files = {}
merged_env = {}
+ # If we're keeping a list of environment files separately, include the
+ # contents of the files in the files dict
+ include_env_in_files = env_list_tracker is not None
+
if env_paths:
for env_path in env_paths:
- files, env = process_environment_and_files(env_path, template,
- template_url,
- env_path_is_object,
- object_request)
+ files, env = process_environment_and_files(
+ env_path=env_path,
+ template=template,
+ template_url=template_url,
+ env_path_is_object=env_path_is_object,
+ object_request=object_request,
+ include_env_in_files=include_env_in_files)
# 'files' looks like {"filename1": contents, "filename2": contents}
# so a simple update is enough for merging
@@ -209,12 +243,32 @@ def process_multiple_environments_and_files(env_paths=None, template=None,
# not enough
merged_env = deep_update(merged_env, env)
+ if env_list_tracker is not None:
+ env_url = utils.normalise_file_path_to_url(env_path)
+ env_list_tracker.append(env_url)
+
return merged_files, merged_env
-def process_environment_and_files(env_path=None, template=None,
- template_url=None, env_path_is_object=None,
- object_request=None):
+def process_environment_and_files(env_path=None,
+ template=None,
+ template_url=None,
+ env_path_is_object=None,
+ object_request=None,
+ include_env_in_files=False):
+ """Loads a single environment file.
+
+ Returns an entry suitable for the files dict which maps the environment
+ filename to its contents.
+
+ :param env_path: full path to the file to load
+ :type env_path: str or None
+ :param include_env_in_files: if specified, the raw environment file itself
+ will be included in the returned files dict
+ :type include_env_in_files: bool
+ :return: tuple of files dict and the loaded environment as a dict
+ :rtype: (dict, dict)
+ """
files = {}
env = {}
@@ -234,6 +288,10 @@ def process_environment_and_files(env_path=None, template=None,
env_url = utils.normalise_file_path_to_url(env_path)
env_base_url = utils.base_url_for_url(env_url)
raw_env = request.urlopen(env_url).read()
+
+ if include_env_in_files:
+ files[env_url] = raw_env
+
env = environment_format.parse(raw_env)
resolve_environment_urls(
@@ -246,6 +304,15 @@ def process_environment_and_files(env_path=None, template=None,
def resolve_environment_urls(resource_registry, files, env_base_url,
is_object=False, object_request=None):
+ """Handles any resource URLs specified in an environment.
+
+ :param resource_registry: mapping of type name to template filename
+ :type resource_registry: dict
+ :param files: dict to store loaded file contents into
+ :type files: dict
+ :param env_base_url: base URL to look in when loading files
+ :type env_base_url: str or None
+ """
if resource_registry is None:
return
diff --git a/heatclient/tests/unit/test_template_utils.py b/heatclient/tests/unit/test_template_utils.py
index c6ffebd..920573d 100644
--- a/heatclient/tests/unit/test_template_utils.py
+++ b/heatclient/tests/unit/test_template_utils.py
@@ -374,6 +374,42 @@ class ShellEnvironmentTest(testtools.TestCase):
self.assertEqual(self.template_a.decode('utf-8'),
files['http://no.where/path/to/b/a.yaml'])
+ def test_process_multiple_environments_and_files_tracker(self):
+ # Setup
+ self.m.StubOutWithMock(request, 'urlopen')
+ env_file1 = '/home/my/dir/env1.yaml'
+
+ env1 = b'''
+ parameters:
+ "param1": "value1"
+ resource_registry:
+ "OS::Thingy1": "file:///home/b/a.yaml"
+ '''
+ request.urlopen('file://%s' % env_file1).AndReturn(
+ six.BytesIO(env1))
+ request.urlopen('file:///home/b/a.yaml').AndReturn(
+ six.BytesIO(self.template_a))
+ request.urlopen('file:///home/b/a.yaml').AndReturn(
+ six.BytesIO(self.template_a))
+ self.m.ReplayAll()
+
+ # Test
+ env_file_list = []
+ files, env = template_utils.process_multiple_environments_and_files(
+ [env_file1], env_list_tracker=env_file_list)
+
+ # Verify
+ expected_env = {'parameters': {'param1': 'value1'},
+ 'resource_registry':
+ {'OS::Thingy1': 'file:///home/b/a.yaml'}
+ }
+ self.assertEqual(expected_env, env)
+
+ self.assertEqual(self.template_a.decode('utf-8'),
+ files['file:///home/b/a.yaml'])
+
+ self.assertEqual(['file:///home/my/dir/env1.yaml'], env_file_list)
+
def test_global_files(self):
url = 'file:///home/b/a.yaml'
env = '''
diff --git a/heatclient/v1/shell.py b/heatclient/v1/shell.py
index d530958..43395d0 100644
--- a/heatclient/v1/shell.py
+++ b/heatclient/v1/shell.py
@@ -103,8 +103,9 @@ def do_stack_create(hc, args):
args.template_url,
args.template_object,
_authenticated_fetcher(hc))
+ env_files_list = []
env_files, env = template_utils.process_multiple_environments_and_files(
- env_paths=args.environment_file)
+ env_paths=args.environment_file, env_list_tracker=env_files_list)
if args.create_timeout:
logger.warning(_LW('%(arg1)s is deprecated, '
@@ -128,6 +129,10 @@ def do_stack_create(hc, args):
'environment': env
}
+ # If one or more environments is found, pass the listing to the server
+ if env_files_list:
+ fields['environment_files'] = env_files_list
+
if args.tags:
fields['tags'] = args.tags
timeout = args.timeout or args.create_timeout
@@ -249,8 +254,9 @@ def do_stack_preview(hc, args):
args.template_url,
args.template_object,
_authenticated_fetcher(hc))
+ env_files_list = []
env_files, env = template_utils.process_multiple_environments_and_files(
- env_paths=args.environment_file)
+ env_paths=args.environment_file, env_list_tracker=env_files_list)
fields = {
'stack_name': args.name,
@@ -265,6 +271,10 @@ def do_stack_preview(hc, args):
'environment': env
}
+ # If one or more environments is found, pass the listing to the server
+ if env_files_list:
+ fields['environment_files'] = env_files_list
+
if args.tags:
fields['tags'] = args.tags
@@ -461,9 +471,9 @@ def do_stack_update(hc, args):
args.template_object,
_authenticated_fetcher(hc),
existing=args.existing)
-
+ env_files_list = []
env_files, env = template_utils.process_multiple_environments_and_files(
- env_paths=args.environment_file)
+ env_paths=args.environment_file, env_list_tracker=env_files_list)
if args.pre_update:
template_utils.hooks_to_env(env, args.pre_update, 'pre-update')
@@ -480,6 +490,10 @@ def do_stack_update(hc, args):
'environment': env
}
+ # If one or more environments is found, pass the listing to the server
+ if env_files_list:
+ fields['environment_files'] = env_files_list
+
if args.tags:
fields['tags'] = args.tags
if args.timeout:
@@ -831,8 +845,10 @@ def do_template_validate(hc, args):
args.template_object,
_authenticated_fetcher(hc))
+ env_files_list = []
env_files, env = template_utils.process_multiple_environments_and_files(
- env_paths=args.environment_file)
+ env_paths=args.environment_file, env_list_tracker=env_files_list)
+
fields = {
'template': template,
'parameters': utils.format_parameters(args.parameters),
@@ -843,6 +859,10 @@ def do_template_validate(hc, args):
if args.ignore_errors:
fields['ignore_errors'] = args.ignore_errors
+ # If one or more environments is found, pass the listing to the server
+ if env_files_list:
+ fields['environment_files'] = env_files_list
+
if args.show_nested:
fields['show_nested'] = args.show_nested