diff --git a/robottelo/host_helpers/capsule_mixins.py b/robottelo/host_helpers/capsule_mixins.py index 9e283509696..a38868a2b65 100644 --- a/robottelo/host_helpers/capsule_mixins.py +++ b/robottelo/host_helpers/capsule_mixins.py @@ -1,6 +1,11 @@ -from datetime import datetime +from datetime import ( + datetime, + timedelta, +) import time +from dateutil.parser import parse + from robottelo.constants import PUPPET_CAPSULE_INSTALLER, PUPPET_COMMON_INSTALLER_OPTS from robottelo.logging import logger from robottelo.utils.installer import InstallerCommand @@ -66,21 +71,57 @@ def wait_for_sync(self, timeout=600, start_time=None): # Assert that a task to sync lifecycle environment to the capsule # is started (or finished already) if start_time is None: - start_time = datetime.utcnow().replace(microsecond=0) + start_time = datetime.utcnow() + # 1s margin of safety for rounding + start_time = ( + (start_time - timedelta(seconds=1)) + .replace(microsecond=0) + .strftime('%Y-%m-%d %H:%M:%S UTC') + ) logger.info(f"Waiting for capsule {self.hostname} sync to finish ...") sync_status = self.nailgun_capsule.content_get_sync() logger.info(f"Active tasks {sync_status['active_sync_tasks']}") - assert ( - len(sync_status['active_sync_tasks']) - or datetime.strptime(sync_status['last_sync_time'], '%Y-%m-%d %H:%M:%S UTC') - >= start_time - ) + ongoing_sync = True if sync_status['active_sync_tasks'] else False - # Wait till capsule sync finishes and assert the sync task succeeded + if not sync_status['last_sync_time']: # never synced + # we expect some in-progress sync task(s) ongoing + assert len(sync_status['active_sync_tasks']) > 0, ( + f'last_sync_time for capsule {self.hostname} was None (never).' + ' Expected one or more ongoing sync task(s), but found none.' + ) + else: + if parse(sync_status['last_sync_time']) < parse(start_time): + # last sync is prior to start_time, expect some ongoing sync task(s) + assert len(sync_status['active_sync_tasks']) > 0, ( + f'last_sync_time: ({sync_status["last_sync_time"]}), for capsule {self.hostname},' + f' was older than start_time: {start_time}.' + ' Expected one or more ongoing sync task(s), but found none.' + ) + # else: pass, last sync was after or same as start_time + + # Wait till capsule sync(s) finish, + # by default polled task must succeed for task in sync_status['active_sync_tasks']: self.satellite.api.ForemanTask(id=task['id']).poll(timeout=timeout) + + # fetch updated sync status, assert no failed tasks sync_status = self.nailgun_capsule.content_get_sync() - assert len(sync_status['last_failed_sync_tasks']) == 0 + assert len(sync_status['last_failed_sync_tasks']) == 0, ( + f'One or more sync tasks have failed for capsule {self.hostname},' + f' {sync_status["last_failed_sync_tasks"]}' + ) + # there was a prior active sync, when polled had finished: + # assert current status last_sync_time updated to on or after start_time + # assert current status shows no active sync tasks remaining + if ongoing_sync: + assert parse(sync_status['last_sync_time']) >= parse(start_time), ( + f'After polling finished in-progress sync task(s), for capsule {self.hostname},' + f' last_sync_time: {sync_status["last_sync_time"]}, was not on or after start_time: {start_time}.' + ) + assert len(sync_status['active_sync_tasks']) == 0, ( + f'Unexpected active sync task(s) remaining for capsule {self.hostname},' + f' {sync_status["active_sync_tasks"]}' + ) def get_published_repo_url(self, org, prod, repo, lce=None, cv=None): """Forms url of a repo or CV published on a Satellite or Capsule. diff --git a/tests/foreman/api/test_capsulecontent.py b/tests/foreman/api/test_capsulecontent.py index abbfd9f9f32..1db528e0284 100644 --- a/tests/foreman/api/test_capsulecontent.py +++ b/tests/foreman/api/test_capsulecontent.py @@ -358,7 +358,10 @@ def test_positive_capsule_sync( cvv = cv.version[-1].read() # Promote content view to lifecycle environment + # invoking capsule sync task(s) + timestamp = datetime.utcnow() cvv.promote(data={'environment_ids': function_lce.id}) + module_capsule_configured.wait_for_sync(start_time=timestamp) cvv = cvv.read() assert len(cvv.environment) == 2 @@ -368,8 +371,6 @@ def test_positive_capsule_sync( # repository assert repo.content_counts['rpm'] == cvv.package_count - module_capsule_configured.wait_for_sync() - # Assert that the content published on the capsule is exactly the # same as in repository on satellite sat_repo_url = target_sat.get_published_repo_url( @@ -404,14 +405,14 @@ def test_positive_capsule_sync( cv = cv.read() cv.version.sort(key=lambda version: version.id) cvv = cv.version[-1].read() - # Promote new content view version to lifecycle environment + # Promote new content view version to lifecycle environment, + # capsule sync task(s) invoked and succeed + timestamp = datetime.utcnow() cvv.promote(data={'environment_ids': function_lce.id}) - cvv = cvv.read() + module_capsule_configured.wait_for_sync(start_time=timestamp) + cvv = cvv.read() assert len(cvv.environment) == 2 - - module_capsule_configured.wait_for_sync() - # Assert that the value of repomd revision of repository in # lifecycle environment on the capsule has not changed new_lce_revision_capsule = get_repomd_revision(caps_repo_url) @@ -427,9 +428,11 @@ def test_positive_capsule_sync( cv = cv.read() cv.version.sort(key=lambda version: version.id) cvv = cv.version[-1].read() + + timestamp = datetime.utcnow() cvv.promote(data={'environment_ids': function_lce.id}) + module_capsule_configured.wait_for_sync(start_time=timestamp) cvv = cvv.read() - assert len(cvv.environment) == 2 # Assert that packages count in the repository is updated @@ -440,8 +443,6 @@ def test_positive_capsule_sync( # repository assert repo.content_counts['rpm'] == cvv.package_count - module_capsule_configured.wait_for_sync() - # Assert that the content published on the capsule is exactly the # same as in the repository sat_files = get_repo_files_by_url(sat_repo_url)