← Back to team overview

deja-dup-team team mailing list archive

[Merge] lp:~mwrius/deja-dup/GCS into lp:deja-dup

 

Marius Nuennerich has proposed merging lp:~mwrius/deja-dup/GCS into lp:deja-dup.

Requested reviews:
  Déjà Dup Developers (deja-dup-hackers)

For more details, see:
https://code.launchpad.net/~mwrius/deja-dup/GCS/+merge/276188

Add support for Google Cloud Storage.
-- 
Your team Déjà Dup Developers is requested to review the proposed merge of lp:~mwrius/deja-dup/GCS into lp:deja-dup.
=== modified file 'AUTHORS'
--- AUTHORS	2014-10-25 13:52:17 +0000
+++ AUTHORS	2015-10-29 20:58:01 +0000
@@ -17,6 +17,10 @@
 Copyright: 2008–2013 Rosetta Contributors and Canonical Ltd
 License: GPL-3+
 
+Files: libdeja/BackendGCS.vala deja-dup/widgets/ConfigLocationGCS.vala
+Copyright: 2015 Marius Nünnerich <mnu@xxxxxxxxxx>
+License: GPL-3+
+
 Files: libdeja/uriutils.c libdeja/uriutils.h
 Copyright: 2006–2007 Red Hat, Inc
 License: GPL-3+

=== modified file 'data/org.gnome.DejaDup.gschema.xml.in'
--- data/org.gnome.DejaDup.gschema.xml.in	2014-04-29 02:38:47 +0000
+++ data/org.gnome.DejaDup.gschema.xml.in	2015-10-29 20:58:01 +0000
@@ -65,6 +65,7 @@
       <choices>
         <choice value='auto'/>
         <choice value='file'/>
+        <choice value='gcs'/>
         <choice value='gdrive'/>
         <choice value='rackspace'/>
         <choice value='s3'/>
@@ -76,6 +77,7 @@
     </key>
     <child name="rackspace" schema="org.gnome.DejaDup.Rackspace"/>
     <child name="s3" schema="org.gnome.DejaDup.S3"/>
+    <child name="gcs" schema="org.gnome.DejaDup.GCS"/>
     <child name="gdrive" schema="org.gnome.DejaDup.GDrive"/>
     <child name="file" schema="org.gnome.DejaDup.File"/>
   </schema>
@@ -96,6 +98,23 @@
       <_description>An optional folder name to store files in.  This folder will be created in the chosen bucket.</_description>
     </key>
   </schema>
+  <schema id="org.gnome.DejaDup.GCS" path="/org/gnome/deja-dup/gcs/">
+    <key name="id" type="s">
+      <default>''</default>
+      <_summary>Google Cloud Storage Access Key ID</_summary>
+      <_description>Your Google Cloud Storage Access Key Identifier.  This acts as your Google Cloud Storage username.</_description>
+    </key>
+    <key name="bucket" type="s">
+      <default>''</default>
+      <_summary>The Google Cloud Storage bucket name to use</_summary>
+      <_description>Which Google Cloud Storage bucket to store files in.  This does not need to exist already.  Only legal hostname strings are valid.</_description>
+    </key>
+    <key name="folder" type="s">
+      <default>'$HOSTNAME'</default>
+      <_summary>The Google Cloud Storage folder</_summary>
+      <_description>An optional folder name to store files in.  This folder will be created in the chosen bucket.</_description>
+    </key>
+  </schema>
   <schema id="org.gnome.DejaDup.GDrive" path="/org/gnome/deja-dup/gdrive/">
     <key name="email" type="s">
       <default>''</default>

=== modified file 'debian/control'
--- debian/control	2014-09-20 14:51:29 +0000
+++ debian/control	2015-10-29 20:58:01 +0000
@@ -36,6 +36,7 @@
             policykit-1,
 Suggests: deja-dup-backend-cloudfiles,
           deja-dup-backend-s3,
+          deja-dup-backend-gcs,
 Description: Back up your files
  Déjà Dup is a simple backup tool. It hides the complexity of backing up the
  Right Way (encrypted, off-site, and regular) and uses duplicity as the
@@ -73,6 +74,18 @@
  .
  This package adds Rackspace Cloudfiles support to Déjà Dup.
 
