encbs 0.1.3 → 0.2.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -3,8 +3,10 @@ source "http://rubygems.org"
3
3
  gem "fog"
4
4
  gem "slop"
5
5
  gem "ruby-progressbar"
6
+ gem "lzoruby"
6
7
 
7
8
  group :development do
9
+ gem "rake", "0.8.4"
8
10
  gem "bundler", "~> 1.0.0"
9
11
  gem "jeweler", "~> 1.6.0"
10
12
  gem "rcov", ">= 0"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0.alpha
data/bin/encbs CHANGED
@@ -8,218 +8,261 @@ require 'fileutils'
8
8
  require 'openssl'
9
9
  require 'socket'
10
10
  require 'helpers'
11
-
12
- safe_require do
13
- require 'slop'
14
- require 'fog'
15
- require 'progressbar'
16
- end
11
+ require 'slop'
12
+ require 'fog'
13
+ require 'progressbar'
14
+ require 'lzoruby'
15
+ require 'zlib'
16
+ require 'base64'
17
17
 
18
18
  require 'backup'
19
19
  require 'encbsconfig'
20
20
 
21
- opts = Slop.parse :help => true do
22
- on :a, :add, "Add path to backup", true
23
- on :b, :bucket, "Set Amazon S3 bucket to backup", true
24
- on :c, :config, "Use config file to upload backup", true
25
- on :colorize, "Colorize print to console"
26
- on :d, :date, "Date for backup restore (default: last)", true
27
- on :g, :generate, "Generate RSA keys (option: 4096, 2048)", true
28
- on :h, :hostname, "Set hostname (default: system)", true
29
- on :i, :increment, "Use increment mode for backup (default: false)"
30
- on :j, :jar, "Versions of jar (option: hash or path)", true
31
- on :k, :key, "Set API key to access Amazon S3", true
32
- on :l, :local, "Backup in local directory", true
33
- on :list, "List of jars"
34
- on :r, :rescue, "Return data from backup (option: jar, path or filter)", true
35
- on :s, :secret, "Set API secret to access Amazon S3", true
36
- on :t, :to, "Path to recovery (default: /)", true
37
- on :token, "RSA Key to encrypt/decrypt backup data", true
38
- on :v, :verbose, "Verbose mode"
39
-
40
- banner "Usage:\n $ encbs [options]\n\nOptions:"
41
- end
42
-
43
- @config = EncbsConfig.new
44
-
45
- if ARGV.empty?
46
- if File.exists?("#{Dir.getwd}/Encbsfile")
47
- @config.load "#{Dir.getwd}/Encbsfile"
21
+ begin
22
+ if lock_exists?
23
+ puts_fail "Lock file exists. Make sure that an encbs instance does not exists."
48
24
  else
49
- puts opts.help
50
- exit
51
- end
52
- end
53
-
54
- @config.load opts[:config] if opts.config?
55
-
56
- $PRINT_VERBOSE = @config.verbose || opts.verbose?
57
- $COLORIZE = @config.colorize || opts.colorize?
58
-
59
- if opts.generate?
60
- bits = opts[:generate].to_i
61
- puts_fail "Unsupport #{bits} bits" unless bits == 4096 or bits == 2048
62
- puts "Generate #{bits} bits RSA keys"
63
- Crypto::create_keys(
64
- File.join(Dir.getwd, "rsa_key"),
65
- File.join(Dir.getwd, "rsa_key.pub"),
66
- bits
67
- )
68
- puts "Done!"
69
-
70
- exit
71
- end
72
-
73
- if opts.local?
74
- try_create_dir opts[:local]
75
- @backup = Backup::Instance.new opts[:local]
76
- else
77
- [:key, :secret, :bucket].each do |arg|
78
- if opts[arg].nil? and @config.send(arg).nil?
79
- puts_fail "Argument '--#{arg}' should not be empty"
80
- end
25
+ create_lock
81
26
  end
82
- @backup = Backup::Instance.new(
83
- "backups",
84
- true,
85
- :bucket => @config.bucket || opts[:bucket],
86
- :key => @config.key || opts[:key],
87
- :secret => @config.secret || opts[:secret]
88
- )
89
- end
90
-
91
- @backup.hostname = @config.hostname || (opts[:hostname] if opts.hostname?)
92
-
93
- if opts.list?
94
- jars_list = @backup.jars
95
-
96
- unless jars_list.empty?
97
- puts "List of jars:\n"
98
- jars_list.keys.sort.each do |key|
99
- puts " #{key.dark_green}: #{jars_list[key]}"
100
- end
101
- else
102
- puts "Nothing to listing."
27
+
28
+ opts = Slop.parse :help => true do
29
+ on :a, :add, "Add path to backup", true
30
+ on :b, :bucket, "Set Amazon S3 bucket to backup", true
31
+ on :c, :config, "Use config file to upload backup", true
32
+ on :colorize, "Colorize print to console"
33
+ on :compression, "Use gzip for file (de)compression (option: gzip, lzo)", true
34
+ on :d, :date, "Date for backup restore (default: last)", true
35
+ on :g, :generate, "Generate RSA keys (option: 4096, 2048)", true
36
+ on :h, :hostname, "Set hostname (default: system)", true
37
+ on :i, :increment, "Use increment mode for backup (default: false)"
38
+ on :j, :jar, "Versions of jar (option: hash or path)", true
39
+ on :k, :key, "Set API key to access Amazon S3", true
40
+ on :l, :local, "Backup in local directory", true
41
+ on :list, "List of jars"
42
+ on :r, :rescue, "Return data from backup (option: jar, path or filter)", true
43
+ on :s, :secret, "Set API secret to access Amazon S3", true
44
+ on :t, :to, "Path to recovery (default: /)", true
45
+ on :timeout, "Timeout(sec) to try pushing into cloud (default: 60s)", true
46
+ on :token, "RSA key to encrypt/decrypt backup data", true
47
+ on :size, "RSA private key length (default: 4096)", true
48
+ on :v, :verbose, "Verbose mode"
49
+
50
+ banner "Usage:\n $ encbs [options]\n\nOptions:"
103
51
  end
104
52
 
105
- exit
106
- end
53
+ @config = EncbsConfig.new
107
54
 
108
- if @config.token || opts.token?
109
- key = @config.token || opts[:token]
55
+ if ARGV.empty?
56
+ if File.exists?("#{Dir.getwd}/Encbsfile")
57
+ @config.load "#{Dir.getwd}/Encbsfile"
58
+ else
59
+ puts opts.help
60
+ exit
61
+ end
62
+ end
110
63
 
111
- puts_fail "Key #{key.dark_green} not found" unless File.exists? key
64
+ @config.load opts[:config] if opts.config?
112
65
 
113
- @backup.key = key
114
- end
66
+ $PRINT_VERBOSE = @config.verbose || opts.verbose?
67
+ $COLORIZE = @config.colorize || opts.colorize?
115
68
 
116
- if opts.date?
117
- date = opts[:date].split("-")
69
+ if opts.generate?
70
+ bits = opts[:generate].to_i
71
+ puts_fail "Unsupport #{bits} bits" unless bits == 4096 or bits == 2048
72
+ puts "Generate #{bits} bits RSA keys"
73
+ Crypto::create_keys(
74
+ File.join(Dir.getwd, "rsa_key"),
75
+ File.join(Dir.getwd, "rsa_key.pub"),
76
+ bits
77
+ )
78
+ puts "Done!"
118
79
 
119
- unless date.length == 1
120
- @start_date = Backup::Timestamp.parse_timestamp date[0]
121
- @end_date = Backup::Timestamp.parse_timestamp date[1], true
80
+ exit
81
+ end
122
82
 
123
- puts_fail "Last date less than start date" if start_date > end_date
83
+ if opts.local?
84
+ try_create_dir opts[:local]
85
+ @backup = Backup::Instance.new opts[:local]
124
86
  else
125
- @start_date = Backup::Timestamp.parse_timestamp date[0]
126
- @end_date = Backup::Timestamp.parse_timestamp date[0], true
87
+ [:key, :secret, :bucket].each do |arg|
88
+ if opts[arg].nil? and @config.send(arg).nil?
89
+ puts_fail "Argument '--#{arg}' should not be empty"
90
+ end
91
+ end
92
+ @backup = Backup::Instance.new(
93
+ "backups",
94
+ true,
95
+ :bucket => @config.bucket || opts[:bucket],
96
+ :key => @config.key || opts[:key],
97
+ :secret => @config.secret || opts[:secret]
98
+ )
127
99
  end
128
- else
129
- @start_date = nil
130
- @end_date = Time.now.utc
131
- end
132
100
 
133
- if opts.jar?
134
- opts[:jar].split(" ").each do |jar|
135
- versions = @backup.jar_versions(jar)
101
+ hostname = @config.hostname || opts[:hostname] if opts.hostname?
102
+ @backup.hostname = hostname unless hostname.nil?
103
+
104
+ timeout = @config.timeout || opts[:timeout] if opts.timeout?
105
+ @backup.file_item.timeout = timeout unless timeout.nil?
136
106
 
137
- unless versions.empty?
138
- puts "Versions of backup '#{jar}':"
107
+ compression = @config.compression || opts[:compression] if opts.compression?
108
+ @backup.compression = compression unless compression.nil?
139
109
 
140
- versions.each do |version|
141
- puts " => #{version.dark_green}: #{Backup::Timestamp.to_str(version)}"
110
+ if opts.list?
111
+ jars_list = @backup.jars
112
+
113
+ unless jars_list.empty?
114
+ puts "List of jars:\n"
115
+ jars_list.keys.sort.each do |key|
116
+ puts " #{key.dark_green}: #{jars_list[key]}"
142
117
  end
143
118
  else
144
- puts "Versions doesn't exists for jar: #{jar}"
119
+ puts "Nothing to listing."
145
120
  end
121
+
122
+ exit
146
123
  end
147
124
 
148
- exit
149
- end
125
+ if !!@config.token || opts.token?
126
+ key = @config.token || opts[:token]
127
+ puts_fail "Key #{key.dark_green} not found" unless File.exists? key
150
128
 
151
- #TODO: Support rescue option as hash
152
- if opts.rescue?
153
- paths = opts[:rescue].split(" ")
154
- jars_list = @backup.jars
129
+ size = (@config.size || opts[:size]).to_i
130
+ puts_fail "Unsupport #{size} bits" unless size == 4096 or size == 2048
155
131
 
156
- include_path = lambda {|path| jars_list.keys.include? path}
132
+ @backup.rsa_key(key, size)
133
+ end
157
134
 
158
- jars_hashes = paths.map do |path|
159
- path = File.expand_path path
135
+ if opts.date?
136
+ date = opts[:date].split("-")
160
137
 
161
- unless include_path[path] or include_path["#{path}/"]
162
- puts_fail "Jar \"#{path}\" not exists."
163
- end
138
+ unless date.length == 1
139
+ @start_date = Backup::Timestamp.parse_timestamp date[0]
140
+ @end_date = Backup::Timestamp.parse_timestamp date[1], true
164
141
 
165
- jars_list[path] || jars_list["#{path}/"]
142
+ puts_fail "Last date less than start date" if start_date > end_date
143
+ else
144
+ @start_date = Backup::Timestamp.parse_timestamp date[0]
145
+ @end_date = Backup::Timestamp.parse_timestamp date[0], true
146
+ end
147
+ else
148
+ @start_date = nil
149
+ @end_date = Time.now.utc
166
150
  end
167
151
 
168
- if opts.to?
169
- @to = File.expand_path opts[:to]
170
- try_create_dir @to
171
- else
172
- @to = "/"
152
+ if opts.jar?
153
+ opts[:jar].split(" ").each do |jar|
154
+ versions = @backup.jar_versions(jar)
155
+
156
+ unless versions.empty?
157
+ puts "Versions of backup '#{jar}':"
158
+
159
+ versions.each do |version|
160
+ puts " => #{version.dark_green}: #{Backup::Timestamp.to_str(version)}"
161
+ end
162
+ else
163
+ puts "Versions doesn't exists for jar: #{jar}"
164
+ end
165
+ end
166
+
167
+ exit
173
168
  end
174
169
 
175
- #TODO: Confirm flag
176
- #TODO: Empty destination directory
170
+ #TODO: Support rescue option as hash
171
+ if opts.rescue?
172
+ paths = opts[:rescue].split(" ")
173
+ jars_list = @backup.jars
177
174
 
178
- @index = {}
175
+ include_path = lambda {|path| jars_list.keys.include? path}
179
176
 
180
- jars_hashes.each do |hash|
181
- versions = @backup.jar_versions(hash)
182
- # puts "Versions: #{versions}" #FIXME
177
+ jars_hashes = paths.map do |path|
178
+ path = File.expand_path path
183
179
 
184
- last_version = Backup::Timestamp.last_from(versions, @end_date, @start_date)
180
+ unless include_path[path] or include_path["#{path}/"]
181
+ puts_fail "Jar \"#{path}\" not exists."
182
+ end
185
183
 
186
- unless last_version.nil?
187
- @index[hash] = last_version
184
+ jars_list[path] || jars_list["#{path}/"]
185
+ end
186
+
187
+ if opts.to?
188
+ @to = File.expand_path opts[:to]
189
+ try_create_dir @to
188
190
  else
189
- error_path = "#{Backup::Jar.hash_to_path(@backup.root_path, hash)}"
190
- start_date = Backup::Timestamp.to_s(@start_date)
191
- end_date = Backup::Timestamp.to_s(@end_date)
191
+ @to = "/"
192
+ end
193
+
194
+ #TODO: Confirm flag
195
+ #TODO: Empty destination directory
196
+
197
+ @index = {}
198
+
199
+ jars_hashes.each do |hash|
200
+ versions = @backup.jar_versions(hash)
201
+ # puts "Versions: #{versions}" #FIXME
202
+
203
+ last_version = Backup::Timestamp.last_from(versions, @end_date, @start_date)
192
204
 
193
- unless @end_date == @start_date
194
- puts_fail "Nothing found for #{error_path}, between date: #{start_date} - #{end_date}"
205
+ unless last_version.nil?
206
+ @index[hash] = last_version
195
207
  else
196
- puts_fail "Nothing found for #{error_path}, for date: #{end_date}"
208
+ error_path = "#{Backup::Jar.hash_to_path(@backup.file_item, @backup.root_path, hash)}"
209
+ start_date = Backup::Timestamp.to_s(@start_date)
210
+ end_date = Backup::Timestamp.to_s(@end_date)
211
+
212
+ unless @start_date.nil?
213
+ puts_fail "Nothing found for #{error_path}, between date: #{start_date} - #{end_date}"
214
+ else
215
+ puts_fail "Nothing found for #{error_path}, for date: #{end_date}"
216
+ end
197
217
  end
198
218
  end
199
- end
200
219
 
201
- @index.each do |hash, timestamp|
202
- # puts "#{hash}: #{timestamp}" #FIXME
203
- @backup.restore_jar_to(hash, timestamp, @to)
220
+ @index.each do |hash, timestamp|
221
+ @backup.restore_jar_to(hash, timestamp, @to)
222
+ end
223
+
224
+ puts "Done!".green
225
+ exit
204
226
  end
205
227
 
206
- puts "Done!".green
207
- exit
208
- end
228
+ if !!@config.paths or opts.add?
229
+ if File.exists? "/var/tmp/encbs.swap"
230
+ meta = YAML::load open("/var/tmp/encbs.swap").read
231
+ jar_path, timestamp = meta[:jar_path], meta[:timestamp]
232
+ dirs = @backup.file_item.dir File.expand_path("../", jar_path)
233
+
234
+ if dirs.include? File.basename(jar_path)
235
+ meta.delete :timestamp
236
+ meta.delete :jar_path
237
+
238
+ @backup.file_item.create_file_once(
239
+ "#{jar_path}/#{timestamp}.yml",
240
+ meta.to_yaml
241
+ )
242
+
243
+ FileUtils.rm "/var/tmp/encbs.swap"
244
+ end
245
+ end
246
+
247
+ if opts.add?
248
+ paths = opts[:add].split(" ")
249
+ else
250
+ paths = @config.paths.split(" ")
251
+ end
209
252
 
210
- if @config.paths || opts.add?
211
- paths = @config.paths.split(" ") || opts[:add].split(" ")
253
+ paths = paths.map do |path|
254
+ path = File.expand_path path
255
+ puts_fail "Path \"#{path}\" not exists." unless File.exists? path
212
256
 
213
- paths = paths.map do |path|
214
- path = File.expand_path path
215
- puts_fail "Path \"#{path}\" not exists." unless File.exists? path
257
+ path
258
+ end
216
259
 
217
- path
218
- end
260
+ paths.each do |path|
261
+ @backup.create! path, @config.increment || opts.increment?
262
+ end
219
263
 
220
- paths.each do |path|
221
- @backup.create! path, @config.increment || opts.increment?
264
+ puts "Done!".green
222
265
  end
