Merge lp:~user3942934/duplicity/pydrive into lp:~duplicity-team/duplicity/0.7-series

Proposed by westerngateguard
Status: Merged
Merged at revision: 1054
Proposed branch: lp:~user3942934/duplicity/pydrive
Merge into: lp:~duplicity-team/duplicity/0.7-series
Diff against target: 202 lines (+149/-2)
4 files modified
bin/duplicity.1 (+40/-1)
duplicity/backend.py (+1/-1)
duplicity/backends/pydrivebackend.py (+107/-0)
duplicity/commandline.py (+1/-0)
To merge this branch: bzr merge lp:~user3942934/duplicity/pydrive
Reviewer Review Type Date Requested Status
duplicity-team Pending
Review via email: mp+246258@code.launchpad.net

Description of the change

Hello,

Currently duplicity uses gdocs backend for Google Drive backups.
gdocs uses deprecated API and don't allow backups for managed Google accounts.
(see https://bugs.launchpad.net/duplicity/+bug/1315684)

I added pydrive backend that solves both of those problems. I published it also on GitHub: https://github.com/westerngateguard/duplicity-pydrive-backend.

It works fine on my Debian (Wheezy) machine.

To post a comment you must log in.
Revision history for this message
Michael Terry (mterry) wrote :

I think you accidentally deleted some azure documentation from the man page?

Also, you use a keyfile specified by the URL. But all of the other backends tend to use environment variables for that sort of thing. Is there a reason you went with a file?

Revision history for this message
Kenneth Loafman (kenneth-loafman) wrote :

I agree with Michael on this. We should stay with environment variables
until we can go to a backup level configuration file.

On Tue, Jan 13, 2015 at 7:54 AM, Michael Terry <email address hidden>
wrote:

> I think you accidentally deleted some azure documentation from the man
> page?
>
> Also, you use a keyfile specified by the URL. But all of the other
> backends tend to use environment variables for that sort of thing. Is
> there a reason you went with a file?
> --
> https://code.launchpad.net/~user3942934/duplicity/duplicity/+merge/246258
> You are subscribed to branch lp:duplicity.
>

lp:~user3942934/duplicity/pydrive updated
1052. By Yigal Asnis <email address hidden>

Changed passing keyfile via query in URL to passing the key by environment variable. Fixed manpage accordingly. Restored Azure info in manpage that was somehow mistakenly deleted.

Revision history for this message
westerngateguard (user3942934) wrote :

Do I need resubmit the merging proposal or the commit message is enough?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/duplicity.1'
--- bin/duplicity.1 2015-01-10 19:38:59 +0000
+++ bin/duplicity.1 2015-01-14 11:12:41 +0000
@@ -149,7 +149,15 @@
149.br149.br
150(also see150(also see
151.BR "A NOTE ON SSL CERTIFICATE VERIFICATION" ).151.BR "A NOTE ON SSL CERTIFICATE VERIFICATION" ).
152152.TP
153.BR "pydrive backend"
154.B PyDrive -- a wrapper library of google-api-python-client
155- https://pypi.python.org/pypi/PyDrive
156.br
157(also see
158.BR "A NOTE ON PYDRIVE BACKEND"
159) below.
160.br
153.SH DESCRIPTION161.SH DESCRIPTION
154Duplicity incrementally backs up files and folders into162Duplicity incrementally backs up files and folders into
155tar-format volumes encrypted with GnuPG and places them to a163tar-format volumes encrypted with GnuPG and places them to a
@@ -1277,6 +1285,16 @@
1277.B alternatively1285.B alternatively
1278try lftp+webdav[s]://1286try lftp+webdav[s]://
1279.RE1287.RE
1288.PP
1289.BR "pydrive"
1290.PP
1291.RS
1292pydrive://<service account' email address>@developer.gserviceaccount.com/some_dir
1293.PP
1294See also
1295.B "A NOTE ON PYDRIVE BACKEND"
1296below.
1297.RE
12801298
1281.SH TIME FORMATS1299.SH TIME FORMATS
1282duplicity uses time strings in two places. Firstly, many of the files1300duplicity uses time strings in two places. Firstly, many of the files
@@ -1859,6 +1877,27 @@
1859.B SWIFT_AUTHVERSION1877.B SWIFT_AUTHVERSION
1860is unspecified, it will default to version 1.1878is unspecified, it will default to version 1.
18611879
1880.SH A NOTE ON PYDRIVE BACKEND
1881The pydrive backend requires Python PyDrive package to be installed on the system. See
1882.B REQUIREMENTS
1883above.
1884
1885You need to create a service account in "Google developers console" at https://console.developers.google.com
1886
1887Make sure Drive API is enabled.
1888
1889The email address of the account will be used as part of URL. See
1890.B URL FORMAT
1891above.
1892
1893Download the .p12 key file of the account and convert it to the .pem format:
1894.br
1895openssl pkcs12 -in XXX.p12 -nodes -nocerts > pydriveprivatekey.pem
1896
1897The content of .pem file should be passed to
1898.BR GOOGLE_DRIVE_ACCOUNT_KEY
1899environment variable for authentification.
1900
1862.SH A NOTE ON SYMMETRIC ENCRYPTION AND SIGNING1901.SH A NOTE ON SYMMETRIC ENCRYPTION AND SIGNING
1863Signing and symmetrically encrypt at the same time with the gpg binary on the1902Signing and symmetrically encrypt at the same time with the gpg binary on the
1864command line, as used within duplicity, is a specifically challenging issue.1903command line, as used within duplicity, is a specifically challenging issue.
18651904
=== modified file 'duplicity/backend.py'
--- duplicity/backend.py 2014-12-17 10:35:11 +0000
+++ duplicity/backend.py 2015-01-14 11:12:41 +0000
@@ -251,7 +251,7 @@
251 pu = urlparse.urlparse(url_string)251 pu = urlparse.urlparse(url_string)
252 except Exception:252 except Exception:
253 raise InvalidBackendURL("Syntax error in: %s" % url_string)253 raise InvalidBackendURL("Syntax error in: %s" % url_string)
254254
255 try:255 try:
256 self.scheme = pu.scheme256 self.scheme = pu.scheme
257 except Exception:257 except Exception:
258258
=== added file 'duplicity/backends/pydrivebackend.py'
--- duplicity/backends/pydrivebackend.py 1970-01-01 00:00:00 +0000
+++ duplicity/backends/pydrivebackend.py 2015-01-14 11:12:41 +0000
@@ -0,0 +1,107 @@
1# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
2#
3# Copyright 2015 Yigal Asnis
4#
5# This file is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the
7# Free Software Foundation; either version 2 of the License, or (at your
8# option) any later version.
9#
10# It is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with duplicity; if not, write to the Free Software Foundation,
17# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19import string
20import os
21
22import duplicity.backend
23from duplicity.errors import BackendException
24
25class PyDriveBackend(duplicity.backend.Backend):
26 """Connect to remote store using PyDrive API"""
27
28 def __init__(self, parsed_url):
29 duplicity.backend.Backend.__init__(self, parsed_url)
30 try:
31 global pydrive
32 import httplib2
33 from apiclient.discovery import build
34 from oauth2client.client import SignedJwtAssertionCredentials
35 from pydrive.auth import GoogleAuth
36 from pydrive.drive import GoogleDrive
37 except ImportError:
38 raise BackendException('PyDrive backend requires PyDrive installation'
39 'Please read the manpage to fix.')
40
41 if 'GOOGLE_DRIVE_ACCOUNT_KEY' not in os.environ:
42 raise BackendException('GOOGLE_DRIVE_ACCOUNT_KEY environment variable not set. Please read the manpage to fix.')
43 account_key = os.environ['GOOGLE_DRIVE_ACCOUNT_KEY']
44
45 credentials = SignedJwtAssertionCredentials(parsed_url.username + '@' + parsed_url.hostname, account_key, scope='https://www.googleapis.com/auth/drive')
46 credentials.authorize(httplib2.Http())
47 gauth = GoogleAuth()
48 gauth.credentials = credentials
49 self.drive = GoogleDrive(gauth)
50
51 # Dirty way to find root folder id
52 file_list = self.drive.ListFile({'q': "'Root' in parents"}).GetList()
53 if file_list:
54 parent_folder_id = file_list[0]['parents'][0]['id']
55 else:
56 file_in_root = self.drive.CreateFile({'title': 'i_am_in_root'})
57 file_in_root.Upload()
58 parent_folder_id = file_in_root['parents'][0]['id']
59
60 # Fetch destination folder entry and create hierarchy if required.
61 folder_names = string.split(parsed_url.path, '/')
62 for folder_name in folder_names:
63 if not folder_name:
64 continue
65 file_list = self.drive.ListFile({'q': "'" + parent_folder_id + "' in parents"}).GetList()
66 folder = next((item for item in file_list if item['title'] == folder_name and item['mimeType'] == 'application/vnd.google-apps.folder'), None)
67 if folder is None:
68 folder = self.drive.CreateFile({'title': folder_name, 'mimeType': "application/vnd.google-apps.folder", 'parents': [{'id': parent_folder_id}]})
69 folder.Upload()
70 parent_folder_id = folder['id']
71 self.folder = parent_folder_id
72
73 def FilesList(self):
74 return self.drive.ListFile({'q': "'" + self.folder + "' in parents"}).GetList()
75
76 def id_by_name(self, filename):
77 try:
78 return next(item for item in self.FilesList() if item['title'] == filename)['id']
79 except:
80 return ''
81
82 def _put(self, source_path, remote_filename):
83 drive_file = self.drive.CreateFile({'title': remote_filename, 'parents': [{"kind": "drive#fileLink", "id": self.folder}]})
84 drive_file.SetContentFile(source_path.name)
85 drive_file.Upload()
86
87 def _get(self, remote_filename, local_path):
88 drive_file = self.drive.CreateFile({'id': self.id_by_name(remote_filename)})
89 drive_file.GetContentFile(local_path.name)
90
91 def _list(self):
92 return [item['title'] for item in self.FilesList()]
93
94 def _delete(self, filename):
95 file_id = self.id_by_name(filename)
96 drive_file = self.drive.CreateFile({'id': file_id})
97 drive_file.auth.service.files().delete(fileId=drive_file['id']).execute()
98
99 def _query(self, filename):
100 try:
101 size = int((item for item in self.FilesList() if item['title'] == filename).next()['fileSize'])
102 except:
103 size = -1
104 return {'size': size}
105
106duplicity.backend.register_backend('pydrive', PyDriveBackend)
107duplicity.backend.uses_netloc.extend([ 'pydrive' ])
0108
=== modified file 'duplicity/commandline.py'
--- duplicity/commandline.py 2015-01-10 19:38:59 +0000
+++ duplicity/commandline.py 2015-01-14 11:12:41 +0000
@@ -856,6 +856,7 @@
856 webdav://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s856 webdav://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s
857 webdavs://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s857 webdavs://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s
858 gdocs://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s858 gdocs://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s
859 pydrive://%(user)s@%(other_host)s/%(some_dir)s
859 mega://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s860 mega://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s
860 copy://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s861 copy://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s
861 dpbx:///%(some_dir)s862 dpbx:///%(some_dir)s

Subscribers

People subscribed via source and target branches