Merge lp:~mwrius/deja-dup/GCS into lp:deja-dup/34

Proposed by Marius Nuennerich
Status: Merged
Approved by: Michael Terry
Approved revision: 1548
Merged at revision: 1552
Proposed branch: lp:~mwrius/deja-dup/GCS
Merge into: lp:deja-dup/34
Diff against target: 459 lines (+283/-6)
10 files modified
AUTHORS (+4/-0)
data/org.gnome.DejaDup.gschema.xml.in (+19/-0)
debian/control (+13/-0)
deja-dup/widgets/CMakeLists.txt (+1/-0)
deja-dup/widgets/ConfigLocation.vala (+14/-0)
deja-dup/widgets/ConfigLocationGCS.vala (+41/-0)
libdeja/Backend.vala (+3/-0)
libdeja/BackendAuto.vala (+14/-6)
libdeja/BackendGCS.vala (+173/-0)
libdeja/CMakeLists.txt (+1/-0)
To merge this branch: bzr merge lp:~mwrius/deja-dup/GCS
Reviewer Review Type Date Requested Status
Michael Terry Approve
Review via email: mp+276188@code.launchpad.net

Description of the change

Add support for Google Cloud Storage.

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

Oh wow, neat!

But I'd actually be more interested in finishing the Google Drive backend. Very few desktop users are familiar with GCS.

Revision history for this message
Marius Nuennerich (mwrius) wrote :

I'm interested in that too. Can you point me to bugs or such that describe
what is wrong with it?

As the code for GCS is already there, how can I help to get it included
upstream?

2015-11-05 18:26 GMT+00:00 Michael Terry <email address hidden>:

> Oh wow, neat!
>
> But I'd actually be more interested in finishing the Google Drive
> backend. Very few desktop users are familiar with GCS.
> --
> https://code.launchpad.net/~mwrius/deja-dup/GCS/+merge/276188
> You are the owner of lp:~mwrius/deja-dup/GCS.
>

Revision history for this message
Marius Nuennerich (mwrius) wrote :

Is there anything missing to get this merged?

Revision history for this message
Michael Terry (mterry) wrote :

No, this looks good. I was just hesitant to add support for something called "Google Cloud Storage" which will be confusing next to "Google Drive" when we flip the switch to enable that. But if Google is OK with it... Plus, when we do that, maybe we can add a label like "only for advanced users" or some such thing to make it clearer. As if asking for a "bucket" and "secret access key" isn't intimidating enough.

As for why Google Drive support isn't finished, it's because at the time, Duplicity's Google Drive plugin was a bit in flux. The API it used was about to be deprecated by Google, and I was waiting for that to settle. It probably has now... I need to look at that. But the code is all there in deja-dup, just hidden from the UI for now.

But this branch as-is is fine, thank you! Sorry for the long delay. I'll probably move your credit in AUTHORS into the main block at the top, but that's it. Thanks!

review: Approve
Revision history for this message
Michael Terry (mterry) wrote :

I also switched the default order of backends to prefer S3 ahead of GCS. S3 is slightly easier for users since it autogenerates the bucket name for them and is currently still a bigger name. Ideally we'd finish Google Drive support and that can be the new favorite default. It's much simpler for the user than either S3 or GCS.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'AUTHORS'
2--- AUTHORS 2014-10-25 13:52:17 +0000
3+++ AUTHORS 2015-10-29 20:58:01 +0000
4@@ -17,6 +17,10 @@
5 Copyright: 2008–2013 Rosetta Contributors and Canonical Ltd
6 License: GPL-3+
7
8+Files: libdeja/BackendGCS.vala deja-dup/widgets/ConfigLocationGCS.vala
9+Copyright: 2015 Marius Nünnerich <mnu@google.com>
10+License: GPL-3+
11+
12 Files: libdeja/uriutils.c libdeja/uriutils.h
13 Copyright: 2006–2007 Red Hat, Inc
14 License: GPL-3+
15
16=== modified file 'data/org.gnome.DejaDup.gschema.xml.in'
17--- data/org.gnome.DejaDup.gschema.xml.in 2014-04-29 02:38:47 +0000
18+++ data/org.gnome.DejaDup.gschema.xml.in 2015-10-29 20:58:01 +0000
19@@ -65,6 +65,7 @@
20 <choices>
21 <choice value='auto'/>
22 <choice value='file'/>
23+ <choice value='gcs'/>
24 <choice value='gdrive'/>
25 <choice value='rackspace'/>
26 <choice value='s3'/>
27@@ -76,6 +77,7 @@
28 </key>
29 <child name="rackspace" schema="org.gnome.DejaDup.Rackspace"/>
30 <child name="s3" schema="org.gnome.DejaDup.S3"/>
31+ <child name="gcs" schema="org.gnome.DejaDup.GCS"/>
32 <child name="gdrive" schema="org.gnome.DejaDup.GDrive"/>
33 <child name="file" schema="org.gnome.DejaDup.File"/>
34 </schema>
35@@ -96,6 +98,23 @@
36 <_description>An optional folder name to store files in. This folder will be created in the chosen bucket.</_description>
37 </key>
38 </schema>
39+ <schema id="org.gnome.DejaDup.GCS" path="/org/gnome/deja-dup/gcs/">
40+ <key name="id" type="s">
41+ <default>''</default>
42+ <_summary>Google Cloud Storage Access Key ID</_summary>
43+ <_description>Your Google Cloud Storage Access Key Identifier. This acts as your Google Cloud Storage username.</_description>
44+ </key>
45+ <key name="bucket" type="s">
46+ <default>''</default>
47+ <_summary>The Google Cloud Storage bucket name to use</_summary>
48+ <_description>Which Google Cloud Storage bucket to store files in. This does not need to exist already. Only legal hostname strings are valid.</_description>
49+ </key>
50+ <key name="folder" type="s">
51+ <default>'$HOSTNAME'</default>
52+ <_summary>The Google Cloud Storage folder</_summary>
53+ <_description>An optional folder name to store files in. This folder will be created in the chosen bucket.</_description>
54+ </key>
55+ </schema>
56 <schema id="org.gnome.DejaDup.GDrive" path="/org/gnome/deja-dup/gdrive/">
57 <key name="email" type="s">
58 <default>''</default>
59
60=== modified file 'debian/control'
61--- debian/control 2014-09-20 14:51:29 +0000
62+++ debian/control 2015-10-29 20:58:01 +0000
63@@ -36,6 +36,7 @@
64 policykit-1,
65 Suggests: deja-dup-backend-cloudfiles,
66 deja-dup-backend-s3,
67+ deja-dup-backend-gcs,
68 Description: Back up your files
69 Déjà Dup is a simple backup tool. It hides the complexity of backing up the
70 Right Way (encrypted, off-site, and regular) and uses duplicity as the
71@@ -73,6 +74,18 @@
72 .
73 This package adds Rackspace Cloudfiles support to Déjà Dup.
74
75+Package: deja-dup-backend-gcs
76+Architecture: all
77+Depends: ${misc:Depends},
78+ deja-dup,
79+ python-boto (>= 2.20),
80+Description: Google Cloud Storage support for Déjà Dup
81+ Déjà Dup is a simple backup tool. It hides the complexity of backing up the
82+ Right Way (encrypted, off-site, and regular) and uses duplicity as the
83+ backend.
84+ .
85+ This package adds Google Cloud Storage support to Déjà Dup.
86+
87 #Package: deja-dup-backend-gdrive
88 #Architecture: all
89 #Depends: ${misc:Depends},
90
91=== modified file 'deja-dup/widgets/CMakeLists.txt'
92--- deja-dup/widgets/CMakeLists.txt 2014-04-29 02:38:47 +0000
93+++ deja-dup/widgets/CMakeLists.txt 2015-10-29 20:58:01 +0000
94@@ -31,6 +31,7 @@
95 ConfigLocationDAV.vala
96 ConfigLocationFile.vala
97 ConfigLocationFTP.vala
98+ ConfigLocationGCS.vala
99 ConfigLocationGDrive.vala
100 ConfigLocationRackspace.vala
101 ConfigLocationS3.vala
102
103=== modified file 'deja-dup/widgets/ConfigLocation.vala'
104--- deja-dup/widgets/ConfigLocation.vala 2014-04-29 02:38:47 +0000
105+++ deja-dup/widgets/ConfigLocation.vala 2015-10-29 20:58:01 +0000
106@@ -61,6 +61,7 @@
107 int index_ftp;
108 int index_dav;
109 int index_s3 = -2;
110+ int index_gcs = -2;
111 int index_gdrive = -2;
112 int index_rackspace = -2;
113 int index_u1 = -2;
114@@ -112,6 +113,7 @@
115 // Insert cloud providers
116 insert_u1();
117 insert_s3();
118+ insert_gcs();
119 insert_gdrive();
120 insert_rackspace();
121
122@@ -180,6 +182,14 @@
123 ref index_s3, insert_s3);
124 }
125
126+ void insert_gcs() {
127+ insert_cloud_if_available("gcs", BackendGCS.get_checker(),
128+ new ThemedIcon("deja-dup-cloud"),
129+ _("Google Cloud Storage"),
130+ new ConfigLocationGCS(label_sizes),
131+ ref index_gcs, insert_gcs);
132+ }
133+
134 void insert_gdrive() {
135 insert_cloud_if_available("gdrive", BackendGDrive.get_checker(),
136 new ThemedIcon("deja-dup-cloud"),
137@@ -424,6 +434,8 @@
138 var backend = Backend.get_default_type();
139 if (backend == "s3")
140 index = index_s3;
141+ else if (backend == "gcs")
142+ index = index_gcs;
143 else if (backend == "gdrive")
144 index = index_gdrive;
145 else if (backend == "rackspace")
146@@ -516,6 +528,8 @@
147
148 if (index == index_s3)
149 settings.set_string(BACKEND_KEY, "s3");
150+ else if (index == index_gcs)
151+ settings.set_string(BACKEND_KEY, "gcs");
152 else if (index == index_gdrive)
153 settings.set_string(BACKEND_KEY, "gdrive");
154 else if (index == index_rackspace)
155
156=== added file 'deja-dup/widgets/ConfigLocationGCS.vala'
157--- deja-dup/widgets/ConfigLocationGCS.vala 1970-01-01 00:00:00 +0000
158+++ deja-dup/widgets/ConfigLocationGCS.vala 2015-10-29 20:58:01 +0000
159@@ -0,0 +1,41 @@
160+/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 2 -*- */
161+/*
162+ This file is part of Déjà Dup.
163+ For copyright information, see AUTHORS.
164+
165+ Déjà Dup is free software: you can redistribute it and/or modify
166+ it under the terms of the GNU General Public License as published by
167+ the Free Software Foundation, either version 3 of the License, or
168+ (at your option) any later version.
169+
170+ Déjà Dup is distributed in the hope that it will be useful,
171+ but WITHOUT ANY WARRANTY; without even the implied warranty of
172+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
173+ GNU General Public License for more details.
174+
175+ You should have received a copy of the GNU General Public License
176+ along with Déjà Dup. If not, see <http://www.gnu.org/licenses/>.
177+*/
178+
179+using GLib;
180+
181+namespace DejaDup {
182+
183+public class ConfigLocationGCS : ConfigLocationTable
184+{
185+ public ConfigLocationGCS(Gtk.SizeGroup sg) {
186+ Object(label_sizes: sg);
187+ }
188+
189+ construct {
190+ add_widget(_("Google Cloud Storage Access Key I_D"),
191+ new ConfigEntry(DejaDup.GCS_ID_KEY, DejaDup.GCS_ROOT));
192+ add_widget(_("_Bucket"),
193+ new ConfigEntry(DejaDup.GCS_BUCKET_KEY, DejaDup.GCS_ROOT));
194+ add_widget(_("_Folder"),
195+ new ConfigFolder(DejaDup.GCS_FOLDER_KEY, DejaDup.GCS_ROOT));
196+ }
197+}
198+
199+}
200+
201
202=== modified file 'libdeja/Backend.vala'
203--- libdeja/Backend.vala 2014-04-29 02:38:47 +0000
204+++ libdeja/Backend.vala 2015-10-29 20:58:01 +0000
205@@ -57,6 +57,7 @@
206
207 if (backend != "auto" &&
208 backend != "s3" &&
209+ backend != "gcs" &&
210 backend != "gdrive" &&
211 backend != "rackspace" &&
212 backend != "u1" &&
213@@ -71,6 +72,8 @@
214 var backend_name = get_default_type();
215 if (backend_name == "s3")
216 return new BackendS3();
217+ else if (backend_name == "gcs")
218+ return new BackendGCS();
219 else if (backend_name == "gdrive")
220 return new BackendGDrive();
221 else if (backend_name == "u1")
222
223=== modified file 'libdeja/BackendAuto.vala'
224--- libdeja/BackendAuto.vala 2014-04-29 02:38:47 +0000
225+++ libdeja/BackendAuto.vala 2015-10-29 20:58:01 +0000
226@@ -50,6 +50,7 @@
227
228 static bool started = false;
229 static bool done = false;
230+ Checker gcs_checker;
231 Checker gdrive_checker;
232 Checker s3checker;
233 construct {
234@@ -59,10 +60,13 @@
235 started = true;
236 ref(); // Give us time to finish
237
238- // List is (in order): gdrive, s3, file
239+ // List is (in order): gdrive, gcs, s3, file
240 gdrive_checker = BackendGDrive.get_checker();
241 gdrive_checker.notify["complete"].connect(examine_checkers);
242
243+ gcs_checker = BackendGCS.get_checker();
244+ gcs_checker.notify["complete"].connect(examine_checkers);
245+
246 s3checker = BackendS3.get_checker();
247 s3checker.notify["complete"].connect(examine_checkers);
248
249@@ -78,11 +82,15 @@
250 if (gdrive_checker.complete) {
251 if (gdrive_checker.available)
252 finish("gdrive");
253- else if (s3checker.complete) {
254- if (s3checker.available)
255- finish("s3");
256- else
257- finish("file");
258+ else if (gcs_checker.complete) {
259+ if (gcs_checker.available)
260+ finish("gcs");
261+ else if (s3checker.complete) {
262+ if (s3checker.available)
263+ finish("s3");
264+ else
265+ finish("file");
266+ }
267 }
268 }
269 }
270
271=== added file 'libdeja/BackendGCS.vala'
272--- libdeja/BackendGCS.vala 1970-01-01 00:00:00 +0000
273+++ libdeja/BackendGCS.vala 2015-10-29 20:58:01 +0000
274@@ -0,0 +1,173 @@
275+/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 2 -*- */
276+/*
277+ This file is part of Déjà Dup.
278+ For copyright information, see AUTHORS.
279+
280+ Déjà Dup is free software: you can redistribute it and/or modify
281+ it under the terms of the GNU General Public License as published by
282+ the Free Software Foundation, either version 3 of the License, or
283+ (at your option) any later version.
284+
285+ Déjà Dup is distributed in the hope that it will be useful,
286+ but WITHOUT ANY WARRANTY; without even the implied warranty of
287+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
288+ GNU General Public License for more details.
289+
290+ You should have received a copy of the GNU General Public License
291+ along with Déjà Dup. If not, see <http://www.gnu.org/licenses/>.
292+*/
293+
294+using GLib;
295+
296+namespace DejaDup {
297+
298+public const string GCS_ROOT = "GCS";
299+public const string GCS_ID_KEY = "id";
300+public const string GCS_BUCKET_KEY = "bucket";
301+public const string GCS_FOLDER_KEY = "folder";
302+
303+const string GCS_SERVER = "www.googleapis.com";
304+
305+public class BackendGCS : Backend
306+{
307+ public static Checker get_checker() {
308+ return PythonChecker.get_checker("boto");
309+ }
310+
311+ public override Backend clone() {
312+ return new BackendGCS();
313+ }
314+
315+ public override bool is_native() {
316+ return false;
317+ }
318+
319+ public override Icon? get_icon() {
320+ return new ThemedIcon("deja-dup-cloud");
321+ }
322+
323+ public override async bool is_ready(out string when) {
324+ when = _("Backup will begin when a network connection becomes available.");
325+ return yield Network.get().can_reach ("http://%s/".printf(GCS_SERVER));
326+ }
327+
328+ public override string get_location(ref bool as_root)
329+ {
330+ var settings = get_settings(GCS_ROOT);
331+
332+ var bucket = settings.get_string(GCS_BUCKET_KEY);
333+ var folder = get_folder_key(settings, GCS_FOLDER_KEY);
334+
335+ return "gs://%s/%s".printf(bucket, folder);
336+ }
337+
338+ public override string get_location_pretty()
339+ {
340+ var settings = get_settings(GCS_ROOT);
341+ var bucket = settings.get_string(GCS_BUCKET_KEY);
342+ var folder = get_folder_key(settings, GCS_FOLDER_KEY);
343+ if (folder == "")
344+ return _("Google Cloud Storage");
345+ else
346+ // Translators: %s/%s is a folder.
347+ return _("%s/%s on Google Cloud Storage").printf(bucket, folder);
348+ }
349+
350+ string settings_id;
351+ string id;
352+ string secret_key;
353+ public override async void get_envp() throws Error
354+ {
355+ var settings = get_settings(GCS_ROOT);
356+ settings_id = settings.get_string(GCS_ID_KEY);
357+ id = settings_id == null ? "" : settings_id;
358+
359+ if (id != "" && secret_key != null) {
360+ // We've already been run before and got the key
361+ got_secret_key();
362+ return;
363+ }
364+
365+ if (id != "") {
366+ // First, try user's keyring
367+ try {
368+ secret_key = yield Secret.password_lookup(Secret.SCHEMA_COMPAT_NETWORK,
369+ null,
370+ "user", id,
371+ "server", GCS_SERVER,
372+ "protocol", "https");
373+ if (secret_key != null) {
374+ got_secret_key();
375+ return;
376+ }
377+ }
378+ catch (Error e) {
379+ // fall through to ask_password below
380+ }
381+ }
382+
383+ // Didn't find it, so ask user
384+ ask_password();
385+ }
386+
387+ async void got_password_reply(MountOperation mount_op, MountOperationResult result)
388+ {
389+ if (result != MountOperationResult.HANDLED) {
390+ envp_ready(false, new List<string>(), _("Permission denied"));
391+ return;
392+ }
393+
394+ id = mount_op.username;
395+ secret_key = mount_op.password;
396+
397+ // Save it
398+ var remember = mount_op.password_save;
399+ if (remember != PasswordSave.NEVER) {
400+ string where = (remember == PasswordSave.FOR_SESSION) ?
401+ Secret.COLLECTION_SESSION : Secret.COLLECTION_DEFAULT;
402+ try {
403+ yield Secret.password_store(Secret.SCHEMA_COMPAT_NETWORK,
404+ where,
405+ "%s@%s".printf(id, GCS_SERVER),
406+ secret_key,
407+ null,
408+ "user", id,
409+ "server", GCS_SERVER,
410+ "protocol", "https");
411+ }
412+ catch (Error e) {
413+ warning("%s\n", e.message);
414+ }
415+ }
416+
417+ got_secret_key();
418+ }
419+
420+ void ask_password() {
421+ mount_op.set("label_help", _("You can sign up for a Google Cloud Storage account <a href=\"%s\">online</a>. Remember to enable Interoperability and create keys.").printf("http://cloud.google.com"));
422+ mount_op.set("label_title", _("Connect to Google Cloud Storage"));
423+ mount_op.set("label_username", _("_Access key ID"));
424+ mount_op.set("label_password", _("_Secret access key"));
425+ mount_op.set("label_show_password", _("S_how secret access key"));
426+ mount_op.set("label_remember_password", _("_Remember secret access key"));
427+ mount_op.reply.connect(got_password_reply);
428+ mount_op.ask_password("", id, "",
429+ AskPasswordFlags.NEED_PASSWORD |
430+ AskPasswordFlags.NEED_USERNAME |
431+ AskPasswordFlags.SAVING_SUPPORTED);
432+ }
433+
434+ void got_secret_key() {
435+ var settings = get_settings(GCS_ROOT);
436+ if (id != settings_id)
437+ settings.set_string(GCS_ID_KEY, id);
438+
439+ List<string> envp = new List<string>();
440+ envp.append("GS_ACCESS_KEY_ID=%s".printf(id));
441+ envp.append("GS_SECRET_ACCESS_KEY=%s".printf(secret_key));
442+ envp_ready(true, envp);
443+ }
444+}
445+
446+} // end namespace
447+
448
449=== modified file 'libdeja/CMakeLists.txt'
450--- libdeja/CMakeLists.txt 2014-04-29 02:38:47 +0000
451+++ libdeja/CMakeLists.txt 2015-10-29 20:58:01 +0000
452@@ -20,6 +20,7 @@
453 Backend.vala
454 BackendAuto.vala
455 BackendFile.vala
456+ BackendGCS.vala
457 BackendGDrive.vala
458 BackendRackspace.vala
459 BackendS3.vala

Subscribers

People subscribed via source and target branches