+Package: deja-dup-backend-gcs
+Architecture: all
+Depends: ${misc:Depends},
+         deja-dup,
+         python-boto (>= 2.20),
+Description: Google Cloud Storage support for Déjà Dup
+ Déjà Dup is a simple backup tool. It hides the complexity of backing up the
+ Right Way (encrypted, off-site, and regular) and uses duplicity as the
+ backend.
+ .
+ This package adds Google Cloud Storage support to Déjà Dup.
+
 #Package: deja-dup-backend-gdrive
 #Architecture: all
 #Depends: ${misc:Depends},

=== modified file 'deja-dup/widgets/CMakeLists.txt'
--- deja-dup/widgets/CMakeLists.txt	2014-04-29 02:38:47 +0000
+++ deja-dup/widgets/CMakeLists.txt	2015-10-29 20:58:01 +0000
@@ -31,6 +31,7 @@
                        ConfigLocationDAV.vala
                        ConfigLocationFile.vala
                        ConfigLocationFTP.vala
+                       ConfigLocationGCS.vala
                        ConfigLocationGDrive.vala
                        ConfigLocationRackspace.vala
                        ConfigLocationS3.vala

=== modified file 'deja-dup/widgets/ConfigLocation.vala'
--- deja-dup/widgets/ConfigLocation.vala	2014-04-29 02:38:47 +0000
+++ deja-dup/widgets/ConfigLocation.vala	2015-10-29 20:58:01 +0000
@@ -61,6 +61,7 @@
   int index_ftp;
   int index_dav;
   int index_s3 = -2;
+  int index_gcs = -2;
   int index_gdrive = -2;
   int index_rackspace = -2;
   int index_u1 = -2;
@@ -112,6 +113,7 @@
     // Insert cloud providers
     insert_u1();
     insert_s3();
+    insert_gcs();
     insert_gdrive();
     insert_rackspace();
 
@@ -180,6 +182,14 @@
                               ref index_s3, insert_s3);
   }
 
