Add an "only_run_on" option to consistency checks so you can limit a check to running on particular days of the week (#785).
This commit is contained in:
parent
ebde88ccaa
commit
593c956d33
5 changed files with 196 additions and 3 deletions
3
NEWS
3
NEWS
|
@ -1,4 +1,7 @@
|
|||
1.8.13.dev0
|
||||
* #785: Add an "only_run_on" option to consistency checks so you can limit a check to running on
|
||||
particular days of the week. See the documentation for more information:
|
||||
https://torsion.org/borgmatic/docs/how-to/deal-with-very-large-backups/#check-days
|
||||
* #886: Fix a PagerDuty hook traceback with Python < 3.10.
|
||||
* #889: Fix the Healthchecks ping body size limit, restoring it to the documented 100,000 bytes.
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import calendar
|
||||
import datetime
|
||||
import hashlib
|
||||
import itertools
|
||||
|
@ -99,12 +100,17 @@ def parse_frequency(frequency):
|
|||
raise ValueError(f"Could not parse consistency check frequency '{frequency}'")
|
||||
|
||||
|
||||
WEEKDAY_DAYS = calendar.day_name[0:5]
|
||||
WEEKEND_DAYS = calendar.day_name[5:7]
|
||||
|
||||
|
||||
def filter_checks_on_frequency(
|
||||
config,
|
||||
borg_repository_id,
|
||||
checks,
|
||||
force,
|
||||
archives_check_id=None,
|
||||
datetime_now=datetime.datetime.now,
|
||||
):
|
||||
'''
|
||||
Given a configuration dict with a "checks" sequence of dicts, a Borg repository ID, a sequence
|
||||
|
@ -143,6 +149,29 @@ def filter_checks_on_frequency(
|
|||
if checks and check not in checks:
|
||||
continue
|
||||
|
||||
only_run_on = check_config.get('only_run_on')
|
||||
if only_run_on:
|
||||
# Use a dict instead of a set to preserve ordering.
|
||||
days = dict.fromkeys(only_run_on)
|
||||
|
||||
if 'weekday' in days:
|
||||
days = {
|
||||
**dict.fromkeys(day for day in days if day != 'weekday'),
|
||||
**dict.fromkeys(WEEKDAY_DAYS),
|
||||
}
|
||||
if 'weekend' in days:
|
||||
days = {
|
||||
**dict.fromkeys(day for day in days if day != 'weekend'),
|
||||
**dict.fromkeys(WEEKEND_DAYS),
|
||||
}
|
||||
|
||||
if calendar.day_name[datetime_now().weekday()] not in days:
|
||||
logger.info(
|
||||
f"Skipping {check} check due to day of the week; check only runs on {'/'.join(days)} (use --force to check anyway)"
|
||||
)
|
||||
filtered_checks.remove(check)
|
||||
continue
|
||||
|
||||
frequency_delta = parse_frequency(check_config.get('frequency'))
|
||||
if not frequency_delta:
|
||||
continue
|
||||
|
@ -153,8 +182,8 @@ def filter_checks_on_frequency(
|
|||
|
||||
# If we've not yet reached the time when the frequency dictates we're ready for another
|
||||
# check, skip this check.
|
||||
if datetime.datetime.now() < check_time + frequency_delta:
|
||||
remaining = check_time + frequency_delta - datetime.datetime.now()
|
||||
if datetime_now() < check_time + frequency_delta:
|
||||
remaining = check_time + frequency_delta - datetime_now()
|
||||
logger.info(
|
||||
f'Skipping {check} check due to configured frequency; {remaining} until next check (use --force to check anyway)'
|
||||
)
|
||||
|
|
|
@ -546,6 +546,20 @@ properties:
|
|||
"always": running this check every time checks
|
||||
are run.
|
||||
example: 2 weeks
|
||||
only_run_on:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: |
|
||||
After the "frequency" duration has elapsed, only
|
||||
run this check if the current day of the week
|
||||
matches one of these values (the name of a day of
|
||||
the week in the current locale). "weekday" and
|
||||
"weekend" are also accepted. Defaults to running
|
||||
the check on any day of the week.
|
||||
example:
|
||||
- Saturday
|
||||
- Sunday
|
||||
- required: [name]
|
||||
additionalProperties: false
|
||||
properties:
|
||||
|
@ -579,6 +593,20 @@ properties:
|
|||
"always": running this check every time checks
|
||||
are run.
|
||||
example: 2 weeks
|
||||
only_run_on:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: |
|
||||
After the "frequency" duration has elapsed, only
|
||||
run this check if the current day of the week
|
||||
matches one of these values (the name of a day of
|
||||
the week in the current locale). "weekday" and
|
||||
"weekend" are also accepted. Defaults to running
|
||||
the check on any day of the week.
|
||||
example:
|
||||
- Saturday
|
||||
- Sunday
|
||||
max_duration:
|
||||
type: integer
|
||||
description: |
|
||||
|
@ -627,6 +655,20 @@ properties:
|
|||
"always": running this check every time checks
|
||||
are run.
|
||||
example: 2 weeks
|
||||
only_run_on:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: |
|
||||
After the "frequency" duration has elapsed, only
|
||||
run this check if the current day of the week
|
||||
matches one of these values (the name of a day of
|
||||
the week in the current locale). "weekday" and
|
||||
"weekend" are also accepted. Defaults to running
|
||||
the check on any day of the week.
|
||||
example:
|
||||
- Saturday
|
||||
- Sunday
|
||||
count_tolerance_percentage:
|
||||
type: number
|
||||
description: |
|
||||
|
|
|
@ -242,6 +242,57 @@ check --force` runs `check` even if it's specified in the `skip_actions`
|
|||
option.
|
||||
|
||||
|
||||
### Check days
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 1.8.13</span> You can
|
||||
optionally configure checks to only run on particular days of the week. For
|
||||
instance:
|
||||
|
||||
```yaml
|
||||
checks:
|
||||
- name: repository
|
||||
only_run_on:
|
||||
- Saturday
|
||||
- Sunday
|
||||
- name: archives
|
||||
only_run_on:
|
||||
- weekday
|
||||
- name: spot
|
||||
only_run_on:
|
||||
- Friday
|
||||
- weekend
|
||||
```
|
||||
|
||||
Each day of the week is specified in the current locale (system
|
||||
language/country settings). `weekend` and `weekday` are also accepted.
|
||||
|
||||
Just like with `frequency`, borgmatic only makes a best effort to run checks
|
||||
on the given day of the week. For instance, if you run `borgmatic check`
|
||||
daily, then every day borgmatic will have an opportunity to determine whether
|
||||
your checks are configured to run on that day. If they are, then the checks
|
||||
run. If not, they are skipped.
|
||||
|
||||
For instance, with the above configuration, if borgmatic is run on a Saturday,
|
||||
the `repository` check will run. But on a Monday? The repository check will
|
||||
get skipped. And if borgmatic is never run on a Saturday or a Sunday, that
|
||||
check will never get a chance to run.
|
||||
|
||||
Also, the day of the week configuration applies *after* any configured
|
||||
`frequency` for a check. So for instance, imagine the following configuration:
|
||||
|
||||
```yaml
|
||||
checks:
|
||||
- name: repository
|
||||
frequency: 2 weeks
|
||||
only_run_on:
|
||||
- Monday
|
||||
```
|
||||
|
||||
If you run borgmatic daily with that configuration, then borgmatic will first
|
||||
wait two weeks after the previous check before running the check again—on the
|
||||
first Monday after the `frequency` duration elapses.
|
||||
|
||||
|
||||
### Running only checks
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 1.7.1</span> If you
|
||||
|
|
|
@ -113,6 +113,74 @@ def test_filter_checks_on_frequency_retains_check_without_frequency():
|
|||
) == ('archives',)
|
||||
|
||||
|
||||
def test_filter_checks_on_frequency_retains_check_with_empty_only_run_on():
|
||||
flexmock(module).should_receive('parse_frequency').and_return(None)
|
||||
|
||||
assert module.filter_checks_on_frequency(
|
||||
config={'checks': [{'name': 'archives', 'only_run_on': []}]},
|
||||
borg_repository_id='repo',
|
||||
checks=('archives',),
|
||||
force=False,
|
||||
archives_check_id='1234',
|
||||
datetime_now=flexmock(weekday=lambda: 0),
|
||||
) == ('archives',)
|
||||
|
||||
|
||||
def test_filter_checks_on_frequency_retains_check_with_only_run_on_matching_today():
|
||||
flexmock(module).should_receive('parse_frequency').and_return(None)
|
||||
|
||||
assert module.filter_checks_on_frequency(
|
||||
config={'checks': [{'name': 'archives', 'only_run_on': [module.calendar.day_name[0]]}]},
|
||||
borg_repository_id='repo',
|
||||
checks=('archives',),
|
||||
force=False,
|
||||
archives_check_id='1234',
|
||||
datetime_now=flexmock(weekday=lambda: 0),
|
||||
) == ('archives',)
|
||||
|
||||
|
||||
def test_filter_checks_on_frequency_retains_check_with_only_run_on_matching_today_via_weekday_value():
|
||||
flexmock(module).should_receive('parse_frequency').and_return(None)
|
||||
|
||||
assert module.filter_checks_on_frequency(
|
||||
config={'checks': [{'name': 'archives', 'only_run_on': ['weekday']}]},
|
||||
borg_repository_id='repo',
|
||||
checks=('archives',),
|
||||
force=False,
|
||||
archives_check_id='1234',
|
||||
datetime_now=flexmock(weekday=lambda: 0),
|
||||
) == ('archives',)
|
||||
|
||||
|
||||
def test_filter_checks_on_frequency_retains_check_with_only_run_on_matching_today_via_weekend_value():
|
||||
flexmock(module).should_receive('parse_frequency').and_return(None)
|
||||
|
||||
assert module.filter_checks_on_frequency(
|
||||
config={'checks': [{'name': 'archives', 'only_run_on': ['weekend']}]},
|
||||
borg_repository_id='repo',
|
||||
checks=('archives',),
|
||||
force=False,
|
||||
archives_check_id='1234',
|
||||
datetime_now=flexmock(weekday=lambda: 6),
|
||||
) == ('archives',)
|
||||
|
||||
|
||||
def test_filter_checks_on_frequency_skips_check_with_only_run_on_not_matching_today():
|
||||
flexmock(module).should_receive('parse_frequency').and_return(None)
|
||||
|
||||
assert (
|
||||
module.filter_checks_on_frequency(
|
||||
config={'checks': [{'name': 'archives', 'only_run_on': [module.calendar.day_name[5]]}]},
|
||||
borg_repository_id='repo',
|
||||
checks=('archives',),
|
||||
force=False,
|
||||
archives_check_id='1234',
|
||||
datetime_now=flexmock(weekday=lambda: 0),
|
||||
)
|
||||
== ()
|
||||
)
|
||||
|
||||
|
||||
def test_filter_checks_on_frequency_retains_check_with_elapsed_frequency():
|
||||
flexmock(module).should_receive('parse_frequency').and_return(
|
||||
module.datetime.timedelta(hours=1)
|
||||
|
@ -168,7 +236,7 @@ def test_filter_checks_on_frequency_skips_check_with_unelapsed_frequency():
|
|||
)
|
||||
|
||||
|
||||
def test_filter_checks_on_frequency_restains_check_with_unelapsed_frequency_and_force():
|
||||
def test_filter_checks_on_frequency_retains_check_with_unelapsed_frequency_and_force():
|
||||
assert module.filter_checks_on_frequency(
|
||||
config={'checks': [{'name': 'archives', 'frequency': '1 hour'}]},
|
||||
borg_repository_id='repo',
|
||||
|
|
Loading…
Reference in a new issue