mirror of
https://github.com/ansible/awx.git
synced 2026-01-27 08:31:28 -03:30
Populate the cache the first time the job is run for a revision that needs them, and for future runs for that revision just copy it into the private directory. Delete the cache on project deletion. Invalidate the cache on a new project revision Also download roles/collections during the sync job Since we're writing into a per-revision cache, we can do this easily now. Don't try and install content if there aren't any requirements expecting it Adjust pathing to the proper location. Force install if doing a manual sync. Requirements may be unversioned. Remove the cache when delete-on-update is set Integrate content caching with existing task logic Revert the --force flags use the update id as metric for role caching Shift the movement of cache to job folder from rsync task to python Only install roles and collections if needed Deal with roles and collections for jobs without sync Skip local copy if roles or collections turned off update docs for content caching Design pivot - use empty cache dir to indicate lack of content Do not cache content if we did not install content Test changes to allay concerns about reliability of local_path Do not blow away cache for SCM inventory updates Remove project update vars no longer used Remove job pre-creation of content folders code style edit, always use cache_id as property in tasks Fix log message
147 lines
5.9 KiB
Python
147 lines
5.9 KiB
Python
import pytest
|
|
from unittest import mock
|
|
import os
|
|
|
|
from django.utils.timezone import now, timedelta
|
|
|
|
from awx.main.tasks import (
|
|
RunProjectUpdate, RunInventoryUpdate,
|
|
awx_isolated_heartbeat,
|
|
isolated_manager
|
|
)
|
|
from awx.main.models import (
|
|
ProjectUpdate, InventoryUpdate, InventorySource,
|
|
Instance, InstanceGroup
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def scm_revision_file(tmpdir_factory):
|
|
# Returns path to temporary testing revision file
|
|
revision_file = tmpdir_factory.mktemp('revisions').join('revision.txt')
|
|
with open(str(revision_file), 'w') as f:
|
|
f.write('1234567890123456789012345678901234567890')
|
|
return os.path.join(revision_file.dirname, 'revision.txt')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestDependentInventoryUpdate:
|
|
|
|
def test_dependent_inventory_updates_is_called(self, scm_inventory_source, scm_revision_file):
|
|
task = RunProjectUpdate()
|
|
task.revision_path = scm_revision_file
|
|
proj_update = scm_inventory_source.source_project.create_project_update()
|
|
with mock.patch.object(RunProjectUpdate, '_update_dependent_inventories') as inv_update_mck:
|
|
with mock.patch.object(RunProjectUpdate, 'release_lock'):
|
|
task.post_run_hook(proj_update, 'successful')
|
|
inv_update_mck.assert_called_once_with(proj_update, mock.ANY)
|
|
|
|
def test_no_unwanted_dependent_inventory_updates(self, project, scm_revision_file):
|
|
task = RunProjectUpdate()
|
|
task.revision_path = scm_revision_file
|
|
proj_update = project.create_project_update()
|
|
with mock.patch.object(RunProjectUpdate, '_update_dependent_inventories') as inv_update_mck:
|
|
with mock.patch.object(RunProjectUpdate, 'release_lock'):
|
|
task.post_run_hook(proj_update, 'successful')
|
|
assert not inv_update_mck.called
|
|
|
|
def test_dependent_inventory_updates(self, scm_inventory_source):
|
|
task = RunProjectUpdate()
|
|
scm_inventory_source.scm_last_revision = ''
|
|
proj_update = ProjectUpdate.objects.create(project=scm_inventory_source.source_project)
|
|
with mock.patch.object(RunInventoryUpdate, 'run') as iu_run_mock:
|
|
task._update_dependent_inventories(proj_update, [scm_inventory_source])
|
|
assert InventoryUpdate.objects.count() == 1
|
|
inv_update = InventoryUpdate.objects.first()
|
|
iu_run_mock.assert_called_once_with(inv_update.id)
|
|
assert inv_update.source_project_update_id == proj_update.pk
|
|
|
|
def test_dependent_inventory_project_cancel(self, project, inventory):
|
|
'''
|
|
Test that dependent inventory updates exhibit good behavior on cancel
|
|
of the source project update
|
|
'''
|
|
task = RunProjectUpdate()
|
|
proj_update = ProjectUpdate.objects.create(project=project)
|
|
|
|
kwargs = dict(
|
|
source_project=project,
|
|
source='scm',
|
|
source_path='inventory_file',
|
|
update_on_project_update=True,
|
|
inventory=inventory
|
|
)
|
|
|
|
is1 = InventorySource.objects.create(name="test-scm-inv", **kwargs)
|
|
is2 = InventorySource.objects.create(name="test-scm-inv2", **kwargs)
|
|
|
|
def user_cancels_project(pk):
|
|
ProjectUpdate.objects.all().update(cancel_flag=True)
|
|
|
|
with mock.patch.object(RunInventoryUpdate, 'run') as iu_run_mock:
|
|
iu_run_mock.side_effect = user_cancels_project
|
|
task._update_dependent_inventories(proj_update, [is1, is2])
|
|
# Verify that it bails after 1st update, detecting a cancel
|
|
assert is2.inventory_updates.count() == 0
|
|
iu_run_mock.assert_called_once()
|
|
|
|
|
|
|
|
class MockSettings:
|
|
AWX_ISOLATED_PERIODIC_CHECK = 60
|
|
CLUSTER_HOST_ID = 'tower_1'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestIsolatedManagementTask:
|
|
|
|
@pytest.fixture
|
|
def control_group(self):
|
|
return InstanceGroup.objects.create(name='alpha')
|
|
|
|
@pytest.fixture
|
|
def control_instance(self, control_group):
|
|
return control_group.instances.create(hostname='tower_1')
|
|
|
|
@pytest.fixture
|
|
def needs_updating(self, control_group):
|
|
ig = InstanceGroup.objects.create(name='thepentagon', controller=control_group)
|
|
inst = ig.instances.create(hostname='isolated', capacity=103)
|
|
inst.last_isolated_check=now() - timedelta(seconds=MockSettings.AWX_ISOLATED_PERIODIC_CHECK)
|
|
inst.save()
|
|
return ig
|
|
|
|
@pytest.fixture
|
|
def just_updated(self, control_group):
|
|
ig = InstanceGroup.objects.create(name='thepentagon', controller=control_group)
|
|
inst = ig.instances.create(hostname='isolated', capacity=103)
|
|
inst.last_isolated_check=now()
|
|
inst.save()
|
|
return inst
|
|
|
|
@pytest.fixture
|
|
def old_version(self, control_group):
|
|
ig = InstanceGroup.objects.create(name='thepentagon', controller=control_group)
|
|
inst = ig.instances.create(hostname='isolated-old', capacity=103)
|
|
inst.save()
|
|
return inst
|
|
|
|
def test_takes_action(self, control_instance, needs_updating):
|
|
original_isolated_instance = needs_updating.instances.all().first()
|
|
with mock.patch('awx.main.tasks.settings', MockSettings()):
|
|
with mock.patch.object(isolated_manager.IsolatedManager, 'health_check') as check_mock:
|
|
awx_isolated_heartbeat()
|
|
iso_instance = Instance.objects.get(hostname='isolated')
|
|
call_args, _ = check_mock.call_args
|
|
assert call_args[0][0] == iso_instance
|
|
assert iso_instance.last_isolated_check > original_isolated_instance.last_isolated_check
|
|
assert iso_instance.modified == original_isolated_instance.modified
|
|
|
|
def test_does_not_take_action(self, control_instance, just_updated):
|
|
with mock.patch('awx.main.tasks.settings', MockSettings()):
|
|
with mock.patch.object(isolated_manager.IsolatedManager, 'health_check') as check_mock:
|
|
awx_isolated_heartbeat()
|
|
iso_instance = Instance.objects.get(hostname='isolated')
|
|
check_mock.assert_not_called()
|
|
assert iso_instance.capacity == 103
|