backup 3.0.27 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/LICENSE.md +1 -1
  2. data/README.md +139 -386
  3. data/bin/backup +1 -7
  4. data/lib/backup.rb +3 -9
  5. data/lib/backup/archive.rb +26 -20
  6. data/lib/backup/cleaner.rb +2 -2
  7. data/lib/backup/cli.rb +366 -0
  8. data/lib/backup/compressor/base.rb +2 -2
  9. data/lib/backup/compressor/gzip.rb +35 -1
  10. data/lib/backup/config.rb +1 -2
  11. data/lib/backup/database/base.rb +2 -2
  12. data/lib/backup/database/mongodb.rb +3 -3
  13. data/lib/backup/database/mysql.rb +3 -2
  14. data/lib/backup/database/postgresql.rb +3 -2
  15. data/lib/backup/database/riak.rb +18 -5
  16. data/lib/backup/dependency.rb +144 -93
  17. data/lib/backup/encryptor/base.rb +2 -2
  18. data/lib/backup/logger.rb +108 -110
  19. data/lib/backup/logger/console.rb +51 -0
  20. data/lib/backup/logger/logfile.rb +113 -0
  21. data/lib/backup/logger/syslog.rb +116 -0
  22. data/lib/backup/model.rb +67 -65
  23. data/lib/backup/notifier/base.rb +1 -1
  24. data/lib/backup/notifier/hipchat.rb +1 -1
  25. data/lib/backup/notifier/mail.rb +1 -1
  26. data/lib/backup/notifier/pushover.rb +6 -3
  27. data/lib/backup/packager.rb +4 -4
  28. data/lib/backup/pipeline.rb +17 -3
  29. data/lib/backup/splitter.rb +2 -2
  30. data/lib/backup/storage/base.rb +2 -2
  31. data/lib/backup/storage/cloudfiles.rb +2 -2
  32. data/lib/backup/storage/dropbox.rb +4 -4
  33. data/lib/backup/storage/ftp.rb +2 -2
  34. data/lib/backup/storage/local.rb +2 -2
  35. data/lib/backup/storage/ninefold.rb +2 -2
  36. data/lib/backup/storage/rsync.rb +3 -3
  37. data/lib/backup/storage/s3.rb +2 -2
  38. data/lib/backup/storage/scp.rb +2 -6
  39. data/lib/backup/storage/sftp.rb +2 -5
  40. data/lib/backup/syncer/base.rb +1 -1
  41. data/lib/backup/syncer/cloud/base.rb +15 -8
  42. data/lib/backup/syncer/rsync/local.rb +1 -1
  43. data/lib/backup/syncer/rsync/pull.rb +1 -1
  44. data/lib/backup/syncer/rsync/push.rb +1 -1
  45. data/lib/backup/utilities.rb +211 -0
  46. data/lib/backup/version.rb +1 -1
  47. data/templates/cli/{utility/archive → archive} +4 -8
  48. data/templates/cli/{utility/compressor → compressor}/bzip2 +0 -0
  49. data/templates/cli/{utility/compressor → compressor}/custom +0 -0
  50. data/templates/cli/{utility/compressor → compressor}/gzip +0 -0
  51. data/templates/cli/{utility/compressor → compressor}/lzma +0 -0
  52. data/templates/cli/{utility/compressor → compressor}/pbzip2 +0 -0
  53. data/templates/cli/config +68 -0
  54. data/templates/cli/{utility/database → database}/mongodb +1 -1
  55. data/templates/cli/{utility/database → database}/mysql +1 -1
  56. data/templates/cli/{utility/database → database}/postgresql +1 -1
  57. data/templates/cli/{utility/database → database}/redis +0 -0
  58. data/templates/cli/database/riak +20 -0
  59. data/templates/cli/{utility/encryptor → encryptor}/gpg +0 -0
  60. data/templates/cli/{utility/encryptor → encryptor}/openssl +0 -0
  61. data/templates/cli/{utility/model.erb → model.erb} +4 -4
  62. data/templates/cli/{utility/notifier → notifier}/campfire +0 -0
  63. data/templates/cli/{utility/notifier → notifier}/hipchat +0 -0
  64. data/templates/cli/{utility/notifier → notifier}/mail +0 -0
  65. data/templates/cli/{utility/notifier → notifier}/prowl +0 -0
  66. data/templates/cli/{utility/notifier → notifier}/pushover +0 -0
  67. data/templates/cli/{utility/notifier → notifier}/twitter +0 -0
  68. data/templates/cli/{utility/splitter → splitter} +0 -0
  69. data/templates/cli/{utility/storage → storage}/cloud_files +0 -0
  70. data/templates/cli/{utility/storage → storage}/dropbox +0 -0
  71. data/templates/cli/{utility/storage → storage}/ftp +0 -0
  72. data/templates/cli/{utility/storage → storage}/local +0 -0
  73. data/templates/cli/{utility/storage → storage}/ninefold +0 -0
  74. data/templates/cli/{utility/storage → storage}/rsync +0 -0
  75. data/templates/cli/{utility/storage → storage}/s3 +0 -0
  76. data/templates/cli/{utility/storage → storage}/scp +0 -0
  77. data/templates/cli/{utility/storage → storage}/sftp +0 -0
  78. data/templates/cli/{utility/syncer → syncer}/cloud_files +0 -0
  79. data/templates/cli/{utility/syncer → syncer}/rsync_local +0 -0
  80. data/templates/cli/{utility/syncer → syncer}/rsync_pull +0 -0
  81. data/templates/cli/{utility/syncer → syncer}/rsync_push +0 -0
  82. data/templates/cli/{utility/syncer → syncer}/s3 +0 -0
  83. metadata +55 -131
  84. data/.gitignore +0 -8
  85. data/.travis.yml +0 -10
  86. data/Gemfile +0 -28
  87. data/Guardfile +0 -23
  88. data/backup.gemspec +0 -32
  89. data/lib/backup/cli/helpers.rb +0 -93
  90. data/lib/backup/cli/utility.rb +0 -255
  91. data/spec-live/.gitignore +0 -6
  92. data/spec-live/README +0 -7
  93. data/spec-live/backups/config.rb +0 -83
  94. data/spec-live/backups/config.yml.template +0 -46
  95. data/spec-live/backups/models.rb +0 -184
  96. data/spec-live/compressor/custom_spec.rb +0 -30
  97. data/spec-live/compressor/gzip_spec.rb +0 -30
  98. data/spec-live/encryptor/gpg_keys.rb +0 -239
  99. data/spec-live/encryptor/gpg_spec.rb +0 -287
  100. data/spec-live/notifier/mail_spec.rb +0 -121
  101. data/spec-live/spec_helper.rb +0 -151
  102. data/spec-live/storage/dropbox_spec.rb +0 -151
  103. data/spec-live/storage/local_spec.rb +0 -83
  104. data/spec-live/storage/scp_spec.rb +0 -193
  105. data/spec-live/syncer/cloud/s3_spec.rb +0 -124
  106. data/spec/archive_spec.rb +0 -335
  107. data/spec/cleaner_spec.rb +0 -312
  108. data/spec/cli/helpers_spec.rb +0 -301
  109. data/spec/cli/utility_spec.rb +0 -411
  110. data/spec/compressor/base_spec.rb +0 -52
  111. data/spec/compressor/bzip2_spec.rb +0 -217
  112. data/spec/compressor/custom_spec.rb +0 -106
  113. data/spec/compressor/gzip_spec.rb +0 -217
  114. data/spec/compressor/lzma_spec.rb +0 -123
  115. data/spec/compressor/pbzip2_spec.rb +0 -165
  116. data/spec/config_spec.rb +0 -321
  117. data/spec/configuration/helpers_spec.rb +0 -247
  118. data/spec/configuration/store_spec.rb +0 -39
  119. data/spec/configuration_spec.rb +0 -62
  120. data/spec/database/base_spec.rb +0 -63
  121. data/spec/database/mongodb_spec.rb +0 -510
  122. data/spec/database/mysql_spec.rb +0 -411
  123. data/spec/database/postgresql_spec.rb +0 -353
  124. data/spec/database/redis_spec.rb +0 -334
  125. data/spec/database/riak_spec.rb +0 -176
  126. data/spec/dependency_spec.rb +0 -51
  127. data/spec/encryptor/base_spec.rb +0 -40
  128. data/spec/encryptor/gpg_spec.rb +0 -909
  129. data/spec/encryptor/open_ssl_spec.rb +0 -148
  130. data/spec/errors_spec.rb +0 -306
  131. data/spec/logger_spec.rb +0 -367
  132. data/spec/model_spec.rb +0 -666
  133. data/spec/notifier/base_spec.rb +0 -104
  134. data/spec/notifier/campfire_spec.rb +0 -217
  135. data/spec/notifier/hipchat_spec.rb +0 -211
  136. data/spec/notifier/mail_spec.rb +0 -316
  137. data/spec/notifier/prowl_spec.rb +0 -138
  138. data/spec/notifier/pushover_spec.rb +0 -123
  139. data/spec/notifier/twitter_spec.rb +0 -153
  140. data/spec/package_spec.rb +0 -61
  141. data/spec/packager_spec.rb +0 -213
  142. data/spec/pipeline_spec.rb +0 -259
  143. data/spec/spec_helper.rb +0 -60
  144. data/spec/splitter_spec.rb +0 -120
  145. data/spec/storage/base_spec.rb +0 -166
  146. data/spec/storage/cloudfiles_spec.rb +0 -254
  147. data/spec/storage/cycler_spec.rb +0 -247
  148. data/spec/storage/dropbox_spec.rb +0 -480
  149. data/spec/storage/ftp_spec.rb +0 -271
  150. data/spec/storage/local_spec.rb +0 -259
  151. data/spec/storage/ninefold_spec.rb +0 -343
  152. data/spec/storage/rsync_spec.rb +0 -362
  153. data/spec/storage/s3_spec.rb +0 -245
  154. data/spec/storage/scp_spec.rb +0 -233
  155. data/spec/storage/sftp_spec.rb +0 -244
  156. data/spec/syncer/base_spec.rb +0 -109
  157. data/spec/syncer/cloud/base_spec.rb +0 -515
  158. data/spec/syncer/cloud/cloud_files_spec.rb +0 -181
  159. data/spec/syncer/cloud/s3_spec.rb +0 -174
  160. data/spec/syncer/rsync/base_spec.rb +0 -98
  161. data/spec/syncer/rsync/local_spec.rb +0 -149
  162. data/spec/syncer/rsync/pull_spec.rb +0 -98
  163. data/spec/syncer/rsync/push_spec.rb +0 -333
  164. data/spec/version_spec.rb +0 -21
  165. data/templates/cli/utility/config +0 -32
  166. data/templates/cli/utility/database/riak +0 -11
