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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Terry | Approve | ||
Review via email: mp+276188@code.launchpad.net |
Commit message
Description of the change
Add support for Google Cloud Storage.
Michael Terry (mterry) wrote : | # |
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:/
> You are the owner of lp:~mwrius/deja-dup/GCS.
>
Marius Nuennerich (mwrius) wrote : | # |
Is there anything missing to get this merged?
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!
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
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 |
Oh wow, neat!
But I'd actually be more interested in finishing the Google Drive backend. Very few desktop users are familiar with GCS.