backup-googledrive 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6909a4f268118492f668382203b3c332e19a80c8
4
+ data.tar.gz: eac80a0ce6a50a9314f253a86f93c9c2b9b083ae
5
+ SHA512:
6
+ metadata.gz: c0b37e31e8bf83fde792b284d30b564426425b3603596b73c89ef71426574b5e99b25d5bb058a73b1a9b865d20d5688d8e0a93bacf795675d34abda55c3fb116
7
+ data.tar.gz: 9a3693d57cb08c8d594b0903a075e919db6184f00196a56ab26118a16cd1e73d9abbbeda94739827af671b5239d3d3047e7c301b719702b79bb3f450c39fb078
@@ -0,0 +1,6 @@
1
+ module Backup
2
+ module Storage
3
+ autoload :GoogleDrive, File.join(File.dirname(__FILE__), 'storage', 'googledrive')
4
+ Backup::Config::DSL.const_set('GoogleDrive', Module.new)
5
+ end
6
+ end
@@ -0,0 +1,149 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Storage
5
+ class GoogleDrive < Base
6
+
7
+ include Storage::Cycler
8
+ class Error < Backup::Error; end
9
+
10
+ # This adapter uses gdrive (https://github.com/prasmussen/gdrive) to handle all API interactions.
11
+ # Fortunately for us gdrive handles things like timeouts, error retries and large file chunking, too!
12
+ # I found gdrive's defaults to be acceptable, but should be easy to add accessors to customize if needed
13
+
14
+ # Path to gdrive executable
15
+ attr_accessor :gdrive_exe
16
+
17
+ # Use the gdrive executable to obtain a refresh token. Add that token to your backup model.
18
+ # The gdrive exe will handle refresing the access tokens
19
+ attr_accessor :refresh_token
20
+
21
+ ##
22
+ # Creates a new instance of the storage object
23
+ def initialize(model, storage_id = nil)
24
+ super
25
+
26
+ @path ||= 'backups'
27
+ path.sub!(/^\//, '')
28
+
29
+ required = %w{ refresh_token }
30
+ raise Error, "Configuration Error: a refresh_token is required" if refresh_token.nil?
31
+
32
+ raise Error, "Configuration Error: gdrive executable is required." if gdrive_exe.nil?
33
+ end
34
+
35
+ # private
36
+
37
+
38
+ ##
39
+ # Transfer each of the package files to Dropbox in chunks of +chunk_size+.
40
+ # Each chunk will be retried +chunk_retries+ times, pausing +retry_waitsec+
41
+ # between retries, if errors occur.
42
+ def transfer!
43
+ package.filenames.each do |filename|
44
+ src = File.join(Config.tmp_path, filename)
45
+ dest = File.join(remote_path, filename)
46
+ Logger.info "Storing '#{ dest }'..."
47
+
48
+ parent_id = find_id_from_path(remote_path)
49
+ gdrive_upload(src, parent_id)
50
+ end
51
+ end
52
+
53
+ # # Called by the Cycler.
54
+ # # Any error raised will be logged as a warning.
55
+ def remove!(package)
56
+ Logger.info "Removing backup package dated #{ package.time }..."
57
+ id = find_id_from_path(remote_path_for(package), false)
58
+ if id.to_s.empty?
59
+ raise Error, "Backup packge #{ package.time } not found in Google Drive"
60
+ else
61
+ gdrive_delete(id)
62
+ end
63
+ end
64
+
65
+ def find_id_from_path(path = remote_path, create = true)
66
+ parent = nil
67
+ path.split('/').each do |path_part|
68
+ id = get_folder_id(path_part, parent)
69
+ if id.to_s.empty? && create
70
+ id = gdrive_mkdir(path_part, parent)
71
+ end
72
+ parent = id
73
+ end
74
+ return parent
75
+ end
76
+
77
+
78
+ def get_folder_id(name, parent = nil)
79
+ parent = parent ? parent : 'root'
80
+ gdrive_list("name = '#{name}' and '#{parent}' in parents")
81
+ end
82
+
83
+ def gdrive_list(query)
84
+ unless query.empty?
85
+ cmd = "gdrive --refresh-token '#{refresh_token}' list --no-header -q \"#{query}\""
86
+ output = `#{cmd}`
87
+ if output.downcase.include? "error"
88
+ raise Error, "Could not list or find the object with query string '#{query}'. gdrive output: #{output}"
89
+ elsif output.empty?
90
+ return nil
91
+ else
92
+ begin
93
+ return /^([^ ]*).*/.match(output)[1] # will return an empty string on no match
94
+ rescue => err
95
+ return nil
96
+ end
97
+ end
98
+ else
99
+ raise Error, "A search query is required to list/find a file or folder"
100
+ end
101
+ end
102
+
103
+ def gdrive_mkdir(name, parent = nil)
104
+ unless name.empty?
105
+ parent = parent ? parent : 'root'
106
+ cmd = "gdrive --refresh-token '#{refresh_token}' mkdir -p '#{parent}' '#{name}'"
107
+ output = `#{cmd}`
108
+ if output.downcase.include? "error"
109
+ raise Error, "Could not create the directory '#{name}' with parent '#{parent}'. gdrive output: #{output}"
110
+ else
111
+ id = /^Directory (.*?) created/.match(output)[1]
112
+ raise Error, "Could not determine ID of newly created folder. See gdrive output: #{output}" if id.to_s.empty?
113
+ Logger.info "Created folder #{name} successfully with id '#{id}'"
114
+ return id
115
+ end
116
+ else
117
+ raise Error, "Name parameter is required to make a directory"
118
+ end
119
+ end
120
+
121
+ def gdrive_upload(src, parent = nil)
122
+ parent = parent ? parent : 'root'
123
+ cmd = "gdrive --refresh-token '#{refresh_token}' upload -p '#{parent}' '#{src}'"
124
+ output = `#{cmd}`
125
+ if ( ["error", "failed"].any? {|s| output.downcase.include? s } )
126
+ raise Error, "Could not upload file. See gdrive output: #{output}"
127
+ else
128
+ begin
129
+ id = /.*Uploaded (.*?) .*/.match(output)[1]
130
+ raise Error, "empty id" if id.to_s.empty?
131
+ Logger.info "Uploaded #{src} into parent folder '#{parent}' successfully. Google Drive file_id: #{ id }"
132
+ rescue => err
133
+ raise Error.wrap(err, "Could not determine ID of newly created folder. See gdrive output: #{output}")
134
+ end
135
+ end
136
+ end
137
+
138
+ def gdrive_delete(id, recursive = true)
139
+ cmd = "gdrive --refresh-token '#{refresh_token}' delete #{'-r' if recursive} '#{id}'"
140
+ output = `#{cmd}`
141
+ if output.downcase.include? "error"
142
+ raise Error, "Could not delete object with id: #{id}. See gdrive output: #{output}"
143
+ else
144
+ Logger.info output
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: backup-googledrive
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Joseph Rafferty
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Adds the GoogleDrive storage engine to the Backup ruby gem.
14
+ email: oraff@gmail.comj
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/backup-googledrive.rb
20
+ - lib/storage/googledrive.rb
21
+ homepage: http://github.com/joraff/backup-googledrive
22
+ licenses:
23
+ - MIT
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 2.0.14.1
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: GoogleDrive storage engine to Backup
45
+ test_files: []
46
+ has_rdoc: