Browse Source

keyfile conversion code

Antoine Beaupré 9 years ago
parent
commit
9ab1e1961e
1 changed files with 69 additions and 15 deletions
  1. 69 15
      borg/testsuite/convert.py

+ 69 - 15
borg/testsuite/convert.py

@@ -5,16 +5,27 @@ import shutil
 import tempfile
 import tempfile
 
 
 import attic.repository
 import attic.repository
+import attic.key
+import attic.helpers
 
 
-from ..helpers import IntegrityError
+from ..helpers import IntegrityError, get_keys_dir
 from ..repository import Repository, MAGIC
 from ..repository import Repository, MAGIC
+from ..key import KeyfileKey, KeyfileNotFoundError
 from . import BaseTestCase
 from . import BaseTestCase
 
 
 class NotImplementedException(Exception):
 class NotImplementedException(Exception):
     pass
     pass
 
 
+class AtticKeyfileKey(KeyfileKey):
+    '''backwards compatible Attick key file parser'''
+    FILE_ID = 'ATTIC KEY'
+
 class ConversionTestCase(BaseTestCase):
 class ConversionTestCase(BaseTestCase):
 
 
+    class MockArgs:
+        def __init__(self, path):
+            self.repository = attic.helpers.Location(path)
+
     def open(self, path, repo_type  = Repository, create=False):
     def open(self, path, repo_type  = Repository, create=False):
         return repo_type(os.path.join(path, 'repository'), create = create)
         return repo_type(os.path.join(path, 'repository'), create = create)
 
 
@@ -26,6 +37,10 @@ class ConversionTestCase(BaseTestCase):
         # throw some stuff in that repo, copied from `RepositoryTestCase.test1`_
         # throw some stuff in that repo, copied from `RepositoryTestCase.test1`_
         for x in range(100):
         for x in range(100):
             self.attic_repo.put(('%-32d' % x).encode('ascii'), b'SOMEDATA')
             self.attic_repo.put(('%-32d' % x).encode('ascii'), b'SOMEDATA')
+        self.keysdir = self.MockArgs(self.tmppath)
+        os.environ['ATTIC_KEYS_DIR'] = self.tmppath
+        os.environ['ATTIC_PASSPHRASE'] = 'test'
+        self.key = attic.key.KeyfileKey.create(self.attic_repo, self.keysdir)
         self.attic_repo.close()
         self.attic_repo.close()
 
 
     def test_convert(self):
     def test_convert(self):
@@ -33,9 +48,15 @@ class ConversionTestCase(BaseTestCase):
         # check should fail because of magic number
         # check should fail because of magic number
         assert not self.repository.check() # can't check raises() because check() handles the error
         assert not self.repository.check() # can't check raises() because check() handles the error
         self.repository.close()
         self.repository.close()
+        os.environ['BORG_KEYS_DIR'] = self.tmppath
         self.convert()
         self.convert()
+        # check that the new keyfile is alright
+        keyfile = os.path.join(get_keys_dir(),
+                               os.path.basename(self.key.path))
+        with open(keyfile, 'r') as f:
+            assert f.read().startswith(KeyfileKey.FILE_ID)
         self.repository = self.open(self.tmppath)
         self.repository = self.open(self.tmppath)
-        assert self.repository.check() # can't check raises() because check() handles the error
+        assert self.repository.check()
         self.repository.close()
         self.repository.close()
 
 
     def convert(self):
     def convert(self):
@@ -45,32 +66,52 @@ class ConversionTestCase(BaseTestCase):
         important to least important: segments, key files, and various
         important to least important: segments, key files, and various
         caches, the latter being optional, as they will be rebuilt if
         caches, the latter being optional, as they will be rebuilt if
         missing.'''
         missing.'''
-        self.convert_segments()
-        with pytest.raises(NotImplementedException):
-            self.convert_keyfiles()
+        self.repository = self.open(self.tmppath)
+        segments = [ filename for i, filename in self.repository.io.segment_iterator() ]
+        try:
+            keyfile = self.find_attic_keyfile()
+        except KeyfileNotFoundError:
+            print("no key file found for repository, not converting")
+        else:
+            self.convert_keyfiles(keyfile)
+        self.repository.close()
+        self.convert_segments(segments)
         with pytest.raises(NotImplementedException):
         with pytest.raises(NotImplementedException):
             self.convert_cache()
             self.convert_cache()
 
 
-    def convert_segments(self):
+    def convert_segments(self, segments):
         '''convert repository segments from attic to borg
         '''convert repository segments from attic to borg
 
 
         replacement pattern is `s/ATTICSEG/BORG_SEG/` in files in
         replacement pattern is `s/ATTICSEG/BORG_SEG/` in files in
         `$ATTIC_REPO/data/**`.
         `$ATTIC_REPO/data/**`.
 
 
         luckily the segment length didn't change so we can just
         luckily the segment length didn't change so we can just
-        replace the 8 first bytes of all regular files in there.
-
-        `Repository.segment_iterator()` could be used here.'''
-        self.repository = self.open(self.tmppath)
-        segs = [ filename for i, filename in self.repository.io.segment_iterator() ]
-        self.repository.close()
-        for filename in segs:
+        replace the 8 first bytes of all regular files in there.'''
+        for filename in segments:
             print("converting segment %s..." % filename)
             print("converting segment %s..." % filename)
             with open(filename, 'r+b') as segment:
             with open(filename, 'r+b') as segment:
                 segment.seek(0)
                 segment.seek(0)
                 segment.write(MAGIC)
                 segment.write(MAGIC)
 
 
-    def convert_keyfiles(self):
+    def find_attic_keyfile(self):
+        '''find the attic keyfiles
+
+        this is expected to look into $HOME/.attic/keys or
+        $ATTIC_KEYS_DIR for key files matching the given Borg
+        repository.
+
+        it is expected to raise an exception (KeyfileNotFoundError) if
+        no key is found. whether that exception is from Borg or Attic
+        is unclear.
+
+        this is split in a separate function in case we want to
+        reimplement the attic code here.
+        '''
+        self.repository._location = attic.helpers.Location(self.tmppath)
+        return attic.key.KeyfileKey().find_key_file(self.repository)
+
+    def convert_keyfiles(self, keyfile):
+
         '''convert key files from attic to borg
         '''convert key files from attic to borg
 
 
         replacement pattern is `s/ATTIC KEY/BORG_KEY/` in
         replacement pattern is `s/ATTIC KEY/BORG_KEY/` in
@@ -82,7 +123,20 @@ class ConversionTestCase(BaseTestCase):
         finds the keys with the right identifier for the repo, no need
         finds the keys with the right identifier for the repo, no need
         to decrypt to convert. will need to rewrite the whole key file
         to decrypt to convert. will need to rewrite the whole key file
         because magic number length changed.'''
         because magic number length changed.'''
-        raise NotImplementedException('not implemented')
+        print("converting keyfile %s" % keyfile)
+        with open(keyfile, 'r') as f:
+            data = f.read()
+        data = data.replace(AtticKeyfileKey.FILE_ID,
+                            KeyfileKey.FILE_ID,
+                            1)
+        keyfile = os.path.join(get_keys_dir(),
+                               os.path.basename(keyfile))
+        print("writing borg keyfile to %s" % keyfile)
+        with open(keyfile, 'w') as f:
+            f.write(data)
+        with open(keyfile, 'r') as f:
+            data = f.read()
+        assert data.startswith(KeyfileKey.FILE_ID)
 
 
     def convert_cache(self):
     def convert_cache(self):
         '''convert caches from attic to borg
         '''convert caches from attic to borg