Merge lp:~mterry/duplicity/manifest-oddities into lp:duplicity/0.6

Proposed by Michael Terry
Status: Merged
Merged at revision: 934
Proposed branch: lp:~mterry/duplicity/manifest-oddities
Merge into: lp:duplicity/0.6
Diff against target: 64 lines (+37/-1)
2 files modified
duplicity/manifest.py (+12/-1)
testing/tests/restarttest.py (+25/-0)
To merge this branch: bzr merge lp:~mterry/duplicity/manifest-oddities
Reviewer Review Type Date Requested Status
duplicity-team Pending
Review via email: mp+195460@code.launchpad.net

Description of the change

We may accidentally end up with an oddly inconsistent manifest like so:

Volume 1
Volume 2
Volume 3
Volume 2

As did get reported recently on the mailing list: http://lists.nongnu.org/archive/html/duplicity-talk/2013-11/msg00009.html

One way this can happen (the only way?) is if you back up, then duplicity gets interrupted between writing the manifest and uploading the volume. Then, when restarted, there is no longer enough data to create as many volumes as existed previously.

This situation can cause an exception when trying to restart the backup.

This branch fixes it by deleting any excess volume information encountered when loading in the manifest. We discard volume with higher numbers than the last one read.

To post a comment you must log in.
932. By Michael Terry

Fix test comments

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'duplicity/manifest.py'
--- duplicity/manifest.py 2011-06-17 06:21:42 +0000
+++ duplicity/manifest.py 2013-11-16 02:23:17 +0000
@@ -177,12 +177,23 @@
177 next_vi_string_regexp = re.compile("(^|\\n)(volume\\s.*?)"177 next_vi_string_regexp = re.compile("(^|\\n)(volume\\s.*?)"
178 "(\\nvolume\\s|$)", re.I | re.S)178 "(\\nvolume\\s|$)", re.I | re.S)
179 starting_s_index = 0179 starting_s_index = 0
180 highest_vol = 0
181 latest_vol = 0
180 while 1:182 while 1:
181 match = next_vi_string_regexp.search(s[starting_s_index:])183 match = next_vi_string_regexp.search(s[starting_s_index:])
182 if not match:184 if not match:
183 break185 break
184 self.add_volume_info(VolumeInfo().from_string(match.group(2)))186 vi = VolumeInfo().from_string(match.group(2))
187 self.add_volume_info(vi)
188 highest_vol = max(highest_vol, vi.volume_number)
189 latest_vol = vi.volume_number
185 starting_s_index += match.end(2)190 starting_s_index += match.end(2)
191 # If we restarted after losing some remote volumes, the highest volume
192 # seen may be higher than the last volume recorded. That is, the
193 # manifest could contain "vol1, vol2, vol3, vol2." If so, we don't
194 # want to keep vol3's info.
195 for i in range(latest_vol + 1, highest_vol + 1):
196 self.del_volume_info(i)
186 return self197 return self
187198
188 def __eq__(self, other):199 def __eq__(self, other):
189200
=== modified file 'testing/tests/restarttest.py'
--- testing/tests/restarttest.py 2013-01-07 16:13:29 +0000
+++ testing/tests/restarttest.py 2013-11-16 02:23:17 +0000
@@ -446,6 +446,31 @@
446 assert not os.system("diff %s/file1 testfiles/restore_out/file1" % source)446 assert not os.system("diff %s/file1 testfiles/restore_out/file1" % source)
447 assert not os.system("diff %s/z testfiles/restore_out/z" % source)447 assert not os.system("diff %s/z testfiles/restore_out/z" % source)
448448
449 def test_changed_source_dangling_manifest_volume(self):
450 """
451 If we restart but find remote volumes missing, we can easily end up
452 with a manifest that lists "vol1, vol2, vol3, vol2", leaving a dangling
453 vol3. Make sure we can gracefully handle that. This will only happen
454 if the source data changes to be small enough to not create a vol3 on
455 restart.
456 """
457 source = 'testfiles/largefiles'
458 self.make_largefiles(count=5, size=1)
459 # intentionally interrupt initial backup
460 try:
461 self.backup("full", source, options = ["--vol 1", "--fail 3"])
462 self.fail()
463 except CmdError, e:
464 self.assertEqual(30, e.exit_status)
465 # now delete the last volume on remote end and some source files
466 assert not os.system("rm testfiles/output/duplicity-full*vol3.difftar*")
467 assert not os.system("rm %s/file[2345]" % source)
468 assert not os.system("echo hello > %s/z" % source)
469 # finish backup
470 self.backup("full", source)
471 # and verify we can restore
472 self.restore()
473
449474
450# Note that this class duplicates all the tests in RestartTest475# Note that this class duplicates all the tests in RestartTest
451class RestartTestWithoutEncryption(RestartTest):476class RestartTestWithoutEncryption(RestartTest):

Subscribers

People subscribed via source and target branches

to all changes: