From 9e363d82d8f43485951d23eb5ce96d8009038d5e Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 16 Feb 2017 13:55:18 -0500 Subject: [PATCH] disable activity stream in cleanup_jobs and test context managers --- awx/main/management/commands/cleanup_jobs.py | 39 +++++---------- awx/main/signals.py | 48 ++++++++++++++----- .../models/test_context_managers.py | 47 ++++++++++++++++++ .../rbac_dummy_data_generator.py | 25 ++-------- 4 files changed, 100 insertions(+), 59 deletions(-) create mode 100644 awx/main/tests/functional/models/test_context_managers.py diff --git a/awx/main/management/commands/cleanup_jobs.py b/awx/main/management/commands/cleanup_jobs.py index 84927c755a..cb03e4e9d6 100644 --- a/awx/main/management/commands/cleanup_jobs.py +++ b/awx/main/management/commands/cleanup_jobs.py @@ -13,12 +13,14 @@ from django.utils.timezone import now # AWX from awx.main.models import ( - Job, AdHocCommand, ProjectUpdate, InventorySource, InventoryUpdate, - SystemJob, WorkflowJob, Notification, Group, Host + Job, AdHocCommand, ProjectUpdate, InventoryUpdate, + SystemJob, WorkflowJob, Notification ) from awx.main.signals import ( # noqa emit_update_inventory_on_created_or_deleted, - emit_update_inventory_computed_fields + emit_update_inventory_computed_fields, + disable_activity_stream, + disable_computed_fields ) from django.db.models.signals import post_save, post_delete, m2m_changed # noqa @@ -227,28 +229,12 @@ class Command(NoArgsCommand): deleted += 1 return skipped, deleted - def disable_job_signals(self): - sigstat = [] - sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Host)) - sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Host)) - sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Group)) - sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Group)) - sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.hosts.through)) - sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.parents.through)) - sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Host.inventory_sources.through)) - sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.inventory_sources.through)) - sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=InventorySource)) - sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=InventorySource)) - sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Job)) - sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Job)) - @transaction.atomic def handle_noargs(self, **options): self.verbosity = int(options.get('verbosity', 1)) self.init_logging() self.days = int(options.get('days', 90)) self.dry_run = bool(options.get('dry_run', False)) - self.disable_job_signals() try: self.cutoff = now() - datetime.timedelta(days=self.days) except OverflowError: @@ -261,10 +247,11 @@ class Command(NoArgsCommand): models_to_cleanup.add(m) if not models_to_cleanup: models_to_cleanup.update(model_names) - for m in model_names: - if m in models_to_cleanup: - skipped, deleted = getattr(self, 'cleanup_%s' % m)() - if self.dry_run: - self.logger.log(99, '%s: %d would be deleted, %d would be skipped.', m.replace('_', ' '), deleted, skipped) - else: - self.logger.log(99, '%s: %d deleted, %d skipped.', m.replace('_', ' '), deleted, skipped) + with disable_activity_stream(), disable_computed_fields(): + for m in model_names: + if m in models_to_cleanup: + skipped, deleted = getattr(self, 'cleanup_%s' % m)() + if self.dry_run: + self.logger.log(99, '%s: %d would be deleted, %d would be skipped.', m.replace('_', ' '), deleted, skipped) + else: + self.logger.log(99, '%s: %d deleted, %d skipped.', m.replace('_', ' '), deleted, skipped) diff --git a/awx/main/signals.py b/awx/main/signals.py index ceda8899b1..7412b9e148 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -210,18 +210,24 @@ def cleanup_detached_labels_on_deleted_parent(sender, instance, **kwargs): l.delete() -post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Host) -post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Host) -post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Group) -post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Group) -m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.hosts.through) -m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.parents.through) -m2m_changed.connect(emit_update_inventory_computed_fields, sender=Host.inventory_sources.through) -m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.inventory_sources.through) -post_save.connect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) -post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) -post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Job) -post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Job) +def connect_computed_field_signals(): + post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Host) + post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Host) + post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Group) + post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Group) + m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.hosts.through) + m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.parents.through) + m2m_changed.connect(emit_update_inventory_computed_fields, sender=Host.inventory_sources.through) + m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.inventory_sources.through) + post_save.connect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) + post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) + post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Job) + post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Job) + + +connect_computed_field_signals() + + post_save.connect(emit_job_event_detail, sender=JobEvent) post_save.connect(emit_ad_hoc_command_event_detail, sender=AdHocCommandEvent) m2m_changed.connect(rebuild_role_ancestor_list, Role.parents.through) @@ -340,6 +346,24 @@ def disable_activity_stream(): activity_stream_enabled.enabled = previous_value +@contextlib.contextmanager +def disable_computed_fields(): + post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Host) + post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Host) + post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Group) + post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Group) + m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.hosts.through) + m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.parents.through) + m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Host.inventory_sources.through) + m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.inventory_sources.through) + post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) + post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) + post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Job) + post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Job) + yield + connect_computed_field_signals() + + model_serializer_mapping = { Organization: OrganizationSerializer, Inventory: InventorySerializer, diff --git a/awx/main/tests/functional/models/test_context_managers.py b/awx/main/tests/functional/models/test_context_managers.py new file mode 100644 index 0000000000..61aad54ad4 --- /dev/null +++ b/awx/main/tests/functional/models/test_context_managers.py @@ -0,0 +1,47 @@ +import pytest + +# AWX context managers for testing +from awx.main.models.rbac import batch_role_ancestor_rebuilding +from awx.main.signals import ( + disable_activity_stream, + disable_computed_fields, + update_inventory_computed_fields +) + +# AWX models +from awx.main.models.organization import Organization +from awx.main.models import ActivityStream, Job + + +@pytest.mark.django_db +def test_rbac_batch_rebuilding(rando, organization): + with batch_role_ancestor_rebuilding(): + organization.admin_role.members.add(rando) + inventory = organization.inventories.create(name='test-inventory') + assert rando not in inventory.admin_role + assert rando in inventory.admin_role + + +@pytest.mark.django_db +def test_disable_activity_stream(): + with disable_activity_stream(): + Organization.objects.create(name='test-organization') + assert ActivityStream.objects.filter(organization__isnull=False).count() == 0 + + +@pytest.mark.django_db +class TestComputedFields: + + def test_computed_fields_normal_use(self, mocker, inventory): + job = Job.objects.create(name='fake-job', inventory=inventory) + with mocker.patch.object(update_inventory_computed_fields, 'delay'): + job.delete() + update_inventory_computed_fields.delay.assert_called_once_with(inventory.id, True) + + def test_disable_computed_fields(self, mocker, inventory): + job = Job.objects.create(name='fake-job', inventory=inventory) + with disable_computed_fields(): + with mocker.patch.object(update_inventory_computed_fields, 'delay'): + job.delete() + update_inventory_computed_fields.delay.assert_not_called() + diff --git a/tools/data_generators/rbac_dummy_data_generator.py b/tools/data_generators/rbac_dummy_data_generator.py index b7bc2c7720..71d1783911 100755 --- a/tools/data_generators/rbac_dummy_data_generator.py +++ b/tools/data_generators/rbac_dummy_data_generator.py @@ -42,7 +42,9 @@ from django.db import transaction # noqa from awx.main.models import * # noqa from awx.main.signals import ( # noqa emit_update_inventory_on_created_or_deleted, - emit_update_inventory_computed_fields + emit_update_inventory_computed_fields, + disable_activity_stream, + disable_computed_fields ) from django.db.models.signals import post_save, post_delete, m2m_changed # noqa @@ -194,32 +196,13 @@ def mock_computed_fields(self, **kwargs): PrimordialModel.save = mock_save -sigstat = [] -sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Host)) -sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Host)) -sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Group)) -sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Group)) -sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.hosts.through)) -sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.parents.through)) -sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Host.inventory_sources.through)) -sigstat.append(m2m_changed.disconnect(emit_update_inventory_computed_fields, sender=Group.inventory_sources.through)) -sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=InventorySource)) -sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=InventorySource)) -sigstat.append(post_save.disconnect(emit_update_inventory_on_created_or_deleted, sender=Job)) -sigstat.append(post_delete.disconnect(emit_update_inventory_on_created_or_deleted, sender=Job)) - -print ' status of signal disconnects ' -print ' (True means successful disconnect)' -print str(sigstat) - - startTime = datetime.now() try: with transaction.atomic(): - with batch_role_ancestor_rebuilding(): + with batch_role_ancestor_rebuilding(), disable_computed_fields(): admin, created = User.objects.get_or_create(username = 'admin', is_superuser=True) if created: admin.is_superuser = True