Merge "Defer exceptions in calculating node_data()"
diff --git a/api/gabbits/stacks.yaml b/api/gabbits/stacks.yaml
index b37eabd..cb67e71 100644
--- a/api/gabbits/stacks.yaml
+++ b/api/gabbits/stacks.yaml
@@ -106,6 +106,8 @@
type: OS::Heat::TestResource
properties:
value: {get_param: test_val}
+ action_wait_secs:
+ update: 1
outputs:
output_value:
value: {get_attr: [test, output]}
@@ -138,6 +140,7 @@
delay: 1.0
response_json_paths:
$.stack.stack_status: UPDATE_COMPLETE
+ $.stack.updated_time: /^(?!$HISTORY['poll for stack UPDATE_COMPLETE'].$RESPONSE['$.stack.updated_time'])/
- name: list stack outputs
GET: $LAST_URL/outputs
diff --git a/common/config.py b/common/config.py
index d7126c4..eddac01 100644
--- a/common/config.py
+++ b/common/config.py
@@ -140,6 +140,10 @@
cfg.ListOpt('skip_test_stack_action_list',
help="List of stack actions in tests to skip "
"ex. ABANDON, ADOPT, SUSPEND, RESUME"),
+ cfg.BoolOpt('convergence_engine_enabled',
+ default=True,
+ help="Test features that are only present for stacks with "
+ "convergence enabled."),
cfg.IntOpt('volume_size',
default=1,
help='Default size in GB for volumes created by volumes tests'),
diff --git a/common/test.py b/common/test.py
index d43dece..a6aab7e 100644
--- a/common/test.py
+++ b/common/test.py
@@ -68,6 +68,17 @@
return randbits
+def requires_convergence(test_method):
+ '''Decorator for convergence-only tests.
+
+ The decorated test will be skipped when convergence is disabled.
+ '''
+ convergence_enabled = config.CONF.heat_plugin.convergence_engine_enabled
+ skipper = testtools.skipUnless(convergence_enabled,
+ "Convergence-only tests are disabled")
+ return skipper(test_method)
+
+
class HeatIntegrationTest(testscenarios.WithScenarios,
testtools.TestCase):
diff --git a/functional/test_simultaneous_update.py b/functional/test_simultaneous_update.py
new file mode 100644
index 0000000..0c562c0
--- /dev/null
+++ b/functional/test_simultaneous_update.py
@@ -0,0 +1,93 @@
+# 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 copy
+import time
+
+from heat_integrationtests.common import test
+from heat_integrationtests.functional import functional_base
+
+_test_template = {
+ 'heat_template_version': 'pike',
+ 'description': 'Test template to create two resources.',
+ 'resources': {
+ 'test1': {
+ 'type': 'OS::Heat::TestResource',
+ 'properties': {
+ 'value': 'Test1',
+ 'fail': False,
+ 'update_replace': False,
+ 'wait_secs': 0,
+ }
+ },
+ 'test2': {
+ 'type': 'OS::Heat::TestResource',
+ 'properties': {
+ 'value': 'Test1',
+ 'fail': False,
+ 'update_replace': False,
+ 'wait_secs': 0,
+ 'action_wait_secs': {
+ 'create': 30,
+ }
+ },
+ 'depends_on': ['test1']
+ }
+ }
+}
+
+
+def get_templates(fail=False, delay_s=None):
+ before = copy.deepcopy(_test_template)
+
+ after = copy.deepcopy(before)
+ for r in after['resources'].values():
+ r['properties']['value'] = 'Test2'
+
+ before_props = before['resources']['test2']['properties']
+ before_props['fail'] = fail
+ if delay_s is not None:
+ before_props['action_wait_secs']['create'] = delay_s
+
+ return before, after
+
+
+class SimultaneousUpdateStackTest(functional_base.FunctionalTestsBase):
+
+ @test.requires_convergence
+ def test_retrigger_success(self):
+ before, after = get_templates()
+ stack_id = self.stack_create(template=before,
+ expected_status='CREATE_IN_PROGRESS')
+ time.sleep(10)
+
+ self.update_stack(stack_id, after)
+
+ @test.requires_convergence
+ def test_retrigger_failure(self):
+ before, after = get_templates(fail=True)
+ stack_id = self.stack_create(template=before,
+ expected_status='CREATE_IN_PROGRESS')
+ time.sleep(10)
+
+ self.update_stack(stack_id, after)
+
+ @test.requires_convergence
+ def test_retrigger_timeout(self):
+ before, after = get_templates(delay_s=70)
+ stack_id = self.stack_create(template=before,
+ expected_status='CREATE_IN_PROGRESS',
+ timeout=1)
+ time.sleep(50)
+
+ self.update_stack(stack_id, after)