Merge "Convergence: Implementation of timeout"
This commit is contained in:
commit
f246cd4ea8
|
@ -697,7 +697,8 @@ class Resource(object):
|
|||
'''
|
||||
return self
|
||||
|
||||
def create_convergence(self, template_id, resource_data, engine_id):
|
||||
def create_convergence(self, template_id, resource_data, engine_id,
|
||||
timeout):
|
||||
'''
|
||||
Creates the resource by invoking the scheduler TaskRunner.
|
||||
'''
|
||||
|
@ -712,7 +713,7 @@ class Resource(object):
|
|||
else:
|
||||
adopt_data = self.stack._adopt_kwargs(self)
|
||||
runner = scheduler.TaskRunner(self.adopt, **adopt_data)
|
||||
runner()
|
||||
runner(timeout=timeout)
|
||||
|
||||
@scheduler.wrappertask
|
||||
def create(self):
|
||||
|
@ -865,7 +866,8 @@ class Resource(object):
|
|||
except ValueError:
|
||||
return True
|
||||
|
||||
def update_convergence(self, template_id, resource_data, engine_id):
|
||||
def update_convergence(self, template_id, resource_data, engine_id,
|
||||
timeout):
|
||||
'''
|
||||
Updates the resource by invoking the scheduler TaskRunner
|
||||
and it persists the resource's current_template_id to template_id and
|
||||
|
@ -876,7 +878,7 @@ class Resource(object):
|
|||
new_temp = template.Template.load(self.context, template_id)
|
||||
new_res_def = new_temp.resource_definitions(self.stack)[self.name]
|
||||
runner = scheduler.TaskRunner(self.update, new_res_def)
|
||||
runner()
|
||||
runner(timeout=timeout)
|
||||
|
||||
# update the resource db record (stored in unlock)
|
||||
self.current_template_id = template_id
|
||||
|
@ -1107,7 +1109,7 @@ class Resource(object):
|
|||
expected_engine_id=None
|
||||
)
|
||||
|
||||
def delete_convergence(self, template_id, input_data, engine_id):
|
||||
def delete_convergence(self, template_id, input_data, engine_id, timeout):
|
||||
'''Destroys the resource if it doesn't belong to given
|
||||
template. The given template is suppose to be the current
|
||||
template being provisioned.
|
||||
|
@ -1124,7 +1126,7 @@ class Resource(object):
|
|||
|
||||
if self.current_template_id != template_id:
|
||||
runner = scheduler.TaskRunner(self.destroy)
|
||||
runner()
|
||||
runner(timeout=timeout)
|
||||
|
||||
# update needed_by and replaces of replacement resource
|
||||
self._update_replacement_data(template_id)
|
||||
|
|
|
@ -1655,3 +1655,25 @@ class Stack(collections.Mapping):
|
|||
stack_object.Stack.delete(self.context, self.id)
|
||||
except exception.NotFound:
|
||||
pass
|
||||
|
||||
def time_elapsed(self):
|
||||
'''
|
||||
Time elapsed in seconds since the stack operation started.
|
||||
'''
|
||||
start_time = self.updated_time or self.created_time
|
||||
return (datetime.datetime.utcnow() - start_time).seconds
|
||||
|
||||
def time_remaining(self):
|
||||
'''
|
||||
Time left before stack times out.
|
||||
'''
|
||||
return self.timeout_secs() - self.time_elapsed()
|
||||
|
||||
def has_timed_out(self):
|
||||
'''
|
||||
Returns True if this stack has timed-out.
|
||||
'''
|
||||
if self.status == self.IN_PROGRESS:
|
||||
return self.time_elapsed() > self.timeout_secs()
|
||||
|
||||
return False
|
||||
|
|
|
@ -25,6 +25,7 @@ from heat.common.i18n import _LE
|
|||
from heat.common.i18n import _LI
|
||||
from heat.common import messaging as rpc_messaging
|
||||
from heat.engine import resource
|
||||
from heat.engine import scheduler
|
||||
from heat.engine import stack as parser
|
||||
from heat.engine import sync_point
|
||||
from heat.objects import resource as resource_objects
|
||||
|
@ -107,13 +108,7 @@ class WorkerService(service.Service):
|
|||
{'action': stack.action, 'stack_name': stack.name})
|
||||
stack.rollback()
|
||||
|
||||
def _handle_resource_failure(self, cnxt, stack_id, traversal_id,
|
||||
failure_reason):
|
||||
stack = parser.Stack.load(cnxt, stack_id=stack_id)
|
||||
# make sure no new stack operation was triggered
|
||||
if stack.current_traversal != traversal_id:
|
||||
return
|
||||
|
||||
def _handle_failure(self, cnxt, stack, failure_reason):
|
||||
stack.state_set(stack.action, stack.FAILED, failure_reason)
|
||||
|
||||
if (not stack.disable_rollback and
|
||||
|
@ -122,6 +117,19 @@ class WorkerService(service.Service):
|
|||
else:
|
||||
stack.purge_db()
|
||||
|
||||
def _handle_resource_failure(self, cnxt, stack_id, traversal_id,
|
||||
failure_reason):
|
||||
stack = parser.Stack.load(cnxt, stack_id=stack_id)
|
||||
# make sure no new stack operation was triggered
|
||||
if stack.current_traversal != traversal_id:
|
||||
return
|
||||
|
||||
self._handle_failure(cnxt, stack, failure_reason)
|
||||
|
||||
def _handle_stack_timeout(self, cnxt, stack):
|
||||
failure_reason = u'Timed out'
|
||||
self._handle_failure(cnxt, stack, failure_reason)
|
||||
|
||||
def _load_resource(self, cnxt, resource_id, resource_data, is_update):
|
||||
if is_update:
|
||||
cache_data = {in_data.get(
|
||||
|
@ -141,12 +149,13 @@ class WorkerService(service.Service):
|
|||
return rsrc, stack
|
||||
|
||||
def _do_check_resource(self, cnxt, current_traversal, tmpl, resource_data,
|
||||
is_update, rsrc, stack_id, adopt_stack_data):
|
||||
is_update, rsrc, stack, adopt_stack_data):
|
||||
try:
|
||||
if is_update:
|
||||
try:
|
||||
check_resource_update(rsrc, tmpl.id, resource_data,
|
||||
self.engine_id)
|
||||
self.engine_id,
|
||||
stack.time_remaining())
|
||||
except resource.UpdateReplace:
|
||||
new_res_id = rsrc.make_replacement(tmpl.id)
|
||||
LOG.info("Replacing resource with new id %s", new_res_id)
|
||||
|
@ -160,7 +169,7 @@ class WorkerService(service.Service):
|
|||
|
||||
else:
|
||||
check_resource_cleanup(rsrc, tmpl.id, resource_data,
|
||||
self.engine_id)
|
||||
self.engine_id, stack.time_remaining())
|
||||
|
||||
return True
|
||||
except resource.UpdateInProgress:
|
||||
|
@ -175,7 +184,13 @@ class WorkerService(service.Service):
|
|||
reason = 'Resource %s failed: %s' % (rsrc.action,
|
||||
six.text_type(ex))
|
||||
self._handle_resource_failure(
|
||||
cnxt, stack_id, current_traversal, reason)
|
||||
cnxt, stack.id, current_traversal, reason)
|
||||
except scheduler.Timeout:
|
||||
# reload the stack to verify current traversal
|
||||
stack = parser.Stack.load(cnxt, stack_id=stack.id)
|
||||
if stack.current_traversal != current_traversal:
|
||||
return
|
||||
self._handle_stack_timeout(cnxt, stack)
|
||||
|
||||
return False
|
||||
|
||||
|
@ -267,6 +282,10 @@ class WorkerService(service.Service):
|
|||
LOG.debug('[%s] Traversal cancelled; stopping.', current_traversal)
|
||||
return
|
||||
|
||||
if stack.has_timed_out():
|
||||
self._handle_stack_timeout(cnxt, stack)
|
||||
return
|
||||
|
||||
tmpl = stack.t
|
||||
stack.adopt_stack_data = adopt_stack_data
|
||||
|
||||
|
@ -278,7 +297,7 @@ class WorkerService(service.Service):
|
|||
check_resource_done = self._do_check_resource(cnxt, current_traversal,
|
||||
tmpl, resource_data,
|
||||
is_update,
|
||||
rsrc, stack.id,
|
||||
rsrc, stack,
|
||||
adopt_stack_data)
|
||||
|
||||
if check_resource_done:
|
||||
|
@ -343,18 +362,20 @@ def propagate_check_resource(cnxt, rpc_client, next_res_id,
|
|||
{sender_key: sender_data})
|
||||
|
||||
|
||||
def check_resource_update(rsrc, template_id, resource_data, engine_id):
|
||||
def check_resource_update(rsrc, template_id, resource_data, engine_id,
|
||||
timeout):
|
||||
'''
|
||||
Create or update the Resource if appropriate.
|
||||
'''
|
||||
if rsrc.action == resource.Resource.INIT:
|
||||
rsrc.create_convergence(template_id, resource_data, engine_id)
|
||||
rsrc.create_convergence(template_id, resource_data, engine_id, timeout)
|
||||
else:
|
||||
rsrc.update_convergence(template_id, resource_data, engine_id)
|
||||
rsrc.update_convergence(template_id, resource_data, engine_id, timeout)
|
||||
|
||||
|
||||
def check_resource_cleanup(rsrc, template_id, resource_data, engine_id):
|
||||
def check_resource_cleanup(rsrc, template_id, resource_data, engine_id,
|
||||
timeout):
|
||||
'''
|
||||
Delete the Resource if appropriate.
|
||||
'''
|
||||
rsrc.delete_convergence(template_id, resource_data, engine_id)
|
||||
rsrc.delete_convergence(template_id, resource_data, engine_id, timeout)
|
||||
|
|
|
@ -17,6 +17,7 @@ import mock
|
|||
|
||||
from heat.common import exception
|
||||
from heat.engine import resource
|
||||
from heat.engine import scheduler
|
||||
from heat.engine import stack
|
||||
from heat.engine import sync_point
|
||||
from heat.engine import worker
|
||||
|
@ -146,7 +147,8 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
|
|||
self.is_update, None)
|
||||
mock_cru.assert_called_once_with(self.resource,
|
||||
self.resource.stack.t.id,
|
||||
{}, self.worker.engine_id)
|
||||
{}, self.worker.engine_id,
|
||||
mock.ANY)
|
||||
self.assertFalse(mock_crc.called)
|
||||
|
||||
expected_calls = []
|
||||
|
@ -171,7 +173,8 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
|
|||
self.is_update, None)
|
||||
mock_cru.assert_called_once_with(self.resource,
|
||||
self.resource.stack.t.id,
|
||||
{}, self.worker.engine_id)
|
||||
{}, self.worker.engine_id,
|
||||
self.stack.timeout_secs())
|
||||
self.assertTrue(mock_mr.called)
|
||||
self.assertFalse(mock_crc.called)
|
||||
self.assertFalse(mock_pcr.called)
|
||||
|
@ -188,7 +191,8 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
|
|||
self.is_update, None)
|
||||
mock_cru.assert_called_once_with(self.resource,
|
||||
self.resource.stack.t.id,
|
||||
{}, self.worker.engine_id)
|
||||
{}, self.worker.engine_id,
|
||||
self.stack.timeout_secs())
|
||||
self.assertFalse(mock_crc.called)
|
||||
self.assertFalse(mock_pcr.called)
|
||||
self.assertFalse(mock_csc.called)
|
||||
|
@ -400,6 +404,62 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
|
|||
actual_predecessors = call_args[5]
|
||||
self.assertItemsEqual(expected_predecessors, actual_predecessors)
|
||||
|
||||
@mock.patch.object(stack.Stack, 'purge_db')
|
||||
def test_handle_failure(self, mock_purgedb, mock_cru, mock_crc, mock_pcr,
|
||||
mock_csc, mock_cid):
|
||||
self.worker._handle_failure(self.ctx, self.stack, 'dummy-reason')
|
||||
mock_purgedb.assert_called_once_with()
|
||||
self.assertEqual('dummy-reason', self.stack.status_reason)
|
||||
|
||||
# test with rollback
|
||||
self.worker._trigger_rollback = mock.Mock()
|
||||
self.stack.disable_rollback = False
|
||||
self.stack.state_set(self.stack.UPDATE, self.stack.IN_PROGRESS, '')
|
||||
self.worker._handle_failure(self.ctx, self.stack, 'dummy-reason')
|
||||
self.worker._trigger_rollback.assert_called_once_with(self.stack)
|
||||
|
||||
def test_handle_stack_timeout(self, mock_cru, mock_crc, mock_pcr,
|
||||
mock_csc, mock_cid):
|
||||
self.worker._handle_failure = mock.Mock()
|
||||
self.worker._handle_stack_timeout(self.ctx, self.stack)
|
||||
self.worker._handle_failure.assert_called_once_with(
|
||||
self.ctx, self.stack, u'Timed out')
|
||||
|
||||
def test_do_check_resource_marks_stack_as_failed_if_stack_timesout(
|
||||
self, mock_cru, mock_crc, mock_pcr, mock_csc, mock_cid):
|
||||
mock_cru.side_effect = scheduler.Timeout(None, 60)
|
||||
self.is_update = True
|
||||
self.worker._handle_stack_timeout = mock.Mock()
|
||||
self.worker._do_check_resource(self.ctx, self.stack.current_traversal,
|
||||
self.stack.t, {}, self.is_update,
|
||||
self.resource, self.stack, {})
|
||||
self.worker._handle_stack_timeout.assert_called_once_with(
|
||||
self.ctx, self.stack)
|
||||
|
||||
def test_do_check_resource_ignores_timeout_for_new_update(
|
||||
self, mock_cru, mock_crc, mock_pcr, mock_csc, mock_cid):
|
||||
# Ensure current_traversal is check before marking the stack as
|
||||
# failed due to time-out.
|
||||
mock_cru.side_effect = scheduler.Timeout(None, 60)
|
||||
self.is_update = True
|
||||
self.worker._handle_stack_timeout = mock.Mock()
|
||||
old_traversal = self.stack.current_traversal
|
||||
self.stack.current_traversal = 'new_traversal'
|
||||
self.worker._do_check_resource(self.ctx, old_traversal,
|
||||
self.stack.t, {}, self.is_update,
|
||||
self.resource, self.stack, {})
|
||||
self.assertFalse(self.worker._handle_stack_timeout.called)
|
||||
|
||||
@mock.patch.object(stack.Stack, 'has_timed_out')
|
||||
def test_check_resource_handles_timeout(self, mock_to, mock_cru, mock_crc,
|
||||
mock_pcr, mock_csc, mock_cid):
|
||||
mock_to.return_value = True
|
||||
self.worker._handle_stack_timeout = mock.Mock()
|
||||
self.worker.check_resource(self.ctx, self.resource.id,
|
||||
self.stack.current_traversal,
|
||||
{}, self.is_update, {})
|
||||
self.assertTrue(self.worker._handle_stack_timeout.called)
|
||||
|
||||
|
||||
@mock.patch.object(worker, 'construct_input_data')
|
||||
@mock.patch.object(worker, 'check_stack_complete')
|
||||
|
@ -436,7 +496,8 @@ class CheckWorkflowCleanupTest(common.HeatTestCase):
|
|||
self.assertFalse(mock_cru.called)
|
||||
mock_crc.assert_called_once_with(
|
||||
self.resource, self.resource.stack.t.id,
|
||||
{}, self.worker.engine_id)
|
||||
{}, self.worker.engine_id,
|
||||
self.stack.timeout_secs())
|
||||
|
||||
def test_is_cleanup_traversal_raise_update_inprogress(
|
||||
self, mock_cru, mock_crc, mock_pcr, mock_csc, mock_cid):
|
||||
|
@ -446,7 +507,8 @@ class CheckWorkflowCleanupTest(common.HeatTestCase):
|
|||
self.is_update, None)
|
||||
mock_crc.assert_called_once_with(self.resource,
|
||||
self.resource.stack.t.id,
|
||||
{}, self.worker.engine_id)
|
||||
{}, self.worker.engine_id,
|
||||
self.stack.timeout_secs())
|
||||
self.assertFalse(mock_cru.called)
|
||||
self.assertFalse(mock_pcr.called)
|
||||
self.assertFalse(mock_csc.called)
|
||||
|
@ -512,7 +574,8 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
def test_check_resource_update_init_action(self, mock_update, mock_create):
|
||||
self.resource.action = 'INIT'
|
||||
worker.check_resource_update(self.resource, self.resource.stack.t.id,
|
||||
{}, 'engine-id')
|
||||
{}, 'engine-id',
|
||||
self.stack.timeout_secs())
|
||||
self.assertTrue(mock_create.called)
|
||||
self.assertFalse(mock_update.called)
|
||||
|
||||
|
@ -522,7 +585,8 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
self, mock_update, mock_create):
|
||||
self.resource.action = 'CREATE'
|
||||
worker.check_resource_update(self.resource, self.resource.stack.t.id,
|
||||
{}, 'engine-id')
|
||||
{}, 'engine-id',
|
||||
self.stack.timeout_secs())
|
||||
self.assertFalse(mock_create.called)
|
||||
self.assertTrue(mock_update.called)
|
||||
|
||||
|
@ -532,7 +596,8 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
self, mock_update, mock_create):
|
||||
self.resource.action = 'UPDATE'
|
||||
worker.check_resource_update(self.resource, self.resource.stack.t.id,
|
||||
{}, 'engine-id')
|
||||
{}, 'engine-id',
|
||||
self.stack.timeout_secs())
|
||||
self.assertFalse(mock_create.called)
|
||||
self.assertTrue(mock_update.called)
|
||||
|
||||
|
@ -540,5 +605,6 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
def test_check_resource_cleanup_delete(self, mock_delete):
|
||||
self.resource.current_template_id = 'new-template-id'
|
||||
worker.check_resource_cleanup(self.resource, self.resource.stack.t.id,
|
||||
{}, 'engine-id')
|
||||
{}, 'engine-id',
|
||||
self.stack.timeout_secs())
|
||||
self.assertTrue(mock_delete.called)
|
||||
|
|
|
@ -65,6 +65,7 @@ class ResourceTest(common.HeatTestCase):
|
|||
template.Template(empty_template,
|
||||
env=self.env),
|
||||
stack_id=str(uuid.uuid4()))
|
||||
self.dummy_timeout = 10
|
||||
|
||||
def test_get_class_ok(self):
|
||||
cls = resources.global_env().get_class('GenericResourceType')
|
||||
|
@ -1466,8 +1467,10 @@ class ResourceTest(common.HeatTestCase):
|
|||
self.assertEqual(engine_id, rs.engine_id)
|
||||
self.assertEqual(atomic_key, rs.atomic_key)
|
||||
|
||||
@mock.patch.object(resource.Resource, 'create')
|
||||
def test_create_convergence(self, mock_create):
|
||||
@mock.patch.object(resource.scheduler.TaskRunner, '__init__',
|
||||
return_value=None)
|
||||
@mock.patch.object(resource.scheduler.TaskRunner, '__call__')
|
||||
def test_create_convergence(self, mock_call, mock_init):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||
res.action = res.CREATE
|
||||
|
@ -1475,13 +1478,28 @@ class ResourceTest(common.HeatTestCase):
|
|||
self._assert_resource_lock(res.id, None, None)
|
||||
res_data = {(1, True): {u'id': 1, u'name': 'A', 'attrs': {}},
|
||||
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||
res.create_convergence(self.stack.t.id, res_data, 'engine-007')
|
||||
|
||||
mock_create.assert_called_once_with()
|
||||
res.create_convergence(self.stack.t.id, res_data, 'engine-007',
|
||||
60)
|
||||
|
||||
mock_init.assert_called_once_with(res.create)
|
||||
mock_call.assert_called_once_with(timeout=60)
|
||||
self.assertEqual(self.stack.t.id, res.current_template_id)
|
||||
self.assertItemsEqual([1, 3], res.requires)
|
||||
self._assert_resource_lock(res.id, None, 2)
|
||||
|
||||
def test_create_convergence_throws_timeout(self):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||
res.action = res.CREATE
|
||||
res._store()
|
||||
res_data = {(1, True): {u'id': 1, u'name': 'A', 'attrs': {}},
|
||||
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||
|
||||
self.assertRaises(scheduler.Timeout, res.create_convergence,
|
||||
self.stack.t.id, res_data, 'engine-007',
|
||||
0)
|
||||
|
||||
def test_create_convergence_sets_requires_for_failure(self):
|
||||
'''
|
||||
Ensure that requires are computed correctly even if resource
|
||||
|
@ -1497,7 +1515,7 @@ class ResourceTest(common.HeatTestCase):
|
|||
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||
self.assertRaises(exception.ResourceNotAvailable,
|
||||
res.create_convergence, self.stack.t.id, res_data,
|
||||
'engine-007')
|
||||
'engine-007', self.dummy_timeout)
|
||||
self.assertItemsEqual([5, 3], res.requires)
|
||||
self._assert_resource_lock(res.id, None, 2)
|
||||
|
||||
|
@ -1512,7 +1530,8 @@ class ResourceTest(common.HeatTestCase):
|
|||
self._assert_resource_lock(res.id, None, None)
|
||||
res_data = {(1, True): {u'id': 5, u'name': 'A', 'attrs': {}},
|
||||
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||
res.create_convergence(self.stack.t.id, res_data, 'engine-007')
|
||||
res.create_convergence(self.stack.t.id, res_data, 'engine-007',
|
||||
self.dummy_timeout)
|
||||
|
||||
mock_adopt.assert_called_once_with(
|
||||
resource_data={'resource_id': 'fluffy'})
|
||||
|
@ -1530,11 +1549,13 @@ class ResourceTest(common.HeatTestCase):
|
|||
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||
exc = self.assertRaises(exception.ResourceFailure,
|
||||
res.create_convergence, self.stack.t.id,
|
||||
res_data, 'engine-007')
|
||||
res_data, 'engine-007', self.dummy_timeout)
|
||||
self.assertIn('Resource ID was not provided', six.text_type(exc))
|
||||
|
||||
@mock.patch.object(resource.Resource, 'update')
|
||||
def test_update_convergence(self, mock_update):
|
||||
@mock.patch.object(resource.scheduler.TaskRunner, '__init__',
|
||||
return_value=None)
|
||||
@mock.patch.object(resource.scheduler.TaskRunner, '__call__')
|
||||
def test_update_convergence(self, mock_call, mock_init):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res',
|
||||
'ResourceWithPropsType')
|
||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||
|
@ -1552,14 +1573,34 @@ class ResourceTest(common.HeatTestCase):
|
|||
|
||||
res_data = {(1, True): {u'id': 4, u'name': 'A', 'attrs': {}},
|
||||
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||
res.update_convergence(new_temp.id, res_data, 'engine-007')
|
||||
res.update_convergence(new_temp.id, res_data, 'engine-007', 120)
|
||||
|
||||
mock_update.assert_called_once_with(
|
||||
new_temp.resource_definitions(self.stack)[res.name])
|
||||
expected_rsrc_def = new_temp.resource_definitions(self.stack)[res.name]
|
||||
mock_init.assert_called_once_with(res.update, expected_rsrc_def)
|
||||
mock_call.assert_called_once_with(timeout=120)
|
||||
self.assertEqual(new_temp.id, res.current_template_id)
|
||||
self.assertItemsEqual([3, 4], res.requires)
|
||||
self._assert_resource_lock(res.id, None, 2)
|
||||
|
||||
def test_update_convergence_throws_timeout(self):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res',
|
||||
'ResourceWithPropsType')
|
||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||
res._store()
|
||||
|
||||
new_temp = template.Template({
|
||||
'HeatTemplateFormatVersion': '2012-12-12',
|
||||
'Resources': {
|
||||
'test_res': {'Type': 'ResourceWithPropsType',
|
||||
'Properties': {'Foo': 'abc'}}
|
||||
}}, env=self.env)
|
||||
new_temp.store()
|
||||
|
||||
res_data = {}
|
||||
self.assertRaises(scheduler.Timeout, res.update_convergence,
|
||||
new_temp.id, res_data, 'engine-007',
|
||||
0)
|
||||
|
||||
def test_update_in_progress_convergence(self):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||
|
@ -1574,14 +1615,18 @@ class ResourceTest(common.HeatTestCase):
|
|||
ex = self.assertRaises(resource.UpdateInProgress,
|
||||
res.update_convergence,
|
||||
'template_key',
|
||||
res_data, 'engine-007')
|
||||
res_data, 'engine-007',
|
||||
self.dummy_timeout)
|
||||
msg = ("The resource %s is already being updated." %
|
||||
res.name)
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
# ensure requirements are not updated for failed resource
|
||||
self.assertEqual([1, 2], res.requires)
|
||||
|
||||
def test_delete_convergence_ok(self):
|
||||
@mock.patch.object(resource.scheduler.TaskRunner, '__init__',
|
||||
return_value=None)
|
||||
@mock.patch.object(resource.scheduler.TaskRunner, '__call__')
|
||||
def test_delete_convergence_ok(self, mock_call, mock_init):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||
|
@ -1590,15 +1635,13 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.status = res.COMPLETE
|
||||
res.action = res.CREATE
|
||||
res._store()
|
||||
res_id = res.id
|
||||
res.handle_delete = mock.Mock(return_value=None)
|
||||
res._update_replacement_data = mock.Mock()
|
||||
self._assert_resource_lock(res.id, None, None)
|
||||
res.delete_convergence(2, {}, 'engine-007')
|
||||
self.assertTrue(res.handle_delete.called)
|
||||
self.assertRaises(exception.NotFound,
|
||||
resource_objects.Resource.get_obj,
|
||||
self.stack.context, res_id)
|
||||
res.delete_convergence(2, {}, 'engine-007', 20)
|
||||
|
||||
mock_init.assert_called_once_with(res.destroy)
|
||||
mock_call.assert_called_once_with(timeout=20)
|
||||
self.assertTrue(res._update_replacement_data.called)
|
||||
|
||||
def test_delete_convergence_does_not_delete_same_template_resource(self):
|
||||
|
@ -1607,7 +1650,8 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.current_template_id = 'same-template'
|
||||
res._store()
|
||||
res.destroy = mock.Mock()
|
||||
res.delete_convergence('same-template', {}, 'engine-007')
|
||||
res.delete_convergence('same-template', {}, 'engine-007',
|
||||
self.dummy_timeout)
|
||||
self.assertFalse(res.destroy.called)
|
||||
|
||||
def test_delete_convergence_fail(self):
|
||||
|
@ -1621,7 +1665,8 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.handle_delete = mock.Mock(side_effect=ValueError('test'))
|
||||
self._assert_resource_lock(res.id, None, None)
|
||||
self.assertRaises(exception.ResourceFailure,
|
||||
res.delete_convergence, 2, {}, 'engine-007')
|
||||
res.delete_convergence, 2, {}, 'engine-007',
|
||||
self.dummy_timeout)
|
||||
self.assertTrue(res.handle_delete.called)
|
||||
|
||||
# confirm that the DB object still exists, and it's lock is released.
|
||||
|
@ -1642,7 +1687,7 @@ class ResourceTest(common.HeatTestCase):
|
|||
self._assert_resource_lock(res.id, 'not-this', None)
|
||||
ex = self.assertRaises(resource.UpdateInProgress,
|
||||
res.delete_convergence,
|
||||
1, {}, 'engine-007')
|
||||
1, {}, 'engine-007', self.dummy_timeout)
|
||||
msg = ("The resource %s is already being updated." %
|
||||
res.name)
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
@ -1657,7 +1702,7 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.destroy = mock.Mock()
|
||||
input_data = {(1, False): 4, (2, False): 5} # needed_by resource ids
|
||||
self._assert_resource_lock(res.id, None, None)
|
||||
res.delete_convergence(1, input_data, 'engine-007')
|
||||
res.delete_convergence(1, input_data, 'engine-007', self.dummy_timeout)
|
||||
self.assertItemsEqual([4, 5], res.needed_by)
|
||||
|
||||
@mock.patch.object(resource_objects.Resource, 'get_obj')
|
||||
|
@ -1765,6 +1810,14 @@ class ResourceTest(common.HeatTestCase):
|
|||
test_obj.get.side_effect = AttributeError
|
||||
self.assertIsNone(res._show_resource())
|
||||
|
||||
def test_delete_convergence_throws_timeout(self):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||
res._store()
|
||||
timeout = 0 # to emulate timeout
|
||||
self.assertRaises(scheduler.Timeout, res.delete_convergence,
|
||||
1, {}, 'engine-007', timeout)
|
||||
|
||||
|
||||
class ResourceAdoptTest(common.HeatTestCase):
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
import collections
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import time
|
||||
|
||||
|
@ -100,6 +101,59 @@ class StackTest(common.HeatTestCase):
|
|||
timeout_mins=10)
|
||||
self.assertEqual(600, self.stack.timeout_secs())
|
||||
|
||||
@mock.patch.object(stack, 'datetime')
|
||||
def test_time_elapsed(self, mock_dt):
|
||||
self.stack = stack.Stack(self.ctx, 'test_stack', self.tmpl)
|
||||
# dummy create time 10:00:00
|
||||
self.stack.created_time = datetime.datetime(2015, 7, 27, 10, 0, 0)
|
||||
# mock utcnow set to 10:10:00 (600s offset)
|
||||
mock_dt.datetime.utcnow.return_value = datetime.datetime(2015, 7, 27,
|
||||
10, 10, 0)
|
||||
self.assertEqual(600, self.stack.time_elapsed())
|
||||
|
||||
@mock.patch.object(stack, 'datetime')
|
||||
def test_time_elapsed_with_updated_time(self, mock_dt):
|
||||
self.stack = stack.Stack(self.ctx, 'test_stack', self.tmpl)
|
||||
# dummy create time 10:00:00
|
||||
self.stack.created_time = datetime.datetime(2015, 7, 27, 10, 0, 0)
|
||||
# dummy updated time 11:00:00; should consider this not created_time
|
||||
self.stack.updated_time = datetime.datetime(2015, 7, 27, 11, 0, 0)
|
||||
# mock utcnow set to 11:10:00 (600s offset)
|
||||
mock_dt.datetime.utcnow.return_value = datetime.datetime(2015, 7, 27,
|
||||
11, 10, 0)
|
||||
self.assertEqual(600, self.stack.time_elapsed())
|
||||
|
||||
@mock.patch.object(stack.Stack, 'time_elapsed')
|
||||
def test_time_remaining(self, mock_te):
|
||||
self.stack = stack.Stack(self.ctx, 'test_stack', self.tmpl)
|
||||
# mock time elapsed; set to 600 seconds
|
||||
mock_te.return_value = 600
|
||||
# default stack timeout is 3600 seconds; remaining time 3000 secs
|
||||
self.assertEqual(3000, self.stack.time_remaining())
|
||||
|
||||
@mock.patch.object(stack.Stack, 'time_elapsed')
|
||||
def test_has_timed_out(self, mock_te):
|
||||
self.stack = stack.Stack(self.ctx, 'test_stack', self.tmpl)
|
||||
self.stack.status = self.stack.IN_PROGRESS
|
||||
|
||||
# test with timed out stack
|
||||
mock_te.return_value = 3601
|
||||
# default stack timeout is 3600 seconds; stack should time out
|
||||
self.assertTrue(self.stack.has_timed_out())
|
||||
|
||||
# mock time elapsed; set to 600 seconds
|
||||
mock_te.return_value = 600
|
||||
# default stack timeout is 3600 seconds; remaining time 3000 secs
|
||||
self.assertFalse(self.stack.has_timed_out())
|
||||
|
||||
# has_timed_out has no meaning when stack completes/fails;
|
||||
# should return false
|
||||
self.stack.status = self.stack.COMPLETE
|
||||
self.assertFalse(self.stack.has_timed_out())
|
||||
|
||||
self.stack.status = self.stack.FAILED
|
||||
self.assertFalse(self.stack.has_timed_out())
|
||||
|
||||
def test_no_auth_token(self):
|
||||
ctx = utils.dummy_context()
|
||||
ctx.auth_token = None
|
||||
|
|
Loading…
Reference in New Issue