correct filter events on the migration event horizon

events that existed *prior* to the partition migration will have
`job_created=1970-01-01` auto-applied at migration time; as such,
queries for these events e.g., /api/v2/job/N/job_events/
use 1970-01-01 in related event searche

events created *after* the partition migration (net-new playbook runs
will have `job_created` values that *exactly match* the related
`UnifiedJob.created` field.
This commit is contained in:
Ryan Petrello 2021-02-25 17:51:29 -05:00 committed by Jim Ladd
parent 1e45e2ab9b
commit 08b96a0bd7
4 changed files with 41 additions and 11 deletions

View File

@ -175,9 +175,6 @@ from awx.api.views.root import ( # noqa
from awx.api.views.webhooks import WebhookKeyView, GithubWebhookReceiver, GitlabWebhookReceiver # noqa
EPOCH = datetime.datetime.utcfromtimestamp(0)
logger = logging.getLogger('awx.api.views')
@ -889,7 +886,7 @@ class ProjectUpdateEventsList(SubListAPIView):
def get_queryset(self):
return super(ProjectUpdateEventsList, self).get_queryset().filter(
job_created__in=(self.get_parent_object().created, EPOCH)
job_created=self.get_parent_object().created_or_epoch
)
class SystemJobEventsList(SubListAPIView):
@ -907,7 +904,7 @@ class SystemJobEventsList(SubListAPIView):
def get_queryset(self):
return super(SystemJobEventsList, self).get_queryset().filter(
job_created__in=(self.get_parent_object().created, EPOCH)
job_created=self.get_parent_object().created_or_epoch
)
class ProjectUpdateCancel(RetrieveAPIView):
@ -3828,7 +3825,7 @@ class JobJobEventsList(BaseJobEventsList):
job = self.get_parent_object()
self.check_parent_access(job)
qs = job.job_events.filter(
job_created__in=(job.created, EPOCH)
job_created=self.get_parent_object().created_or_epoch
).select_related('host').order_by('start_line')
return qs.all()
@ -4011,7 +4008,7 @@ class BaseAdHocCommandEventsList(NoTruncateMixin, SubListAPIView):
def get_queryset(self):
return super(BaseAdHocCommandEventsList, self).get_queryset().filter(
job_created__in=(self.get_parent_object().created, EPOCH)
job_created=self.get_parent_object().created_or_epoch
)

View File

@ -41,8 +41,6 @@ from awx.api.views.mixin import RelatedJobsPreventDeleteMixin, ControlledByScmMi
logger = logging.getLogger('awx.api.views.organization')
EPOCH = datetime.datetime.utcfromtimestamp(0)
class InventoryUpdateEventsList(SubListAPIView):
@ -55,7 +53,7 @@ class InventoryUpdateEventsList(SubListAPIView):
def get_queryset(self):
return super(InventoryUpdateEventsList, self).get_queryset().filter(
job_created__in=(self.get_parent_object().created, EPOCH)
job_created=self.get_parent_object().created_or_epoch
)
def finalize_response(self, request, response, *args, **kwargs):

View File

@ -49,6 +49,7 @@ from awx.main.utils import (
getattr_dne,
polymorphic,
schedule_task_manager,
get_event_partition_epoch
)
from awx.main.constants import ACTIVE_STATES, CAN_CANCEL
from awx.main.redact import UriCleaner, REPLACE_STR
@ -735,6 +736,18 @@ class UnifiedJob(
def _get_task_class(cls):
raise NotImplementedError # Implement in subclasses.
@property
def created_or_epoch(self):
# returns self.created *unless* the job was created *prior*
# to the datetime the event partition migration is applied
# (in that case, it returns the epoch, which is the date
# which is automatically applied to all events rows that predate
# that migration)
applied = get_event_partition_epoch()
if applied and self.created < applied:
return datetime.datetime.utcfromtimestamp(0)
return self.created
@property
def can_run_containerized(self):
return False

View File

@ -90,7 +90,7 @@ __all__ = [
'create_temporary_fifo',
'truncate_stdout',
'deepmerge',
'cleanup_new_process',
'get_event_partition_epoch',
]
@ -208,6 +208,28 @@ def memoize_delete(function_name):
return cache.delete(function_name)
@memoize(ttl=3600 * 24) # in practice, we only need this to load once at process startup time
def get_event_partition_epoch():
from django.db.migrations.recorder import MigrationRecorder
return MigrationRecorder.Migration.objects.filter(
app='main', name='0124_event_partitions'
).first().applied
@memoize()
def get_ansible_version():
"""
Return Ansible version installed.
Ansible path needs to be provided to account for custom virtual environments
"""
try:
proc = subprocess.Popen(['ansible', '--version'], stdout=subprocess.PIPE)
result = smart_str(proc.communicate()[0])
return result.split('\n')[0].replace('ansible', '').strip()
except Exception:
return 'unknown'
def get_awx_version():
"""
Return AWX version as reported by setuptools.