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.
- data/.gitignore +8 -0
- data/.travis.yml +10 -0
- data/Gemfile +28 -0
- data/Guardfile +23 -0
- data/LICENSE.md +24 -0
- data/README.md +478 -0
- data/backup.gemspec +32 -0
- data/bin/backup +11 -0
- data/lib/backup.rb +133 -0
- data/lib/backup/archive.rb +117 -0
- data/lib/backup/binder.rb +22 -0
- data/lib/backup/cleaner.rb +121 -0
- data/lib/backup/cli/helpers.rb +93 -0
- data/lib/backup/cli/utility.rb +255 -0
- data/lib/backup/compressor/base.rb +35 -0
- data/lib/backup/compressor/bzip2.rb +50 -0
- data/lib/backup/compressor/custom.rb +53 -0
- data/lib/backup/compressor/gzip.rb +50 -0
- data/lib/backup/compressor/lzma.rb +52 -0
- data/lib/backup/compressor/pbzip2.rb +59 -0
- data/lib/backup/config.rb +174 -0
- data/lib/backup/configuration.rb +33 -0
- data/lib/backup/configuration/helpers.rb +130 -0
- data/lib/backup/configuration/store.rb +24 -0
- data/lib/backup/database/base.rb +53 -0
- data/lib/backup/database/mongodb.rb +230 -0
- data/lib/backup/database/mysql.rb +160 -0
- data/lib/backup/database/postgresql.rb +144 -0
- data/lib/backup/database/redis.rb +136 -0
- data/lib/backup/database/riak.rb +67 -0
- data/lib/backup/dependency.rb +108 -0
- data/lib/backup/encryptor/base.rb +29 -0
- data/lib/backup/encryptor/gpg.rb +760 -0
- data/lib/backup/encryptor/open_ssl.rb +72 -0
- data/lib/backup/errors.rb +124 -0
- data/lib/backup/hooks.rb +68 -0
- data/lib/backup/logger.rb +152 -0
- data/lib/backup/model.rb +409 -0
- data/lib/backup/notifier/base.rb +81 -0
- data/lib/backup/notifier/campfire.rb +155 -0
- data/lib/backup/notifier/hipchat.rb +93 -0
- data/lib/backup/notifier/mail.rb +206 -0
- data/lib/backup/notifier/prowl.rb +65 -0
- data/lib/backup/notifier/pushover.rb +88 -0
- data/lib/backup/notifier/twitter.rb +70 -0
- data/lib/backup/package.rb +47 -0
- data/lib/backup/packager.rb +100 -0
- data/lib/backup/pipeline.rb +110 -0
- data/lib/backup/splitter.rb +75 -0
- data/lib/backup/storage/base.rb +99 -0
- data/lib/backup/storage/cloudfiles.rb +87 -0
- data/lib/backup/storage/cycler.rb +117 -0
- data/lib/backup/storage/dropbox.rb +178 -0
- data/lib/backup/storage/ftp.rb +119 -0
- data/lib/backup/storage/local.rb +82 -0
- data/lib/backup/storage/ninefold.rb +116 -0
- data/lib/backup/storage/rsync.rb +149 -0
- data/lib/backup/storage/s3.rb +94 -0
- data/lib/backup/storage/scp.rb +99 -0
- data/lib/backup/storage/sftp.rb +108 -0
- data/lib/backup/syncer/base.rb +46 -0
- data/lib/backup/syncer/cloud/base.rb +247 -0
- data/lib/backup/syncer/cloud/cloud_files.rb +78 -0
- data/lib/backup/syncer/cloud/s3.rb +68 -0
- data/lib/backup/syncer/rsync/base.rb +49 -0
- data/lib/backup/syncer/rsync/local.rb +55 -0
- data/lib/backup/syncer/rsync/pull.rb +36 -0
- data/lib/backup/syncer/rsync/push.rb +116 -0
- data/lib/backup/template.rb +46 -0
- data/lib/backup/version.rb +43 -0
- data/spec-live/.gitignore +6 -0
- data/spec-live/README +7 -0
- data/spec-live/backups/config.rb +83 -0
- data/spec-live/backups/config.yml.template +46 -0
- data/spec-live/backups/models.rb +184 -0
- data/spec-live/compressor/custom_spec.rb +30 -0
- data/spec-live/compressor/gzip_spec.rb +30 -0
- data/spec-live/encryptor/gpg_keys.rb +239 -0
- data/spec-live/encryptor/gpg_spec.rb +287 -0
- data/spec-live/notifier/mail_spec.rb +121 -0
- data/spec-live/spec_helper.rb +151 -0
- data/spec-live/storage/dropbox_spec.rb +151 -0
- data/spec-live/storage/local_spec.rb +83 -0
- data/spec-live/storage/scp_spec.rb +193 -0
- data/spec-live/syncer/cloud/s3_spec.rb +124 -0
- data/spec/archive_spec.rb +335 -0
- data/spec/cleaner_spec.rb +312 -0
- data/spec/cli/helpers_spec.rb +301 -0
- data/spec/cli/utility_spec.rb +411 -0
- data/spec/compressor/base_spec.rb +52 -0
- data/spec/compressor/bzip2_spec.rb +217 -0
- data/spec/compressor/custom_spec.rb +106 -0
- data/spec/compressor/gzip_spec.rb +217 -0
- data/spec/compressor/lzma_spec.rb +123 -0
- data/spec/compressor/pbzip2_spec.rb +165 -0
- data/spec/config_spec.rb +321 -0
- data/spec/configuration/helpers_spec.rb +247 -0
- data/spec/configuration/store_spec.rb +39 -0
- data/spec/configuration_spec.rb +62 -0
- data/spec/database/base_spec.rb +63 -0
- data/spec/database/mongodb_spec.rb +510 -0
- data/spec/database/mysql_spec.rb +411 -0
- data/spec/database/postgresql_spec.rb +353 -0
- data/spec/database/redis_spec.rb +334 -0
- data/spec/database/riak_spec.rb +176 -0
- data/spec/dependency_spec.rb +51 -0
- data/spec/encryptor/base_spec.rb +40 -0
- data/spec/encryptor/gpg_spec.rb +909 -0
- data/spec/encryptor/open_ssl_spec.rb +148 -0
- data/spec/errors_spec.rb +306 -0
- data/spec/hooks_spec.rb +35 -0
- data/spec/logger_spec.rb +367 -0
- data/spec/model_spec.rb +694 -0
- data/spec/notifier/base_spec.rb +104 -0
- data/spec/notifier/campfire_spec.rb +217 -0
- data/spec/notifier/hipchat_spec.rb +211 -0
- data/spec/notifier/mail_spec.rb +316 -0
- data/spec/notifier/prowl_spec.rb +138 -0
- data/spec/notifier/pushover_spec.rb +123 -0
- data/spec/notifier/twitter_spec.rb +153 -0
- data/spec/package_spec.rb +61 -0
- data/spec/packager_spec.rb +213 -0
- data/spec/pipeline_spec.rb +259 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/splitter_spec.rb +120 -0
- data/spec/storage/base_spec.rb +166 -0
- data/spec/storage/cloudfiles_spec.rb +254 -0
- data/spec/storage/cycler_spec.rb +247 -0
- data/spec/storage/dropbox_spec.rb +480 -0
- data/spec/storage/ftp_spec.rb +271 -0
- data/spec/storage/local_spec.rb +259 -0
- data/spec/storage/ninefold_spec.rb +343 -0
- data/spec/storage/rsync_spec.rb +362 -0
- data/spec/storage/s3_spec.rb +245 -0
- data/spec/storage/scp_spec.rb +233 -0
- data/spec/storage/sftp_spec.rb +244 -0
- data/spec/syncer/base_spec.rb +109 -0
- data/spec/syncer/cloud/base_spec.rb +515 -0
- data/spec/syncer/cloud/cloud_files_spec.rb +181 -0
- data/spec/syncer/cloud/s3_spec.rb +174 -0
- data/spec/syncer/rsync/base_spec.rb +98 -0
- data/spec/syncer/rsync/local_spec.rb +149 -0
- data/spec/syncer/rsync/pull_spec.rb +98 -0
- data/spec/syncer/rsync/push_spec.rb +333 -0
- data/spec/version_spec.rb +21 -0
- data/templates/cli/utility/archive +25 -0
- data/templates/cli/utility/compressor/bzip2 +4 -0
- data/templates/cli/utility/compressor/custom +11 -0
- data/templates/cli/utility/compressor/gzip +4 -0
- data/templates/cli/utility/compressor/lzma +10 -0
- data/templates/cli/utility/compressor/pbzip2 +10 -0
- data/templates/cli/utility/config +32 -0
- data/templates/cli/utility/database/mongodb +18 -0
- data/templates/cli/utility/database/mysql +21 -0
- data/templates/cli/utility/database/postgresql +17 -0
- data/templates/cli/utility/database/redis +16 -0
- data/templates/cli/utility/database/riak +11 -0
- data/templates/cli/utility/encryptor/gpg +27 -0
- data/templates/cli/utility/encryptor/openssl +9 -0
- data/templates/cli/utility/model.erb +23 -0
- data/templates/cli/utility/notifier/campfire +12 -0
- data/templates/cli/utility/notifier/hipchat +15 -0
- data/templates/cli/utility/notifier/mail +22 -0
- data/templates/cli/utility/notifier/prowl +11 -0
- data/templates/cli/utility/notifier/pushover +11 -0
- data/templates/cli/utility/notifier/twitter +13 -0
- data/templates/cli/utility/splitter +7 -0
- data/templates/cli/utility/storage/cloud_files +22 -0
- data/templates/cli/utility/storage/dropbox +20 -0
- data/templates/cli/utility/storage/ftp +12 -0
- data/templates/cli/utility/storage/local +7 -0
- data/templates/cli/utility/storage/ninefold +9 -0
- data/templates/cli/utility/storage/rsync +11 -0
- data/templates/cli/utility/storage/s3 +19 -0
- data/templates/cli/utility/storage/scp +11 -0
- data/templates/cli/utility/storage/sftp +11 -0
- data/templates/cli/utility/syncer/cloud_files +46 -0
- data/templates/cli/utility/syncer/rsync_local +12 -0
- data/templates/cli/utility/syncer/rsync_pull +17 -0
- data/templates/cli/utility/syncer/rsync_push +17 -0
- data/templates/cli/utility/syncer/s3 +43 -0
- data/templates/general/links +11 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +9 -0
- data/templates/notifier/mail/success.erb +7 -0
- data/templates/notifier/mail/warning.erb +9 -0
- data/templates/storage/dropbox/authorization_url.erb +6 -0
- data/templates/storage/dropbox/authorized.erb +4 -0
- data/templates/storage/dropbox/cache_file_written.erb +10 -0
- 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
|