+  void insert_gcs() {
+    insert_cloud_if_available("gcs", BackendGCS.get_checker(),
+                              new ThemedIcon("deja-dup-cloud"),
+                              _("Google Cloud Storage"),
+                              new ConfigLocationGCS(label_sizes),
+                              ref index_gcs, insert_gcs);
+  }
+
   void insert_gdrive() {
     insert_cloud_if_available("gdrive", BackendGDrive.get_checker(),
                               new ThemedIcon("deja-dup-cloud"),
@@ -424,6 +434,8 @@
     var backend = Backend.get_default_type();
     if (backend == "s3")
       index = index_s3;
+    else if (backend == "gcs")
+      index = index_gcs;
     else if (backend == "gdrive")
       index = index_gdrive;
     else if (backend == "rackspace")
@@ -516,6 +528,8 @@
 
     if (index == index_s3)
       settings.set_string(BACKEND_KEY, "s3");
+    else if (index == index_gcs)
+      settings.set_string(BACKEND_KEY, "gcs");
     else if (index == index_gdrive)
       settings.set_string(BACKEND_KEY, "gdrive");
     else if (index == index_rackspace)

=== added file 'deja-dup/widgets/ConfigLocationGCS.vala'
--- deja-dup/widgets/ConfigLocationGCS.vala	1970-01-01 00:00:00 +0000
+++ deja-dup/widgets/ConfigLocationGCS.vala	2015-10-29 20:58:01 +0000
@@ -0,0 +1,41 @@
+/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 2 -*- */
+/*
+    This file is part of Déjà Dup.
+    For copyright information, see AUTHORS.
+
+    Déjà Dup is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Déjà Dup is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Déjà Dup.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using GLib;
+
+namespace DejaDup {
+
+public class ConfigLocationGCS : ConfigLocationTable
+{
+  public ConfigLocationGCS(Gtk.SizeGroup sg) {
+    Object(label_sizes: sg);
+  }
+
+  construct {
+    add_widget(_("Google Cloud Storage Access Key I_D"),
+               new ConfigEntry(DejaDup.GCS_ID_KEY, DejaDup.GCS_ROOT));
+    add_widget(_("_Bucket"),
+               new ConfigEntry(DejaDup.GCS_BUCKET_KEY, DejaDup.GCS_ROOT));
+    add_widget(_("_Folder"),
+               new ConfigFolder(DejaDup.GCS_FOLDER_KEY, DejaDup.GCS_ROOT));
+  }
+}
+
+}
+

=== modified file 'libdeja/Backend.vala'
--- libdeja/Backend.vala	2014-04-29 02:38:47 +0000
+++ libdeja/Backend.vala	2015-10-29 20:58:01 +0000
@@ -57,6 +57,7 @@
 
     if (backend != "auto" &&
         backend != "s3" &&
+        backend != "gcs" &&
         backend != "gdrive" &&
         backend != "rackspace" &&
         backend != "u1" &&
@@ -71,6 +72,8 @@
     var backend_name = get_default_type();
     if (backend_name == "s3")
       return new BackendS3();
+    else if (backend_name == "gcs")
+      return new BackendGCS();
     else if (backend_name == "gdrive")
       return new BackendGDrive();
     else if (backend_name == "u1")

=== modified file 'libdeja/BackendAuto.vala'
--- libdeja/BackendAuto.vala	2014-04-29 02:38:47 +0000
+++ libdeja/BackendAuto.vala	2015-10-29 20:58:01 +0000
@@ -50,6 +50,7 @@
 
   static bool started = false;
   static bool done = false;
+  Checker gcs_checker;
   Checker gdrive_checker;
   Checker s3checker;
   construct {
@@ -59,10 +60,13 @@
       started = true;
       ref(); // Give us time to finish
 
-      // List is (in order): gdrive, s3, file
+      // List is (in order): gdrive, gcs, s3, file
       gdrive_checker = BackendGDrive.get_checker();
       gdrive_checker.notify["complete"].connect(examine_checkers);
 
+      gcs_checker = BackendGCS.get_checker();
+      gcs_checker.notify["complete"].connect(examine_checkers);
+
       s3checker = BackendS3.get_checker();
       s3checker.notify["complete"].connect(examine_checkers);
 
@@ -78,11 +82,15 @@
     if (gdrive_checker.complete) {
       if (gdrive_checker.available)
         finish("gdrive");
-      else if (s3checker.complete) {
-        if (s3checker.available)
-          finish("s3");
-        else
-          finish("file");
+      else if (gcs_checker.complete) {
+        if (gcs_checker.available)
+          finish("gcs");
+        else if (s3checker.complete) {
+          if (s3checker.available)
+            finish("s3");
+          else
+            finish("file");
+        }
       }
     }
   }

=== added file 'libdeja/BackendGCS.vala'
--- libdeja/BackendGCS.vala	1970-01-01 00:00:00 +0000
+++ libdeja/BackendGCS.vala	2015-10-29 20:58:01 +0000
@@ -0,0 +1,173 @@
+/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 2 -*- */
+/*
+    This file is part of Déjà Dup.
+    For copyright information, see AUTHORS.
+
+    Déjà Dup is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Déjà Dup is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Déjà Dup.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using GLib;
+
+namespace DejaDup {
+
+public const string GCS_ROOT = "GCS";
+public const string GCS_ID_KEY = "id";
+public const string GCS_BUCKET_KEY = "bucket";
+public const string GCS_FOLDER_KEY = "folder";
+
+const string GCS_SERVER = "www.googleapis.com";
+
+public class BackendGCS : Backend
+{
+  public static Checker get_checker() {
+    return PythonChecker.get_checker("boto");
+  }
+
+  public override Backend clone() {
+    return new BackendGCS();
+  }
+  
+  public override bool is_native() {
+    return false;
+  }
+  
+  public override Icon? get_icon() {
+    return new ThemedIcon("deja-dup-cloud");
+  }
+
+  public override async bool is_ready(out string when) {
+    when = _("Backup will begin when a network connection becomes available.");
+    return yield Network.get().can_reach ("http://%s/".printf(GCS_SERVER));
+  }
+
+  public override string get_location(ref bool as_root)
+  {
+    var settings = get_settings(GCS_ROOT);
+    
+    var bucket = settings.get_string(GCS_BUCKET_KEY);
+    var folder = get_folder_key(settings, GCS_FOLDER_KEY);
+
+    return "gs://%s/%s".printf(bucket, folder);
+  }
+  
+  public override string get_location_pretty()
+  {
+    var settings = get_settings(GCS_ROOT);
+    var bucket = settings.get_string(GCS_BUCKET_KEY);
+    var folder = get_folder_key(settings, GCS_FOLDER_KEY);
+    if (folder == "")
+      return _("Google Cloud Storage");
+    else
+      // Translators: %s/%s is a folder.
+      return _("%s/%s on Google Cloud Storage").printf(bucket, folder);
+  }
+  
+  string settings_id;
+  string id;
+  string secret_key;
+  public override async void get_envp() throws Error
+  {
+    var settings = get_settings(GCS_ROOT);
+    settings_id = settings.get_string(GCS_ID_KEY);
+    id = settings_id == null ? "" : settings_id;
+    
+    if (id != "" && secret_key != null) {
+      // We've already been run before and got the key
+      got_secret_key();
+      return;
+    }
+    
+    if (id != "") {
+      // First, try user's keyring
+      try {
+        secret_key = yield Secret.password_lookup(Secret.SCHEMA_COMPAT_NETWORK,
+                                                  null, 
+                                                  "user", id,
+                                                  "server", GCS_SERVER,
+                                                  "protocol", "https");
+        if (secret_key != null) {
+          got_secret_key();
+          return;
+        }
+      }
+      catch (Error e) {
+        // fall through to ask_password below
+      }
+    }
+
+    // Didn't find it, so ask user
+    ask_password();
+  }
+
+  async void got_password_reply(MountOperation mount_op, MountOperationResult result)
+  {
+    if (result != MountOperationResult.HANDLED) {
+      envp_ready(false, new List<string>(), _("Permission denied"));
+      return;
+    }
+
+    id = mount_op.username;
+    secret_key = mount_op.password;
+
+    // Save it
+    var remember = mount_op.password_save;
+    if (remember != PasswordSave.NEVER) {
+      string where = (remember == PasswordSave.FOR_SESSION) ?
+                     Secret.COLLECTION_SESSION : Secret.COLLECTION_DEFAULT;
+      try {
+        yield Secret.password_store(Secret.SCHEMA_COMPAT_NETWORK,
+                                    where,
+                                    "%s@%s".printf(id, GCS_SERVER),
+                                    secret_key,
+                                    null,
+                                    "user", id,
+                                    "server", GCS_SERVER,
+                                    "protocol", "https");
+      }
+      catch (Error e) {
+        warning("%s\n", e.message);
+      }
+    }
+
+    got_secret_key();
+  }
+
+  void ask_password() {
+    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";));
+    mount_op.set("label_title", _("Connect to Google Cloud Storage"));
+    mount_op.set("label_username", _("_Access key ID"));
+    mount_op.set("label_password", _("_Secret access key"));
+    mount_op.set("label_show_password", _("S_how secret access key"));
+    mount_op.set("label_remember_password", _("_Remember secret access key"));
+    mount_op.reply.connect(got_password_reply);
+    mount_op.ask_password("", id, "",
+                          AskPasswordFlags.NEED_PASSWORD |
+                          AskPasswordFlags.NEED_USERNAME |
+                          AskPasswordFlags.SAVING_SUPPORTED);
+  }
+  
+  void got_secret_key() {
+    var settings = get_settings(GCS_ROOT);
+    if (id != settings_id)
+      settings.set_string(GCS_ID_KEY, id);
+    
+    List<string> envp = new List<string>();
+    envp.append("GS_ACCESS_KEY_ID=%s".printf(id));
+    envp.append("GS_SECRET_ACCESS_KEY=%s".printf(secret_key));
+    envp_ready(true, envp);
+  }
+}
+
+} // end namespace
+

=== modified file 'libdeja/CMakeLists.txt'
--- libdeja/CMakeLists.txt	2014-04-29 02:38:47 +0000
+++ libdeja/CMakeLists.txt	2015-10-29 20:58:01 +0000
@@ -20,6 +20,7 @@
                        Backend.vala
                        BackendAuto.vala
                        BackendFile.vala
+                       BackendGCS.vala
                        BackendGDrive.vala
                        BackendRackspace.vala
                        BackendS3.vala


Follow ups