backup2everbox 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in backup2everbox.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,96 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ backup2everbox (0.0.1)
5
+ activesupport
6
+ backup
7
+ oauth
8
+ rest-client
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ activemodel (3.0.3)
14
+ activesupport (= 3.0.3)
15
+ builder (~> 2.1.2)
16
+ i18n (~> 0.4)
17
+ activerecord (3.0.3)
18
+ activemodel (= 3.0.3)
19
+ activesupport (= 3.0.3)
20
+ arel (~> 2.0.2)
21
+ tzinfo (~> 0.3.23)
22
+ activesupport (3.0.3)
23
+ arel (2.0.7)
24
+ backup (2.4.4)
25
+ activerecord (>= 2.3.5)
26
+ cloudfiles (>= 1.4.7)
27
+ dropbox (>= 1.1.2)
28
+ fog (~> 0.3.5)
29
+ hirb (>= 0.2.9)
30
+ json_pure (~> 1.4.6)
31
+ net-scp (>= 1.0.2)
32
+ net-sftp (>= 2.0.4)
33
+ net-ssh (>= 2.0.15)
34
+ pony (>= 0.5)
35
+ sqlite3-ruby (>= 1.2.5)
36
+ builder (2.1.2)
37
+ cloudfiles (1.4.10)
38
+ mime-types (>= 1.16)
39
+ dropbox (1.2.3)
40
+ json (>= 1.2.0)
41
+ mechanize (>= 1.0.0)
42
+ multipart-post (>= 1.0)
43
+ oauth (>= 0.3.6)
44
+ excon (0.4.0)
45
+ fog (0.3.34)
46
+ builder
47
+ excon (>= 0.3.6)
48
+ formatador (>= 0.0.16)
49
+ json
50
+ mime-types
51
+ net-ssh (>= 2.0.23)
52
+ nokogiri (>= 1.4.4)
53
+ ruby-hmac
54
+ formatador (0.0.16)
55
+ hirb (0.3.6)
56
+ i18n (0.5.0)
57
+ json (1.5.1)
58
+ json_pure (1.4.6)
59
+ mail (2.2.15)
60
+ activesupport (>= 2.3.6)
61
+ i18n (>= 0.4.0)
62
+ mime-types (~> 1.16)
63
+ treetop (~> 1.4.8)
64
+ mechanize (1.0.0)
65
+ nokogiri (>= 1.2.1)
66
+ mime-types (1.16)
67
+ multipart-post (1.1.0)
68
+ net-scp (1.0.4)
69
+ net-ssh (>= 1.99.1)
70
+ net-sftp (2.0.5)
71
+ net-ssh (>= 2.0.9)
72
+ net-ssh (2.1.0)
73
+ nokogiri (1.4.4)
74
+ oauth (0.4.4)
75
+ polyglot (0.3.1)
76
+ pony (1.1)
77
+ mail (> 2.0)
78
+ rest-client (1.6.1)
79
+ mime-types (>= 1.16)
80
+ ruby-hmac (0.4.0)
81
+ sqlite3 (1.3.3)
82
+ sqlite3-ruby (1.3.3)
83
+ sqlite3 (>= 1.3.3)
84
+ treetop (1.4.9)
85
+ polyglot (>= 0.3.1)
86
+ tzinfo (0.3.24)
87
+
88
+ PLATFORMS
89
+ ruby
90
+
91
+ DEPENDENCIES
92
+ activesupport
93
+ backup
94
+ backup2everbox!
95
+ oauth
96
+ rest-client
data/README ADDED
@@ -0,0 +1,41 @@
1
+ 使用方法
2
+ ========
3
+
4
+ 1. 注册一个 EverBox 帐号: http://www.everbox.com/ , 注意必须注册盛大通行证帐号,
5
+ 如果需要邀请码,请到 http://www.douban.com/group/topic/17242955/
6
+
7
+ 2. 运行 sudo gem install backup2everbox
8
+
9
+ 3. 运行 sudo backup --setup
10
+
11
+ 4. 修改 /opt/backup/config/backup.rb, 改为如下的形式
12
+
13
+ # ----8<----------
14
+ gem 'backup2everbox'
15
+ require 'backup2everbox'
16
+
17
+ backup 'archive-backup-everbox' do
18
+
19
+ adapter :archive do
20
+ files ["/path/to/dir/need/backup"]
21
+ end
22
+
23
+ storage :everbox do
24
+ username 'YOUR SDO USERNAME'
25
+ password 'YOUR SDO PASSWORD'
26
+ path '/backups'
27
+ end
28
+
29
+ keep_backups 10
30
+ encrypt_with_password false
31
+ notify false
32
+
33
+ end
34
+ # ----8<----------
35
+
36
+ 5. 运行 sudo backup -r archive-backup-everbox
37
+
38
+ 6. backup 支持备份目录,数据库等多种源,具体可以参考 backup 的文档:
39
+ https://github.com/meskyanichi/backup
40
+
41
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/TODO ADDED
@@ -0,0 +1 @@
1
+ * don't try create dir if dir already exist
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "backup2everbox/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "backup2everbox"
7
+ s.version = Backup2everbox::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["LI Daobing"]
10
+ s.email = ["lidaobing@gmail.com"]
11
+ s.homepage = "http://rubygems.org/gems/backup2everbox"
12
+ s.summary = %q{backup to EverBox}
13
+ s.description = %q{backup to EverBox}
14
+
15
+ s.rubyforge_project = "backup2everbox"
16
+
17
+ s.add_dependency 'backup'
18
+ s.add_dependency 'activesupport'
19
+ s.add_dependency 'oauth'
20
+ s.add_dependency 'rest-client'
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,240 @@
1
+ require 'cgi'
2
+
3
+ require 'oauth'
4
+ require 'json'
5
+ require 'rest_client'
6
+ require 'pp'
7
+
8
+
9
+ module Backup
10
+ module Connection
11
+ class Everbox
12
+
13
+ class Session
14
+ DEFAULT_OPTIONS = {
15
+ :api_key => 'GW4YtvBpPwHfY0rCSf2xeOqn7tT0YH2O4zftXCOM',
16
+ :secret_access_key => 'xlKLpZLVSe0Gk6q4w05PsDpzjEbV8SyE71exgz1i',
17
+ :oauth_site => 'http://account.everbox.com',
18
+ :fs_site => 'http://fs.everbox.com',
19
+ :chunk_size => 1024*1024*4
20
+ }
21
+ attr_accessor :authorizing_user, :authorizing_password, :access_token
22
+ def initialize(opts={})
23
+ @options = DEFAULT_OPTIONS.merge(opts || {})
24
+ @consumer = OAuth::Consumer.new @options[:api_key], @options[:secret_access_key], :site => 'http://account.everbox.com'
25
+ end
26
+
27
+ def authorize!
28
+ response = @consumer.request(:post, "/oauth/quick_token?provider=sdo&login=#{CGI.escape @authorizing_user}&password=#{CGI.escape @authorizing_password}")
29
+ if response.code.to_i != 200
30
+ raise "login failed: #{response.body}"
31
+ end
32
+
33
+ d = CGI.parse(response.body).inject({}) do |h,(k,v)|
34
+ h[k.strip.to_sym] = v.first
35
+ h[k.strip] = v.first
36
+ h
37
+ end
38
+
39
+ @access_token = OAuth::AccessToken.from_hash(@consumer, d)
40
+ end
41
+
42
+ def authorized?
43
+ !!@access_token
44
+ end
45
+
46
+ def upload(filename, remote_path, opts={})
47
+ remote_path = find_real_remote_path(remote_path)
48
+ mkdir_p(remote_path)
49
+ basename = File.basename(filename)
50
+ target_path = File.expand_path(basename, remote_path)
51
+ keys = calc_digests(filename)
52
+ params = {
53
+ :path => target_path,
54
+ :keys => keys,
55
+ :chunkSize => 4*1024*1024,
56
+ :fileSize => File.open(filename).stat.size,
57
+ :base => ''
58
+ }
59
+ pp params
60
+ info = JSON.parse(access_token.post(fs(:prepare_put), JSON.dump(params), {'Content-Type' => 'text/plain' }).body)
61
+ pp info
62
+ File.open(filename) do |f|
63
+ info["required"].each do |x|
64
+ puts "upload block ##{x["index"]}"
65
+ f.seek(x["index"] * @options[:chunk_size])
66
+ code, response = http_request x['url'], f.read(@options[:chunk_size]), :method => :put
67
+ if code != 200
68
+ raise code.to_s
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ ftime = (Time.now.to_i * 1000 * 1000 * 10).to_s
75
+ params = params.merge :editTime => ftime, :mimeType => 'application/octet-stream'
76
+ pp access_token
77
+ code, response = access_token.post(fs(:commit_put), params.to_json, {'Content-Type' => 'text/plain'})
78
+ pp code, response
79
+ end
80
+
81
+ def delete(remote_path, opts={})
82
+ remote_path = find_real_remote_path(remote_path)
83
+ data = {
84
+ :paths => [remote_path],
85
+ }
86
+ response = access_token.post(fs(:delete), data.to_json, {'Content-Type' => 'text/plain'})
87
+ case response.code
88
+ when "200"
89
+ true
90
+ else
91
+ nil
92
+ #raise "delete failed: #{response}"
93
+ end
94
+ end
95
+
96
+ private
97
+ def find_real_remote_path(path)
98
+ if path.start_with?('/')
99
+ "/home" + path
100
+ else
101
+ "/home/" + path
102
+ end
103
+ end
104
+
105
+ def edit_time(time = nil)
106
+ time ||= Time.now
107
+ (time.to_i * 1000 * 1000 * 10).to_s
108
+ end
109
+
110
+ def mkdir_p(path)
111
+ return if path == "/"
112
+ mkdir_p(File.expand_path("..", path))
113
+ make_remote_path(path, :ignore_conflict => true)
114
+ end
115
+
116
+ def make_remote_path(path, opts = {})
117
+ data = {
118
+ :path => path,
119
+ :editTime => edit_time
120
+ }
121
+ response = access_token.post(fs(:mkdir), data.to_json, {'Content-Type' => 'text/plain'})
122
+ case response.code
123
+ when "200"
124
+ #
125
+ when "409"
126
+ unless opts[:ignore_conflict]
127
+ raise "directory already exist: `#{path}'"
128
+ end
129
+ end
130
+ end
131
+ def http_request url, data = nil, options = {}
132
+ begin
133
+ options[:method] = :post unless options[:method]
134
+ case options[:method]
135
+ when :get
136
+ response = RestClient.get url, data, :content_type => options[:content_type]
137
+ when :post
138
+ response = RestClient.post url, data, :content_type => options[:content_type]
139
+ when :put
140
+ response = RestClient.put url, data
141
+ end
142
+ body = response.body
143
+ data = nil
144
+ data = JSON.parse body unless body.empty?
145
+ [response.code.to_i, data]
146
+ rescue => e
147
+ raise
148
+ code = 0
149
+ data = nil
150
+ body = nil
151
+ res = e.response if e.respond_to? :response
152
+ begin
153
+ code = res.code if res.respond_to? :code
154
+ body = res.body if res.respond_to? :body
155
+ data = JSON.parse body unless body.empty?
156
+ rescue
157
+ data = body
158
+ end
159
+ [code, data]
160
+ end
161
+ end
162
+ def fs(path)
163
+ path = path.to_s
164
+ path = '/' + path unless path.start_with? '/'
165
+ @options[:fs_site] + path
166
+ end
167
+ def urlsafe_base64(content)
168
+ Base64.encode64(content).strip.gsub('+', '-').gsub('/','_')
169
+ end
170
+
171
+ def calc_digests(fname)
172
+ res = []
173
+ File.open(fname) do |ifile|
174
+ while (data = ifile.read(@options[:chunk_size])) do
175
+ res << urlsafe_base64(Digest::SHA1.digest(data))
176
+ end
177
+ end
178
+ res
179
+ end
180
+ end
181
+
182
+ attr_accessor :adapter, :procedure, :final_file, :tmp_path, :api_key, :secret_access_key, :username, :password, :path
183
+
184
+ def initialize(adapter=false)
185
+ if adapter
186
+ self.adapter = adapter
187
+ self.procedure = adapter.procedure
188
+ self.final_file = adapter.final_file
189
+ self.tmp_path = adapter.tmp_path.gsub('\ ', ' ')
190
+
191
+ load_storage_configuration_attributes
192
+ end
193
+ end
194
+
195
+ def static_initialize(procedure)
196
+ self.procedure = procedure
197
+ load_storage_configuration_attributes(true)
198
+ end
199
+
200
+ def session
201
+ opts = {}
202
+ opts[:api_key] = api_key unless api_key.nil?
203
+ opts[:secret_access_key] = secret_access_key unless secret_access_key.nil?
204
+ @session ||= Session.new(opts)
205
+ unless @session.authorized?
206
+ @session.authorizing_user = username
207
+ @session.authorizing_password = password
208
+ @session.authorize!
209
+ end
210
+
211
+ @session
212
+ end
213
+
214
+ def connect
215
+ session
216
+ end
217
+
218
+ def path
219
+ @path || "backups"
220
+ end
221
+
222
+ def store
223
+ path_to_file = File.join(tmp_path, final_file)
224
+ session.upload(path_to_file, path, :mode => :dropbox)
225
+ end
226
+
227
+ private
228
+
229
+ def load_storage_configuration_attributes(static=false)
230
+ %w(api_key secret_access_key username password path).each do |attribute|
231
+ if static
232
+ send("#{attribute}=", procedure.get_storage_configuration.attributes[attribute])
233
+ else
234
+ send("#{attribute}=", adapter.procedure.get_storage_configuration.attributes[attribute])
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,23 @@
1
+ require 'backup/connection/everbox'
2
+
3
+ module Backup
4
+ module Record
5
+ class Everbox < Backup::Record::Base
6
+ def load_specific_settings(adapter)
7
+ end
8
+
9
+ private
10
+
11
+ def self.destroy_backups(procedure, backups)
12
+ everbox = Backup::Connection::Everbox.new
13
+ everbox.static_initialize(procedure)
14
+ session = everbox.session
15
+ backups.each do |backup|
16
+ puts "\nDestroying backup \"#{backup.filename}\"."
17
+ path_to_file = File.join(everbox.path, backup.filename)
18
+ session.delete(path_to_file, :mode => :everbox)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ require 'backup/connection/everbox'
2
+
3
+ module Backup
4
+ module Storage
5
+ class Everbox < Base
6
+ def initialize(adapter)
7
+ everbox = Backup::Connection::Everbox.new(adapter)
8
+ everbox.store
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ require 'active_support/core_ext/module/aliasing'
2
+
3
+ module Backup
4
+ module Connection
5
+ autoload :Everbox, 'backup/connection/everbox'
6
+ end
7
+ module Storage
8
+ autoload :Everbox, 'backup/storage/everbox'
9
+ end
10
+ module Record
11
+ autoload :Everbox, 'backup/record/everbox'
12
+ end
13
+ module Configuration
14
+ class Base
15
+ def storage_class_with_everbox
16
+ res = storage_class_without_everbox
17
+ if res.nil? and @storage_name == :everbox
18
+ res = Backup::Storage::Everbox
19
+ end
20
+ res
21
+ end
22
+ def record_class_with_everbox
23
+ res = record_class_without_everbox
24
+ if res.nil? and @storage_name == :everbox
25
+ res = Backup::Record::Everbox
26
+ end
27
+ res
28
+ end
29
+ alias_method_chain :storage_class, :everbox
30
+ alias_method_chain :record_class, :everbox
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Backup2everbox
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: backup2everbox
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - LI Daobing
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-30 00:00:00 +08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: backup
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: activesupport
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: oauth
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: rest-client
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :runtime
76
+ version_requirements: *id004
77
+ description: backup to EverBox
78
+ email:
79
+ - lidaobing@gmail.com
80
+ executables: []
81
+
82
+ extensions: []
83
+
84
+ extra_rdoc_files: []
85
+
86
+ files:
87
+ - .gitignore
88
+ - Gemfile
89
+ - Gemfile.lock
90
+ - README
91
+ - Rakefile
92
+ - TODO
93
+ - backup2everbox.gemspec
94
+ - lib/backup/connection/everbox.rb
95
+ - lib/backup/record/everbox.rb
96
+ - lib/backup/storage/everbox.rb
97
+ - lib/backup2everbox.rb
98
+ - lib/backup2everbox/version.rb
99
+ has_rdoc: true
100
+ homepage: http://rubygems.org/gems/backup2everbox
101
+ licenses: []
102
+
103
+ post_install_message:
104
+ rdoc_options: []
105
+
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 3
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ requirements: []
127
+
128
+ rubyforge_project: backup2everbox
129
+ rubygems_version: 1.4.2
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: backup to EverBox
133
+ test_files: []
134
+