From 552f94b928dc859d06d8b3d6fa0824aefdbf1cb5 Mon Sep 17 00:00:00 2001 From: ricolin Date: Wed, 19 Jul 2017 21:34:46 +0800 Subject: [PATCH] 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 --- api-ref/source/v1/parameters.yaml | 8 ++ api-ref/source/v1/stacks.inc | 4 + heat/api/openstack/v1/stacks.py | 15 ++-- heat/engine/api.py | 9 +-- heat/engine/resource.py | 3 +- heat/engine/resources/stack_resource.py | 3 +- heat/engine/service.py | 4 +- heat/engine/stack.py | 7 +- heat/engine/update.py | 1 + heat/engine/worker.py | 6 +- heat/rpc/api.py | 4 +- heat/rpc/worker_client.py | 10 ++- .../convergence/framework/worker_wrapper.py | 5 +- .../tests/engine/service/test_stack_update.py | 80 ++++++++++++------- heat/tests/engine/test_engine_worker.py | 2 +- heat/tests/test_convg_stack.py | 12 +-- heat/tests/test_engine_service.py | 8 +- heat/tests/test_stack_resource.py | 19 +++-- ...lag-for-stack-update-e0e92a7fe232f10f.yaml | 7 ++ 19 files changed, 138 insertions(+), 69 deletions(-) create mode 100644 releasenotes/notes/converge-flag-for-stack-update-e0e92a7fe232f10f.yaml diff --git a/api-ref/source/v1/parameters.yaml b/api-ref/source/v1/parameters.yaml index aec388b369..5667839deb 100644 --- a/api-ref/source/v1/parameters.yaml +++ b/api-ref/source/v1/parameters.yaml @@ -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. diff --git a/api-ref/source/v1/stacks.inc b/api-ref/source/v1/stacks.inc index 241d21377a..66b1e85d42 100644 --- a/api-ref/source/v1/stacks.inc +++ b/api-ref/source/v1/stacks.inc @@ -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 --------------- diff --git a/heat/api/openstack/v1/stacks.py b/heat/api/openstack/v1/stacks.py index 1eef46e83e..11520ac09c 100644 --- a/heat/api/openstack/v1/stacks.py +++ b/heat/api/openstack/v1/stacks.py @@ -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 diff --git a/heat/engine/api.py b/heat/engine/api.py index 08f9defd20..a6147a06ab 100644 --- a/heat/engine/api.py +++ b/heat/engine/api.py @@ -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: diff --git a/heat/engine/resource.py b/heat/engine/resource.py index bc90dcb7a0..e468c3c048 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -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) diff --git a/heat/engine/resources/stack_resource.py b/heat/engine/resources/stack_resource.py index 62c1fbb7b3..0970741427 100644 --- a/heat/engine/resources/stack_resource.py +++ b/heat/engine/resources/stack_resource.py @@ -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: diff --git a/heat/engine/service.py b/heat/engine/service.py index 789ba444e1..449cd71965 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -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, diff --git a/heat/engine/stack.py b/heat/engine/stack.py index 89cd46abc3..2ffd31cc02 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -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) diff --git a/heat/engine/update.py b/heat/engine/update.py index 156eeb49af..3bd96cc3ad 100644 --- a/heat/engine/update.py +++ b/heat/engine/update.py @@ -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) diff --git a/heat/engine/worker.py b/heat/engine/worker.py index 2987deb1bf..d625d1b082 100644 --- a/heat/engine/worker.py +++ b/heat/engine/worker.py @@ -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) diff --git a/heat/rpc/api.py b/heat/rpc/api.py index d2d2dd4cfd..bffe783d73 100644 --- a/heat/rpc/api.py +++ b/heat/rpc/api.py @@ -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 = ( diff --git a/heat/rpc/worker_client.py b/heat/rpc/worker_client.py index d0256492e8..5faeb9afb0 100644 --- a/heat/rpc/worker_client.py +++ b/heat/rpc/worker_client.py @@ -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. diff --git a/heat/tests/convergence/framework/worker_wrapper.py b/heat/tests/convergence/framework/worker_wrapper.py index 14d9061e4f..042bc9e07d 100644 --- a/heat/tests/convergence/framework/worker_wrapper.py +++ b/heat/tests/convergence/framework/worker_wrapper.py @@ -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 diff --git a/heat/tests/engine/service/test_stack_update.py b/heat/tests/engine/service/test_stack_update.py index d89e2b8ea8..c96f0bb74a 100644 --- a/heat/tests/engine/service/test_stack_update.py +++ b/heat/tests/engine/service/test_stack_update.py @@ -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) diff --git a/heat/tests/engine/test_engine_worker.py b/heat/tests/engine/test_engine_worker.py index 93781179de..eeae9e3261 100644 --- a/heat/tests/engine/test_engine_worker.py +++ b/heat/tests/engine/test_engine_worker.py @@ -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 ' diff --git a/heat/tests/test_convg_stack.py b/heat/tests/test_convg_stack.py index b365073426..bb7adce407 100644 --- a/heat/tests/test_convg_stack.py +++ b/heat/tests/test_convg_stack.py @@ -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): diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index de5eb55cdd..743e97e059 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -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) diff --git a/heat/tests/test_stack_resource.py b/heat/tests/test_stack_resource.py index fab4e570c7..64ece0c0d8 100644 --- a/heat/tests/test_stack_resource.py +++ b/heat/tests/test_stack_resource.py @@ -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, diff --git a/releasenotes/notes/converge-flag-for-stack-update-e0e92a7fe232f10f.yaml b/releasenotes/notes/converge-flag-for-stack-update-e0e92a7fe232f10f.yaml new file mode 100644 index 0000000000..e49a3a9c7e --- /dev/null +++ b/releasenotes/notes/converge-flag-for-stack-update-e0e92a7fe232f10f.yaml @@ -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.