Merge "Convergence: Implementation of timeout"

This commit is contained in:
Jenkins 2015-08-10 01:42:10 +00:00 committed by Gerrit Code Review
commit f246cd4ea8
6 changed files with 274 additions and 56 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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