backup-agoddard 3.0.27

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.
Files changed (190) hide show
  1. data/.gitignore +8 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +28 -0
  4. data/Guardfile +23 -0
  5. data/LICENSE.md +24 -0
  6. data/README.md +478 -0
  7. data/backup.gemspec +32 -0
  8. data/bin/backup +11 -0
  9. data/lib/backup.rb +133 -0
  10. data/lib/backup/archive.rb +117 -0
  11. data/lib/backup/binder.rb +22 -0
  12. data/lib/backup/cleaner.rb +121 -0
  13. data/lib/backup/cli/helpers.rb +93 -0
  14. data/lib/backup/cli/utility.rb +255 -0
  15. data/lib/backup/compressor/base.rb +35 -0
  16. data/lib/backup/compressor/bzip2.rb +50 -0
  17. data/lib/backup/compressor/custom.rb +53 -0
  18. data/lib/backup/compressor/gzip.rb +50 -0
  19. data/lib/backup/compressor/lzma.rb +52 -0
  20. data/lib/backup/compressor/pbzip2.rb +59 -0
  21. data/lib/backup/config.rb +174 -0
  22. data/lib/backup/configuration.rb +33 -0
  23. data/lib/backup/configuration/helpers.rb +130 -0
  24. data/lib/backup/configuration/store.rb +24 -0
  25. data/lib/backup/database/base.rb +53 -0
  26. data/lib/backup/database/mongodb.rb +230 -0
  27. data/lib/backup/database/mysql.rb +160 -0
  28. data/lib/backup/database/postgresql.rb +144 -0
  29. data/lib/backup/database/redis.rb +136 -0
  30. data/lib/backup/database/riak.rb +67 -0
  31. data/lib/backup/dependency.rb +108 -0
  32. data/lib/backup/encryptor/base.rb +29 -0
  33. data/lib/backup/encryptor/gpg.rb +760 -0
  34. data/lib/backup/encryptor/open_ssl.rb +72 -0
  35. data/lib/backup/errors.rb +124 -0
  36. data/lib/backup/hooks.rb +68 -0
  37. data/lib/backup/logger.rb +152 -0
  38. data/lib/backup/model.rb +409 -0
  39. data/lib/backup/notifier/base.rb +81 -0
  40. data/lib/backup/notifier/campfire.rb +155 -0
  41. data/lib/backup/notifier/hipchat.rb +93 -0
  42. data/lib/backup/notifier/mail.rb +206 -0
  43. data/lib/backup/notifier/prowl.rb +65 -0
  44. data/lib/backup/notifier/pushover.rb +88 -0
  45. data/lib/backup/notifier/twitter.rb +70 -0
  46. data/lib/backup/package.rb +47 -0
  47. data/lib/backup/packager.rb +100 -0
  48. data/lib/backup/pipeline.rb +110 -0
  49. data/lib/backup/splitter.rb +75 -0
  50. data/lib/backup/storage/base.rb +99 -0
  51. data/lib/backup/storage/cloudfiles.rb +87 -0
  52. data/lib/backup/storage/cycler.rb +117 -0
  53. data/lib/backup/storage/dropbox.rb +178 -0
  54. data/lib/backup/storage/ftp.rb +119 -0
  55. data/lib/backup/storage/local.rb +82 -0
  56. data/lib/backup/storage/ninefold.rb +116 -0
  57. data/lib/backup/storage/rsync.rb +149 -0
  58. data/lib/backup/storage/s3.rb +94 -0
  59. data/lib/backup/storage/scp.rb +99 -0
  60. data/lib/backup/storage/sftp.rb +108 -0
  61. data/lib/backup/syncer/base.rb +46 -0
  62. data/lib/backup/syncer/cloud/base.rb +247 -0
  63. data/lib/backup/syncer/cloud/cloud_files.rb +78 -0
  64. data/lib/backup/syncer/cloud/s3.rb +68 -0
  65. data/lib/backup/syncer/rsync/base.rb +49 -0
  66. data/lib/backup/syncer/rsync/local.rb +55 -0
  67. data/lib/backup/syncer/rsync/pull.rb +36 -0
  68. data/lib/backup/syncer/rsync/push.rb +116 -0
  69. data/lib/backup/template.rb +46 -0
  70. data/lib/backup/version.rb +43 -0
  71. data/spec-live/.gitignore +6 -0
  72. data/spec-live/README +7 -0
  73. data/spec-live/backups/config.rb +83 -0
  74. data/spec-live/backups/config.yml.template +46 -0
  75. data/spec-live/backups/models.rb +184 -0
  76. data/spec-live/compressor/custom_spec.rb +30 -0
  77. data/spec-live/compressor/gzip_spec.rb +30 -0
  78. data/spec-live/encryptor/gpg_keys.rb +239 -0
  79. data/spec-live/encryptor/gpg_spec.rb +287 -0
  80. data/spec-live/notifier/mail_spec.rb +121 -0
  81. data/spec-live/spec_helper.rb +151 -0
  82. data/spec-live/storage/dropbox_spec.rb +151 -0
  83. data/spec-live/storage/local_spec.rb +83 -0
  84. data/spec-live/storage/scp_spec.rb +193 -0
  85. data/spec-live/syncer/cloud/s3_spec.rb +124 -0
  86. data/spec/archive_spec.rb +335 -0
  87. data/spec/cleaner_spec.rb +312 -0
  88. data/spec/cli/helpers_spec.rb +301 -0
  89. data/spec/cli/utility_spec.rb +411 -0
  90. data/spec/compressor/base_spec.rb +52 -0
  91. data/spec/compressor/bzip2_spec.rb +217 -0
  92. data/spec/compressor/custom_spec.rb +106 -0
  93. data/spec/compressor/gzip_spec.rb +217 -0
  94. data/spec/compressor/lzma_spec.rb +123 -0
  95. data/spec/compressor/pbzip2_spec.rb +165 -0
  96. data/spec/config_spec.rb +321 -0
  97. data/spec/configuration/helpers_spec.rb +247 -0
  98. data/spec/configuration/store_spec.rb +39 -0
  99. data/spec/configuration_spec.rb +62 -0
  100. data/spec/database/base_spec.rb +63 -0
  101. data/spec/database/mongodb_spec.rb +510 -0
  102. data/spec/database/mysql_spec.rb +411 -0
  103. data/spec/database/postgresql_spec.rb +353 -0
  104. data/spec/database/redis_spec.rb +334 -0
  105. data/spec/database/riak_spec.rb +176 -0
  106. data/spec/dependency_spec.rb +51 -0
  107. data/spec/encryptor/base_spec.rb +40 -0
  108. data/spec/encryptor/gpg_spec.rb +909 -0
  109. data/spec/encryptor/open_ssl_spec.rb +148 -0
  110. data/spec/errors_spec.rb +306 -0
  111. data/spec/hooks_spec.rb +35 -0
  112. data/spec/logger_spec.rb +367 -0
  113. data/spec/model_spec.rb +694 -0
  114. data/spec/notifier/base_spec.rb +104 -0
  115. data/spec/notifier/campfire_spec.rb +217 -0
  116. data/spec/notifier/hipchat_spec.rb +211 -0
  117. data/spec/notifier/mail_spec.rb +316 -0
  118. data/spec/notifier/prowl_spec.rb +138 -0
  119. data/spec/notifier/pushover_spec.rb +123 -0
  120. data/spec/notifier/twitter_spec.rb +153 -0
  121. data/spec/package_spec.rb +61 -0
  122. data/spec/packager_spec.rb +213 -0
  123. data/spec/pipeline_spec.rb +259 -0
  124. data/spec/spec_helper.rb +60 -0
  125. data/spec/splitter_spec.rb +120 -0
  126. data/spec/storage/base_spec.rb +166 -0
  127. data/spec/storage/cloudfiles_spec.rb +254 -0
  128. data/spec/storage/cycler_spec.rb +247 -0
  129. data/spec/storage/dropbox_spec.rb +480 -0
  130. data/spec/storage/ftp_spec.rb +271 -0
  131. data/spec/storage/local_spec.rb +259 -0
  132. data/spec/storage/ninefold_spec.rb +343 -0
  133. data/spec/storage/rsync_spec.rb +362 -0
  134. data/spec/storage/s3_spec.rb +245 -0
  135. data/spec/storage/scp_spec.rb +233 -0
  136. data/spec/storage/sftp_spec.rb +244 -0
  137. data/spec/syncer/base_spec.rb +109 -0
  138. data/spec/syncer/cloud/base_spec.rb +515 -0
  139. data/spec/syncer/cloud/cloud_files_spec.rb +181 -0
  140. data/spec/syncer/cloud/s3_spec.rb +174 -0
  141. data/spec/syncer/rsync/base_spec.rb +98 -0
  142. data/spec/syncer/rsync/local_spec.rb +149 -0
  143. data/spec/syncer/rsync/pull_spec.rb +98 -0
  144. data/spec/syncer/rsync/push_spec.rb +333 -0
  145. data/spec/version_spec.rb +21 -0
  146. data/templates/cli/utility/archive +25 -0
  147. data/templates/cli/utility/compressor/bzip2 +4 -0
  148. data/templates/cli/utility/compressor/custom +11 -0
  149. data/templates/cli/utility/compressor/gzip +4 -0
  150. data/templates/cli/utility/compressor/lzma +10 -0
  151. data/templates/cli/utility/compressor/pbzip2 +10 -0
  152. data/templates/cli/utility/config +32 -0
  153. data/templates/cli/utility/database/mongodb +18 -0
  154. data/templates/cli/utility/database/mysql +21 -0
  155. data/templates/cli/utility/database/postgresql +17 -0
  156. data/templates/cli/utility/database/redis +16 -0
  157. data/templates/cli/utility/database/riak +11 -0
  158. data/templates/cli/utility/encryptor/gpg +27 -0
  159. data/templates/cli/utility/encryptor/openssl +9 -0
  160. data/templates/cli/utility/model.erb +23 -0
  161. data/templates/cli/utility/notifier/campfire +12 -0
  162. data/templates/cli/utility/notifier/hipchat +15 -0
  163. data/templates/cli/utility/notifier/mail +22 -0
  164. data/templates/cli/utility/notifier/prowl +11 -0
  165. data/templates/cli/utility/notifier/pushover +11 -0
  166. data/templates/cli/utility/notifier/twitter +13 -0
  167. data/templates/cli/utility/splitter +7 -0
  168. data/templates/cli/utility/storage/cloud_files +22 -0
  169. data/templates/cli/utility/storage/dropbox +20 -0
  170. data/templates/cli/utility/storage/ftp +12 -0
  171. data/templates/cli/utility/storage/local +7 -0
  172. data/templates/cli/utility/storage/ninefold +9 -0
  173. data/templates/cli/utility/storage/rsync +11 -0
  174. data/templates/cli/utility/storage/s3 +19 -0
  175. data/templates/cli/utility/storage/scp +11 -0
  176. data/templates/cli/utility/storage/sftp +11 -0
  177. data/templates/cli/utility/syncer/cloud_files +46 -0
  178. data/templates/cli/utility/syncer/rsync_local +12 -0
  179. data/templates/cli/utility/syncer/rsync_pull +17 -0
  180. data/templates/cli/utility/syncer/rsync_push +17 -0
  181. data/templates/cli/utility/syncer/s3 +43 -0
  182. data/templates/general/links +11 -0
  183. data/templates/general/version.erb +2 -0
  184. data/templates/notifier/mail/failure.erb +9 -0
  185. data/templates/notifier/mail/success.erb +7 -0
  186. data/templates/notifier/mail/warning.erb +9 -0
  187. data/templates/storage/dropbox/authorization_url.erb +6 -0
  188. data/templates/storage/dropbox/authorized.erb +4 -0
  189. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  190. metadata +277 -0
