From b15b58e964a61fd580c506689c62e949a8f6956a Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Thu, 29 Apr 2021 19:45:29 -0500 Subject: [PATCH] Add wait to check the import task status Once Glance finish the import operation(change image status to active), it move the task to 'success' state but in between of image become active and task is transitioning to 'success', tempest try to check the task status and race condition happen. Adding waiter method in test for task status check. Closes-Bug: #1926671 Change-Id: I960b80314f1b0926eca33af830bc827f31cbeda6 --- tempest/api/image/v2/test_images.py | 10 ++++------ tempest/common/waiters.py | 24 ++++++++++++++++++++++++ tempest/tests/common/test_waiters.py | 23 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py index efa23bb83a..d283ab3c1f 100644 --- a/tempest/api/image/v2/test_images.py +++ b/tempest/api/image/v2/test_images.py @@ -105,12 +105,10 @@ class ImportImagesTest(base.BaseV2ImageTest): 'validate the image/tasks API.') return - # Make sure we can access the task and that some of the key - # fields look legit. - tasks = self.client.show_image_tasks(image_id) - self.assertEqual(1, len(tasks['tasks'])) - task = tasks['tasks'][0] - self.assertEqual('success', task['status']) + tasks = waiters.wait_for_image_tasks_status( + self.client, image_id, 'success') + self.assertEqual(1, len(tasks)) + task = tasks[0] self.assertEqual(resp.response['x-openstack-request-id'], task['request_id']) self.assertEqual('glance-direct', diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py index eaac05e6dd..3750b113f8 100644 --- a/tempest/common/waiters.py +++ b/tempest/common/waiters.py @@ -193,6 +193,30 @@ def wait_for_image_status(client, image_id, status): raise lib_exc.TimeoutException(message) +def wait_for_image_tasks_status(client, image_id, status): + """Waits for an image tasks to reach a given status.""" + pending_tasks = [] + start = int(time.time()) + while int(time.time()) - start < client.build_timeout: + tasks = client.show_image_tasks(image_id)['tasks'] + + pending_tasks = [task for task in tasks if task['status'] != status] + if not pending_tasks: + return tasks + time.sleep(client.build_interval) + + message = ('Image %(image_id)s tasks: %(pending_tasks)s ' + 'failed to reach %(status)s state within the required ' + 'time (%(timeout)s s).' % {'image_id': image_id, + 'pending_tasks': pending_tasks, + 'status': status, + 'timeout': client.build_timeout}) + caller = test_utils.find_test_caller() + if caller: + message = '(%s) %s' % (caller, message) + raise lib_exc.TimeoutException(message) + + def wait_for_image_imported_to_stores(client, image_id, stores=None): """Waits for an image to be imported to all requested stores. diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py index d64d7b020f..f801243011 100755 --- a/tempest/tests/common/test_waiters.py +++ b/tempest/tests/common/test_waiters.py @@ -120,6 +120,29 @@ class TestImageWaiters(base.TestCase): waiters.wait_for_image_copied_to_stores, self.client, 'fake_image_id') + def test_wait_for_image_tasks_status(self): + self.client.show_image_tasks.return_value = ({ + 'tasks': [{'status': 'success'}]}) + start_time = int(time.time()) + waiters.wait_for_image_tasks_status( + self.client, 'fake_image_id', 'success') + end_time = int(time.time()) + # Ensure waiter returns before build_timeout + self.assertLess((end_time - start_time), 10) + + def test_wait_for_image_tasks_status_timeout(self): + time_mock = self.patch('time.time') + self.patch('time.time', side_effect=[0., 1.]) + time_mock.side_effect = utils.generate_timeout_series(1) + + self.client.show_image_tasks.return_value = ({ + 'tasks': [ + {'status': 'success'}, + {'status': 'processing'}]}) + self.assertRaises(lib_exc.TimeoutException, + waiters.wait_for_image_tasks_status, + self.client, 'fake_image_id', 'success') + class TestInterfaceWaiters(base.TestCase):