223
-
224
- puts "Done!".green
225
- end
266
+ ensure
267
+ remove_lock
268
+ end
data/encbs.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{encbs}
8
- s.version = "0.1.3"
8
+ s.version = "0.2.0.alpha"
9
9
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Timothy Klim"]
12
- s.date = %q{2011-05-14}
12
+ s.date = %q{2011-05-23}
13
13
  s.default_executable = %q{encbs}
14
14
  s.description = %q{Simple backup system for pushing into cloud}
15
15
  s.email = %q{klimtimothy@gmail.com}
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
26
26
  "VERSION",
27
27
  "bin/encbs",
28
28
  "encbs.gemspec",
29
+ "lib/archive.rb",
29
30
  "lib/backup.rb",
30
31
  "lib/backup/file_item.rb",
31
32
  "lib/backup/file_item/base.rb",
@@ -58,6 +59,8 @@ Gem::Specification.new do |s|
58
59
  s.add_runtime_dependency(%q<fog>, [">= 0"])
59
60
  s.add_runtime_dependency(%q<slop>, [">= 0"])
60
61
  s.add_runtime_dependency(%q<ruby-progressbar>, [">= 0"])
62
+ s.add_runtime_dependency(%q<lzoruby>, [">= 0"])
63
+ s.add_development_dependency(%q<rake>, ["= 0.8.4"])
61
64
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
62
65
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.0"])
63
66
  s.add_development_dependency(%q<rcov>, [">= 0"])
@@ -65,6 +68,8 @@ Gem::Specification.new do |s|
65
68
  s.add_dependency(%q<fog>, [">= 0"])
66
69
  s.add_dependency(%q<slop>, [">= 0"])
67
70
  s.add_dependency(%q<ruby-progressbar>, [">= 0"])
71
+ s.add_dependency(%q<lzoruby>, [">= 0"])
72
+ s.add_dependency(%q<rake>, ["= 0.8.4"])
68
73
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
74
  s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
70
75
  s.add_dependency(%q<rcov>, [">= 0"])
@@ -73,6 +78,8 @@ Gem::Specification.new do |s|
73
78
  s.add_dependency(%q<fog>, [">= 0"])
74
79
  s.add_dependency(%q<slop>, [">= 0"])
75
80
  s.add_dependency(%q<ruby-progressbar>, [">= 0"])
81
+ s.add_dependency(%q<lzoruby>, [">= 0"])
82
+ s.add_dependency(%q<rake>, ["= 0.8.4"])
76
83
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
77
84
  s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
78
85
  s.add_dependency(%q<rcov>, [">= 0"])
data/lib/archive.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'zlib'
2
+ require 'lzoruby'
3
+
4
+ module GZIP
5
+ def self.compress string, level
6
+ z = Zlib::Deflate.new level
7
+ dst = z.deflate string, Zlib::FINISH
8
+ z.close
9
+ dst
10
+ end
11
+
12
+ def self.decompress string
13
+ zstream = Zlib::Inflate.new
14
+ buf = zstream.inflate string
15
+ zstream.finish
16
+ zstream.close
17
+ buf
18
+ end
19
+ end
20
+
21
+ class Archive
22
+ attr_reader :type
23
+
24
+ def initialize type
25
+ fail "Error" unless [:lzo, :gzip].include? type.downcase
26
+ instance_eval %{@type = #{type.to_s.upcase}}
27
+ end
28
+
29
+ def method_missing name, *args
30
+ StringIO.new @type.send(name, *args)
31
+ end
32
+ end
@@ -9,7 +9,7 @@ module Backup
9
9
  end
10
10
  end
11
11
 
12
- def stat(file, timestamp = nil)
12
+ def stat(file)
13
13
  files = {}
14
14
 
15
15
  stat = File.new(file).stat
@@ -18,10 +18,8 @@ module Backup
18
18
  :gid => stat.gid,
19
19
  :mode => stat.mode
20
20
  }
21
- files[file][:timestamp] = timestamp if timestamp
22
-
23
21
  unless Dir.exists?(file)
24
- files[file][:checksum] = Digest::MD5.hexdigest(File.open(file).read)
22
+ files[file][:checksum] = Digest::MD5.hexdigest File.open(file, 'rb').read
25
23
  end
26
24
 
27
25
  files
@@ -3,7 +3,7 @@ require 'backup/file_item/base'
3
3
  module Backup
4
4
  module FileItem
5
5
  class Cloud < Backup::FileItem::Base
6
- attr_reader :key, :secret, :backet, :provider
6
+ attr_reader :key, :secret, :backet, :provider, :timeout
7
7
 
8
8
  def initialize(args = {})
9
9
  puts_fail "Empty hash in Cloud initialize method" if args.empty?
@@ -12,12 +12,16 @@ module Backup
12
12
  puts_fail "'#{arg.to_s.green}' should not be empty" if args[arg].nil?
13
13
  instance_eval %{@#{arg} = args[:#{arg}]}
14
14
  end
15
+ @timeout = 60
15
16
 
16
- try_to_connect_with_cloud
17
+ try_to_connect_with_cloud
18
+ end
19
+
20
+ def timeout=(time)
21
+ @timeout = time
17
22
  end
18
23
 
19
24
  def create_directory_once(*directories)
20
- # Nothing happen
21
25
  end
22
26
 
23
27
  def create_file_once(file, data)
@@ -44,7 +48,7 @@ module Backup
44
48
  files = @directory.files.all(
45
49
  :prefix => path,
46
50
  :max_keys => 30_000
47
- ).map &:key
51
+ ).map(&:key)
48
52
 
49
53
  files.map do |item|
