Refactor functions out of ProcessTemplateAction

This needs to be common utility code for the next change in this
series, and breaking them out allows the test coverage to be improved.

Change-Id: I9d55ccfbd2875046dc7512413c3be3254a45910a
Blueprint: container-prepare-workflow
This commit is contained in:
Steve Baker 2018-04-18 11:47:34 +12:00
parent 5c0fc20c2a
commit 5c77d51678
3 changed files with 237 additions and 76 deletions

View File

@ -13,15 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import jinja2
import json
import logging
import os
import requests
import six
import tempfile as tf
import yaml
from heatclient.common import template_utils
from heatclient import exc as heat_exc
from mistral_lib import actions
from swiftclient import exceptions as swiftexceptions
@ -34,14 +31,6 @@ from tripleo_common.utils import tarball
LOG = logging.getLogger(__name__)
def _create_temp_file(data):
handle, env_temp_file = tf.mkstemp()
with open(env_temp_file, 'w') as temp_file:
temp_file.write(json.dumps(data))
os.close(handle)
return env_temp_file
class J2SwiftLoader(jinja2.BaseLoader):
"""Jinja2 loader to fetch included files from swift
@ -371,82 +360,29 @@ class ProcessTemplatesAction(base.TripleOAction):
return actions.Result(error=six.text_type(err))
template_name = plan_env.get('template', "")
environments = plan_env.get('environments', [])
env_paths = []
temp_files = []
template_object = os.path.join(swift.url, self.container,
template_name)
LOG.debug('Template: %s' % template_name)
LOG.debug('Environments: %s' % environments)
try:
for env in environments:
if env.get('path'):
env_paths.append(os.path.join(swift.url, self.container,
env['path']))
elif env.get('data'):
env_temp_file = _create_temp_file(env['data'])
temp_files.append(env_temp_file)
env_paths.append(env_temp_file)
# create a dict to hold all user set params and merge
# them in the appropriate order
merged_params = {}
# merge generated passwords into params first
passwords = plan_env.get('passwords', {})
merged_params.update(passwords)
# derived parameters are merged before 'parameter defaults'
# so that user-specified values can override the derived values.
derived_params = plan_env.get('derived_parameters', {})
merged_params.update(derived_params)
# handle user set parameter values next in case a user has set
# a new value for a password parameter
params = plan_env.get('parameter_defaults', {})
merged_params = template_utils.deep_update(merged_params, params)
if merged_params:
env_temp_file = _create_temp_file(
{'parameter_defaults': merged_params})
temp_files.append(env_temp_file)
env_paths.append(env_temp_file)
registry = plan_env.get('resource_registry', {})
if registry:
env_temp_file = _create_temp_file(
{'resource_registry': registry})
temp_files.append(env_temp_file)
env_paths.append(env_temp_file)
def _env_path_is_object(env_path):
retval = env_path.startswith(swift.url)
LOG.debug('_env_path_is_object %s: %s' % (env_path, retval))
return retval
def _object_request(method, url, token=context.auth_token):
response = requests.request(
method, url, headers={'X-Auth-Token': token})
response.raise_for_status()
return response.content
template_files, template = template_utils.get_template_contents(
template_object=template_object,
object_request=_object_request)
env_files, env = (
template_utils.process_multiple_environments_and_files(
env_paths=env_paths,
env_path_is_object=_env_path_is_object,
object_request=_object_request))
template_files, template = plan_utils.get_template_contents(
swift, template_object)
except Exception as err:
error_text = six.text_type(err)
LOG.exception("Error occurred while fetching %s" % template_object)
temp_env_paths = []
try:
env_paths, temp_env_paths = plan_utils.build_env_paths(
swift, self.container, plan_env)
env_files, env = plan_utils.process_environments_and_files(
swift, env_paths)
except Exception as err:
error_text = six.text_type(err)
LOG.exception("Error occurred while processing plan files.")
finally:
# cleanup any local temp files
for f in temp_files:
for f in temp_env_paths:
os.remove(f)
if error_text:

View File

@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import mock
import os
from swiftclient import exceptions as swiftexceptions
@ -111,3 +113,137 @@ class PlanTest(base.TestCase):
self.swift.get_object.assert_called()
self.swift.put_object.assert_called()
def test_write_json_temp_file(self):
name = plan_utils.write_json_temp_file({'foo': 'bar'})
with open(name) as f:
self.assertEqual({'foo': 'bar'}, json.load(f))
os.remove(name)
@mock.patch('requests.request', autospec=True)
def test_object_request(self, request):
request.return_value.content = 'foo'
content = plan_utils.object_request('GET', '/foo/bar', 'asdf1234')
self.assertEqual('foo', content)
request.assert_called_once_with(
'GET', '/foo/bar', headers={'X-Auth-Token': 'asdf1234'})
@mock.patch('tripleo_common.utils.plan.object_request',
autospec=True)
def test_process_environments_and_files(self, object_request):
swift_url = 'https://192.0.2.1:8443/foo'
url = '%s/bar' % swift_url
object_request.return_value = 'parameter_defaults: {foo: bar}'
swift = mock.Mock()
swift.url = swift_url
swift.token = 'asdf1234'
result = plan_utils.process_environments_and_files(swift, [url])
self.assertEqual(
{'parameter_defaults': {'foo': 'bar'}},
result[1]
)
object_request.assert_called_once_with(
'GET',
'https://192.0.2.1:8443/foo/bar',
'asdf1234'
)
@mock.patch('tripleo_common.utils.plan.object_request',
autospec=True)
def test_get_template_contents(self, object_request):
swift_url = 'https://192.0.2.1:8443/foo'
url = '%s/bar' % swift_url
object_request.return_value = 'heat_template_version: 2016-04-30'
swift = mock.Mock()
swift.url = swift_url
swift.token = 'asdf1234'
result = plan_utils.get_template_contents(swift, url)
self.assertEqual(
{'heat_template_version': '2016-04-30'},
result[1]
)
object_request.assert_called_once_with(
'GET',
'https://192.0.2.1:8443/foo/bar',
'asdf1234'
)
def test_build_env_paths(self):
swift = mock.Mock()
swift.url = 'https://192.0.2.1:8443/foo'
swift.token = 'asdf1234'
plan = {
'version': '1.0',
'environments': [
{'path': 'bar.yaml'},
{'data': {
'parameter_defaults': {'InlineParam': 1}}}
],
'passwords': {
'ThePassword': 'password1'
},
'derived_parameters': {
'DerivedParam': 'DerivedValue',
'MergableParam': {
'one': 'derived one',
'two': 'derived two',
},
},
'parameter_defaults': {
'Foo': 'bar',
'MergableParam': {
'one': 'user one',
'three': 'user three',
},
},
'resource_registry': {
'Foo::Bar': 'foo_bar.yaml'
},
}
env_paths, temp_env_paths = plan_utils.build_env_paths(
swift, 'overcloud', plan)
self.assertEqual(3, len(temp_env_paths))
self.assertEqual(
['https://192.0.2.1:8443/foo/overcloud/bar.yaml'] + temp_env_paths,
env_paths
)
with open(env_paths[1]) as f:
self.assertEqual(
{'parameter_defaults': {'InlineParam': 1}},
json.load(f)
)
with open(env_paths[2]) as f:
self.assertEqual(
{'parameter_defaults': {
'ThePassword': 'password1',
'DerivedParam': 'DerivedValue',
'Foo': 'bar',
'MergableParam': {
'one': 'user one',
'two': 'derived two',
'three': 'user three',
}
}},
json.load(f)
)
with open(env_paths[3]) as f:
self.assertEqual(
{'resource_registry': {
'Foo::Bar': 'foo_bar.yaml'
}},
json.load(f)
)
for path in temp_env_paths:
os.remove(path)

View File

@ -13,6 +13,12 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from heatclient.common import template_utils
import json
import os
import requests
import tempfile
import yaml
from tripleo_common import constants
@ -71,3 +77,86 @@ def put_user_env(swift, container_name, env):
constants.USER_ENVIRONMENT,
yaml.safe_dump(env, default_flow_style=False)
)
def write_json_temp_file(data):
"""Writes the provided data to a json file and return the filename"""
with tempfile.NamedTemporaryFile(delete=False, mode='w') as temp_file:
temp_file.write(json.dumps(data))
return temp_file.name
def object_request(method, url, token):
"""Fetch an object with the provided token"""
response = requests.request(
method, url, headers={'X-Auth-Token': token})
response.raise_for_status()
return response.content
def process_environments_and_files(swift, env_paths):
"""Wrap process_multiple_environments_and_files with swift object fetch"""
def _env_path_is_object(env_path):
return env_path.startswith(swift.url)
def _object_request(method, url, token=swift.token):
return object_request(method, url, token)
return template_utils.process_multiple_environments_and_files(
env_paths=env_paths,
env_path_is_object=_env_path_is_object,
object_request=_object_request)
def get_template_contents(swift, template_object):
"""Wrap get_template_contents with swift object fetch"""
def _object_request(method, url, token=swift.token):
return object_request(method, url, token)
return template_utils.get_template_contents(
template_object=template_object,
object_request=_object_request)
def build_env_paths(swift, container, plan_env):
environments = plan_env.get('environments', [])
env_paths = []
temp_env_paths = []
for env in environments:
if env.get('path'):
env_paths.append(os.path.join(swift.url, container, env['path']))
elif env.get('data'):
env_file = write_json_temp_file(env['data'])
temp_env_paths.append(env_file)
# create a dict to hold all user set params and merge
# them in the appropriate order
merged_params = {}
# merge generated passwords into params first
passwords = plan_env.get('passwords', {})
merged_params.update(passwords)
# derived parameters are merged before 'parameter defaults'
# so that user-specified values can override the derived values.
derived_params = plan_env.get('derived_parameters', {})
merged_params.update(derived_params)
# handle user set parameter values next in case a user has set
# a new value for a password parameter
params = plan_env.get('parameter_defaults', {})
merged_params = template_utils.deep_update(merged_params, params)
if merged_params:
env_temp_file = write_json_temp_file(
{'parameter_defaults': merged_params})
temp_env_paths.append(env_temp_file)
registry = plan_env.get('resource_registry', {})
if registry:
env_temp_file = write_json_temp_file(
{'resource_registry': registry})
temp_env_paths.append(env_temp_file)
env_paths.extend(temp_env_paths)
return env_paths, temp_env_paths