deb-heat/heat/tests/test_environment.py
Ton Ngo b8ba69e05f Pass list of parameters to engine service to reset
For stack-update, a new argument named clear-parameters with
a list is passed in from the CLI and the new PATCH ReST API
to delete the indicated parameters from the existing parameters
in the DB, allowing the default parameters in the template to
be used.  A new method in environment handles the reset.

Partially-implements: blueprint troubleshooting-low-level-control
Partial-Bug: 1224828
Change-Id: Ia1270b679f27e264e6977c590d676b947c74c5da
2014-09-15 17:51:32 -07:00

453 lines
18 KiB
Python

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os.path
import sys
import fixtures
import mock
from oslo.config import cfg
import six
from heat.common import environment_format
from heat.engine import environment
from heat.engine import resources
from heat.tests import common
from heat.tests import generic_resource
cfg.CONF.import_opt('environment_dir', 'heat.common.config')
class EnvironmentTest(common.HeatTestCase):
def setUp(self):
super(EnvironmentTest, self).setUp()
self.g_env = resources.global_env()
def test_load_old_parameters(self):
old = {u'a': u'ff', u'b': u'ss'}
expected = {u'parameters': old,
u'resource_registry': {u'resources': {}}}
env = environment.Environment(old)
self.assertEqual(expected, env.user_env_as_dict())
def test_load_new_env(self):
new_env = {u'parameters': {u'a': u'ff', u'b': u'ss'},
u'resource_registry': {u'OS::Food': u'fruity.yaml',
u'resources': {}}}
env = environment.Environment(new_env)
self.assertEqual(new_env, env.user_env_as_dict())
def test_existing_parameters(self):
# This tests reusing the existing parameters as is
prev_params = {'foo': 'bar', 'tester': 'Yes'}
params = {}
expected = {'parameters': prev_params,
'resource_registry': {'resources': {}}}
prev_env = environment.Environment(
{'parameters': prev_params,
'resource_registry': {'resources': {}}})
env = environment.Environment(params)
env.patch_previous_parameters(prev_env)
self.assertEqual(expected, env.user_env_as_dict())
def test_patch_existing_parameters(self):
# This tests patching cli parameters over the existing parameters
prev_params = {'foo': 'bar', 'tester': 'Yes'}
params = {'tester': 'patched'}
expected = {'parameters': {'foo': 'bar', 'tester': 'patched'},
'resource_registry': {'resources': {}}}
prev_env = environment.Environment(
{'parameters': prev_params,
'resource_registry': {'resources': {}}})
env = environment.Environment(params)
env.patch_previous_parameters(prev_env)
self.assertEqual(expected, env.user_env_as_dict())
def test_patch_and_clear_existing_parameters(self):
# This tests patching cli parameters over the existing parameters
prev_params = {'foo': 'bar', 'tester': 'Yes',
'another_tester': 'Yes'}
params = {'tester': 'patched'}
expected = {'parameters': {'foo': 'bar', 'tester': 'patched'},
'resource_registry': {'resources': {}}}
prev_env = environment.Environment(
{'parameters': prev_params,
'resource_registry': {'resources': {}}})
env = environment.Environment(params)
env.patch_previous_parameters(prev_env, ['another_tester'])
self.assertEqual(expected, env.user_env_as_dict())
def test_clear_existing_parameters(self):
# This tests removing some parameters in the existing set of parameters
prev_params = {'foo': 'bar', 'tester': 'Yes'}
params = {}
expected = {'parameters': {'foo': 'bar'},
'resource_registry': {'resources': {}}}
prev_env = environment.Environment(
{'parameters': prev_params,
'resource_registry': {'resources': {}}})
env = environment.Environment(params)
env.patch_previous_parameters(prev_env, ['tester'])
self.assertEqual(expected, env.user_env_as_dict())
def test_global_registry(self):
self.g_env.register_class('CloudX::Nova::Server',
generic_resource.GenericResource)
new_env = {u'parameters': {u'a': u'ff', u'b': u'ss'},
u'resource_registry': {u'OS::*': 'CloudX::*'}}
env = environment.Environment(new_env)
self.assertEqual('CloudX::Nova::Server',
env.get_resource_info('OS::Nova::Server',
'my_db_server').name)
def test_map_one_resource_type(self):
new_env = {u'parameters': {u'a': u'ff', u'b': u'ss'},
u'resource_registry': {u'resources':
{u'my_db_server':
{u'OS::DBInstance': 'db.yaml'}}}}
env = environment.Environment(new_env)
info = env.get_resource_info('OS::DBInstance', 'my_db_server')
self.assertEqual('db.yaml', info.value)
def test_map_all_resources_of_type(self):
self.g_env.register_class('OS::Nova::FloatingIP',
generic_resource.GenericResource)
new_env = {u'parameters': {u'a': u'ff', u'b': u'ss'},
u'resource_registry':
{u'OS::Networking::FloatingIP': 'OS::Nova::FloatingIP',
u'OS::Loadbalancer': 'lb.yaml'}}
env = environment.Environment(new_env)
self.assertEqual('OS::Nova::FloatingIP',
env.get_resource_info('OS::Networking::FloatingIP',
'my_fip').name)
def test_resource_sort_order_len(self):
new_env = {u'resource_registry': {u'resources': {u'my_fip': {
u'OS::Networking::FloatingIP': 'ip.yaml'}}},
u'OS::Networking::FloatingIP': 'OS::Nova::FloatingIP'}
env = environment.Environment(new_env)
self.assertEqual('ip.yaml',
env.get_resource_info('OS::Networking::FloatingIP',
'my_fip').value)
def test_env_load(self):
new_env = {u'resource_registry': {u'resources': {u'my_fip': {
u'OS::Networking::FloatingIP': 'ip.yaml'}}}}
env = environment.Environment()
self.assertIsNone(env.get_resource_info('OS::Networking::FloatingIP',
'my_fip'))
env.load(new_env)
self.assertEqual('ip.yaml',
env.get_resource_info('OS::Networking::FloatingIP',
'my_fip').value)
def test_constraints(self):
env = environment.Environment({})
first_constraint = object()
second_constraint = object()
env.register_constraint("constraint1", first_constraint)
env.register_constraint("constraint2", second_constraint)
self.assertIs(first_constraint, env.get_constraint("constraint1"))
self.assertIs(second_constraint, env.get_constraint("constraint2"))
self.assertIs(None, env.get_constraint("no_constraint"))
def test_constraints_registry(self):
constraint_content = '''
class MyConstraint(object):
pass
def constraint_mapping():
return {"constraint1": MyConstraint}
'''
plugin_dir = self.useFixture(fixtures.TempDir())
plugin_file = os.path.join(plugin_dir.path, 'test.py')
with open(plugin_file, 'w+') as ef:
ef.write(constraint_content)
self.addCleanup(sys.modules.pop, "heat.engine.plugins.test")
cfg.CONF.set_override('plugin_dirs', plugin_dir.path)
env = environment.Environment({})
resources._load_global_environment(env)
self.assertEqual("MyConstraint",
env.get_constraint("constraint1").__name__)
self.assertIs(None, env.get_constraint("no_constraint"))
def test_constraints_registry_error(self):
constraint_content = '''
def constraint_mapping():
raise ValueError("oops")
'''
plugin_dir = self.useFixture(fixtures.TempDir())
plugin_file = os.path.join(plugin_dir.path, 'test.py')
with open(plugin_file, 'w+') as ef:
ef.write(constraint_content)
self.addCleanup(sys.modules.pop, "heat.engine.plugins.test")
cfg.CONF.set_override('plugin_dirs', plugin_dir.path)
env = environment.Environment({})
error = self.assertRaises(ValueError,
resources._load_global_environment, env)
self.assertEqual("oops", six.text_type(error))
def test_constraints_registry_stevedore(self):
env = environment.Environment({})
resources._load_global_environment(env)
self.assertEqual("FlavorConstraint",
env.get_constraint("nova.flavor").__name__)
self.assertIs(None, env.get_constraint("no_constraint"))
class EnvironmentDuplicateTest(common.HeatTestCase):
scenarios = [
('same', dict(resource_type='test.yaml',
expected_equal=True)),
('diff_temp', dict(resource_type='not.yaml',
expected_equal=False)),
('diff_map', dict(resource_type='OS::SomethingElse',
expected_equal=False)),
('diff_path', dict(resource_type='a/test.yaml',
expected_equal=False)),
]
def test_env_load(self):
env_initial = {u'resource_registry': {
u'OS::Test::Dummy': 'test.yaml'}}
env = environment.Environment()
env.load(env_initial)
info = env.get_resource_info('OS::Test::Dummy', 'something')
replace_log = 'Changing %s from %s to %s' % ('OS::Test::Dummy',
'test.yaml',
self.resource_type)
self.assertNotIn(replace_log, self.LOG.output)
env_test = {u'resource_registry': {
u'OS::Test::Dummy': self.resource_type}}
env.load(env_test)
if self.expected_equal:
# should return exactly the same object.
self.assertIs(info, env.get_resource_info('OS::Test::Dummy',
'my_fip'))
self.assertNotIn(replace_log, self.LOG.output)
else:
self.assertIn(replace_log, self.LOG.output)
self.assertNotEqual(info,
env.get_resource_info('OS::Test::Dummy',
'my_fip'))
class GlobalEnvLoadingTest(common.HeatTestCase):
def test_happy_path(self):
with mock.patch('glob.glob') as m_ldir:
m_ldir.return_value = ['/etc_etc/heat/environment.d/a.yaml']
env_dir = '/etc_etc/heat/environment.d'
env_content = '{"resource_registry": {}}'
env = environment.Environment({}, user_env=False)
with mock.patch('heat.engine.environment.open',
mock.mock_open(read_data=env_content),
create=True) as m_open:
environment.read_global_environment(env, env_dir)
m_ldir.assert_called_once_with(env_dir + '/*')
m_open.assert_called_once_with('%s/a.yaml' % env_dir)
def test_empty_env_dir(self):
with mock.patch('glob.glob') as m_ldir:
m_ldir.return_value = []
env_dir = '/etc_etc/heat/environment.d'
env = environment.Environment({}, user_env=False)
environment.read_global_environment(env, env_dir)
m_ldir.assert_called_once_with(env_dir + '/*')
def test_continue_on_ioerror(self):
"""assert we get all files processed even if there are
processing exceptions.
"""
with mock.patch('glob.glob') as m_ldir:
m_ldir.return_value = ['/etc_etc/heat/environment.d/a.yaml',
'/etc_etc/heat/environment.d/b.yaml']
env_dir = '/etc_etc/heat/environment.d'
env_content = '{}'
env = environment.Environment({}, user_env=False)
with mock.patch('heat.engine.environment.open',
mock.mock_open(read_data=env_content),
create=True) as m_open:
m_open.side_effect = IOError
environment.read_global_environment(env, env_dir)
m_ldir.assert_called_once_with(env_dir + '/*')
expected = [mock.call('%s/a.yaml' % env_dir),
mock.call('%s/b.yaml' % env_dir)]
self.assertEqual(expected, m_open.call_args_list)
def test_continue_on_parse_error(self):
"""assert we get all files processed even if there are
processing exceptions.
"""
with mock.patch('glob.glob') as m_ldir:
m_ldir.return_value = ['/etc_etc/heat/environment.d/a.yaml',
'/etc_etc/heat/environment.d/b.yaml']
env_dir = '/etc_etc/heat/environment.d'
env_content = '{@$%#$%'
env = environment.Environment({}, user_env=False)
with mock.patch('heat.engine.environment.open',
mock.mock_open(read_data=env_content),
create=True) as m_open:
environment.read_global_environment(env, env_dir)
m_ldir.assert_called_once_with(env_dir + '/*')
expected = [mock.call('%s/a.yaml' % env_dir),
mock.call('%s/b.yaml' % env_dir)]
self.assertEqual(expected, m_open.call_args_list)
def test_env_resources_override_plugins(self):
# assertion: any template resources in the global environment
# should override the default plugins.
# 1. set our own global test env
# (with a template resource that shadows a plugin)
g_env_content = '''
resource_registry:
"OS::Nova::Server": "file:///not_really_here.yaml"
'''
envdir = self.useFixture(fixtures.TempDir())
#
envfile = os.path.join(envdir.path, 'test.yaml')
with open(envfile, 'w+') as ef:
ef.write(g_env_content)
cfg.CONF.set_override('environment_dir', envdir.path)
# 2. load global env
g_env = environment.Environment({}, user_env=False)
resources._load_global_environment(g_env)
# 3. assert our resource is in place.
self.assertEqual('file:///not_really_here.yaml',
g_env.get_resource_info('OS::Nova::Server').value)
def test_env_one_resource_disable(self):
# prove we can disable a resource in the global environment
g_env_content = '''
resource_registry:
"OS::Nova::Server":
'''
# 1. fake an environment file
envdir = self.useFixture(fixtures.TempDir())
envfile = os.path.join(envdir.path, 'test.yaml')
with open(envfile, 'w+') as ef:
ef.write(g_env_content)
cfg.CONF.set_override('environment_dir', envdir.path)
# 2. load global env
g_env = environment.Environment({}, user_env=False)
resources._load_global_environment(g_env)
# 3. assert our resource is in now gone.
self.assertIsNone(g_env.get_resource_info('OS::Nova::Server'))
# 4. make sure we haven't removed something we shouldn't have
self.assertEqual(resources.instance.Instance,
g_env.get_resource_info('AWS::EC2::Instance').value)
def test_env_multi_resources_disable(self):
# prove we can disable resources in the global environment
g_env_content = '''
resource_registry:
"AWS::*":
'''
# 1. fake an environment file
envdir = self.useFixture(fixtures.TempDir())
envfile = os.path.join(envdir.path, 'test.yaml')
with open(envfile, 'w+') as ef:
ef.write(g_env_content)
cfg.CONF.set_override('environment_dir', envdir.path)
# 2. load global env
g_env = environment.Environment({}, user_env=False)
resources._load_global_environment(g_env)
# 3. assert our resources are now gone.
self.assertIsNone(g_env.get_resource_info('AWS::EC2::Instance'))
# 4. make sure we haven't removed something we shouldn't have
self.assertEqual(resources.server.Server,
g_env.get_resource_info('OS::Nova::Server').value)
def test_env_user_cant_disable_sys_resource(self):
# prove a user can't disable global resources from the user environment
u_env_content = '''
resource_registry:
"AWS::*":
'''
# 1. load user env
u_env = environment.Environment()
u_env.load(environment_format.parse(u_env_content))
# 2. assert global resources are NOT gone.
self.assertEqual(
resources.instance.Instance,
u_env.get_resource_info('AWS::EC2::Instance').value)
def test_env_ignore_files_starting_dot(self):
# prove we can disable a resource in the global environment
g_env_content = ''
# 1. fake an environment file
envdir = self.useFixture(fixtures.TempDir())
with open(os.path.join(envdir.path, 'a.yaml'), 'w+') as ef:
ef.write(g_env_content)
with open(os.path.join(envdir.path, '.test.yaml'), 'w+') as ef:
ef.write(g_env_content)
with open(os.path.join(envdir.path, 'b.yaml'), 'w+') as ef:
ef.write(g_env_content)
cfg.CONF.set_override('environment_dir', envdir.path)
# 2. load global env
g_env = environment.Environment({}, user_env=False)
with mock.patch('heat.engine.environment.open',
mock.mock_open(read_data=g_env_content),
create=True) as m_open:
resources._load_global_environment(g_env)
# 3. assert that the file were ignored
expected = [mock.call('%s/a.yaml' % envdir.path),
mock.call('%s/b.yaml' % envdir.path)]
call_list = m_open.call_args_list
expected.sort()
call_list.sort()
self.assertEqual(expected, call_list)