encbs 0.1.3 → 0.2.0.alpha

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/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: