1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-02-26 07:53:58 +00:00

Merge pull request #7284 from ThomasWaldmann/prune-in-localtime

fix prune tests, prune in localtime
This commit is contained in:
TW 2023-01-19 01:31:49 +01:00 committed by GitHub
commit 57e0579cf8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 27 deletions

View file

@ -53,10 +53,8 @@ def prune_split(archives, rule, n, kept_because=None):
a = None
for a in sorted(archives, key=attrgetter("ts"), reverse=True):
# we compute the pruning in UTC time zone
# note: we used to compute the pruning in local timezone (tz=None),
# but this is causing test failures in some time zones (like e.g. UTC+12).
period = a.ts.astimezone(tz=timezone.utc).strftime(pattern)
# we compute the pruning in local time zone
period = a.ts.astimezone().strftime(pattern)
if period != last:
last = period
if a.id not in kept_because:
@ -216,12 +214,12 @@ def build_parser_prune(self, subparsers, common_parser, mid_common_parser):
up to 7 most recent days with backups (days without backups do not count).
The rules are applied from secondly to yearly, and backups selected by previous
rules do not count towards those of later rules. The time that each backup
starts is used for pruning purposes. Dates and times are interpreted in
the local timezone, and weeks go from Monday to Sunday. Specifying a
negative number of archives to keep means that there is no limit. As of borg
1.2.0, borg will retain the oldest archive if any of the secondly, minutely,
hourly, daily, weekly, monthly, or yearly rules was not otherwise able to meet
its retention target. This enables the first chronological archive to continue
starts is used for pruning purposes. Dates and times are interpreted in the local
timezone of the system where borg prune runs, and weeks go from Monday to Sunday.
Specifying a negative number of archives to keep means that there is no limit.
As of borg 1.2.0, borg will retain the oldest archive if any of the secondly,
minutely, hourly, daily, weekly, monthly, or yearly rules was not otherwise able to
meet its retention target. This enables the first chronological archive to continue
aging until it is replaced by a newer archive that meets the retention criteria.
The ``--keep-last N`` option is doing the same as ``--keep-secondly N`` (and it will

View file

@ -405,6 +405,17 @@ def __repr__(self):
return f"{self.id}: {self.ts.isoformat()}"
# This is the local timezone of the system running the tests.
# We need this e.g. to construct archive timestamps for the prune tests,
# because borg prune operates in the local timezone (it first converts the
# archive timestamp to the local timezone). So, if we want the y/m/d/h/m/s
# values which prune uses to be exactly the ones we give [and NOT shift them
# by tzoffset], we need to give the timestamps in the same local timezone.
# Please note that the timestamps in a real borg archive or manifest are
# stored in UTC timezone.
local_tz = datetime.now(tz=timezone.utc).astimezone(tz=None).tzinfo
@pytest.mark.parametrize(
"rule,num_to_keep,expected_ids",
[
@ -424,23 +435,23 @@ def subset(lst, ids):
archives = [
# years apart
MockArchive(datetime(2015, 1, 1, 10, 0, 0, tzinfo=timezone.utc), 1),
MockArchive(datetime(2016, 1, 1, 10, 0, 0, tzinfo=timezone.utc), 2),
MockArchive(datetime(2017, 1, 1, 10, 0, 0, tzinfo=timezone.utc), 3),
MockArchive(datetime(2015, 1, 1, 10, 0, 0, tzinfo=local_tz), 1),
MockArchive(datetime(2016, 1, 1, 10, 0, 0, tzinfo=local_tz), 2),
MockArchive(datetime(2017, 1, 1, 10, 0, 0, tzinfo=local_tz), 3),
# months apart
MockArchive(datetime(2017, 2, 1, 10, 0, 0, tzinfo=timezone.utc), 4),
MockArchive(datetime(2017, 3, 1, 10, 0, 0, tzinfo=timezone.utc), 5),
MockArchive(datetime(2017, 2, 1, 10, 0, 0, tzinfo=local_tz), 4),
MockArchive(datetime(2017, 3, 1, 10, 0, 0, tzinfo=local_tz), 5),
# days apart
MockArchive(datetime(2017, 3, 2, 10, 0, 0, tzinfo=timezone.utc), 6),
MockArchive(datetime(2017, 3, 3, 10, 0, 0, tzinfo=timezone.utc), 7),
MockArchive(datetime(2017, 3, 4, 10, 0, 0, tzinfo=timezone.utc), 8),
MockArchive(datetime(2017, 3, 2, 10, 0, 0, tzinfo=local_tz), 6),
MockArchive(datetime(2017, 3, 3, 10, 0, 0, tzinfo=local_tz), 7),
MockArchive(datetime(2017, 3, 4, 10, 0, 0, tzinfo=local_tz), 8),
# minutes apart
MockArchive(datetime(2017, 10, 1, 9, 45, 0, tzinfo=timezone.utc), 9),
MockArchive(datetime(2017, 10, 1, 9, 55, 0, tzinfo=timezone.utc), 10),
MockArchive(datetime(2017, 10, 1, 9, 45, 0, tzinfo=local_tz), 9),
MockArchive(datetime(2017, 10, 1, 9, 55, 0, tzinfo=local_tz), 10),
# seconds apart
MockArchive(datetime(2017, 10, 1, 10, 0, 1, tzinfo=timezone.utc), 11),
MockArchive(datetime(2017, 10, 1, 10, 0, 3, tzinfo=timezone.utc), 12),
MockArchive(datetime(2017, 10, 1, 10, 0, 5, tzinfo=timezone.utc), 13),
MockArchive(datetime(2017, 10, 1, 10, 0, 1, tzinfo=local_tz), 11),
MockArchive(datetime(2017, 10, 1, 10, 0, 3, tzinfo=local_tz), 12),
MockArchive(datetime(2017, 10, 1, 10, 0, 5, tzinfo=local_tz), 13),
]
kept_because = {}
keep = prune_split(archives, rule, num_to_keep, kept_because)
@ -456,12 +467,12 @@ def subset(lst, ids):
archives = [
# oldest backup, but not last in its year
MockArchive(datetime(2018, 1, 1, 10, 0, 0, tzinfo=timezone.utc), 1),
MockArchive(datetime(2018, 1, 1, 10, 0, 0, tzinfo=local_tz), 1),
# an interim backup
MockArchive(datetime(2018, 12, 30, 10, 0, 0, tzinfo=timezone.utc), 2),
MockArchive(datetime(2018, 12, 30, 10, 0, 0, tzinfo=local_tz), 2),
# year end backups
MockArchive(datetime(2018, 12, 31, 10, 0, 0, tzinfo=timezone.utc), 3),
MockArchive(datetime(2019, 12, 31, 10, 0, 0, tzinfo=timezone.utc), 4),
MockArchive(datetime(2018, 12, 31, 10, 0, 0, tzinfo=local_tz), 3),
MockArchive(datetime(2019, 12, 31, 10, 0, 0, tzinfo=local_tz), 4),
]
# Keep oldest when retention target can't otherwise be met