data/spec/logger_spec.rb DELETED
@@ -1,367 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require File.expand_path('../spec_helper.rb', __FILE__)
4
-
5
- describe Backup::Logger do
6
- let(:logger_time) { Time.now.strftime("%Y/%m/%d %H:%M:%S") }
7
- let(:logfile_path) { File.join(Backup::Config.log_path, 'backup.log') }
8
- let(:logfile_mock) { mock }
9
- let(:s) { sequence '' }
10
-
11
- before do
12
- Timecop.freeze(Time.now)
13
-
14
- # stubbed in spec_helper
15
- [:message, :error, :warn, :normal, :silent].each do |message_type|
16
- subject.unstub(message_type)
17
- end
18
-
19
- subject.quiet = nil
20
- subject.send(:remove_instance_variable, :@messages) rescue nil
21
- subject.send(:remove_instance_variable, :@has_warnings) rescue nil
22
- end
23
-
24
- describe '#message' do
25
- it 'sends a regular message to the console and log file' do
26
- subject.expects(:loggify).in_sequence(s).
27
- with('regular message', :message, :green).
28
- returns(:green_regular_message)
29
- subject.expects(:to_console).in_sequence(s).
30
- with(:green_regular_message)
31
- subject.expects(:loggify).in_sequence(s).
32
- with('regular message', :message).
33
- returns(:uncolored_regular_message)
34
- subject.expects(:to_file).in_sequence(s).
35
- with(:uncolored_regular_message)
36
-
37
- subject.message('regular message')
38
- end
39
- end
40
-
41
- describe '#error' do
42
- it 'sends an error message to the console (stderr) and log file' do
43
- subject.expects(:loggify).in_sequence(s).
44
- with('error message', :error, :red).
45
- returns(:red_error_message)
46
- subject.expects(:to_console).in_sequence(s).
47
- with(:red_error_message, true)
48
- subject.expects(:loggify).in_sequence(s).
49
- with('error message', :error).
50
- returns(:uncolored_error_message)
51
- subject.expects(:to_file).in_sequence(s).
52
- with(:uncolored_error_message)
53
-
54
- subject.error('error message')
55
- end
56
- end
57
-
58
- describe '#warn' do
59
- it 'sends a warning message to the console (stderr) and log file' do
60
- subject.expects(:loggify).in_sequence(s).
61
- with('warning message', :warning, :yellow).
62
- returns(:yellow_warning_message)
63
- subject.expects(:to_console).in_sequence(s).
64
- with(:yellow_warning_message, true)
65
- subject.expects(:loggify).in_sequence(s).
66
- with('warning message', :warning).
67
- returns(:uncolored_warning_message)
68
- subject.expects(:to_file).in_sequence(s).
69
- with(:uncolored_warning_message)
70
-
71
- subject.warn('warning message')
72
- end
73
-
74
- it 'sets has_warnings? to true' do
75
- subject.stubs(:to_console)
76
- subject.stubs(:to_file)
77
- expect { subject.warn('warning') }.
78
- to change{ subject.has_warnings? }.from(false).to(true)
79
- end
80
- end
81
-
82
- describe '#normal' do
83
- it 'sends a normal, unformatted message to the console and log file' do
84
- subject.expects(:loggify).in_sequence(s).
85
- with('normal message').
86
- returns(:unformatted_message)
87
- subject.expects(:to_console).in_sequence(s).
88
- with(:unformatted_message)
89
- subject.expects(:loggify).in_sequence(s).
90
- with('normal message').
91
- returns(:unformatted_message)
92
- subject.expects(:to_file).in_sequence(s).
93
- with(:unformatted_message)
94
-
95
- subject.normal('normal message')
96
- end
97
- end
98
-
99
- describe '#silent' do
100
- it 'sends a silent message to the log file' do
101
- subject.expects(:to_console).never
102
- subject.expects(:loggify).in_sequence(s).
103
- with('silent message', :silent).
104
- returns(:silent_message)
105
- subject.expects(:to_file).in_sequence(s).
106
- with(:silent_message)
107
-
108
- subject.silent('silent message')
109
- end
110
- end
111
-
112
- describe '#messages' do
113
-
114
- it 'returns an empty array if no messages have been sent' do
115
- subject.messages.should == []
116
- end
117
-
118
- it 'returns an array of all lines sent to the log file' do
119
- File.stubs(:open).yields(stub(:puts))
120
- strings = ['an array', 'of message', 'strings']
121
- subject.send(:to_file, strings)
122
- subject.messages.should == strings
123
- end
124
-
125
- it 'does not track lines sent to the console' do
126
- subject.stubs(:puts)
127
- strings = ['an array', 'of message', 'strings']
128
- subject.send(:to_console, strings)
129
- subject.messages.should == []
130
- end
131
-
132
- end # describe '#messages'
133
-
134
- describe '#clear!' do
135
- it 'should clear the log and reset has_warnings?' do
136
- subject.messages << 'foo'
137
- subject.instance_variable_set(:@has_warnings, true)
138
- subject.messages.count.should == 1
139
- subject.has_warnings?.should be_true
140
-
141
- subject.clear!
142
- subject.messages.should be_empty
143
- subject.has_warnings?.should be_false
144
- end
145
- end # describe '#clear!'
146
-
147
- describe '#truncate!' do
148
- context 'when log file does not exist' do
149
- before { File.stubs(:exist?).returns(false) }
150
- it 'should do nothing' do
151
- File.expects(:stat).never
152
- subject.truncate!
153
- end
154
- end
155
-
156
- context 'when log file is <= max_bytes' do
157
- before { File.stubs(:exist?).returns(true) }
158
- it 'should do nothing' do
159
- stat = mock
160
- File.expects(:stat).twice.with(
161
- File.join(Backup::Config.log_path, 'backup.log')
162
- ).returns(stat)
163
-
164
- [1, 2].each do |size|
165
- stat.expects(:size).returns(size)
166
-
167
- FileUtils.expects(:mv).never
168
- File.expects(:open).never
169
- FileUtils.expects(:rm_f).never
170
-
171
- subject.truncate!(2)
172
- end
173
- end
174
- end
175
-
176
- context 'when log file is > max_bytes' do
177
- before do
178
- FileUtils.unstub(:mkdir_p)
179
- FileUtils.unstub(:mv)
180
- FileUtils.unstub(:rm)
181
- FileUtils.unstub(:rm_f)
182
- end
183
-
184
- after do
185
- Backup::Config.send(:reset!)
186
- end
187
-
188
- it 'should truncate the file, removing older lines' do
189
- max_bytes = 5_000
190
- line_len = 120
191
- Dir.mktmpdir do |dir|
192
- Backup::Config.update(:root_path => dir)
193
- FileUtils.mkdir_p(Backup::Config.log_path)
194
- log_file = File.join(Backup::Config.log_path, 'backup.log')
195
-
196
- lineno = 0
197
- over_max = (max_bytes * 1.25).to_i
198
- File.open(log_file, 'w') do |f|
199
- until f.pos > over_max
200
- f.puts "#{ lineno += 1 }: ".ljust(line_len, 'x')
201
- end
202
- end
203
- File.stat(log_file).size.
204
- should be_within(line_len + 2).of(over_max)
205
-
206
- subject.truncate!(max_bytes)
207
-
208
- File.stat(log_file).size.
209
- should be_within(line_len + 2).of(max_bytes)
210
-
211
- File.readlines(log_file).last.chomp.
212
- should == "#{ lineno }: ".ljust(line_len, 'x')
213
-
214
- File.exist?(log_file + '~').should be_false
215
- end
216
- end
217
- end
218
-
219
- end # describe '#truncate!'
220
-
221
- describe '#loggify' do
222
-
223
- it 'returns an array of strings split on newline separators' do
224
- str_aa = "first line\nsecond line"
225
- str_ab = "first line\nsecond line\n"
226
- expected_a = ["[#{logger_time}][msg_type] first line",
227
- "[#{logger_time}][msg_type] second line"]
228
-
229
- str_b = 'string with no newline'
230
- expected_b = ["[#{logger_time}][msg_type] string with no newline"]
231
-
232
- subject.send(:loggify, str_aa, :msg_type).should == expected_a
233
- subject.send(:loggify, str_ab, :msg_type).should == expected_a
234
- subject.send(:loggify, str_b, :msg_type).should == expected_b
235
- end
236
-
237
- it 'formats a string with color if color is given' do
238
- green_type = ["[#{logger_time}][#{"\e[32mmsg_type\e[0m"}] foo"]
239
- yellow_type = ["[#{logger_time}][#{"\e[33mmsg_type\e[0m"}] foo"]
240
- red_type = ["[#{logger_time}][#{"\e[31mmsg_type\e[0m"}] foo"]
241
-
242
- subject.send(:loggify, 'foo', :msg_type, :green ).should == green_type
243
- subject.send(:loggify, 'foo', :msg_type, :yellow).should == yellow_type
244
- subject.send(:loggify, 'foo', :msg_type, :red ).should == red_type
245
- end
246
-
247
- it 'does not colorize if no color given' do
248
- no_color = ["[#{logger_time}][msg_type] foo"]
249
- subject.send(:loggify, 'foo', :msg_type).should == no_color
250
- end
251
-
252
- it 'accepts blank lines in the message' do
253
- str = "first line\n\nthird line"
254
- expected = ["[#{logger_time}][msg_type] first line",
255
- "[#{logger_time}][msg_type] ",
256
- "[#{logger_time}][msg_type] third line"]
257
-
258
- subject.send(:loggify, str, :msg_type).should == expected
259
- end
260
-
261
- it 'accepts an object responding to #to_s for the message' do
262
- obj = StandardError.new("first line\nsecond line")
263
- expected = ["[#{logger_time}][msg_type] first line",
264
- "[#{logger_time}][msg_type] second line"]
265
-
266
- subject.send(:loggify, obj, :msg_type).should == expected
267
- end
268
-
269
- it 'returns an unformatted lines if type is not given' do
270
- str_a = 'single line'
271
- str_b = "first line\n\nthird line"
272
- expected_a = ['single line']
273
- expected_b = ['first line', '', 'third line']
274
-
275
- subject.send(:loggify, str_a).should == expected_a
276
- subject.send(:loggify, str_b).should == expected_b
277
- end
278
-
279
- end # describe '#loggify'
280
-
281
- describe '#to_console' do
282
-
283
- context 'when +stderr+ is not set (false)' do
284
- it 'writes an array of strings to stdout' do
285
- lines = [ 'line one', 'line two', 'line three']
286
- lines.each {|line| subject.expects(:puts).with(line).in_sequence(s) }
287
- subject.send(:to_console, lines)
288
- end
289
- end
290
-
291
- context 'when +stderr+ is set (true)' do
292
- it 'writes an array of strings to stdout' do
293
- lines = [ 'line one', 'line two', 'line three']
294
- lines.each {|line| Kernel.expects(:warn).with(line).in_sequence(s) }
295
- subject.send(:to_console, lines, true)
296
- end
297
- end
298
-
299
- it 'returns nil if quiet? is true' do
300
- subject.quiet = true
301
- subject.expects(:puts).never
302
- subject.send(:to_console, 'a string')
303
- end
304
-
305
- end # describe '#to_console'
306
-
307
- describe '#to_file' do
308
-
309
- it 'writes an array of strings to the log file' do
310
- lines = ['line one', 'line two', 'line three']
311
- File.stubs(:open).yields(logfile_mock)
312
- lines.each {|line| logfile_mock.expects(:puts).with(line).in_sequence(s) }
313
- subject.send(:to_file, lines)
314
- end
315
-
316
- it 'appends each line written to #messages' do
317
- lines = ['line one', 'line two', 'line three']
318
- File.stubs(:open)
319
- a_mock = mock
320
- subject.expects(:messages).returns(a_mock)
321
- a_mock.expects(:push).with('line one', 'line two', 'line three')
322
- subject.send(:to_file, lines)
323
- end
324
-
325
- it 'only opens the log file once to append multiple lines' do
326
- lines = ['line one', 'line two', 'line three']
327
- File.expects(:open).once.with(logfile_path, 'a').yields(logfile_mock)
328
- logfile_mock.expects(:puts).times(3)
329
- subject.send(:to_file, lines)
330
- end
331
-
332
- end # describe '#to_file'
333
-
334
- describe 'color methods' do
335
-
336
- it 'color methods send strings to #colorize with color codes' do
337
- colors = [ [:green, 32], [:yellow, 33], [:red, 31] ]
338
- colors.each do |color, code|
339
- subject.expects(:colorize).with('foo', code).in_sequence(s)
340
- end
341
- colors.each {|color, code| subject.send(color, 'foo') }
342
- end
343
-
344
- it '#colorize adds the code to the string' do
345
- [32, 33, 31].each do |code|
346
- subject.send(:colorize, 'foo', code).
347
- should == "\e[#{code}mfoo\e[0m"
348
- end
349
- end
350
-
351
- end # color methods
352
-
353
- describe '#quiet' do
354
- it 'should be nil by default' do
355
- # of course, 'before' is setting it to nil ;)
356
- subject.quiet.should be_nil
357
- end
358
-
359
- it 'can be set true/false' do
360
- subject.quiet = true
361
- subject.quiet.should be_true
362
- subject.quiet = false
363
- subject.quiet.should be_false
364
- end
365
- end
366
-
367
- end
data/spec/model_spec.rb DELETED
@@ -1,666 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require File.expand_path('../spec_helper.rb', __FILE__)
4
-
5
- describe 'Backup::Model' do
6
- let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
- let(:s) { sequence '' }
8
-
9
- before do
10
- Backup::Model.all.clear
11
- end
12
-
13
- describe '.all' do
14
- it 'should be an empty array by default' do
15
- Backup::Model.all.should == []
16
- end
17
- end
18
-
19
- describe 'finder methods' do
20
- before do
21
- [:a, :b, :c, :b, :d].each_with_index do |sym, i|
22
- Backup::Model.new("trigger_#{sym}", "label#{i}")
23
- end
24
- end
25
-
26
- describe '.find' do
27
- it 'should return the first matching model' do
28
- Backup::Model.find('trigger_b').label.should == 'label1'
29
- end
30
-
31
- it 'should accept symbols' do
32
- Backup::Model.find(:trigger_b).label.should == 'label1'
33
- end
34
-
35
- it 'should raise an error if trigger is not found' do
36
- expect do
37
- Backup::Model.find(:f)
38
- end.to raise_error(
39
- Backup::Errors::Model::MissingTriggerError,
40
- "Model::MissingTriggerError: Could not find trigger 'f'."
41
- )
42
- end
43
- end
44
-
45
- describe '.find_matching' do
46
- it 'should find all triggers matching a wildcard' do
47
- Backup::Model.find_matching('tri*_b').count.should be(2)
48
- Backup::Model.find_matching('trigg*').count.should be(5)
49
- end
50
-
51
- it 'should return an empty array if no matches are found' do
52
- Backup::Model.find_matching('foo*').should == []
53
- end
54
- end
55
-
56
- end # describe 'finder methods'
57
-
58
- describe '#initialize' do
59
-
60
- it 'should convert trigger to a string' do
61
- Backup::Model.new(:foo, :bar).trigger.should == 'foo'
62
- end
63
-
64
- it 'should convert label to a string' do
65
- Backup::Model.new(:foo, :bar).label.should == 'bar'
66
- end
67
-
68
- it 'should set all procedure variables to an empty array' do
69
- model.send(:procedure_instance_variables).each do |var|
70
- model.instance_variable_get(var).should == []
71
- end
72
- end
73
-
74
- it 'should accept and instance_eval a block' do
75
- block = lambda {|model| throw(:instance, model) }
76
- caught = catch(:instance) do
77
- Backup::Model.new('gotcha', '', &block)
78
- end
79
- caught.trigger.should == 'gotcha'
80
- end
81
-
82
- it 'should add itself to Model.all' do
83
- Backup::Model.all.should == [model]
84
- end
85
-
86
- end # describe '#initialize'
87
-
88
- describe 'DSL Methods' do
89
-
90
- module Fake
91
- module NoArg
92
- class Base
93
- attr_accessor :block_arg
94
- def initialize(&block)
95
- instance_eval(&block) if block_given?
96
- end
97
- end
98
- end
99
- module OneArg
100
- class Base
101
- attr_accessor :arg1, :block_arg
102
- def initialize(arg1, &block)
103
- @arg1 = arg1
104
- instance_eval(&block) if block_given?
105
- end
106
- end
107
- end
108
- module TwoArgs
109
- class Base
110
- attr_accessor :arg1, :arg2, :block_arg
111
- def initialize(arg1, arg2, &block)
112
- @arg1 = arg1
113
- @arg2 = arg2
114
- instance_eval(&block) if block_given?
115
- end
116
- end
117
- end
118
- end
119
-
120
- # Set +const+ to +replacement+ for the calling block
121
- def using_fake(const, replacement)
122
- orig = Backup.const_get(const)
123
- Backup.send(:remove_const, const)
124
- Backup.const_set(const, replacement)
125
- yield
126
- Backup.send(:remove_const, const)
127
- Backup.const_set(const, orig)
128
- end
129
-
130
- describe '#archive' do
131
- it 'should add archives' do
132
- using_fake('Archive', Fake::TwoArgs::Base) do
133
- model.archive('foo') {|a| a.block_arg = :foo }
134
- model.archive('bar') {|a| a.block_arg = :bar }
135
- model.archives.count.should == 2
136
- a1, a2 = model.archives
137
- a1.arg1.should be(model)
138
- a1.arg2.should == 'foo'
139
- a1.block_arg.should == :foo
140
- a2.arg1.should be(model)
141
- a2.arg2.should == 'bar'
142
- a2.block_arg.should == :bar
143
- end
144
- end
145
- end
146
-
147
- describe '#database' do
148
- it 'should add databases' do
149
- using_fake('Database', Fake::OneArg) do
150
- model.database('Base') {|a| a.block_arg = :foo }
151
- model.database('Base') {|a| a.block_arg = :bar }
152
- model.databases.count.should be(2)
153
- d1, d2 = model.databases
154
- d1.arg1.should be(model)
155
- d1.block_arg.should == :foo
156
- d2.arg1.should be(model)
157
- d2.block_arg.should == :bar
158
- end
159
- end
160
-
161
- it 'should accept a nested class name' do
162
- using_fake('Database', Fake) do
163
- model.database('OneArg::Base')
164
- model.databases.first.should be_an_instance_of Fake::OneArg::Base
165
- end
166
- end
167
- end
168
-
169
- describe '#store_with' do
170
- it 'should add storages' do
171
- using_fake('Storage', Fake::TwoArgs) do
172
- model.store_with('Base', 'foo') {|a| a.block_arg = :foo }
173
- model.store_with('Base', 'bar') {|a| a.block_arg = :bar }
174
- model.storages.count.should be(2)
175
- s1, s2 = model.storages
176
- s1.arg1.should be(model)
177
- s1.arg2.should == 'foo'
178
- s1.block_arg.should == :foo
179
- s2.arg1.should be(model)
180
- s2.arg2.should == 'bar'
181
- s2.block_arg.should == :bar
182
- end
183
- end
184
-
185
- it 'should accept a nested class name' do
186
- using_fake('Storage', Fake) do
187
- model.store_with('TwoArgs::Base')
188
- model.storages.first.should be_an_instance_of Fake::TwoArgs::Base
189
- end
190
- end
191
- end
192
-
193
- describe '#sync_with' do
194
- it 'should add syncers' do
195
- using_fake('Syncer', Fake::NoArg) do
196
- model.sync_with('Base') {|a| a.block_arg = :foo }
197
- model.sync_with('Base') {|a| a.block_arg = :bar }
198
- model.syncers.count.should be(2)
199
- s1, s2 = model.syncers
200
- s1.block_arg.should == :foo
201
- s2.block_arg.should == :bar
202
- end
203
- end
204
-
205
- it 'should accept a nested class name' do
206
- using_fake('Syncer', Fake) do
207
- model.sync_with('NoArg::Base')
208
- model.syncers.first.should be_an_instance_of Fake::NoArg::Base
209
- end
210
- end
211
-
212
- it 'should warn user of change from RSync to RSync::Push' do
213
- Backup::Logger.expects(:warn)
214
- model.sync_with('Backup::Config::RSync')
215
- model.syncers.first.should
216
- be_an_instance_of Backup::Syncer::RSync::Push
217
- end
218
-
219
- it 'should warn user of change from S3 to Cloud::S3' do
220
- Backup::Logger.expects(:warn)
221
- model.sync_with('Backup::Config::S3')
222
- model.syncers.first.should
223
- be_an_instance_of Backup::Syncer::Cloud::S3
224
- end
225
-
226
- it 'should warn user of change from CloudFiles to Cloud::CloudFiles' do
227
- Backup::Logger.expects(:warn)
228
- model.sync_with('Backup::Config::CloudFiles')
229
- model.syncers.first.should
230
- be_an_instance_of Backup::Syncer::Cloud::CloudFiles
231
- end
232
- end
233
-
234
- describe '#notify_by' do
235
- it 'should add notifiers' do
236
- using_fake('Notifier', Fake::OneArg) do
237
- model.notify_by('Base') {|a| a.block_arg = :foo }
238
- model.notify_by('Base') {|a| a.block_arg = :bar }
239
- model.notifiers.count.should be(2)
240
- n1, n2 = model.notifiers
241
- n1.arg1.should be(model)
242
- n1.block_arg.should == :foo
243
- n2.arg1.should be(model)
244
- n2.block_arg.should == :bar
245
- end
246
- end
247
-
248
- it 'should accept a nested class name' do
249
- using_fake('Notifier', Fake) do
250
- model.notify_by('OneArg::Base')
251
- model.notifiers.first.should be_an_instance_of Fake::OneArg::Base
252
- end
253
- end
254
- end
255
-
256
- describe '#encrypt_with' do
257
- it 'should add an encryptor' do
258
- using_fake('Encryptor', Fake::NoArg) do
259
- model.encrypt_with('Base') {|a| a.block_arg = :foo }
260
- model.encryptor.should be_an_instance_of Fake::NoArg::Base
261
- model.encryptor.block_arg.should == :foo
262
- end
263
- end
264
-
265
- it 'should accept a nested class name' do
266
- using_fake('Encryptor', Fake) do
267
- model.encrypt_with('NoArg::Base')
268
- model.encryptor.should be_an_instance_of Fake::NoArg::Base
269
- end
270
- end
271
- end
272
-
273
- describe '#compress_with' do
274
- it 'should add a compressor' do
275
- using_fake('Compressor', Fake::NoArg) do
276
- model.compress_with('Base') {|a| a.block_arg = :foo }
277
- model.compressor.should be_an_instance_of Fake::NoArg::Base
278
- model.compressor.block_arg.should == :foo
279
- end
280
- end
281
-
282
- it 'should accept a nested class name' do
283
- using_fake('Compressor', Fake) do
284
- model.compress_with('NoArg::Base')
285
- model.compressor.should be_an_instance_of Fake::NoArg::Base
286
- end
287
- end
288
- end
289
-
290
- describe '#split_into_chunks_of' do
291
- it 'should add a splitter' do
292
- using_fake('Splitter', Fake::TwoArgs::Base) do
293
- model.split_into_chunks_of(123)
294
- model.splitter.should be_an_instance_of Fake::TwoArgs::Base
295
- model.splitter.arg1.should be(model)
296
- model.splitter.arg2.should == 123
297
- end
298
- end
299
-
300
- it 'should raise an error if chunk_size is not an Integer' do
301
- expect do
302
- model.split_into_chunks_of('345')
303
- end.to raise_error {|err|
304
- err.should be_an_instance_of Backup::Errors::Model::ConfigurationError
305
- err.message.should match(/must be an Integer/)
306
- }
307
- end
308
- end
309
-
310
- end # describe 'DSL Methods'
311
-
312
- describe '#prepare!' do
313
- it 'should prepare for the backup' do
314
- FileUtils.expects(:mkdir_p).with(
315
- File.join(Backup::Config.data_path, 'test_trigger')
316
- )
317
- Backup::Cleaner.expects(:prepare).with(model)
318
-
319
- model.prepare!
320
- end
321
- end
322
-
323
- describe '#perform!' do
324
- let(:procedure_a) { lambda {} }
325
- let(:procedure_b) { mock }
326
- let(:procedure_c) { mock }
327
- let(:procedure_d) { lambda {} }
328
- let(:procedure_e) { lambda {} }
329
- let(:procedure_f) { mock }
330
- let(:procedures) do
331
- [ procedure_a, [procedure_b, procedure_c],
332
- procedure_d, procedure_e, [procedure_f] ]
333
- end
334
- let(:syncer_a) { mock }
335
- let(:syncer_b) { mock }
336
- let(:syncers) { [syncer_a, syncer_b] }
337
- let(:notifier_a) { mock }
338
- let(:notifier_b) { mock }
339
- let(:notifiers) { [notifier_a, notifier_b] }
340
-
341
- it 'should set the @time and @started_at variables' do
342
- Timecop.freeze(Time.now)
343
- started_at = Time.now
344
- time = started_at.strftime("%Y.%m.%d.%H.%M.%S")
345
- model.expects(:log!).with(:started)
346
- model.expects(:log!).with(:finished)
347
-
348
- model.perform!
349
- model.time.should == time
350
- model.instance_variable_get(:@started_at).should == started_at
351
- end
352
-
353
- context 'when no errors occur' do
354
- before do
355
- model.expects(:procedures).returns(procedures)
356
- model.expects(:syncers).returns(syncers)
357
- model.expects(:notifiers).returns(notifiers)
358
- end
359
-
360
- context 'when databases are configured' do
361
- before do
362
- model.instance_variable_set(:@databases, [true])
363
- end
364
-
365
- it 'should perform all procedures' do
366
- model.expects(:log!).in_sequence(s).with(:started)
367
-
368
- procedure_a.expects(:call).in_sequence(s)
369
- procedure_b.expects(:perform!).in_sequence(s)
370
- procedure_c.expects(:perform!).in_sequence(s)
371
- procedure_d.expects(:call).in_sequence(s)
372
- procedure_e.expects(:call).in_sequence(s)
373
- procedure_f.expects(:perform!).in_sequence(s)
374
-
375
- syncer_a.expects(:perform!).in_sequence(s)
376
- syncer_b.expects(:perform!).in_sequence(s)
377
-
378
- notifier_a.expects(:perform!).in_sequence(s)
379
- notifier_b.expects(:perform!).in_sequence(s)
380
-
381
- model.expects(:log!).in_sequence(s).with(:finished)
382
-
383
- model.perform!
384
- end
385
- end
386
-
387
- context 'when archives are configured' do
388
- before do
389
- model.instance_variable_set(:@archives, [true])
390
- end
391
-
392
- it 'should perform all procedures' do
393
- model.expects(:log!).in_sequence(s).with(:started)
394
-
395
- procedure_a.expects(:call).in_sequence(s)
396
- procedure_b.expects(:perform!).in_sequence(s)
397
- procedure_c.expects(:perform!).in_sequence(s)
398
- procedure_d.expects(:call).in_sequence(s)
399
- procedure_e.expects(:call).in_sequence(s)
400
- procedure_f.expects(:perform!).in_sequence(s)
401
-
402
- syncer_a.expects(:perform!).in_sequence(s)
403
- syncer_b.expects(:perform!).in_sequence(s)
404
-
405
- notifier_a.expects(:perform!).in_sequence(s)
406
- notifier_b.expects(:perform!).in_sequence(s)
407
-
408
- model.expects(:log!).in_sequence(s).with(:finished)
409
-
410
- model.perform!
411
- end
412
- end
413
-
414
- end # context 'when no errors occur'
415
-
416
- # for the purposes of testing the error handling, we're just going to
417
- # stub the first thing this method calls and raise an error
418
- context 'when errors occur' do
419
- let(:error_a) { mock }
420
- let(:error_b) { mock }
421
- let(:notifier) { mock }
422
-
423
- before do
424
- error_a.stubs(:backtrace).returns(['many', 'backtrace', 'lines'])
425
- end
426
-
427
- it 'logs, notifies and continues if a StandardError is rescued' do
428
- Time.stubs(:now).raises(StandardError, 'non-fatal error')
429
-
430
- Backup::Errors::ModelError.expects(:wrap).in_sequence(s).with do |err, msg|
431
- err.message.should == 'non-fatal error'
432
- msg.should match(/Backup for test label \(test_trigger\) Failed!/)
433
- end.returns(error_a)
434
- Backup::Logger.expects(:error).in_sequence(s).with(error_a)
435
- Backup::Logger.expects(:error).in_sequence(s).with(
436
- "\nBacktrace:\n\s\smany\n\s\sbacktrace\n\s\slines\n\n"
437
- )
438
-
439
- Backup::Cleaner.expects(:warnings).in_sequence(s).with(model)
440
-
441
- Backup::Errors::ModelError.expects(:new).in_sequence(s).with do |msg|
442
- msg.should match(/Backup will now attempt to continue/)
443
- end.returns(error_b)
444
- Backup::Logger.expects(:message).in_sequence(s).with(error_b)
445
-
446
- # notifiers called, but any Exception is ignored
447
- notifier.expects(:perform!).with(true).raises(Exception)
448
- model.expects(:notifiers).returns([notifier])
449
-
450
- # returns to allow next trigger to run
451
- expect { model.perform! }.not_to raise_error
452
- end
453
-
454
- it 'logs, notifies and exits if an Exception is rescued' do
455
- Time.stubs(:now).raises(Exception, 'fatal error')
456
-
457
- Backup::Errors::ModelError.expects(:wrap).in_sequence(s).with do |err, msg|
458
- err.message.should == 'fatal error'
459
- msg.should match(/Backup for test label \(test_trigger\) Failed!/)
460
- end.returns(error_a)
461
- Backup::Logger.expects(:error).in_sequence(s).with(error_a)
462
- Backup::Logger.expects(:error).in_sequence(s).with(
463
- "\nBacktrace:\n\s\smany\n\s\sbacktrace\n\s\slines\n\n"
464
- )
465
-
466
- Backup::Cleaner.expects(:warnings).in_sequence(s).with(model)
467
-
468
- Backup::Errors::ModelError.expects(:new).in_sequence(s).with do |msg|
469
- msg.should match(/Backup will now exit/)
470
- end.returns(error_b)
471
- Backup::Logger.expects(:error).in_sequence(s).with(error_b)
472
-
473
- expect do
474
- # notifiers called, but any Exception is ignored
475
- notifier = mock
476
- notifier.expects(:perform!).with(true).raises(Exception)
477
- model.expects(:notifiers).returns([notifier])
478
- end.not_to raise_error
479
-
480
- expect do
481
- model.perform!
482
- end.to raise_error(SystemExit) {|exit| exit.status.should be(1) }
483
- end
484
-
485
- end # context 'when errors occur'
486
-
487
- end # describe '#perform!'
488
-
489
- describe '#package!' do
490
- it 'should package the backup' do
491
- Backup::Packager.expects(:package!).in_sequence(s).with(model)
492
- Backup::Cleaner.expects(:remove_packaging).in_sequence(s).with(model)
493
-
494
- model.send(:package!)
495
- model.package.should be_an_instance_of Backup::Package
496
- end
497
- end
498
-
499
- describe '#clean' do
500
- it 'should remove the final packaged files' do
501
- package = mock
502
- model.instance_variable_set(:@package, package)
503
- Backup::Cleaner.expects(:remove_package).with(package)
504
-
505
- model.send(:clean!)
506
- end
507
- end
508
-
509
- describe '#procedures' do
510
- it 'should return an array of specific, ordered procedures' do
511
- model.stubs(:databases).returns(:databases)
512
- model.stubs(:archives).returns(:archives)
513
- model.stubs(:package!).returns(:package)
514
- model.stubs(:storages).returns(:storages)
515
- model.stubs(:clean!).returns(:clean)
516
-
517
- one, two, three, four, five = model.send(:procedures)
518
- one.should == :databases
519
- two.should == :archives
520
- three.call.should == :package
521
- four.should == :storages
522
- five.call.should == :clean
523
- end
524
- end
525
-
526
- describe '#procedure_instance_variables' do
527
- # these are all set to an empty Array in #initialize
528
- it 'should return an array of Array holding instance variables' do
529
- model.send(:procedure_instance_variables).should ==
530
- [:@databases, :@archives, :@storages, :@notifiers, :@syncers]
531
- end
532
- end
533
-
534
- describe '#get_class_from_scope' do
535
-
536
- module Fake
537
- module TestScope
538
- class TestKlass; end
539
- end
540
- end
541
- module TestScope
542
- module TestKlass; end
543
- end
544
-
545
- context 'when name is given as a string' do
546
- it 'should return the constant for the given scope and name' do
547
- model.send(
548
- :get_class_from_scope,
549
- Fake,
550
- 'TestScope'
551
- ).should == Fake::TestScope
552
- end
553
-
554
- it 'should accept a nested class name' do
555
- model.send(
556
- :get_class_from_scope,
557
- Fake,
558
- 'TestScope::TestKlass'
559
- ).should == Fake::TestScope::TestKlass
560
- end
561
- end
562
-
563
- context 'when name is given as a module' do
564
- it 'should return the constant for the given scope and name' do
565
- model.send(
566
- :get_class_from_scope,
567
- Fake,
568
- TestScope
569
- ).should == Fake::TestScope
570
- end
571
-
572
- it 'should accept a nested class name' do
573
- model.send(
574
- :get_class_from_scope,
575
- Fake,
576
- TestScope::TestKlass
577
- ).should == Fake::TestScope::TestKlass
578
- end
579
- end
580
-
581
- context 'when name is given as a module defined under Backup::Config' do
582
- # this is necessary since the specs in spec/config_spec.rb
583
- # remove all the constants from Backup::Config as part of those tests.
584
- before(:all) do
585
- module Backup::Config
586
- module TestScope
587
- module TestKlass; end
588
- end
589
- end
590
- end
591
-
592
- it 'should return the constant for the given scope and name' do
593
- model.send(
594
- :get_class_from_scope,
595
- Fake,
596
- Backup::Config::TestScope
597
- ).should == Fake::TestScope
598
- end
599
-
600
- it 'should accept a nested class name' do
601
- model.send(
602
- :get_class_from_scope,
603
- Fake,
604
- Backup::Config::TestScope::TestKlass
605
- ).should == Fake::TestScope::TestKlass
606
- end
607
- end
608
-
609
- end # describe '#get_class_from_scope'
610
-
611
- describe '#log!' do
612
- context 'when action is :started' do
613
- it 'should log that the backup has started with the version' do
614
- Backup::Logger.expects(:message).with(
615
- "Performing Backup for 'test label (test_trigger)'!\n" +
616
- "[ backup #{ Backup::Version.current } : #{ RUBY_DESCRIPTION } ]"
617
- )
618
- model.send(:log!, :started)
619
- end
620
- end
621
-
622
- context 'when action is :finished' do
623
- before { model.expects(:elapsed_time).returns('01:02:03') }
624
- context 'when warnings were issued' do
625
- before { Backup::Logger.expects(:has_warnings?).returns(true) }
626
- it 'should log a warning that the backup has finished with warnings' do
627
- Backup::Logger.expects(:warn).with(
628
- "Backup for 'test label (test_trigger)' " +
629
- "Completed Successfully (with Warnings) in 01:02:03"
630
- )
631
- model.send(:log!, :finished)
632
- end
633
- end
634
-
635
- context 'when no warnings were issued' do
636
- it 'should log that the backup has finished with the elapsed time' do
637
- Backup::Logger.expects(:message).with(
638
- "Backup for 'test label (test_trigger)' " +
639
- "Completed Successfully in 01:02:03"
640
- )
641
- model.send(:log!, :finished)
642
- end
643
- end
644
- end
645
- end # describe '#log!'
646
-
647
- describe '#elapsed_time' do
648
- it 'should return a string representing the elapsed time' do
649
- Timecop.freeze(Time.now)
650
- { 0 => '00:00:00', 1 => '00:00:01', 59 => '00:00:59',
651
- 60 => '00:01:00', 61 => '00:01:01', 119 => '00:01:59',
652
- 3540 => '00:59:00', 3541 => '00:59:01', 3599 => '00:59:59',
653
- 3600 => '01:00:00', 3601 => '01:00:01', 3659 => '01:00:59',
654
- 3660 => '01:01:00', 3661 => '01:01:01', 3719 => '01:01:59',
655
- 7140 => '01:59:00', 7141 => '01:59:01', 7199 => '01:59:59',
656
- 212400 => '59:00:00', 212401 => '59:00:01', 212459 => '59:00:59',
657
- 212460 => '59:01:00', 212461 => '59:01:01', 212519 => '59:01:59',
658
- 215940 => '59:59:00', 215941 => '59:59:01', 215999 => '59:59:59'
659
- }.each do |duration, expected|
660
- model.instance_variable_set(:@started_at, Time.now - duration)
661
- model.send(:elapsed_time).should == expected
662
- end
663
- end
664
- end
665
-
666
- end