@@ -0,0 +1,343 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
+
5
+ describe Backup::Storage::Ninefold do
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:storage) do
8
+ Backup::Storage::Ninefold.new(model) do |nf|
9
+ nf.storage_token = 'my_token'
10
+ nf.storage_secret = 'my_secret'
11
+ nf.keep = 5
12
+ end
13
+ end
14
+
15
+ it 'should be a subclass of Storage::Base' do
16
+ Backup::Storage::Ninefold.
17
+ superclass.should == Backup::Storage::Base
18
+ end
19
+
20
+ describe '#initialize' do
21
+ after { Backup::Storage::Ninefold.clear_defaults! }
22
+
23
+ it 'should load pre-configured defaults through Base' do
24
+ Backup::Storage::Ninefold.any_instance.expects(:load_defaults!)
25
+ storage
26
+ end
27
+
28
+ it 'should pass the model reference to Base' do
29
+ storage.instance_variable_get(:@model).should == model
30
+ end
31
+
32
+ it 'should pass the storage_id to Base' do
33
+ storage = Backup::Storage::Ninefold.new(model, 'my_storage_id')
34
+ storage.storage_id.should == 'my_storage_id'
35
+ end
36
+
37
+ context 'when no pre-configured defaults have been set' do
38
+ it 'should use the values given' do
39
+ storage.storage_token.should == 'my_token'
40
+ storage.storage_secret.should == 'my_secret'
41
+ storage.path.should == 'backups'
42
+
43
+ storage.storage_id.should be_nil
44
+ storage.keep.should == 5
45
+ end
46
+
47
+ it 'should use default values if none are given' do
48
+ storage = Backup::Storage::Ninefold.new(model)
49
+
50
+ storage.storage_token.should be_nil
51
+ storage.storage_secret.should be_nil
52
+ storage.path.should == 'backups'
53
+
54
+ storage.storage_id.should be_nil
55
+ storage.keep.should be_nil
56
+ end
57
+ end # context 'when no pre-configured defaults have been set'
58
+
59
+ context 'when pre-configured defaults have been set' do
60
+ before do
61
+ Backup::Storage::Ninefold.defaults do |s|
62
+ s.storage_token = 'some_token'
63
+ s.storage_secret = 'some_secret'
64
+ s.path = 'some_path'
65
+ s.keep = 15
66
+ end
67
+ end
68
+
69
+ it 'should use pre-configured defaults' do
70
+ storage = Backup::Storage::Ninefold.new(model)
71
+
72
+ storage.storage_token.should == 'some_token'
73
+ storage.storage_secret.should == 'some_secret'
74
+ storage.path.should == 'some_path'
75
+
76
+ storage.storage_id.should be_nil
77
+ storage.keep.should == 15
78
+ end
79
+
80
+ it 'should override pre-configured defaults' do
81
+ storage = Backup::Storage::Ninefold.new(model) do |s|
82
+ s.storage_token = 'new_token'
83
+ s.storage_secret = 'new_secret'
84
+ s.path = 'new_path'
85
+ s.keep = 10
86
+ end
87
+
88
+ storage.storage_token.should == 'new_token'
89
+ storage.storage_secret.should == 'new_secret'
90
+ storage.path.should == 'new_path'
91
+
92
+ storage.storage_id.should be_nil
93
+ storage.keep.should == 10
94
+ end
95
+ end # context 'when pre-configured defaults have been set'
96
+ end # describe '#initialize'
97
+
98
+ describe '#provider' do
99
+ it 'should set the Fog provider' do
100
+ storage.send(:provider).should == 'Ninefold'
101
+ end
102
+ end
103
+
104
+ describe '#connection' do
105
+ let(:connection) { mock }
106
+
107
+ it 'should create a new connection' do
108
+ Fog::Storage.expects(:new).once.with(
109
+ :provider => 'Ninefold',
110
+ :ninefold_storage_token => 'my_token',
111
+ :ninefold_storage_secret => 'my_secret'
112
+ ).returns(connection)
113
+ storage.send(:connection).should == connection
114
+ end
115
+
116
+ it 'should return an existing connection' do
117
+ Fog::Storage.expects(:new).once.returns(connection)
118
+ storage.send(:connection).should == connection
119
+ storage.send(:connection).should == connection
120
+ end
121
+ end # describe '#connection'
122
+
123
+ describe '#directory_for' do
124
+ let(:connection) { mock }
125
+ let(:directories) { mock }
126
+ let(:directory) { mock }
127
+
128
+ before do
129
+ storage.stubs(:connection).returns(connection)
130
+ connection.stubs(:directories).returns(directories)
131
+ end
132
+
133
+ context 'when the directory for the remote_path exists' do
134
+ it 'should return the directory' do
135
+ directories.expects(:get).with('remote_path').returns(directory)
136
+ storage.send(:directory_for, 'remote_path').should be(directory)
137
+ end
138
+ end
139
+
140
+ context 'when the directory for the remote_path does not exist' do
141
+ before do
142
+ directories.expects(:get).with('remote_path').returns(nil)
143
+ end
144
+
145
+ context 'when create is set to false' do
146
+ it 'should return nil' do
147
+ storage.send(:directory_for, 'remote_path').should be_nil
148
+ end
149
+ end
150
+
151
+ context 'when create is set to true' do
152
+ it 'should create and return the directory' do
153
+ directories.expects(:create).with(:key => 'remote_path').returns(directory)
154
+ storage.send(:directory_for, 'remote_path', true).should be(directory)
155
+ end
156
+ end
157
+ end
158
+ end # describe '#directory_for'
159
+
160
+ describe '#remote_path_for' do
161
+ let(:package) { mock }
162
+
163
+ before do
164
+ # for superclass method
165
+ package.expects(:trigger).returns('trigger')
166
+ package.expects(:time).returns('time')
167
+ end
168
+
169
+ it 'should remove any preceeding slash from the remote path' do
170
+ storage.path = '/backups'
171
+ storage.send(:remote_path_for, package).should == 'backups/trigger/time'
172
+ end
173
+ end
174
+
175
+ describe '#transfer!' do
176
+ let(:package) { mock }
177
+ let(:directory) { mock }
178
+ let(:directory_files) { mock }
179
+ let(:file) { mock }
180
+ let(:s) { sequence '' }
181
+
182
+ before do
183
+ storage.instance_variable_set(:@package, package)
184
+ storage.stubs(:storage_name).returns('Storage::Ninefold')
185
+ storage.stubs(:local_path).returns('/local/path')
186
+ directory.stubs(:files).returns(directory_files)
187
+ end
188
+
189
+ it 'should transfer the package files' do
190
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
191
+ returns('remote/path')
192
+ storage.expects(:directory_for).with('remote/path', true).returns(directory)
193
+
194
+ storage.expects(:files_to_transfer_for).in_sequence(s).with(package).
195
+ multiple_yields(
196
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
197
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
198
+ )
199
+ # first yield
200
+ Backup::Logger.expects(:message).in_sequence(s).with(
201
+ "Storage::Ninefold started transferring " +
202
+ "'2011.12.31.11.00.02.backup.tar.enc-aa'."
203
+ )
204
+ File.expects(:open).in_sequence(s).with(
205
+ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-aa'), 'r'
206
+ ).yields(file)
207
+ directory_files.expects(:create).in_sequence(s).with(
208
+ :key => 'backup.tar.enc-aa', :body => file
209
+ )
210
+ # second yield
211
+ Backup::Logger.expects(:message).in_sequence(s).with(
212
+ "Storage::Ninefold started transferring " +
213
+ "'2011.12.31.11.00.02.backup.tar.enc-ab'."
214
+ )
215
+ File.expects(:open).in_sequence(s).with(
216
+ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-ab'), 'r'
217
+ ).yields(file)
218
+ directory_files.expects(:create).in_sequence(s).with(
219
+ :key => 'backup.tar.enc-ab', :body => file
220
+ )
221
+
222
+ storage.send(:transfer!)
223
+ end
224
+ end # describe '#transfer!'
225
+
226
+ describe '#remove!' do
227
+ let(:package) { mock }
228
+ let(:directory) { mock }
229
+ let(:directory_files) { mock }
230
+ let(:file) { mock }
231
+ let(:s) { sequence '' }
232
+
233
+ before do
234
+ storage.stubs(:storage_name).returns('Storage::Ninefold')
235
+ directory.stubs(:files).returns(directory_files)
236
+ end
237
+
238
+ it 'should remove the package files' do
239
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
240
+ returns('remote/path')
241
+ storage.expects(:directory_for).with('remote/path').returns(directory)
242
+
243
+ storage.expects(:transferred_files_for).in_sequence(s).with(package).
244
+ multiple_yields(
245
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
246
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
247
+ )
248
+ # first yield
249
+ Backup::Logger.expects(:message).in_sequence(s).with(
250
+ "Storage::Ninefold started removing " +
251
+ "'2011.12.31.11.00.02.backup.tar.enc-aa' from Ninefold."
252
+ )
253
+ directory_files.expects(:get).in_sequence(s).
254
+ with('backup.tar.enc-aa').returns(file)
255
+ file.expects(:destroy).in_sequence(s)
256
+ # second yield
257
+ Backup::Logger.expects(:message).in_sequence(s).with(
258
+ "Storage::Ninefold started removing " +
259
+ "'2011.12.31.11.00.02.backup.tar.enc-ab' from Ninefold."
260
+ )
261
+ directory_files.expects(:get).in_sequence(s).
262
+ with('backup.tar.enc-ab').returns(file)
263
+ file.expects(:destroy).in_sequence(s)
264
+
265
+ directory.expects(:destroy).in_sequence(s)
266
+
267
+ expect do
268
+ storage.send(:remove!, package)
269
+ end.not_to raise_error
270
+ end
271
+
272
+ context 'when the remote directory does not exist' do
273
+ it 'should raise an error' do
274
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
275
+ returns('remote/path')
276
+ storage.expects(:directory_for).with('remote/path').returns(nil)
277
+
278
+ storage.expects(:transferred_files_for).never
279
+ directory_files.expects(:get).never
280
+ file.expects(:destroy).never
281
+ directory.expects(:destroy).never
282
+
283
+ expect do
284
+ storage.send(:remove!, package)
285
+ end.to raise_error {|err|
286
+ err.should be_an_instance_of Backup::Errors::Storage::Ninefold::NotFoundError
287
+ err.message.should == 'Storage::Ninefold::NotFoundError: ' +
288
+ "Directory at 'remote/path' not found"
289
+ }
290
+ end
291
+ end
292
+
293
+ context 'when remote files do not exist' do
294
+ it 'should collect their names and raise an error after proceeding' do
295
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
296
+ returns('remote/path')
297
+ storage.expects(:directory_for).with('remote/path').returns(directory)
298
+
299
+ storage.expects(:transferred_files_for).in_sequence(s).with(package).
300
+ multiple_yields(
301
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
302
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab'],
303
+ ['2011.12.31.11.00.02.backup.tar.enc-ac', 'backup.tar.enc-ac']
304
+ )
305
+ # first yield (file not found)
306
+ Backup::Logger.expects(:message).in_sequence(s).with(
307
+ "Storage::Ninefold started removing " +
308
+ "'2011.12.31.11.00.02.backup.tar.enc-aa' from Ninefold."
309
+ )
310
+ directory_files.expects(:get).in_sequence(s).
311
+ with('backup.tar.enc-aa').returns(nil)
312
+ # second yield (file found and removed)
313
+ Backup::Logger.expects(:message).in_sequence(s).with(
314
+ "Storage::Ninefold started removing " +
315
+ "'2011.12.31.11.00.02.backup.tar.enc-ab' from Ninefold."
316
+ )
317
+ directory_files.expects(:get).in_sequence(s).
318
+ with('backup.tar.enc-ab').returns(file)
319
+ file.expects(:destroy).in_sequence(s)
320
+ # third yield (file not found)
321
+ Backup::Logger.expects(:message).in_sequence(s).with(
322
+ "Storage::Ninefold started removing " +
323
+ "'2011.12.31.11.00.02.backup.tar.enc-ac' from Ninefold."
324
+ )
325
+ directory_files.expects(:get).in_sequence(s).
326
+ with('backup.tar.enc-ac').returns(nil)
327
+
328
+ # directory removed
329
+ directory.expects(:destroy).in_sequence(s)
330
+
331
+ expect do
332
+ storage.send(:remove!, package)
333
+ end.to raise_error {|err|
334
+ err.should be_an_instance_of Backup::Errors::Storage::Ninefold::NotFoundError
335
+ err.message.should == 'Storage::Ninefold::NotFoundError: ' +
336
+ "The following file(s) were not found in 'remote/path'\n" +
337
+ " backup.tar.enc-aa\n backup.tar.enc-ac"
338
+ }
339
+ end
340
+ end
341
+ end # describe '#remove!'
342
+
343
+ end
@@ -0,0 +1,362 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
+
5
+ describe Backup::Storage::RSync do
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:storage) do
8
+ Backup::Storage::RSync.new(model) do |rsync|
9
+ rsync.username = 'my_username'
10
+ rsync.password = 'my_password'
11
+ rsync.ip = '123.45.678.90'
12
+ rsync.keep = 5
13
+ end
14
+ end
15
+
16
+ it 'should be a subclass of Storage::Base' do
17
+ Backup::Storage::RSync.
18
+ superclass.should == Backup::Storage::Base
19
+ end
20
+
21
+ describe '#initialize' do
22
+ after { Backup::Storage::RSync.clear_defaults! }
23
+
24
+ it 'should load pre-configured defaults through Base' do
25
+ Backup::Storage::RSync.any_instance.expects(:load_defaults!)
26
+ storage
27
+ end
28
+
29
+ it 'should pass the model reference to Base' do
30
+ storage.instance_variable_get(:@model).should == model
31
+ end
32
+
33
+ it 'should pass the storage_id to Base' do
34
+ storage = Backup::Storage::RSync.new(model, 'my_storage_id')
35
+ storage.storage_id.should == 'my_storage_id'
36
+ end
37
+
38
+ context 'when no pre-configured defaults have been set' do
39
+ it 'should use the values given' do
40
+ storage.username.should == 'my_username'
41
+ storage.password.should == 'my_password'
42
+ storage.ip.should == '123.45.678.90'
43
+ storage.port.should == 22
44
+ storage.path.should == 'backups'
45
+ storage.local.should == false
46
+
47
+ storage.storage_id.should be_nil
48
+ storage.keep.should == 5
49
+ end
50
+
51
+ it 'should use default values if none are given' do
52
+ storage = Backup::Storage::RSync.new(model)
53
+
54
+ storage.username.should be_nil
55
+ storage.password.should be_nil
56
+ storage.ip.should be_nil
57
+ storage.port.should == 22
58
+ storage.path.should == 'backups'
59
+ storage.local.should == false
60
+
61
+ storage.storage_id.should be_nil
62
+ storage.keep.should be_nil
63
+ end
64
+ end # context 'when no pre-configured defaults have been set'
65
+
66
+ context 'when pre-configured defaults have been set' do
67
+ before do
68
+ Backup::Storage::RSync.defaults do |s|
69
+ s.username = 'some_username'
70
+ s.password = 'some_password'
71
+ s.ip = 'some_ip'
72
+ s.port = 'some_port'
73
+ s.path = 'some_path'
74
+ s.local = 'some_local'
75
+ s.keep = 'some_keep'
76
+ end
77
+ end
78
+
79
+ it 'should use pre-configured defaults' do
80
+ storage = Backup::Storage::RSync.new(model)
81
+
82
+ storage.username.should == 'some_username'
83
+ storage.password.should == 'some_password'
84
+ storage.ip.should == 'some_ip'
85
+ storage.port.should == 'some_port'
86
+ storage.path.should == 'some_path'
87
+ storage.local.should == 'some_local'
88
+
89
+ storage.storage_id.should be_nil
90
+ storage.keep.should == 'some_keep'
91
+ end
92
+
93
+ it 'should override pre-configured defaults' do
94
+ storage = Backup::Storage::RSync.new(model) do |s|
95
+ s.username = 'new_username'
96
+ s.password = 'new_password'
97
+ s.ip = 'new_ip'
98
+ s.port = 'new_port'
99
+ s.path = 'new_path'
100
+ s.local = 'new_local'
101
+ s.keep = 'new_keep'
102
+ end
103
+
104
+ storage.username.should == 'new_username'
105
+ storage.password.should == 'new_password'
106
+ storage.ip.should == 'new_ip'
107
+ storage.port.should == 'new_port'
108
+ storage.path.should == 'new_path'
109
+ storage.local.should == 'new_local'
110
+
111
+ storage.storage_id.should be_nil
112
+ storage.keep.should == 'new_keep'
113
+ end
114
+ end # context 'when pre-configured defaults have been set'
115
+ end # describe '#initialize'
116
+
117
+ describe '#remote_path_for' do
118
+ let(:package) { mock }
119
+ before do
120
+ storage.instance_variable_set(:@package, package)
121
+ package.expects(:trigger).returns(model.trigger)
122
+ end
123
+
124
+ it 'should override superclass so the time folder is not used' do
125
+ storage.send(:remote_path_for, package).should ==
126
+ File.join('backups', 'test_trigger')
127
+ end
128
+ end
129
+
130
+ describe '#connection' do
131
+ let(:connection) { mock }
132
+ it 'should yield a Net::SSH connection' do
133
+ Net::SSH.expects(:start).with(
134
+ '123.45.678.90', 'my_username', :password => 'my_password', :port => 22
135
+ ).yields(connection)
136
+
137
+ storage.send(:connection) do |ssh|
138
+ ssh.should be(connection)
139
+ end
140
+ end
141
+ end
142
+
143
+ describe '#transfer!' do
144
+ let(:package) { mock }
145
+ let(:s) { sequence '' }
146
+
147
+ before do
148
+ storage.instance_variable_set(:@package, package)
149
+ storage.stubs(:storage_name).returns('Storage::RSync')
150
+ storage.stubs(:local_path).returns('/local/path')
151
+ storage.stubs(:rsync_options).returns(:rsync_options)
152
+ storage.stubs(:rsync_port).returns(:rsync_port)
153
+ storage.stubs(:rsync_password_file).returns(:rsync_password_file)
154
+ storage.expects(:utility).with(:rsync).times(0..2).returns('rsync')
155
+ end
156
+
157
+ context 'when @local is set to false' do
158
+ it 'should transfer the package files to the remote' do
159
+ storage.expects(:write_password_file!).in_sequence(s)
160
+
161
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
162
+ returns('remote/path')
163
+
164
+ storage.expects(:create_remote_path!).in_sequence(s).with('remote/path')
165
+
166
+ storage.expects(:files_to_transfer_for).in_sequence(s).with(package).
167
+ multiple_yields(
168
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
169
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
170
+ )
171
+ # first yield
172
+ Backup::Logger.expects(:message).in_sequence(s).with(
173
+ "Storage::RSync started transferring " +
174
+ "'2011.12.31.11.00.02.backup.tar.enc-aa' to '123.45.678.90'."
175
+ )
176
+ storage.expects(:run).in_sequence(s).with(
177
+ "rsync rsync_options rsync_port rsync_password_file " +
178
+ "'#{ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-aa') }' " +
179
+ "'my_username@123.45.678.90:#{ File.join('remote/path', 'backup.tar.enc-aa') }'"
180
+ )
181
+ # second yield
182
+ Backup::Logger.expects(:message).in_sequence(s).with(
183
+ "Storage::RSync started transferring " +
184
+ "'2011.12.31.11.00.02.backup.tar.enc-ab' to '123.45.678.90'."
185
+ )
186
+ storage.expects(:run).in_sequence(s).with(
187
+ "rsync rsync_options rsync_port rsync_password_file " +
188
+ "'#{ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-ab') }' " +
189
+ "'my_username@123.45.678.90:#{ File.join('remote/path', 'backup.tar.enc-ab') }'"
190
+ )
191
+
192
+ storage.expects(:remove_password_file!).in_sequence(s)
193
+
194
+ storage.send(:transfer!)
195
+ end
196
+
197
+ it 'should ensure password file removal' do
198
+ storage.expects(:write_password_file!).raises('error message')
199
+ storage.expects(:remove_password_file!)
200
+
201
+ expect do
202
+ storage.send(:transfer!)
203
+ end.to raise_error(RuntimeError, 'error message')
204
+ end
205
+ end # context 'when @local is set to false'
206
+
207
+ context 'when @local is set to true' do
208
+ before { storage.local = true }
209
+
210
+ it 'should transfer the package files locally' do
211
+ storage.expects(:write_password_file!).never
212
+
213
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
214
+ returns('remote/path')
215
+
216
+ storage.expects(:create_remote_path!).in_sequence(s).with('remote/path')
217
+
218
+ storage.expects(:files_to_transfer_for).in_sequence(s).with(package).
219
+ multiple_yields(
220
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
221
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
222
+ )
223
+ # first yield
224
+ Backup::Logger.expects(:message).in_sequence(s).with(
225
+ "Storage::RSync started transferring " +
226
+ "'2011.12.31.11.00.02.backup.tar.enc-aa' to 'remote/path'."
227
+ )
228
+ storage.expects(:run).in_sequence(s).with(
229
+ "rsync " +
230
+ "'#{ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-aa') }' " +
231
+ "'#{ File.join('remote/path', 'backup.tar.enc-aa') }'"
232
+ )
233
+ # second yield
234
+ Backup::Logger.expects(:message).in_sequence(s).with(
235
+ "Storage::RSync started transferring " +
236
+ "'2011.12.31.11.00.02.backup.tar.enc-ab' to 'remote/path'."
237
+ )
238
+ storage.expects(:run).in_sequence(s).with(
239
+ "rsync " +
240
+ "'#{ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-ab') }' " +
241
+ "'#{ File.join('remote/path', 'backup.tar.enc-ab') }'"
242
+ )
243
+
244
+ storage.expects(:remove_password_file!).never
245
+
246
+ storage.send(:transfer!)
247
+ end
248
+
249
+ end # context 'when @local is set to true'
250
+
251
+ end # describe '#transfer!'
252
+
253
+ ##
254
+ # Note: Storage::RSync doesn't cycle
255
+ describe '#remove!' do
256
+ it 'should never even be called' do
257
+ storage.send(:remove!).should be_nil
258
+ end
259
+ end
260
+
261
+ describe '#create_remote_path!' do
262
+ let(:connection) { mock }
263
+
264
+ context 'when @local is set to false' do
265
+ it 'should create the remote_path on the remote' do
266
+ FileUtils.expects(:mkdir_p).never
267
+
268
+ storage.expects(:connection).yields(connection)
269
+ connection.expects(:exec!).with("mkdir -p 'remote/path'")
270
+
271
+ storage.send(:create_remote_path!, 'remote/path')
272
+ end
273
+ end
274
+
275
+ context 'when @local is set to true' do
276
+ before { storage.local = true }
277
+ it 'should create the remote_path locally' do
278
+ storage.expects(:connection).never
279
+
280
+ FileUtils.expects(:mkdir_p).with('remote/path')
281
+
282
+ storage.send(:create_remote_path!, 'remote/path')
283
+ end
284
+ end
285
+ end
286
+
287
+ describe '#write_password_file!' do
288
+ let(:file) { mock }
289
+
290
+ context 'when a @password is set' do
291
+ it 'should write the password to file and set @password_file' do
292
+ Tempfile.expects(:new).with('backup-rsync-password').returns(file)
293
+ file.expects(:write).with('my_password')
294
+ file.expects(:close)
295
+
296
+ storage.send(:write_password_file!)
297
+ storage.instance_variable_get(:@password_file).should be(file)
298
+ end
299
+ end
300
+
301
+ context 'when a @password is not set' do
302
+ before { storage.password = nil }
303
+ it 'should do nothing' do
304
+ Tempfile.expects(:new).never
305
+
306
+ storage.send(:write_password_file!)
307
+ storage.instance_variable_get(:@password_file).should be_nil
308
+ end
309
+ end
310
+ end
311
+
312
+ describe '#remove_password_file!' do
313
+ let(:file) { mock }
314
+
315
+ context 'when @password_file is set' do
316
+ before { storage.instance_variable_set(:@password_file, file) }
317
+ it 'should delete the file and clear @password_file' do
318
+ file.expects(:delete)
319
+ storage.send(:remove_password_file!)
320
+ storage.instance_variable_get(:@password_file).should be_nil
321
+ end
322
+ end
323
+
324
+ context 'when @password_file is not set' do
325
+ it 'should do nothing' do
326
+ file.expects(:delete).never
327
+ storage.send(:remove_password_file!)
328
+ end
329
+ end
330
+ end
331
+
332
+ describe '#rsync_password_file' do
333
+ let(:file) { mock }
334
+
335
+ context 'when @password_file is set' do
336
+ before { storage.instance_variable_set(:@password_file, file) }
337
+ it 'should return the syntax for rsync to use the password file' do
338
+ file.expects(:path).returns('/path/to/file')
339
+ storage.send(:rsync_password_file).should == "--password-file='/path/to/file'"
340
+ end
341
+ end
342
+
343
+ context 'when @password_file is not set' do
344
+ it 'should return nil' do
345
+ storage.send(:rsync_password_file).should be_nil
346
+ end
347
+ end
348
+ end
349
+
350
+ describe '#rsync_port' do
351
+ it 'should return the syntax for rsync to set the port' do
352
+ storage.send(:rsync_port).should == "-e 'ssh -p 22'"
353
+ end
354
+ end
355
+
356
+ describe '#rsync_options' do
357
+ it 'should return the syntax for rsync to set other options' do
358
+ storage.send(:rsync_options).should == '-z'
359
+ end
360
+ end
361
+
362
+ end