Keep encrypted_param_names environment internal to heat

Currently this is reflected in the user_env_as_dict output, which means
not only is it stored in the DB (which we want), but also passed around
to nested stack (which we don't want because it's derived from the parent
stack hidden parameters), and also to the user via stack environment show
(the API returns it but heatclient currently hides it), which we also don't
want because it's not a valid key in user-provided environments.

Change-Id: If5821ccb4a8bbf98012a2541ddd3c8e91455e5cc
Closes-Bug: #1590507
This commit is contained in:
Steven Hardy 2016-06-08 19:15:51 +01:00
parent 9befccd0ac
commit b47c002556
6 changed files with 86 additions and 15 deletions

View File

@ -704,12 +704,21 @@ class Environment(object):
env_snippet.get(env_fmt.PARAMETER_DEFAULTS, {}))
self._update_event_sinks(env_snippet.get(env_fmt.EVENT_SINKS, []))
def env_as_dict(self):
"""Get the entire environment as a dict."""
user_env = self.user_env_as_dict()
user_env.update(
# Any data here is to be stored in the DB but not reflected
# as part of the user environment (e.g to pass to nested stacks
# or made visible to the user via API calls etc
{env_fmt.ENCRYPTED_PARAM_NAMES: self.encrypted_param_names})
return user_env
def user_env_as_dict(self):
"""Get the environment as a dict, ready for storing in the db."""
"""Get the environment as a dict, only user-allowed keys."""
return {env_fmt.RESOURCE_REGISTRY: self.registry.as_dict(),
env_fmt.PARAMETERS: self.params,
env_fmt.PARAMETER_DEFAULTS: self.param_defaults,
env_fmt.ENCRYPTED_PARAM_NAMES: self.encrypted_param_names,
env_fmt.EVENT_SINKS: self._event_sinks}
def register_class(self, resource_type, resource_class, path=None):

View File

@ -855,7 +855,7 @@ class EngineService(service.Service):
# any environment provided into the existing one and attempt
# to use the existing stack template, if one is not provided.
if args.get(rpc_api.PARAM_EXISTING):
existing_env = current_stack.env.user_env_as_dict()
existing_env = current_stack.env.env_as_dict()
existing_params = existing_env[env_fmt.PARAMETERS]
clear_params = set(args.get(rpc_api.PARAM_CLEAR_PARAMETERS, []))
retained = dict((k, v) for k, v in existing_params.items()

View File

@ -138,7 +138,7 @@ class Template(collections.Mapping):
rt = {
'template': self.t,
'files_id': self.files.store(context),
'environment': self.env.user_env_as_dict()
'environment': self.env.env_as_dict()
}
if self.id is None:
new_rt = template_object.RawTemplate.create(context, rt)

View File

@ -22,6 +22,7 @@ from heat.common import exception
from heat.common import messaging
from heat.common import service_utils
from heat.common import template_format
from heat.db import api as db_api
from heat.engine.clients.os import glance
from heat.engine.clients.os import nova
from heat.engine import environment
@ -216,6 +217,65 @@ class ServiceStackUpdateTest(common.HeatTestCase):
tmpl.env.params)
self.assertEqual(stk.identifier(), result)
def test_stack_update_existing_encrypted_parameters(self):
# Create the stack with encryption enabled
# On update encrypted_param_names should be used from existing stack
hidden_param_template = u'''
heat_template_version: 2013-05-23
parameters:
param2:
type: string
description: value2.
hidden: true
resources:
a_resource:
type: GenericResourceType
'''
cfg.CONF.set_override('encrypt_parameters_and_properties', True,
enforce_type=True)
stack_name = 'service_update_test_stack_encrypted_parameters'
t = template_format.parse(hidden_param_template)
env1 = environment.Environment({'param2': 'bar'})
stk = stack.Stack(self.ctx, stack_name,
templatem.Template(t, env=env1))
stk.store()
stk.set_stack_user_project_id('1234')
# Verify that hidden parameters are stored encrypted
db_tpl = db_api.raw_template_get(self.ctx, stk.t.id)
db_params = db_tpl.environment['parameters']
self.assertEqual('cryptography_decrypt_v1', db_params['param2'][0])
self.assertNotEqual("foo", db_params['param2'][1])
# Verify that loaded stack has decrypted paramters
loaded_stack = stack.Stack.load(self.ctx, stack_id=stk.id)
params = loaded_stack.t.env.params
self.assertEqual('bar', params.get('param2'))
update_params = {'encrypted_param_names': [],
'parameter_defaults': {},
'event_sinks': [],
'parameters': {},
'resource_registry': {'resources': {}}}
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True}
with mock.patch('heat.engine.stack.Stack') as mock_stack:
stk.update = mock.Mock()
mock_stack.load.return_value = loaded_stack
mock_stack.validate.return_value = None
result = self.man.update_stack(self.ctx, stk.identifier(),
t,
update_params,
None, api_args)
tmpl = mock_stack.call_args[0][2]
self.assertEqual({u'param2': u'bar'}, tmpl.env.params)
# encrypted_param_names must be passed from existing to new
# stack otherwise the updated stack won't decrypt the params
self.assertEqual([u'param2'], tmpl.env.encrypted_param_names)
self.assertEqual(stk.identifier(), result)
def test_stack_update_existing_parameters_remove(self):
"""Test case for updating stack with changed parameters.
@ -288,7 +348,7 @@ class ServiceStackUpdateTest(common.HeatTestCase):
stk = utils.parse_stack(t, stack_name=stack_name, params=intial_params,
files=initial_files)
stk.set_stack_user_project_id('1234')
self.assertEqual(intial_params, stk.t.env.user_env_as_dict())
self.assertEqual(intial_params, stk.t.env.env_as_dict())
expected_reg = {'OS::Foo': 'foo.yaml',
'OS::Foo2': 'newfoo2.yaml',
@ -317,7 +377,7 @@ class ServiceStackUpdateTest(common.HeatTestCase):
api_args)
tmpl = mock_stack.call_args[0][2]
self.assertEqual(expected_env,
tmpl.env.user_env_as_dict())
tmpl.env.env_as_dict())
self.assertEqual(expected_files,
tmpl.files.files)
self.assertEqual(stk.identifier(), result)
@ -361,7 +421,7 @@ class ServiceStackUpdateTest(common.HeatTestCase):
None, api_args)
tmpl = mock_stack.call_args[0][2]
self.assertEqual(expected_env,
tmpl.env.user_env_as_dict())
tmpl.env.env_as_dict())
self.assertEqual(stk.identifier(), result)
def test_stack_update_reuses_api_params(self):

View File

@ -47,6 +47,8 @@ class EnvironmentTest(common.HeatTestCase):
u'event_sinks': [],
u'resource_registry': {u'resources': {}}}
env = environment.Environment(old)
self.assertEqual(expected, env.env_as_dict())
del(expected['encrypted_param_names'])
self.assertEqual(expected, env.user_env_as_dict())
def test_load_new_env(self):
@ -57,6 +59,8 @@ class EnvironmentTest(common.HeatTestCase):
u'resource_registry': {u'OS::Food': u'fruity.yaml',
u'resources': {}}}
env = environment.Environment(new_env)
self.assertEqual(new_env, env.env_as_dict())
del(new_env['encrypted_param_names'])
self.assertEqual(new_env, env.user_env_as_dict())
def test_global_registry(self):
@ -155,7 +159,7 @@ class EnvironmentTest(common.HeatTestCase):
'b.yaml',
path=['resources', 'res_x', 'test::two'])
self.assertEqual(env.user_env_as_dict(), env2.user_env_as_dict())
self.assertEqual(env.env_as_dict(), env2.env_as_dict())
def test_constraints(self):
env = environment.Environment({})
@ -520,7 +524,7 @@ class ChildEnvTest(common.HeatTestCase):
'event_sinks': [],
'resource_registry': {'resources': {}}}
cenv = environment.get_child_environment(penv, new_params)
self.assertEqual(expected, cenv.user_env_as_dict())
self.assertEqual(expected, cenv.env_as_dict())
def test_params_normal(self):
new_params = {'parameters': {'foo': 'bar', 'tester': 'Yes'}}
@ -531,7 +535,7 @@ class ChildEnvTest(common.HeatTestCase):
'resource_registry': {'resources': {}}}
expected.update(new_params)
cenv = environment.get_child_environment(penv, new_params)
self.assertEqual(expected, cenv.user_env_as_dict())
self.assertEqual(expected, cenv.env_as_dict())
def test_params_parent_overwritten(self):
new_params = {'parameters': {'foo': 'bar', 'tester': 'Yes'}}
@ -543,7 +547,7 @@ class ChildEnvTest(common.HeatTestCase):
'resource_registry': {'resources': {}}}
expected.update(new_params)
cenv = environment.get_child_environment(penv, new_params)
self.assertEqual(expected, cenv.user_env_as_dict())
self.assertEqual(expected, cenv.env_as_dict())
def test_registry_merge_simple(self):
env1 = {u'resource_registry': {u'OS::Food': u'fruity.yaml'}}

View File

@ -844,8 +844,7 @@ class WithTemplateTest(StackResourceBaseTest):
child_env = {'parameter_defaults': {},
'event_sinks': [],
'parameters': self.params,
'resource_registry': {'resources': {}},
'encrypted_param_names': []}
'resource_registry': {'resources': {}}}
self.parent_resource.child_params = mock.Mock(
return_value=self.params)
res_name = self.parent_resource.physical_resource_name()
@ -891,8 +890,7 @@ class WithTemplateTest(StackResourceBaseTest):
child_env = {'parameter_defaults': {},
'event_sinks': [],
'parameters': self.params,
'resource_registry': {'resources': {}},
'encrypted_param_names': []}
'resource_registry': {'resources': {}}}
self.parent_resource.child_params = mock.Mock(
return_value=self.params)
res_name = self.parent_resource.physical_resource_name()