Add converge flag in stack update for observing on reality

Add converge parameter for stack update API and RPC call,
that allow triggering observe on reality. This will be
triggered by API call with converge argument (with True
or False value) within. This flag also works for resources
within nested stack.
Implements bp get-reality-for-resources

Change-Id: I151b575b714dcc9a5971a1573c126152ecd7ea93
This commit is contained in:
ricolin 2017-07-19 21:34:46 +08:00 committed by Rico Lin
parent d5f78c2b28
commit 552f94b928
19 changed files with 138 additions and 69 deletions

View File

@ -476,6 +476,14 @@ config_name:
in: body
required: true
type: string
converge:
description: |
Set to ``true`` to force the stack to observe the reality
before update.
in: body
required: false
default: false
type: boolean
created_at:
description: |
The date and time when the service engine was created.

View File

@ -374,6 +374,7 @@ Request Parameters
- template: template
- template_url: template_url
- timeout_mins: timeout_mins
- converge: converge
Request Example
---------------
@ -440,6 +441,7 @@ Request Parameters
- template: template
- template_url: template_url
- timeout_mins: timeout_mins
- converge: converge
Request Example
---------------
@ -505,6 +507,7 @@ Request Parameters
- template: template
- template_url: template_url
- timeout_mins: timeout_mins
- converge: converge
Request Example
---------------
@ -575,6 +578,7 @@ Request Parameters
- template: template
- template_url: template_url
- timeout_mins: timeout_mins
- converge: converge
Request Example
---------------

View File

@ -374,7 +374,7 @@ class StackController(object):
formatted_stack = stacks_view.format_stack(req, result)
return {'stack': formatted_stack}
def prepare_args(self, data):
def prepare_args(self, data, is_update=False):
args = data.args()
key = rpc_api.PARAM_TIMEOUT
if key in args:
@ -382,6 +382,11 @@ class StackController(object):
key = rpc_api.PARAM_TAGS
if args.get(key) is not None:
args[key] = self._extract_tags_param(args[key])
key = rpc_api.PARAM_CONVERGE
if not is_update and key in args:
msg = _("%s flag only supported in stack update (or update "
"preview) request.") % key
raise exc.HTTPBadRequest(six.text_type(msg))
return args
@util.policy_enforce
@ -472,7 +477,7 @@ class StackController(object):
"""Update an existing stack with a new template and/or parameters."""
data = InstantiationData(body)
args = self.prepare_args(data)
args = self.prepare_args(data, is_update=True)
self.rpc_client.update_stack(
req.context,
identity,
@ -493,7 +498,7 @@ class StackController(object):
"""
data = InstantiationData(body, patch=True)
args = self.prepare_args(data)
args = self.prepare_args(data, is_update=True)
self.rpc_client.update_stack(
req.context,
identity,
@ -518,7 +523,7 @@ class StackController(object):
"""Preview update for existing stack with a new template/parameters."""
data = InstantiationData(body)
args = self.prepare_args(data)
args = self.prepare_args(data, is_update=True)
show_nested = self._param_show_nested(req)
if show_nested is not None:
args[rpc_api.PARAM_SHOW_NESTED] = show_nested
@ -538,7 +543,7 @@ class StackController(object):
"""Preview PATCH update for existing stack."""
data = InstantiationData(body, patch=True)
args = self.prepare_args(data)
args = self.prepare_args(data, is_update=True)
show_nested = self._param_show_nested(req)
if show_nested is not None:
args['show_nested'] = show_nested

View File

@ -46,11 +46,10 @@ def extract_args(params):
kwargs[rpc_api.PARAM_TIMEOUT] = timeout
else:
raise ValueError(_('Invalid timeout value %s') % timeout)
name = rpc_api.PARAM_DISABLE_ROLLBACK
if name in params:
disable_rollback = param_utils.extract_bool(name, params[name])
kwargs[name] = disable_rollback
for name in [rpc_api.PARAM_CONVERGE, rpc_api.PARAM_DISABLE_ROLLBACK]:
if name in params:
bool_value = param_utils.extract_bool(name, params[name])
kwargs[name] = bool_value
name = rpc_api.PARAM_SHOW_DELETED
if name in params:

View File

@ -247,6 +247,7 @@ class Resource(status.ResourceStatus):
self.root_stack_id = None
self._calling_engine_id = None
self._atomic_key = None
self.converge = False
if not self.stack.in_convergence_check:
resource = stack.db_resource_get(name)
@ -1438,7 +1439,7 @@ class Resource(status.ResourceStatus):
self.translate_properties(after_props)
self.translate_properties(before_props)
if cfg.CONF.observe_on_update and before_props:
if (cfg.CONF.observe_on_update or self.converge) and before_props:
if not self.resource_id:
raise UpdateReplace(self)

View File

@ -497,7 +497,8 @@ class StackResource(resource.Resource):
kwargs.update({
'stack_identity': dict(self.nested_identifier()),
'args': {rpc_api.PARAM_TIMEOUT: timeout_mins}
'args': {rpc_api.PARAM_TIMEOUT: timeout_mins,
rpc_api.PARAM_CONVERGE: self.converge}
})
with self.translate_remote_exceptions:
try:

View File

@ -874,7 +874,7 @@ class EngineService(service.ServiceBase):
"""
# Now parse the template and any parameters for the updated
# stack definition. If PARAM_EXISTING is specified, we merge
# stack definition. If PARAM_EXISTING is specified, we merge
# 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):
@ -947,6 +947,8 @@ class EngineService(service.ServiceBase):
current_stack.timeout_mins)
common_params.setdefault(rpc_api.PARAM_DISABLE_ROLLBACK,
current_stack.disable_rollback)
common_params.setdefault(rpc_api.PARAM_CONVERGE,
current_stack.converge)
if args.get(rpc_api.PARAM_EXISTING):
common_params.setdefault(rpc_api.STACK_TAGS,

View File

@ -121,7 +121,7 @@ class Stack(collections.Mapping):
nested_depth=0, strict_validate=True, convergence=False,
current_traversal=None, tags=None, prev_raw_template_id=None,
current_deps=None, cache_data=None,
deleted_time=None):
deleted_time=None, converge=False):
"""Initialise the Stack.
@ -181,6 +181,7 @@ class Stack(collections.Mapping):
self._worker_client = None
self._convg_deps = None
self.thread_group_mgr = None
self.converge = converge
# strict_validate can be used to disable value validation
# in the resource properties schema, this is useful when
@ -1266,6 +1267,7 @@ class Stack(collections.Mapping):
if new_stack is not None:
self.disable_rollback = new_stack.disable_rollback
self.timeout_mins = new_stack.timeout_mins
self.converge = new_stack.converge
self.defn = new_stack.defn
self._set_param_stackid()
@ -1345,7 +1347,8 @@ class Stack(collections.Mapping):
self.worker_client.check_resource(self.context, rsrc_id,
self.current_traversal,
input_data, is_update,
self.adopt_stack_data)
self.adopt_stack_data,
self.converge)
if scheduler.ENABLE_SLEEP:
eventlet.sleep(1)

View File

@ -213,6 +213,7 @@ class StackUpdate(object):
existing_res.stack)
existing_res.stack.resources[existing_res.name] = substitute
existing_res = substitute
existing_res.converge = self.new_stack.converge
return existing_res.update(new_snippet, existing_snippet,
prev_resource=prev_res)

View File

@ -61,7 +61,7 @@ class WorkerService(object):
or expect replies from these messages.
"""
RPC_API_VERSION = '1.3'
RPC_API_VERSION = '1.4'
def __init__(self,
host,
@ -161,7 +161,7 @@ class WorkerService(object):
@context.request_context
@log_exceptions
def check_resource(self, cnxt, resource_id, current_traversal, data,
is_update, adopt_stack_data):
is_update, adopt_stack_data, converge=False):
"""Process a node in the dependency graph.
The node may be associated with either an update or a cleanup of its
@ -176,6 +176,8 @@ class WorkerService(object):
if rsrc is None:
return
rsrc.converge = converge
msg_queue = eventlet.queue.LightQueue()
try:
self.thread_group_mgr.add_msg_queue(stack.id, msg_queue)

View File

@ -20,14 +20,14 @@ PARAM_KEYS = (
PARAM_CLEAR_PARAMETERS, PARAM_GLOBAL_TENANT, PARAM_LIMIT,
PARAM_NESTED_DEPTH, PARAM_TAGS, PARAM_SHOW_HIDDEN, PARAM_TAGS_ANY,
PARAM_NOT_TAGS, PARAM_NOT_TAGS_ANY, TEMPLATE_TYPE, PARAM_WITH_DETAIL,
RESOLVE_OUTPUTS, PARAM_IGNORE_ERRORS
RESOLVE_OUTPUTS, PARAM_IGNORE_ERRORS, PARAM_CONVERGE
) = (
'timeout_mins', 'disable_rollback', 'adopt_stack_data',
'show_deleted', 'show_nested', 'existing',
'clear_parameters', 'global_tenant', 'limit',
'nested_depth', 'tags', 'show_hidden', 'tags_any',
'not_tags', 'not_tags_any', 'template_type', 'with_detail',
'resolve_outputs', 'ignore_errors'
'resolve_outputs', 'ignore_errors', 'converge'
)
STACK_KEYS = (

View File

@ -28,6 +28,7 @@ class WorkerClient(object):
1.1 - Added check_resource.
1.2 - Add adopt data argument to check_resource.
1.3 - Added cancel_check_resource API.
1.4 - Add converge argument to check_resource
"""
BASE_RPC_API_VERSION = '1.0'
@ -50,13 +51,16 @@ class WorkerClient(object):
client.cast(ctxt, method, **kwargs)
def check_resource(self, ctxt, resource_id,
current_traversal, data, is_update, adopt_stack_data):
current_traversal, data, is_update, adopt_stack_data,
converge=False):
self.cast(ctxt,
self.make_msg(
'check_resource', resource_id=resource_id,
current_traversal=current_traversal, data=data,
is_update=is_update, adopt_stack_data=adopt_stack_data),
version='1.2')
is_update=is_update, adopt_stack_data=adopt_stack_data,
converge=converge
),
version='1.4')
def cancel_check_resource(self, ctxt, stack_id, engine_id):
"""Send check-resource cancel message.

View File

@ -28,13 +28,14 @@ class Worker(message_processor.MessageProcessor):
@message_processor.asynchronous
def check_resource(self, ctxt, resource_id,
current_traversal, data,
is_update, adopt_stack_data):
is_update, adopt_stack_data, converge=False):
worker.WorkerService("fake_host", "fake_topic",
"fake_engine", mock.Mock()).check_resource(
ctxt, resource_id,
current_traversal,
data, is_update,
adopt_stack_data)
adopt_stack_data,
converge)
def stop_all_workers(self, current_stack):
pass

View File

@ -71,7 +71,7 @@ class ServiceStackUpdateTest(common.HeatTestCase):
self.patchobject(eventlet.queue, 'LightQueue', return_value=msgq_mock)
# do update
api_args = {'timeout_mins': 60}
api_args = {'timeout_mins': 60, rpc_api.PARAM_CONVERGE: True}
result = self.man.update_stack(self.ctx, old_stack.identifier(),
template, params, None, api_args)
@ -97,7 +97,9 @@ class ServiceStackUpdateTest(common.HeatTestCase):
tenant_id='test_tenant_id',
timeout_mins=60,
user_creds_id=u'1',
username='test_username')
username='test_username',
converge=True
)
mock_load.assert_called_once_with(self.ctx, stack=s)
mock_validate.assert_called_once_with()
@ -127,7 +129,8 @@ class ServiceStackUpdateTest(common.HeatTestCase):
# Test
environment_files = ['env_1']
self.man.update_stack(self.ctx, old_stack.identifier(),
template, params, None, {},
template, params, None,
{rpc_api.PARAM_CONVERGE: False},
environment_files=environment_files)
# Verify
@ -160,7 +163,7 @@ class ServiceStackUpdateTest(common.HeatTestCase):
self.patchobject(eventlet.queue, 'LightQueue', return_value=msgq_mock)
# do update
api_args = {'timeout_mins': 60}
api_args = {'timeout_mins': 60, rpc_api.PARAM_CONVERGE: False}
result = self.man.update_stack(self.ctx, old_stack.identifier(),
None, None, None, api_args,
template_id=tmpl_id)
@ -186,7 +189,9 @@ class ServiceStackUpdateTest(common.HeatTestCase):
tenant_id='test_tenant_id',
timeout_mins=60,
user_creds_id=u'1',
username='test_username')
username='test_username',
converge=False
)
mock_load.assert_called_once_with(self.ctx, stack=s)
mock_validate.assert_called_once_with()
@ -202,7 +207,8 @@ class ServiceStackUpdateTest(common.HeatTestCase):
'parameters': {'newparam': 123},
'resource_registry': {'resources': {}}}
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True}
rpc_api.PARAM_EXISTING: True,
rpc_api.PARAM_CONVERGE: False}
t = template_format.parse(tools.wp_template)
stk = tools.get_stack(stack_name, self.ctx, with_params=True)
@ -265,7 +271,8 @@ resources:
'parameters': {},
'resource_registry': {'resources': {}}}
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True}
rpc_api.PARAM_EXISTING: True,
rpc_api.PARAM_CONVERGE: False}
with mock.patch('heat.engine.stack.Stack') as mock_stack:
stk.update = mock.Mock()
@ -297,7 +304,8 @@ resources:
'resource_registry': {'resources': {}}}
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True,
rpc_api.PARAM_CLEAR_PARAMETERS: ['removeme']}
rpc_api.PARAM_CLEAR_PARAMETERS: ['removeme'],
rpc_api.PARAM_CONVERGE: False}
t = template_format.parse(tools.wp_template)
t['parameters']['removeme'] = {'type': 'string'}
@ -383,7 +391,8 @@ resources:
update_files = {'newfoo2.yaml': 'newfoo',
'myother.yaml': 'myother'}
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True}
rpc_api.PARAM_EXISTING: True,
rpc_api.PARAM_CONVERGE: False}
t = template_format.parse(tools.wp_template)
stk = utils.parse_stack(t, stack_name=stack_name, params=intial_params,
@ -439,7 +448,8 @@ resources:
'parameters': {},
'resource_registry': {}}
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True}
rpc_api.PARAM_EXISTING: True,
rpc_api.PARAM_CONVERGE: False}
t = template_format.parse(tools.wp_template)
stk = utils.parse_stack(t, stack_name=stack_name, params=intial_params)
@ -489,7 +499,8 @@ resources:
# do update
result = self.man.update_stack(self.ctx, old_stack.identifier(),
template, params, None, {})
template, params, None,
{rpc_api.PARAM_CONVERGE: False})
# assertions
self.assertEqual(old_stack.identifier(), result)
@ -511,7 +522,9 @@ resources:
strict_validate=True,
tenant_id='test_tenant_id', timeout_mins=1,
user_creds_id=u'1',
username='test_username')
username='test_username',
converge=False
)
def test_stack_cancel_update_same_engine(self):
stack_name = 'service_update_stack_test_cancel_same_engine'
@ -619,7 +632,7 @@ resources:
# do update
cfg.CONF.set_override('max_resources_per_stack', 3)
api_args = {'timeout_mins': 60}
api_args = {'timeout_mins': 60, rpc_api.PARAM_CONVERGE: False}
result = self.man.update_stack(self.ctx, old_stack.identifier(),
template, params, None, api_args)
@ -642,7 +655,9 @@ resources:
stack_user_project_id='1234', strict_validate=True,
tenant_id='test_tenant_id',
timeout_mins=60, user_creds_id=u'1',
username='test_username')
username='test_username',
converge=False
)
mock_load.assert_called_once_with(self.ctx, stack=s)
mock_validate.assert_called_once_with()
@ -678,7 +693,8 @@ resources:
return_value=old_stack)
result = self.man.update_stack(self.ctx, create_stack.identifier(),
tpl, {}, None, {})
tpl, {}, None,
{rpc_api.PARAM_CONVERGE: False})
old_stack._persist_state()
self.assertEqual((old_stack.UPDATE, old_stack.COMPLETE),
@ -710,7 +726,7 @@ resources:
ex = self.assertRaises(dispatcher.ExpectedException,
self.man.update_stack, self.ctx,
old_stack.identifier(), tpl, params,
None, {})
None, {rpc_api.PARAM_CONVERGE: False})
self.assertEqual(exception.RequestLimitExceeded, ex.exc_info[0])
self.assertIn(exception.StackResourceLimitExceeded.msg_fmt,
six.text_type(ex.exc_info[1]))
@ -738,7 +754,7 @@ resources:
mock_validate = self.patchobject(stk, 'validate',
side_effect=ex_expected)
# do update
api_args = {'timeout_mins': 60}
api_args = {'timeout_mins': 60, rpc_api.PARAM_CONVERGE: False}
ex = self.assertRaises(dispatcher.ExpectedException,
self.man.update_stack,
self.ctx, old_stack.identifier(),
@ -758,7 +774,9 @@ resources:
stack_user_project_id='1234', strict_validate=True,
tenant_id='test_tenant_id',
timeout_mins=60, user_creds_id=u'1',
username='test_username')
username='test_username',
converge=False
)
mock_load.assert_called_once_with(self.ctx, stack=s)
mock_validate.assert_called_once_with()
@ -771,7 +789,7 @@ resources:
ex = self.assertRaises(dispatcher.ExpectedException,
self.man.update_stack,
self.ctx, stk.identifier(), template,
params, None, {})
params, None, {rpc_api.PARAM_CONVERGE: False})
self.assertEqual(exception.EntityNotFound, ex.exc_info[0])
def test_stack_update_no_credentials(self):
@ -798,7 +816,7 @@ resources:
mock_env = self.patchobject(environment, 'Environment',
return_value=stk.env)
api_args = {'timeout_mins': 60}
api_args = {'timeout_mins': 60, rpc_api.PARAM_CONVERGE: False}
ex = self.assertRaises(dispatcher.ExpectedException,
self.man.update_stack, self.ctx,
stk.identifier(),
@ -820,14 +838,17 @@ resources:
stack_user_project_id='1234',
strict_validate=True,
tenant_id='test_tenant_id', timeout_mins=60,
user_creds_id=u'1', username='test_username')
user_creds_id=u'1', username='test_username',
converge=False
)
mock_load.assert_called_once_with(self.ctx, stack=s)
def test_stack_update_existing_template(self):
'''Update a stack using the same template.'''
stack_name = 'service_update_test_stack_existing_template'
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True}
rpc_api.PARAM_EXISTING: True,
rpc_api.PARAM_CONVERGE: False}
t = template_format.parse(tools.wp_template)
# Don't actually run the update as the mocking breaks it, instead
# we just ensure the expected template is passed in to the updated
@ -859,7 +880,8 @@ resources:
'''Update a stack using the same template doesn't work when FAILED.'''
stack_name = 'service_update_test_stack_existing_template'
api_args = {rpc_api.PARAM_TIMEOUT: 60,
rpc_api.PARAM_EXISTING: True}
rpc_api.PARAM_EXISTING: True,
rpc_api.PARAM_CONVERGE: False}
t = template_format.parse(tools.wp_template)
# Don't actually run the update as the mocking breaks it, instead
# we just ensure the expected template is passed in to the updated
@ -912,7 +934,7 @@ parameters:
self.man.update_stack,
self.ctx, old_stack.identifier(),
old_stack.t.t, params,
None, {})
None, {rpc_api.PARAM_CONVERGE: False})
self.assertEqual(exception.ImmutableParameterModified, exc.exc_info[0])
self.assertEqual('The following parameters are immutable and may not '
'be updated: param1', exc.exc_info[1].message)
@ -948,7 +970,7 @@ parameters:
params = {'param1': 'bar'}
result = self.man.update_stack(self.ctx, old_stack.identifier(),
templatem.Template(template), params,
None, {})
None, {rpc_api.PARAM_CONVERGE: False})
self.assertEqual(s.id, result['stack_id'])
def test_update_immutable_parameter_same_value(self):
@ -982,7 +1004,7 @@ parameters:
params = {'param1': 'foo'}
result = self.man.update_stack(self.ctx, old_stack.identifier(),
templatem.Template(template), params,
None, {})
None, {rpc_api.PARAM_CONVERGE: False})
self.assertEqual(s.id, result['stack_id'])
@ -1057,7 +1079,7 @@ resources:
return_value=None)
# do preview_update_stack
api_args = {'timeout_mins': 60}
api_args = {'timeout_mins': 60, rpc_api.PARAM_CONVERGE: False}
result = self.man.preview_update_stack(
self.ctx,
old_stack.identifier(),
@ -1073,7 +1095,9 @@ resources:
disable_rollback=True, nested_depth=0, owner_id=None,
parent_resource=None, stack_user_project_id='1234',
strict_validate=True, tenant_id='test_tenant_id', timeout_mins=60,
user_creds_id=u'1', username='test_username')
user_creds_id=u'1', username='test_username',
converge=False
)
mock_load.assert_called_once_with(self.ctx, stack=s)
mock_tmpl.assert_called_once_with(new_template, files=None)
mock_env.assert_called_once_with(params)

View File

@ -29,7 +29,7 @@ from heat.tests import utils
class WorkerServiceTest(common.HeatTestCase):
def test_make_sure_rpc_version(self):
self.assertEqual(
'1.3',
'1.4',
worker.WorkerService.RPC_API_VERSION,
('RPC version is changed, please update this test to new version '
'and make sure additional test cases are added for RPC APIs '

View File

@ -72,7 +72,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
mock.call.worker_client.WorkerClient.check_resource(
stack.context, rsrc_id, stack.current_traversal,
{'input_data': {}},
is_update, None))
is_update, None, False))
self.assertEqual(expected_calls, mock_cr.mock_calls)
def test_conv_string_five_instance_stack_create(self, mock_cr):
@ -129,7 +129,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
mock.call.worker_client.WorkerClient.check_resource(
stack.context, rsrc_id, stack.current_traversal,
{'input_data': {}},
is_update, None))
is_update, None, False))
self.assertEqual(expected_calls, mock_cr.mock_calls)
def _mock_convg_db_update_requires(self):
@ -269,7 +269,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
mock.call.worker_client.WorkerClient.check_resource(
stack.context, rsrc_id, stack.current_traversal,
{'input_data': {}},
is_update, None))
is_update, None, False))
leaves = curr_stack.convergence_dependencies.leaves()
for rsrc_id, is_update in leaves:
@ -277,7 +277,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
mock.call.worker_client.WorkerClient.check_resource(
curr_stack.context, rsrc_id, curr_stack.current_traversal,
{'input_data': {}},
is_update, None))
is_update, None, False))
self.assertEqual(expected_calls, mock_cr.mock_calls)
def test_conv_empty_template_stack_update_delete(self, mock_cr):
@ -352,7 +352,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
mock.call.worker_client.WorkerClient.check_resource(
stack.context, rsrc_id, stack.current_traversal,
{'input_data': {}},
is_update, None))
is_update, None, False))
leaves = curr_stack.convergence_dependencies.leaves()
for rsrc_id, is_update in leaves:
@ -360,7 +360,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
mock.call.worker_client.WorkerClient.check_resource(
curr_stack.context, rsrc_id, curr_stack.current_traversal,
{'input_data': {}},
is_update, None))
is_update, None, False))
self.assertEqual(expected_calls, mock_cr.mock_calls)
def test_mark_complete_purges_db(self, mock_cr):

View File

@ -35,6 +35,7 @@ from heat.engine import service
from heat.engine import stack as parser
from heat.engine import template as templatem
from heat.objects import stack as stack_object
from heat.rpc import api as rpc_api
from heat.tests import common
from heat.tests.engine import tools
from heat.tests import generic_resource as generic_rsrc
@ -230,6 +231,7 @@ class StackConvergenceServiceCreateUpdateTest(common.HeatTestCase):
template=tools.string_template_five,
convergence=True)
stack.converge = None
self.m.StubOutWithMock(templatem, 'Template')
self.m.StubOutWithMock(environment, 'Environment')
self.m.StubOutWithMock(parser, 'Stack')
@ -295,14 +297,16 @@ class StackConvergenceServiceCreateUpdateTest(common.HeatTestCase):
convergence=old_stack.convergence,
current_traversal=old_stack.current_traversal,
prev_raw_template_id=old_stack.prev_raw_template_id,
current_deps=old_stack.current_deps).AndReturn(stack)
current_deps=old_stack.current_deps,
converge=False).AndReturn(stack)
self.m.StubOutWithMock(stack, 'validate')
stack.validate().AndReturn(None)
self.m.ReplayAll()
api_args = {'timeout_mins': 60, 'disable_rollback': False}
api_args = {'timeout_mins': 60, 'disable_rollback': False,
rpc_api.PARAM_CONVERGE: False}
result = self.man.update_stack(self.ctx, old_stack.identifier(),
template, params, None, api_args)
self.assertTrue(old_stack.convergence)

View File

@ -32,6 +32,7 @@ from heat.engine import template as templatem
from heat.objects import raw_template
from heat.objects import stack as stack_object
from heat.objects import stack_lock
from heat.rpc import api as rpc_api
from heat.tests import common
from heat.tests import generic_resource as generic_rsrc
from heat.tests import utils
@ -931,9 +932,9 @@ class WithTemplateTest(StackResourceBaseTest):
rpcc.return_value._create_stack.assert_called_once_with(
self.ctx,
stack_name=res_name,
args={'disable_rollback': True,
'adopt_stack_data': adopt_data_str,
'timeout_mins': self.timeout_mins},
args={rpc_api.PARAM_DISABLE_ROLLBACK: True,
rpc_api.PARAM_ADOPT_STACK_DATA: adopt_data_str,
rpc_api.PARAM_TIMEOUT: self.timeout_mins},
environment_files=None,
stack_user_project_id='aprojectid',
parent_resource_name='test',
@ -980,9 +981,9 @@ class WithTemplateTest(StackResourceBaseTest):
rpcc.return_value._create_stack.assert_called_once_with(
self.ctx,
stack_name=res_name,
args={'disable_rollback': True,
'adopt_stack_data': adopt_data_str,
'timeout_mins': self.timeout_mins},
args={rpc_api.PARAM_DISABLE_ROLLBACK: True,
rpc_api.PARAM_ADOPT_STACK_DATA: adopt_data_str,
rpc_api.PARAM_TIMEOUT: self.timeout_mins},
environment_files=None,
stack_user_project_id='aprojectid',
parent_resource_name='test',
@ -1025,7 +1026,8 @@ class WithTemplateTest(StackResourceBaseTest):
template=None,
params=None,
files=None,
args={'timeout_mins': self.timeout_mins})
args={rpc_api.PARAM_TIMEOUT: self.timeout_mins,
rpc_api.PARAM_CONVERGE: False})
def test_update_with_template_failure(self):
class StackValidationFailed_Remote(exception.StackValidationFailed):
@ -1061,7 +1063,8 @@ class WithTemplateTest(StackResourceBaseTest):
template=None,
params=None,
files=None,
args={'timeout_mins': self.timeout_mins})
args={rpc_api.PARAM_TIMEOUT: self.timeout_mins,
rpc_api.PARAM_CONVERGE: False})
self.assertIsNotNone(template_id.match)
self.assertRaises(exception.NotFound,
raw_template.RawTemplate.get_by_id,

View File

@ -0,0 +1,7 @@
---
features:
- Add `converge` parameter for stack update (and update preview) API.
This parameter will force resources to observe the reality of resources
before actually update it. The value of this parameter can be any
boolean value. This will replace config flag `observe_on_update` in
near future.