Browse Source

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

fix prune tests, prune in localtime
TW 2 năm trước cách đây
mục cha
commit
57e0579cf8

+ 8 - 10
src/borg/archiver/prune_cmd.py

@@ -53,10 +53,8 @@ def prune_split(archives, rule, n, kept_because=None):
 
 
     a = None
     a = None
     for a in sorted(archives, key=attrgetter("ts"), reverse=True):
     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:
         if period != last:
             last = period
             last = period
             if a.id not in kept_because:
             if a.id not in kept_because:
@@ -216,12 +214,12 @@ class PruneMixIn:
         up to 7 most recent days with backups (days without backups do not count).
         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
         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
         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.
         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
         The ``--keep-last N`` option is doing the same as ``--keep-secondly N`` (and it will

+ 28 - 17
src/borg/testsuite/helpers.py

@@ -405,6 +405,17 @@ class MockArchive:
         return f"{self.id}: {self.ts.isoformat()}"
         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(
 @pytest.mark.parametrize(
     "rule,num_to_keep,expected_ids",
     "rule,num_to_keep,expected_ids",
     [
     [
@@ -424,23 +435,23 @@ def test_prune_split(rule, num_to_keep, expected_ids):
 
 
     archives = [
     archives = [
         # years apart
         # 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
         # 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
         # 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
         # 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
         # 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 = {}
     kept_because = {}
     keep = prune_split(archives, rule, num_to_keep, kept_because)
     keep = prune_split(archives, rule, num_to_keep, kept_because)
@@ -456,12 +467,12 @@ def test_prune_split_keep_oldest():
 
 
     archives = [
     archives = [
         # oldest backup, but not last in its year
         # 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
         # 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
         # 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
     # Keep oldest when retention target can't otherwise be met