50
54
  match = item.match(/^#{path}\/([^\/]+#{mask}).*$/)
@@ -64,6 +68,7 @@ module Backup
64
68
  begin
65
69
  yield
66
70
  rescue Exception => e
71
+ sleep @timeout
67
72
  try_to_connect_with_cloud
68
73
 
69
74
  yield
@@ -1,8 +1,14 @@
1
1
  require 'backup/file_item/base'
2
2
 
3
3
  module Backup
4
- module FileItem
4
+ module FileItem
5
5
  class Local < Backup::FileItem::Base
6
+ attr_reader :timeout
7
+
8
+ def initialize
9
+ @timeout = 0
10
+ end
11
+
6
12
  def create_directory_once(*directories)
7
13
  directories.each do |path|
8
14
  FileUtils.mkdir_p(path) unless Dir.exists?(path)
@@ -10,16 +16,24 @@ module Backup
10
16
  end
11
17
 
12
18
  def create_file_once(file, data)
13
- date = date.read if date.is_a? File
14
- File.open(file, "w").puts(data) unless File.exists?(file)
19
+ data = data.read if data.is_a? File or data.is_a? StringIO
20
+ File.open(file, "wb").puts(data) unless File.exists?(file)
15
21
  end
16
22
 
17
23
  def read_file(file)
18
- open(file).read if File.exists? file
24
+ File.open(file, 'rb').read if File.exists? file
25
+ end
26
+
27
+ def timeout=(time)
19
28
  end
20
29
 
21
30
  def dir(path, mask = "*")
22
- Dir["#{path}/#{mask}"]
31
+ r_mask = mask.gsub('.', '\.').gsub('*', '[^\/]')
32
+
33
+ Dir["#{path}/#{mask}"].map do |item|
34
+ match = item.match(/^#{path}\/([^\/]+#{r_mask}).*$/)
35
+ match[1] if match
36
+ end.compact.uniq
23
37
  end
24
38
  end
25
39
  end
data/lib/backup/jar.rb CHANGED
@@ -12,82 +12,112 @@ module Backup
12
12
  Digest::MD5.hexdigest(@local_path)
13
13
  end
14
14
 
15
- def save(increment = false)
16
- unless increment
17
- @local_files = hash_local_files
18
- else
19
- @local_files = {}
20
- current_files = hash_local_files
15
+ def save increment = false, compression = nil
16
+ @meta_index = {}
17
+ @local_files = hash_local_files
21
18
 
19
+ if increment
22
20
  last_timestamp = Jar.jar_versions(@file_item, @root_path, jar_hash, true).last
23
21
 
24
22
  if last_timestamp.nil?
25
23
  puts_fail "First you must create a full backup for #{@local_path.dark_green}"
26
24
  end
27
25
 
28
- last_index = Jar.fetch_index_for(@file_item, @root_path, jar_hash, last_timestamp)
29
-
30
- current_files.keys.each do |file|
31
- @local_files[file] = current_files[file]
26
+ @last_index = Jar.fetch_index_for(@file_item, @root_path, jar_hash, last_timestamp)
32
27
 
33
- #TODO: Cut to a new method {
34
- current = current_files[file].dup
28
+ @local_files.keys.each do |file|
29
+ current = @local_files[file].dup
35
30
  current.delete(:timestamp)
36
-
37
- unless last_index[file].nil?
38
- backup = last_index[file].dup
31
+
32
+ unless @last_index[file].nil?
33
+ backup = @last_index[file].dup
39
34
  backup.delete(:timestamp)
40
-
35
+
41
36
  if (current == backup) or
42
37
  (!current[:checksum].nil? and current[:checksum] == backup[:checksum])
43
-
44
- @local_files[file][:timestamp] = last_index[file][:timestamp]
38
+
39
+ @meta_index[file] = @local_files[file]
40
+ @meta_index[file][:timestamp] = @last_index[file][:timestamp]
45
41
  end
46
42
  end
47
- # }
48
43
  end
49
44
  end
50
45
 
46
+ unless @key.nil?
47
+ @meta_index.merge!({
48
+ :checksum => Base64.encode64(@key.encrypt(@timestamp))
49
+ })
50
+ end
51
+
52
+ unless compression.nil?
53
+ @meta_index.merge!({
54
+ :compression => compression.type.to_s
55
+ })
56
+ end
57
+
51
58
  @file_item.create_directory_once meta_jars_path, meta_jar_path, jar_data_path
52
59
  @file_item.create_file_once(
53
- "#{meta_jars_path}/#{jar_hash}",
60
+ "#{meta_jars_path}/#{jar_hash}",
54
61
  @file_item.semantic_path(@local_path)
55
62
  )
56
- @file_item.create_file_once(
57
- "#{meta_jar_path}/#{@timestamp}.yml",
58
- @local_files.to_yaml
59
- )
60
63
 
61
- @local_files.select! {|k, v| v[:timestamp] == @timestamp}
62
64
  if @file_item.is_a? Backup::FileItem::Cloud
63
65
  pbar = ProgressBar.new(
64
- "Uploading",
66
+ "Uploading",
65
67
  @local_files.keys.count
66
68
  )
67
69
  else
68
70
  pbar = ProgressBar.new(
69
- "Copying",
71
+ "Copying",
70
72
  @local_files.keys.count
71
73
  )
72
74
  end
73
-
75
+
74
76
  pbar.bar_mark = '*'
75
77
 
76
- @local_files.keys.each do |file|
77
- unless Dir.exists?(file)
78
- data = if @key
79
- @key.encrypt_to_stream(File.open(file).read)
80
- else
81
- File.open(file)
78
+ #@local_files = @meta_index.select {|k, v| v[:timestamp] == @timestamp if v.is_a? Hash}
79
+
80
+ begin
81
+ @local_files.keys.each do |file|
82
+ if @meta_index[file].nil?
83
+ unless Dir.exists?(file)
84
+ data = StringIO.new File.open(file, 'rb').read
85
+ checksum = Digest::MD5.hexdigest(data.read)
86
+
87
+ data.seek 0
88
+ data = compression.compress(data.read, 3) unless compression.nil?
89
+
90
+ data = @key.encrypt_to_stream(data) if @key
91
+
92
+ @file_item.create_file_once(
93
+ "#{jar_data_path}/#{@file_item.file_hash file}",
94
+ data
95
+ )
96
+
97
+ pbar.inc
98
+ end
99
+
100
+ @meta_index[file] = @local_files[file]
101
+ @meta_index[file][:checksum] = checksum
102
+ @meta_index[file][:timestamp] = @timestamp
82
103
  end
83
-
84
- @file_item.create_file_once(
85
- "#{jar_data_path}/#{@file_item.file_hash file}",
86
- data
87
- )
88
-
89
- pbar.inc
90
104
  end
105
+ rescue Exception => e
106
+ @meta_index.merge!({
107
+ :jar_path => meta_jar_path,
108
+ :timestamp => @timestamp
109
+ })
110
+ File.open("/var/tmp/encbs.swap", "w") do |f|
111
+ f.puts @meta_index.to_yaml
112
+ end
113
+
114
+ puts
115
+ puts_fail "Index file has been saved that to allow upload into cloud in next run."
116
+ else
117
+ @file_item.create_file_once(
118
+ "#{meta_jar_path}/#{@timestamp}.yml",
119
+ @meta_index.to_yaml
120
+ )
91
121
  end
92
122
 
93
123
  pbar.finish
@@ -96,8 +126,6 @@ module Backup
96
126
  def hash_local_files
97
127
  files = {}
98
128
 
99
- puts_verbose "Create index for #{@local_path.dark_green}"
100
-
101
129
  if Dir.exists? @local_path
102
130
  matches = Dir.glob(File.join(@local_path, "/**/*"), File::FNM_DOTMATCH)
103
131
 
@@ -108,10 +136,10 @@ module Backup
108
136
  matches << @local_path
109
137
 
110
138
  matches.each do |match|
111
- files.merge!(@file_item.stat(match, @timestamp))
139
+ files.merge!(@file_item.stat match)
112
140
  end
113
141
  else
114
- files = @file_item.stat(@local_path, @timestamp)
142
+ files = @file_item.stat @local_path
115
143
  end
116
144
 
117
145
  files
data/lib/backup.rb CHANGED
@@ -1,11 +1,14 @@
1
+ require 'stringio'
2
+
1
3
  require 'backup/file_item'
2
4
  require 'backup/timestamp'
3
5
  require 'backup/jar'
6
+ require 'archive'
4
7
  require 'crypto'
5
8
 
6
9
  module Backup
7
10
  class Instance
8
- attr_reader :root_path, :timestamp, :hostname, :file_item
11
+ attr_reader :root_path, :timestamp, :hostname, :file_item, :compression
9
12
 
10
13
  def initialize(root_path, cloud = false, *args)
11
14
  if cloud
@@ -15,22 +18,27 @@ module Backup
15
18
  end
16
19
 
17
20
  @hostname = Socket.gethostname
21
+ @_root_path = @root_path
18
22
  @root_path = "#{root_path}/#{@hostname}"
19
23
  @timestamp = Backup::Timestamp.create
20
24
  end
21
25
 
26
+ def compression= type
27
+ @compression = Archive.new type.to_sym
28
+ end
29
+
22
30
  def hostname=(host)
23
31
  @hostname = host
24
- @root_path = "#{root_path}/#{@hostname}"
32
+ @root_path = "#{@_root_path}/#{@hostname}"
25
33
  end
26
34
 
27
- def key=(path)
28
- @key = Crypto::Key.from_file(path)
35
+ def rsa_key(path, size)
36
+ @key = Crypto::Key.from_file(path, size)
29
37
  end
30
38
 
31
39
  def create!(local_path, increment = false)
32
40
  jar = Jar.new(@file_item, @root_path, local_path, @key)
33
- jar.save(increment)
41
+ jar.save(increment, @compression)
34
42
  end
35
43
 
36
44
  def jars
@@ -44,8 +52,24 @@ module Backup
44
52
  def restore_jar_to(hash, timestamp, to)
45
53
  files = Jar.fetch_index_for(@file_item, @root_path, hash, timestamp)
46
54
 
55
+ unless files[:checksum].nil?
56
+ if @key.nil? or
57
+ @key.decrypt(Base64.decode64(files[:checksum])) != timestamp
58
+
59
+ puts_fail "Checksum does not match."
60
+ end
61
+ files.delete :checksum
62
+ end
63
+
64
+ unless files[:compression].nil?
65
+ compression = Archive.new files[:compression].to_sym
66
+ files.delete :compression
67
+ else
68
+ compression = nil
69
+ end
70
+
47
71
  pbar = ProgressBar.new(
48
- "Restoring",
72
+ "Restoring",
49
73
  files.keys.length
50
74
  )
51
75
  pbar.bar_mark = '*'
@@ -67,22 +91,27 @@ module Backup
67
91
  restore_file,
68
92
  file_ok[:uid],
69
93
  file_ok[:gid],
70
- current_file[:uid],
94
+ current_file[:uid],
71
95
  current_file[:gid]
72
96
  )
73
97
  else
74
98
  try_create_dir(File.dirname restore_file)
75
99
 
76
100
  begin
77
- File.open(restore_file, "w") do |f|
101
+ File.open(restore_file, "wb") do |f|
78
102
  f.chmod current_file[:mode]
79
103
  f.chown current_file[:uid], current_file[:gid]
80
104
 
81
105
  remote_path = "#{@root_path}/#{hash}/#{current_file[:timestamp]}"
82
106
  remote_path += "/#{@file_item.file_hash file}"
83
107
 
84
- data = @file_item.read_file remote_path
85
- data = @key.decrypt_from_stream data if @key
108
+ data = @file_item.read_file(remote_path)
109
+ data = @key.decrypt_from_stream data if @key
110
+
111
+ unless compression.nil?
112
+ data = compression.decompress(data.chomp).read
113
+ end
114
+
86
115
  f.puts data
87
116
  end
88
117
 
@@ -90,7 +119,7 @@ module Backup
90
119
 
91
120
  check_mode(restore_file, file_ok[:mode], current_file[:mode])
92
121
  check_rights(
93
- restore_file,
122
+ restore_file,
94
123
  file_ok[:uid],
95
124
  file_ok[:gid],
96
125
  current_file[:uid],
data/lib/crypto.rb CHANGED
@@ -11,13 +11,14 @@ module Crypto
11
11
  end
12
12
 
13
13
  class Key
14
- def initialize(data)
14
+ def initialize(data, size)
15
15
  @public = (data =~ /^-----BEGIN (RSA|DSA) PRIVATE KEY-----$/).nil?
16
16
  @key = OpenSSL::PKey::RSA.new(data)
17
+ @size = (size == 4096 ? 512 : 256)
17
18
  end
18
19
 
19
- def self.from_file(filename)
20
- self.new File.read( filename )
20
+ def self.from_file(filename, size = 4096)
21
+ self.new(File.read(filename), size)
21
22
  end
22
23
 
23
24
  def encrypt_to_stream(data)
@@ -28,15 +29,16 @@ module Crypto
28
29
  encrypt_data << encrypt(buf)
29
30
  end
30
31
 
31
- encrypt_data.seek(0)
32
+ encrypt_data.seek 0
32
33
  encrypt_data
33
34
  end
34
35
 
35
36
  def decrypt_from_stream(data)
36
- encrypt_data = StringIO.new data
37
+ encrypt_data = StringIO.new(data.chomp)
38
+ encrypt_data.seek 0
37
39
  decrypt_data = ""
38
40
 
39
- while buf = encrypt_data.read(256) do
41
+ while buf = encrypt_data.read(@size) do
40
42
  decrypt_data += decrypt(buf)
41
43
  end
42
44
 
@@ -45,10 +47,14 @@ module Crypto
45
47
 
46
48
  def encrypt(text)
47
49
  @key.send("#{key_type}_encrypt", text)
50
+ rescue Exception => e
51
+ puts_fail "RSA encrypt error: #{e.message}"
48
52
  end
49
53
 
50
54
  def decrypt(text)
51
55
  @key.send("#{key_type}_decrypt", text)
56
+ rescue Exception => e
57
+ puts_fail "RSA decrypt error: #{e.message}"
52
58
  end
53
59
 
54
60
  def private?
data/lib/encbsconfig.rb CHANGED
@@ -1,21 +1,21 @@
1
1
  class EncbsConfig
2
- attr_reader :paths, :bucket, :colorize, :hostname, :increment, :key,
3
- :secret, :token, :verbose
2
+ attr_reader :paths, :bucket, :colorize, :compression, :hostname, :increment,
3
+ :key, :secret, :size, :token, :timeout, :verbose
4
4
 
5
5
  def initialize
6
6
  @paths = ""
7
7
  end
8
8
 
9
9
  def load(path)
10
- [:bucket, :colorize, :hostname, :increment, :key, :secret,
11
- :token, :verbose].each {|attr| eval "@#{attr} = nil"}
10
+ [:bucket, :colorize, :compression, :hostname, :increment, :key, :secret,
11
+ :size, :token, :timeout, :verbose].each {|attr| eval "@#{attr} = nil"}
12
12
 
13
13
  @paths = ""
14
14
 
15
- eval "#{open(path).read}"
15
+ instance_eval "#{open(path).read}"
16
16
  end
17
17
 
18
- def use_hostname(attr)
18
+ def use_hostname attr
19
19
  @hostname = attr
20
20
  end
21
21
 
@@ -27,27 +27,39 @@ class EncbsConfig
27
27
  @colorize = true
28
28
  end
29
29
 
30
- def public_key(attr)
30
+ def public_key attr
31
31
  @token = attr
32
32
  end
33
33
 
34
34
  def increment!
35
35
  @increment = true
36
36
  end
37
+
38
+ def use_compression attr
39
+ @compression = attr
40
+ end
37
41
 
38
- def aws_key(attr)
42
+ def aws_key attr
39
43
  @key = attr
40
44
  end
41
45
 
42
- def aws_secret(attr)
46
+ def aws_secret attr
43
47
  @secret = attr
44
48
  end
45
49
 
46
- def aws_bucket(attr)
50
+ def aws_bucket attr
47
51
  @bucket = attr
48
52
  end
49
53
 
54
+ def key_size attr
55
+ @size = attr
56
+ end
57
+
50
58
  def verbose!
51
59
  @verbose = true
52
60
  end
61
+
62
+ def set_timeout attr
63
+ @timeout = attr
64
+ end
53
65
  end
data/lib/helpers.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  def puts_fail(msg)
2
- STDERR.puts "#{"Error: ".red}#{msg}"
2
+ STDERR.puts "#{"Error! ".red}#{msg}"
3
3
 
4
4
  exit msg.length
5
5
  end
@@ -12,15 +12,6 @@ def print_verbose(msg)
12
12
  print msg if $PRINT_VERBOSE
13
13
  end
14
14
 
15
- def safe_require(&block)
16
- yield
17
- rescue Exception => e
18
- puts_fail %Q{This script use these gems: fog, slop.
19
- Make sure that you have them all.
20
- If you don't have, you may install them: $ gem install fog slop ruby-progressbar
21
- }
22
- end
23
-
24
15
  def try_create_dir(dir)
25
16
  begin
26
17
  FileUtils.mkdir_p dir unless Dir.exists? dir
@@ -41,6 +32,20 @@ def check_rights(file, first_uid, first_gid, second_uid, second_gid)
41
32
  end
42
33
  end
43
34
 
35
+ def create_lock
36
+ open("/tmp/encbs.lock", "w") do |f|
37
+ f.puts Process.pid
38
+ end
39
+ end
40
+
41
+ def remove_lock
42
+ FileUtils.rm "/tmp/encbs.lock" if File.exists? "/tmp/encbs.lock"
43
+ end
44
+
45
+ def lock_exists?
46
+ File.exists? "/tmp/encbs.lock"
47
+ end
48
+
44
49
  class String
45
50
  def red
46
51
  colorize(self, "\e[1m\e[31m")
data/test/helper.rb CHANGED
@@ -9,6 +9,9 @@ require 'socket'
9
9
  require 'helpers'
10
10
  require 'progressbar'
11
11
  require 'test/unit'
12
+ require 'base64'
13
+ require 'lzoruby'
14
+ require 'zlib'
12
15
 
13
16
  require 'backup'
14
17
 
@@ -12,30 +12,22 @@ class BackupFileItemTest < Test::Unit::TestCase
12
12
  end
13
13
 
14
14
  def test_file_stat
15
- file = @file_item.stat(
16
- __FILE__,
17
- Backup::Timestamp.create
18
- )
15
+ file = @file_item.stat __FILE__
19
16
  key = file.keys.first
20
17
 
21
18
  assert_not_nil file[key][:uid]
22
19
  assert_not_nil file[key][:gid]
23
20
  assert_not_nil file[key][:mode]
24
21
  assert_not_nil file[key][:checksum]
25
- assert_not_nil file[key][:timestamp]
26
22
  end
27
23
 
28
24
  def test_directory_stat
29
- file = @file_item.stat(
30
- File.dirname(__FILE__),
31
- Backup::Timestamp.create
32
- )
25
+ file = @file_item.stat File.dirname(__FILE__)
33
26
  key = file.keys.first
34
27
 
35
28
  assert_not_nil file[key][:uid]
36
29
  assert_not_nil file[key][:gid]
37
30
  assert_not_nil file[key][:mode]
38
31
  assert_nil file[key][:checksum]
39
- assert_not_nil file[key][:timestamp]
40
32
  end
41
33
  end
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: encbs
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.1.3
4
+ prerelease: 6
5
+ version: 0.2.0.alpha
6
6
  platform: ruby
7
7
  authors:
8
8
  - Timothy Klim
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-14 00:00:00 +04:00
13
+ date: 2011-05-23 00:00:00 +04:00
14
14
  default_executable: encbs
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -47,8 +47,30 @@ dependencies:
47
47
  prerelease: false
48
48
  version_requirements: *id003
49
49
  - !ruby/object:Gem::Dependency
50
- name: bundler
50
+ name: lzoruby
51
51
  requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: rake
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - "="
66
+ - !ruby/object:Gem::Version
67
+ version: 0.8.4
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: bundler
73
+ requirement: &id006 !ruby/object:Gem::Requirement
52
74
  none: false
53
75
  requirements:
54
76
  - - ~>
@@ -56,10 +78,10 @@ dependencies:
56
78
  version: 1.0.0
57
79
  type: :development
58
80
  prerelease: false
59
- version_requirements: *id004
81
+ version_requirements: *id006
60
82
  - !ruby/object:Gem::Dependency
61
83
  name: jeweler
62
- requirement: &id005 !ruby/object:Gem::Requirement
84
+ requirement: &id007 !ruby/object:Gem::Requirement
63
85
  none: false
64
86
  requirements:
65
87
  - - ~>
@@ -67,10 +89,10 @@ dependencies:
67
89
  version: 1.6.0
68
90
  type: :development
69
91
  prerelease: false
70
- version_requirements: *id005
92
+ version_requirements: *id007
71
93
  - !ruby/object:Gem::Dependency
72
94
  name: rcov
73
- requirement: &id006 !ruby/object:Gem::Requirement
95
+ requirement: &id008 !ruby/object:Gem::Requirement
74
96
  none: false
75
97
  requirements:
76
98
  - - ">="
@@ -78,7 +100,7 @@ dependencies:
78
100
  version: "0"
79
101
  type: :development
80
102
  prerelease: false
81
- version_requirements: *id006
103
+ version_requirements: *id008
82
104
  description: Simple backup system for pushing into cloud
83
105
  email: klimtimothy@gmail.com
84
106
  executables:
@@ -96,6 +118,7 @@ files:
96
118
  - VERSION
97
119
  - bin/encbs
98
120
  - encbs.gemspec
121
+ - lib/archive.rb
99
122
  - lib/backup.rb
100
123
  - lib/backup/file_item.rb
101
124
  - lib/backup/file_item/base.rb
@@ -128,16 +151,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
151
  requirements:
129
152
  - - ">="
130
153
  - !ruby/object:Gem::Version
131
- hash: -3909970263344363647
154
+ hash: -2056087500533480585
132
155
  segments:
133
156
  - 0
134
157
  version: "0"
135
158
  required_rubygems_version: !ruby/object:Gem::Requirement
136
159
  none: false
137
160
  requirements:
138
- - - ">="
161
+ - - ">"
139
162
  - !ruby/object:Gem::Version
140
- version: "0"
163
+ version: 1.3.1
141
164
  requirements: []
142
165
 
143
166
  rubyforge_project: