webtranslateit-safe 0.4.0
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.
- checksums.yaml +7 -0
- data/.autotest +3 -0
- data/.document +5 -0
- data/.github/dependabot.yml +26 -0
- data/.github/release-drafter.yml +36 -0
- data/.github/workflows/ci.yml +51 -0
- data/.github/workflows/release-drafter.yml +29 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/.rubocop_todo.yml +552 -0
- data/CHANGELOG +42 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +89 -0
- data/LICENSE.txt +22 -0
- data/README.markdown +237 -0
- data/Rakefile +8 -0
- data/TODO +31 -0
- data/bin/webtranslateit-safe +64 -0
- data/lib/extensions/mktmpdir.rb +45 -0
- data/lib/webtranslateit/safe/archive.rb +29 -0
- data/lib/webtranslateit/safe/backup.rb +27 -0
- data/lib/webtranslateit/safe/cloudfiles.rb +77 -0
- data/lib/webtranslateit/safe/config/builder.rb +100 -0
- data/lib/webtranslateit/safe/config/node.rb +79 -0
- data/lib/webtranslateit/safe/ftp.rb +85 -0
- data/lib/webtranslateit/safe/gpg.rb +52 -0
- data/lib/webtranslateit/safe/gzip.rb +29 -0
- data/lib/webtranslateit/safe/local.rb +55 -0
- data/lib/webtranslateit/safe/mongodump.rb +30 -0
- data/lib/webtranslateit/safe/mysqldump.rb +36 -0
- data/lib/webtranslateit/safe/pgdump.rb +36 -0
- data/lib/webtranslateit/safe/pipe.rb +23 -0
- data/lib/webtranslateit/safe/s3.rb +80 -0
- data/lib/webtranslateit/safe/sftp.rb +96 -0
- data/lib/webtranslateit/safe/sink.rb +40 -0
- data/lib/webtranslateit/safe/source.rb +51 -0
- data/lib/webtranslateit/safe/stream.rb +40 -0
- data/lib/webtranslateit/safe/svndump.rb +17 -0
- data/lib/webtranslateit/safe/tmp_file.rb +53 -0
- data/lib/webtranslateit/safe/version.rb +9 -0
- data/lib/webtranslateit/safe.rb +70 -0
- data/spec/integration/archive_integration_spec.rb +89 -0
- data/spec/integration/cleanup_spec.rb +62 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/webtranslateit/safe/archive_spec.rb +67 -0
- data/spec/webtranslateit/safe/cloudfiles_spec.rb +175 -0
- data/spec/webtranslateit/safe/config_spec.rb +307 -0
- data/spec/webtranslateit/safe/gpg_spec.rb +148 -0
- data/spec/webtranslateit/safe/gzip_spec.rb +64 -0
- data/spec/webtranslateit/safe/local_spec.rb +109 -0
- data/spec/webtranslateit/safe/mongodump_spec.rb +54 -0
- data/spec/webtranslateit/safe/mysqldump_spec.rb +83 -0
- data/spec/webtranslateit/safe/pgdump_spec.rb +45 -0
- data/spec/webtranslateit/safe/s3_spec.rb +168 -0
- data/spec/webtranslateit/safe/svndump_spec.rb +39 -0
- data/templates/script.rb +183 -0
- data/webtranslateit-safe.gemspec +32 -0
- metadata +149 -0
@@ -0,0 +1,307 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WebTranslateIt::Safe::Config do
|
4
|
+
it 'parses example config' do
|
5
|
+
config = WebTranslateIt::Safe::Config::Node.new do
|
6
|
+
|
7
|
+
dry_run false
|
8
|
+
local_only true
|
9
|
+
verbose true
|
10
|
+
|
11
|
+
local do
|
12
|
+
path 'path'
|
13
|
+
end
|
14
|
+
|
15
|
+
s3 do
|
16
|
+
key 's3 key'
|
17
|
+
secret 'secret'
|
18
|
+
bucket 'bucket'
|
19
|
+
path 'path1'
|
20
|
+
end
|
21
|
+
|
22
|
+
sftp do
|
23
|
+
user 'sftp user'
|
24
|
+
password 'sftp password'
|
25
|
+
host 'sftp host'
|
26
|
+
end
|
27
|
+
|
28
|
+
gpg do
|
29
|
+
password 'astrails'
|
30
|
+
key 'gpg-key'
|
31
|
+
end
|
32
|
+
|
33
|
+
keep do
|
34
|
+
s3 20
|
35
|
+
local 4
|
36
|
+
end
|
37
|
+
|
38
|
+
mysqldump do
|
39
|
+
options '-ceKq --single-transaction --create-options'
|
40
|
+
|
41
|
+
user 'astrails'
|
42
|
+
password ''
|
43
|
+
host 'localhost'
|
44
|
+
port 3306
|
45
|
+
socket '/var/run/mysqld/mysqld.sock'
|
46
|
+
|
47
|
+
database :blog
|
48
|
+
|
49
|
+
database :production do
|
50
|
+
keep local: 3
|
51
|
+
|
52
|
+
gpg do
|
53
|
+
password 'custom-production-pass'
|
54
|
+
end
|
55
|
+
|
56
|
+
skip_tables [:logger_exceptions, :request_logs]
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
pgdump do
|
62
|
+
options '-i -x -O'
|
63
|
+
|
64
|
+
user 'astrails'
|
65
|
+
password ''
|
66
|
+
host 'localhost'
|
67
|
+
port 5432
|
68
|
+
|
69
|
+
database :blog
|
70
|
+
|
71
|
+
database :production do
|
72
|
+
keep local: 3
|
73
|
+
|
74
|
+
skip_tables [:logger_exceptions, :request_logs]
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
svndump do
|
80
|
+
repo :my_repo do
|
81
|
+
repo_path '/home/svn/my_repo'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
tar do
|
86
|
+
archive 'git-repositories' do
|
87
|
+
files '/home/git/repositories'
|
88
|
+
end
|
89
|
+
|
90
|
+
archive 'etc-files' do
|
91
|
+
files '/etc'
|
92
|
+
exclude '/etc/puppet/other'
|
93
|
+
end
|
94
|
+
|
95
|
+
archive 'dot-configs' do
|
96
|
+
files '/home/*/.[^.]*'
|
97
|
+
end
|
98
|
+
|
99
|
+
archive 'blog' do
|
100
|
+
files '/var/www/blog.astrails.com/'
|
101
|
+
exclude ['/var/www/blog.astrails.com/log', '/var/www/blog.astrails.com/tmp']
|
102
|
+
end
|
103
|
+
|
104
|
+
archive :misc do
|
105
|
+
files ['/backup/*.rb']
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
mongodump do
|
110
|
+
host 'host'
|
111
|
+
database 'database'
|
112
|
+
user 'user'
|
113
|
+
password 'password'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
expected = {
|
118
|
+
'dry_run' => false,
|
119
|
+
'local_only' => true,
|
120
|
+
'verbose' => true,
|
121
|
+
|
122
|
+
'local' => {'path' => 'path'},
|
123
|
+
|
124
|
+
's3' => {
|
125
|
+
'key' => 's3 key',
|
126
|
+
'secret' => 'secret',
|
127
|
+
'bucket' => 'bucket',
|
128
|
+
'path' => 'path1'
|
129
|
+
},
|
130
|
+
|
131
|
+
'sftp' => {
|
132
|
+
'user' => 'sftp user',
|
133
|
+
'password' => 'sftp password',
|
134
|
+
'host' => 'sftp host'
|
135
|
+
},
|
136
|
+
|
137
|
+
'gpg' => {'password' => 'astrails', 'key' => 'gpg-key'},
|
138
|
+
|
139
|
+
'keep' => {'s3' => 20, 'local' => 4},
|
140
|
+
|
141
|
+
'mysqldump' => {
|
142
|
+
'options' => '-ceKq --single-transaction --create-options',
|
143
|
+
'user' => 'astrails',
|
144
|
+
'password' => '',
|
145
|
+
'host' => 'localhost',
|
146
|
+
'port' => 3306,
|
147
|
+
'socket' => '/var/run/mysqld/mysqld.sock',
|
148
|
+
|
149
|
+
'databases' => {
|
150
|
+
'blog' => {},
|
151
|
+
'production' => {
|
152
|
+
'keep' => {'local' => 3},
|
153
|
+
'gpg' => {'password' => 'custom-production-pass'},
|
154
|
+
'skip_tables' => ['logger_exceptions', 'request_logs']
|
155
|
+
}
|
156
|
+
}
|
157
|
+
},
|
158
|
+
|
159
|
+
'pgdump' => {
|
160
|
+
'options' => '-i -x -O',
|
161
|
+
'user' => 'astrails',
|
162
|
+
'password' => '',
|
163
|
+
'host' => 'localhost',
|
164
|
+
'port' => 5432,
|
165
|
+
|
166
|
+
'databases' => {
|
167
|
+
'blog' => {},
|
168
|
+
'production' => {
|
169
|
+
'keep' => {'local' => 3},
|
170
|
+
'skip_tables' => ['logger_exceptions', 'request_logs']
|
171
|
+
}
|
172
|
+
}
|
173
|
+
},
|
174
|
+
|
175
|
+
'svndump' => {
|
176
|
+
'repos' => {
|
177
|
+
'my_repo' => {
|
178
|
+
'repo_path' => '/home/svn/my_repo'
|
179
|
+
}
|
180
|
+
}
|
181
|
+
},
|
182
|
+
|
183
|
+
'tar' => {
|
184
|
+
'archives' => {
|
185
|
+
'git-repositories' => {'files' => ['/home/git/repositories']},
|
186
|
+
'etc-files' => {'files' => ['/etc'], 'exclude' => ['/etc/puppet/other']},
|
187
|
+
'dot-configs' => {'files' => ['/home/*/.[^.]*']},
|
188
|
+
'blog' => {
|
189
|
+
'files' => ['/var/www/blog.astrails.com/'],
|
190
|
+
'exclude' => ['/var/www/blog.astrails.com/log', '/var/www/blog.astrails.com/tmp']
|
191
|
+
},
|
192
|
+
'misc' => {'files' => ['/backup/*.rb']}
|
193
|
+
}
|
194
|
+
},
|
195
|
+
|
196
|
+
'mongodump' => {
|
197
|
+
'host' => 'host',
|
198
|
+
'databases' => {
|
199
|
+
'database' => {}
|
200
|
+
},
|
201
|
+
'user' => 'user',
|
202
|
+
'password' => 'password'
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
config.to_hash.should == expected
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'makes an array from multivalues' do
|
210
|
+
config = WebTranslateIt::Safe::Config::Node.new do
|
211
|
+
skip_tables 'a'
|
212
|
+
skip_tables 'b'
|
213
|
+
files '/foo'
|
214
|
+
files '/bar'
|
215
|
+
exclude '/foo/bar'
|
216
|
+
exclude '/foo/bar/baz'
|
217
|
+
end
|
218
|
+
|
219
|
+
expected = {
|
220
|
+
'skip_tables' => ['a', 'b'],
|
221
|
+
'files' => ['/foo', '/bar'],
|
222
|
+
'exclude' => ['/foo/bar', '/foo/bar/baz']
|
223
|
+
}
|
224
|
+
|
225
|
+
config.to_hash.should == expected
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'raises error on key duplication' do
|
229
|
+
proc do
|
230
|
+
WebTranslateIt::Safe::Config::Node.new do
|
231
|
+
path 'foo'
|
232
|
+
path 'bar'
|
233
|
+
end
|
234
|
+
end.should raise_error(ArgumentError, "duplicate value for 'path'")
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'accepts hash as data' do
|
238
|
+
WebTranslateIt::Safe::Config::Node.new do
|
239
|
+
tar do
|
240
|
+
archive 'blog', files: 'foo', exclude: ['aaa', 'bbb']
|
241
|
+
end
|
242
|
+
end.to_hash.should == {
|
243
|
+
'tar' => {
|
244
|
+
'archives' => {
|
245
|
+
'blog' => {
|
246
|
+
'files' => ['foo'],
|
247
|
+
'exclude' => ['aaa', 'bbb']
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'accepts hash as data and a block' do
|
255
|
+
WebTranslateIt::Safe::Config::Node.new do
|
256
|
+
tar do
|
257
|
+
archive 'blog', files: 'foo' do
|
258
|
+
exclude ['aaa', 'bbb']
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end.to_hash.should == {
|
262
|
+
'tar' => {
|
263
|
+
'archives' => {
|
264
|
+
'blog' => {
|
265
|
+
'files' => ['foo'],
|
266
|
+
'exclude' => ['aaa', 'bbb']
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
}
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'accepts multiple levels of data hash' do
|
274
|
+
config = WebTranslateIt::Safe::Config::Node.new nil, tar: {
|
275
|
+
s3: {bucket: '_bucket', key: '_key', secret: '_secret'},
|
276
|
+
keep: {s3: 2}
|
277
|
+
}
|
278
|
+
|
279
|
+
config.to_hash.should == {
|
280
|
+
'tar' => {
|
281
|
+
's3' => {'bucket' => '_bucket', 'key' => '_key', 'secret' => '_secret'},
|
282
|
+
'keep' => {'s3' => 2}
|
283
|
+
}
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'sets multi value as array' do
|
288
|
+
config = WebTranslateIt::Safe::Config::Node.new do
|
289
|
+
tar do
|
290
|
+
archive 'foo' do
|
291
|
+
files 'bar'
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
config.to_hash.should == {
|
297
|
+
'tar' => {
|
298
|
+
'archives' => {
|
299
|
+
'foo' => {
|
300
|
+
'files' => ['bar']
|
301
|
+
}
|
302
|
+
}
|
303
|
+
}
|
304
|
+
}
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WebTranslateIt::Safe::Gpg do
|
4
|
+
def def_backup
|
5
|
+
{
|
6
|
+
compressed: false,
|
7
|
+
command: 'command',
|
8
|
+
extension: '.foo',
|
9
|
+
filename: 'qweqwe'
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def gpg(config = {}, backup = def_backup)
|
14
|
+
WebTranslateIt::Safe::Gpg.new(
|
15
|
+
WebTranslateIt::Safe::Config::Node.new(nil, config),
|
16
|
+
WebTranslateIt::Safe::Backup.new(backup)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
after(:each) { WebTranslateIt::Safe::TmpFile.cleanup }
|
21
|
+
|
22
|
+
describe :process do
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
@gpg = gpg
|
26
|
+
stub(@gpg).gpg_password_file { 'pwd-file' }
|
27
|
+
stub(@gpg).pipe { '|gpg -BLAH' }
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'when active' do
|
31
|
+
before(:each) do
|
32
|
+
stub(@gpg).active? { true }
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'adds .gpg extension' do
|
36
|
+
mock(@gpg.backup.extension) << '.gpg'
|
37
|
+
@gpg.process
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'adds command pipe' do
|
41
|
+
mock(@gpg.backup.command) << (/\|gpg -BLAH/)
|
42
|
+
@gpg.process
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'sets compressed' do
|
46
|
+
mock(@gpg.backup).compressed = true
|
47
|
+
@gpg.process
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'when inactive' do
|
52
|
+
before(:each) do
|
53
|
+
stub(@gpg).active? { false }
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'does not touch extension' do
|
57
|
+
dont_allow(@gpg.backup.extension) << anything
|
58
|
+
@gpg.process
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'does not touch command' do
|
62
|
+
dont_allow(@gpg.backup.command) << anything
|
63
|
+
@gpg.process
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'does not touch compressed' do
|
67
|
+
dont_allow(@gpg.backup).compressed = anything
|
68
|
+
@gpg.process
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe :active? do
|
74
|
+
|
75
|
+
describe 'with key' do
|
76
|
+
it 'is true' do
|
77
|
+
expect(gpg(gpg: {key: :foo})).to be_active
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'with password' do
|
82
|
+
it 'is true' do
|
83
|
+
expect(gpg(gpg: {password: :foo})).to be_active
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'without key & password' do
|
88
|
+
it 'is false' do
|
89
|
+
expect(gpg).not_to be_active
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'with key & password' do
|
94
|
+
it 'raises RuntimeError' do
|
95
|
+
lambda {
|
96
|
+
gpg(gpg: {key: 'foo', password: 'bar'}).send :active?
|
97
|
+
}.should raise_error(RuntimeError, "can't use both gpg password and pubkey")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe :pipe do
|
103
|
+
|
104
|
+
describe 'with key' do
|
105
|
+
def kgpg(extra = {})
|
106
|
+
gpg({gpg: {key: 'foo', options: 'GPG-OPT'}.merge(extra), options: 'OPT'})
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'does not call gpg_password_file' do
|
110
|
+
g = kgpg
|
111
|
+
dont_allow(g).gpg_password_file(anything)
|
112
|
+
g.send(:pipe)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "uses '-r' and :options" do
|
116
|
+
kgpg.send(:pipe).should == '|gpg GPG-OPT -e -r foo'
|
117
|
+
end
|
118
|
+
|
119
|
+
it "uses the 'command' options" do
|
120
|
+
kgpg(command: 'other-gpg').send(:pipe).should == '|other-gpg GPG-OPT -e -r foo'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'with password' do
|
125
|
+
def pgpg(extra = {})
|
126
|
+
returning(gpg({gpg: {password: 'bar', options: 'GPG-OPT'}.merge(extra), options: 'OPT'})) do |g|
|
127
|
+
stub(g).gpg_password_file(anything) { 'pass-file' }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it "uses '--passphrase-file' and :options" do
|
132
|
+
pgpg.send(:pipe).should == '|gpg GPG-OPT -c --passphrase-file pass-file'
|
133
|
+
end
|
134
|
+
|
135
|
+
it "uses the 'command' options" do
|
136
|
+
pgpg(command: 'other-gpg').send(:pipe).should == '|other-gpg GPG-OPT -c --passphrase-file pass-file'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe :gpg_password_file do
|
142
|
+
it 'creates password file' do
|
143
|
+
file = gpg.send(:gpg_password_file, 'foo')
|
144
|
+
expect(File.exist?(file)).to be true
|
145
|
+
File.read(file).should == 'foo'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WebTranslateIt::Safe::Gzip do
|
4
|
+
|
5
|
+
def def_backup
|
6
|
+
{
|
7
|
+
compressed: false,
|
8
|
+
command: 'command',
|
9
|
+
extension: '.foo',
|
10
|
+
filename: 'qweqwe'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
after(:each) { WebTranslateIt::Safe::TmpFile.cleanup }
|
15
|
+
|
16
|
+
def gzip(config = {}, backup = def_backup)
|
17
|
+
WebTranslateIt::Safe::Gzip.new(
|
18
|
+
@config = WebTranslateIt::Safe::Config::Node.new(nil, config),
|
19
|
+
@backup = WebTranslateIt::Safe::Backup.new(backup)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe :preocess do
|
24
|
+
|
25
|
+
describe 'when not yet compressed' do
|
26
|
+
before(:each) { @gzip = gzip }
|
27
|
+
|
28
|
+
it 'adds .gz extension' do
|
29
|
+
mock(@backup.extension) << '.gz'
|
30
|
+
@gzip.process
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'adds |gzip pipe' do
|
34
|
+
mock(@backup.command) << '|gzip'
|
35
|
+
@gzip.process
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'sets compressed' do
|
39
|
+
mock(@backup).compressed = true
|
40
|
+
@gzip.process
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'when already compressed' do
|
45
|
+
|
46
|
+
before(:each) { @gzip = gzip({}, extension: '.foo', command: 'foobar', compressed: true) }
|
47
|
+
|
48
|
+
it 'does not touch extension' do
|
49
|
+
@gzip.process
|
50
|
+
@backup.extension.should == '.foo'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'does not touch command' do
|
54
|
+
@gzip.process
|
55
|
+
@backup.command.should == 'foobar'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'does not touch compressed' do
|
59
|
+
@gzip.process
|
60
|
+
@backup.compressed.should == true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WebTranslateIt::Safe::Local do
|
4
|
+
def def_config
|
5
|
+
{
|
6
|
+
local: {
|
7
|
+
path: '/:kind~:id~:timestamp'
|
8
|
+
},
|
9
|
+
keep: {
|
10
|
+
local: 2
|
11
|
+
}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def def_backup
|
16
|
+
{
|
17
|
+
kind: 'mysqldump',
|
18
|
+
id: 'blog',
|
19
|
+
timestamp: 'NoW',
|
20
|
+
compressed: true,
|
21
|
+
command: 'command',
|
22
|
+
extension: '.foo.gz',
|
23
|
+
filename: 'qweqwe'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def local(config = def_config, backup = def_backup)
|
28
|
+
WebTranslateIt::Safe::Local.new(
|
29
|
+
@config = WebTranslateIt::Safe::Config::Node.new(nil, config),
|
30
|
+
@backup = WebTranslateIt::Safe::Backup.new(backup)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe :active? do
|
35
|
+
it 'is true' do
|
36
|
+
expect(local).to be_active
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe :path do
|
41
|
+
it 'raises RuntimeError when no path' do
|
42
|
+
lambda {
|
43
|
+
local({}).send :path
|
44
|
+
}.should raise_error(RuntimeError, 'missing :local/:path')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'uses local/path' do
|
48
|
+
local.send(:path).should == '/mysqldump~blog~NoW'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe :save do
|
53
|
+
before(:each) do
|
54
|
+
@local = local
|
55
|
+
stub(@local).system
|
56
|
+
stub(@local).full_path { 'file-path' }
|
57
|
+
stub(FileUtils).mkdir_p
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'calls system to save the file' do
|
61
|
+
mock(@local).system('command>file-path')
|
62
|
+
@local.send(:save)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'creates directory' do
|
66
|
+
mock(FileUtils).mkdir_p('/mysqldump~blog~NoW')
|
67
|
+
@local.send(:save)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'sets backup.path' do
|
71
|
+
mock(@backup).path = 'file-path'
|
72
|
+
@local.send(:save)
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'dry run' do
|
76
|
+
before(:each) { @local.config[:dry_run] = true }
|
77
|
+
|
78
|
+
it 'should not create directory'
|
79
|
+
it 'should not call system'
|
80
|
+
it 'sets backup.path' do
|
81
|
+
mock(@backup).path = 'file-path'
|
82
|
+
@local.send(:save)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe :cleanup do
|
88
|
+
before(:each) do
|
89
|
+
@files = [4, 1, 3, 2].map { |i| "/mysqldump~blog~NoW/qweqwe.#{i}" }
|
90
|
+
stub(File).file?(anything) { true }
|
91
|
+
stub(File).size(anything) { 1 }
|
92
|
+
stub(File).unlink
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'checks [:keep, :local]' do
|
96
|
+
@local = local(def_config.merge(keep: {}))
|
97
|
+
dont_allow(Dir).[]
|
98
|
+
@local.send :cleanup
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'deletes extra files' do
|
102
|
+
@local = local
|
103
|
+
mock(Dir).[]('/mysqldump~blog~NoW/qweqwe.*') { @files }
|
104
|
+
mock(File).unlink('/mysqldump~blog~NoW/qweqwe.1')
|
105
|
+
mock(File).unlink('/mysqldump~blog~NoW/qweqwe.2')
|
106
|
+
@local.send :cleanup
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WebTranslateIt::Safe::Mongodump do
|
4
|
+
def def_config
|
5
|
+
{
|
6
|
+
host: 'prod.example.com',
|
7
|
+
user: 'testuser',
|
8
|
+
password: 'p4ssw0rd'
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def mongodump(id = :foo, config = def_config)
|
13
|
+
WebTranslateIt::Safe::Mongodump.new(id, WebTranslateIt::Safe::Config::Node.new(nil, config))
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
stub(Time).now.stub!.strftime { 'NOW' }
|
18
|
+
@output_folder = File.join(WebTranslateIt::Safe::TmpFile.tmproot, 'mongodump')
|
19
|
+
end
|
20
|
+
|
21
|
+
after(:each) { WebTranslateIt::Safe::TmpFile.cleanup }
|
22
|
+
|
23
|
+
describe :backup do
|
24
|
+
before(:each) do
|
25
|
+
@mongo = mongodump
|
26
|
+
end
|
27
|
+
|
28
|
+
{
|
29
|
+
id: 'foo',
|
30
|
+
kind: 'mongodump',
|
31
|
+
extension: '.tar',
|
32
|
+
filename: 'mongodump-foo.NOW'
|
33
|
+
}.each do |k, v|
|
34
|
+
it "sets #{k} to #{v}" do
|
35
|
+
@mongo.backup.send(k).should == v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'sets the command' do
|
40
|
+
@mongo.backup.send(:command).should == "mongodump -q \"{xxxx : { \\$ne : 0 } }\" --db foo --host prod.example.com -u testuser -p p4ssw0rd --out #{@output_folder} && cd #{@output_folder} && tar cf - ."
|
41
|
+
end
|
42
|
+
|
43
|
+
{
|
44
|
+
host: '--host ',
|
45
|
+
user: '-u ',
|
46
|
+
password: '-p '
|
47
|
+
}.each do |key, v|
|
48
|
+
it "does not add #{key} to command if it is not present" do
|
49
|
+
@mongo = mongodump(:foo, def_config.reject! { |k, _v| k == key })
|
50
|
+
@mongo.backup.send(:command).should_not =~ /#{v}/
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|