More precise handling of schedule computed fields no-ops

Do not set a next_run value for disabled schedules
Bail if no fields are changed
Do not update related template if its fields did not change

Change call pattern to schedule.update_computed_fields()
in doing so, fix bug where template does not pick up schedule
  due to schedules next_run not being saved

Handle the case (also a bug) where template was not updated
  when schedule was deleted
This commit is contained in:
AlanCoding
2019-05-20 20:47:28 -04:00
parent 9a1b205e06
commit 40f9d0b512
4 changed files with 185 additions and 29 deletions

View File

@@ -227,15 +227,23 @@ class Schedule(PrimordialModel, LaunchTimeConfig):
job_kwargs['_eager_fields'] = {'launch_type': 'scheduled', 'schedule': self}
return job_kwargs
def update_computed_fields(self):
future_rs = Schedule.rrulestr(self.rrule)
next_run_actual = future_rs.after(now())
def update_computed_fields_no_save(self):
affects_fields = ['next_run', 'dtstart', 'dtend']
starting_values = {}
for field_name in affects_fields:
starting_values[field_name] = getattr(self, field_name)
if next_run_actual is not None:
if not datetime_exists(next_run_actual):
# skip imaginary dates, like 2:30 on DST boundaries
next_run_actual = future_rs.after(next_run_actual)
next_run_actual = next_run_actual.astimezone(pytz.utc)
future_rs = Schedule.rrulestr(self.rrule)
if self.enabled:
next_run_actual = future_rs.after(now())
if next_run_actual is not None:
if not datetime_exists(next_run_actual):
# skip imaginary dates, like 2:30 on DST boundaries
next_run_actual = future_rs.after(next_run_actual)
next_run_actual = next_run_actual.astimezone(pytz.utc)
else:
next_run_actual = None
self.next_run = next_run_actual
try:
@@ -248,11 +256,38 @@ class Schedule(PrimordialModel, LaunchTimeConfig):
self.dtend = future_rs[-1].astimezone(pytz.utc)
except IndexError:
self.dtend = None
changed = any(getattr(self, field_name) != starting_values[field_name] for field_name in affects_fields)
return changed
def update_computed_fields(self):
changed = self.update_computed_fields_no_save()
if not changed:
return
emit_channel_notification('schedules-changed', dict(id=self.id, group_name='schedules'))
# Must save self here before calling unified_job_template computed fields
# in order for that method to be correct
# by adding modified to update fields, we avoid updating modified time
super(Schedule, self).save(update_fields=['next_run', 'dtstart', 'dtend', 'modified'])
with ignore_inventory_computed_fields():
self.unified_job_template.update_computed_fields()
def save(self, *args, **kwargs):
self.update_computed_fields()
self.rrule = Schedule.coerce_naive_until(self.rrule)
changed = self.update_computed_fields_no_save()
if changed and 'update_fields' in kwargs:
for field_name in ['next_run', 'dtstart', 'dtend']:
if field_name not in kwargs['update_fields']:
kwargs['update_fields'].append(field_name)
super(Schedule, self).save(*args, **kwargs)
if changed:
with ignore_inventory_computed_fields():
self.unified_job_template.update_computed_fields()
def delete(self, *args, **kwargs):
ujt = self.unified_job_template
r = super(Schedule, self).delete(*args, **kwargs)
if ujt:
with ignore_inventory_computed_fields():
ujt.update_computed_fields()
return r