backup2everbox 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+