Skip to content

Commit

Permalink
feat: cleanup of old indirect host audit records
Browse files Browse the repository at this point in the history
  • Loading branch information
pb82 committed Jan 31, 2025
1 parent bc08e03 commit b252f45
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 7 deletions.
16 changes: 15 additions & 1 deletion awx/main/tasks/host_indirect.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ def save_indirect_host_entries_of_job(job: Job) -> None:
job.event_queries_processed = True


def cleanup_old_indirect_host_entries() -> None:
"""
We assume that indirect host audit results older than one week have already been collected for analysis
and can be cleaned up
"""
limit = now() - timedelta(days=settings.INDIRECT_HOST_AUDIT_RECORD_MAX_AGE_DAYS)
IndirectManagedNodeAudit.objects.filter(created__lt=limit).delete()


@task(queue=get_task_queuename)
def save_indirect_host_entries(job_id: int, wait_for_events: bool = True) -> None:
try:
Expand Down Expand Up @@ -148,10 +157,15 @@ def save_indirect_host_entries(job_id: int, wait_for_events: bool = True) -> Non


@task(queue=get_task_queuename)
def save_indirect_host_entries_fallback() -> None:
def cleanup_and_save_indirect_host_entries_fallback() -> None:
if not flag_enabled("FEATURE_INDIRECT_NODE_COUNTING_ENABLED"):
return

try:
cleanup_old_indirect_host_entries()
except Exception as e:
logger.error(f"error cleaning up indirect host audit records: {e}")

Check warning on line 167 in awx/main/tasks/host_indirect.py

View check run for this annotation

Codecov / codecov/patch

awx/main/tasks/host_indirect.py#L166-L167

Added lines #L166 - L167 were not covered by tests

job_ct = 0
right_now_time = now()
window_end = right_now_time - timedelta(seconds=settings.INDIRECT_HOST_QUERY_FALLBACK_MINUTES)
Expand Down
40 changes: 36 additions & 4 deletions awx/main/tests/functional/tasks/test_host_indirect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@

from django.utils.timezone import now, timedelta

from awx.main.tasks.host_indirect import build_indirect_host_data, fetch_job_event_query, save_indirect_host_entries, save_indirect_host_entries_fallback
from awx.main.tasks.host_indirect import (
build_indirect_host_data,
fetch_job_event_query,
save_indirect_host_entries,
cleanup_and_save_indirect_host_entries_fallback,
)
from awx.main.models.event_query import EventQuery
from awx.main.models.indirect_managed_node_audit import IndirectManagedNodeAudit

from awx.main.tests.functional.conftest import organization

"""These are unit tests, similar to test_indirect_host_counting in the live tests"""

Expand Down Expand Up @@ -39,12 +44,30 @@ def create_event_query(fqcn='demo.query'):
return EventQuery.objects.create(fqcn=fqcn, collection_version='1.0.1', event_query=yaml.dump({module_name: TEST_JQ}, default_flow_style=False))


def create_audit_record(name, job, organization, created=now()):
record = IndirectManagedNodeAudit.objects.create(name=name, job=job, organization=organization)
record.created = created
record.save()
return record


@pytest.fixture
def event_query():
"This is ordinarily created by the artifacts callback"
return create_event_query()


@pytest.fixture
def old_audit_record(bare_job, organization):
created_at = now() - timedelta(days=10)
return create_audit_record(name="old_job", job=bare_job, organization=organization, created=created_at)


@pytest.fixture
def new_audit_record(bare_job, organization):
return IndirectManagedNodeAudit.objects.create(name="new_job", job=bare_job, organization=organization)


# ---- end fixtures ----


Expand Down Expand Up @@ -167,7 +190,7 @@ def test_events_not_fully_processed_no_op(bare_job):

# Right away, the fallback processing will not run either
with mock.patch.object(save_indirect_host_entries, 'delay') as mock_delay:
save_indirect_host_entries_fallback()
cleanup_and_save_indirect_host_entries_fallback()
mock_delay.assert_not_called()
bare_job.refresh_from_db()
assert bare_job.event_queries_processed is False
Expand All @@ -178,7 +201,7 @@ def test_events_not_fully_processed_no_op(bare_job):

# The fallback task will now process indirect host query data for this job
with mock.patch.object(save_indirect_host_entries, 'delay') as mock_delay:
save_indirect_host_entries_fallback()
cleanup_and_save_indirect_host_entries_fallback()
mock_delay.assert_called_once_with(bare_job.id, wait_for_events=True)

# Test code to process anyway, events collected or not
Expand All @@ -190,3 +213,12 @@ def test_events_not_fully_processed_no_op(bare_job):
@pytest.mark.django_db
def test_job_id_does_not_exist():
save_indirect_host_entries(10000001)


@pytest.mark.django_db
def test_cleanup_old_audit_records(old_audit_record, new_audit_record):
count_before_cleanup = IndirectManagedNodeAudit.objects.count()
assert count_before_cleanup == 2
cleanup_and_save_indirect_host_entries_fallback()
count_after_cleanup = IndirectManagedNodeAudit.objects.count()
assert count_after_cleanup == 1
2 changes: 1 addition & 1 deletion awx/main/tests/unit/tasks/test_host_indirect_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_compare_equal_data(self, data):
[['a', {'b': 'c'}], ['a', {'b': 'd'}]],
],
)
def test_compar_different_data(self, data, other_data):
def test_compare_different_data(self, data, other_data):
assert data != other_data # sanity, otherwise why test this?
assert get_hashable_form(data) != get_hashable_form(other_data)
assert hash(get_hashable_form(data)) != hash(get_hashable_form(other_data))
Expand Down
10 changes: 9 additions & 1 deletion awx/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,10 @@
'cleanup_host_metrics': {'task': 'awx.main.tasks.host_metrics.cleanup_host_metrics', 'schedule': timedelta(hours=3, minutes=30)},
'host_metric_summary_monthly': {'task': 'awx.main.tasks.host_metrics.host_metric_summary_monthly', 'schedule': timedelta(hours=4)},
'periodic_resource_sync': {'task': 'awx.main.tasks.system.periodic_resource_sync', 'schedule': timedelta(minutes=15)},
'save_indirect_host_entries_fallback': {'task': 'awx.main.tasks.host_indirect.save_indirect_host_entries_fallback', 'schedule': timedelta(minutes=60)},
'cleanup_and_save_indirect_host_entries_fallback': {
'task': 'awx.main.tasks.host_indirect.save_indirect_host_entries_fallback',
'schedule': timedelta(minutes=60),
},
}

# Django Caching Configuration
Expand Down Expand Up @@ -1069,6 +1072,11 @@
# If an error happens in event collection, give up after this time
INDIRECT_HOST_QUERY_FALLBACK_GIVEUP_DAYS = 3

# Maximum age for indirect host audit records
# Older records will be cleaned up
INDIRECT_HOST_AUDIT_RECORD_MAX_AGE_DAYS = 7


# feature flags
FLAGS = {'FEATURE_INDIRECT_NODE_COUNTING_ENABLED': [{'condition': 'boolean', 'value': False}]}

Expand Down

0 comments on commit b252f45

Please sign in to comment.