backup 3.0.21 → 3.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +3 -1
- data/README.md +4 -3
- data/lib/backup.rb +8 -4
- data/lib/backup/config.rb +1 -1
- data/lib/backup/configuration/syncer/cloud.rb +23 -0
- data/lib/backup/configuration/syncer/cloud_files.rb +30 -0
- data/lib/backup/configuration/syncer/s3.rb +5 -11
- data/lib/backup/dependency.rb +6 -0
- data/lib/backup/notifier/twitter.rb +1 -1
- data/lib/backup/syncer/base.rb +25 -0
- data/lib/backup/syncer/cloud.rb +187 -0
- data/lib/backup/syncer/cloud_files.rb +56 -0
- data/lib/backup/syncer/rsync/base.rb +0 -26
- data/lib/backup/syncer/s3.rb +21 -102
- data/lib/backup/version.rb +1 -1
- data/spec/cli/utility_spec.rb +2 -2
- data/spec/configuration/syncer/cloud_files_spec.rb +44 -0
- data/spec/configuration/syncer/s3_spec.rb +0 -4
- data/spec/notifier/twitter_spec.rb +3 -3
- data/spec/syncer/cloud_files_spec.rb +192 -0
- data/spec/syncer/s3_spec.rb +155 -191
- data/templates/cli/utility/archive +20 -8
- data/templates/cli/utility/database/mongodb +3 -3
- data/templates/cli/utility/database/mysql +4 -4
- data/templates/cli/utility/database/postgresql +4 -4
- data/templates/cli/utility/database/redis +1 -1
- data/templates/cli/utility/encryptor/openssl +2 -2
- data/templates/cli/utility/notifier/campfire +3 -3
- data/templates/cli/utility/notifier/hipchat +6 -6
- data/templates/cli/utility/notifier/mail +7 -7
- data/templates/cli/utility/notifier/presently +4 -4
- data/templates/cli/utility/notifier/prowl +2 -2
- data/templates/cli/utility/notifier/twitter +4 -4
- data/templates/cli/utility/storage/cloud_files +22 -0
- data/templates/cli/utility/storage/dropbox +15 -10
- data/templates/cli/utility/storage/ftp +4 -4
- data/templates/cli/utility/storage/local +1 -1
- data/templates/cli/utility/storage/ninefold +3 -3
- data/templates/cli/utility/storage/rsync +4 -4
- data/templates/cli/utility/storage/s3 +6 -6
- data/templates/cli/utility/storage/scp +4 -4
- data/templates/cli/utility/storage/sftp +4 -4
- data/templates/cli/utility/syncer/cloud_files +48 -0
- data/templates/cli/utility/syncer/s3 +31 -1
- metadata +69 -39
- data/templates/cli/utility/storage/cloudfiles +0 -12
data/spec/syncer/s3_spec.rb
CHANGED
@@ -1,228 +1,192 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
2
|
require File.expand_path('../../spec_helper.rb', __FILE__)
|
4
3
|
|
4
|
+
class Parallel; end
|
5
|
+
|
5
6
|
describe Backup::Syncer::S3 do
|
6
|
-
|
7
|
-
Backup::Syncer::S3.new
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
describe '#perform!' do
|
8
|
+
let(:syncer) { Backup::Syncer::S3.new }
|
9
|
+
let(:connection) { stub('connection',
|
10
|
+
:directories => stub('directories', :get => bucket)) }
|
11
|
+
let(:bucket) { stub('bucket', :files => files) }
|
12
|
+
let(:files) { [] }
|
13
|
+
let(:content) { stub('content') }
|
14
|
+
|
15
|
+
before :each do
|
16
|
+
Fog::Storage.stubs(:new).returns connection
|
17
|
+
File.stubs(:open).returns content
|
18
|
+
File.stubs(:exist?).returns true
|
19
|
+
files.stubs(:create).returns true
|
20
|
+
|
21
|
+
syncer.directories << 'tmp'
|
22
|
+
syncer.path = 'storage'
|
23
|
+
|
24
|
+
Backup::Syncer::S3::SyncContext.any_instance.
|
25
|
+
stubs(:`).returns 'MD5(tmp/foo)= 123abcdef'
|
26
|
+
end
|
17
27
|
|
18
|
-
|
19
|
-
|
28
|
+
it "respects the concurrency_type setting with threads" do
|
29
|
+
syncer.concurrency_type = :threads
|
30
|
+
|
31
|
+
Parallel.expects(:each).with(anything, {:in_threads => 2}, anything)
|
32
|
+
|
33
|
+
syncer.perform!
|
20
34
|
end
|
21
|
-
end
|
22
35
|
|
23
|
-
|
36
|
+
it "respects the parallel thread count" do
|
37
|
+
syncer.concurrency_type = :threads
|
38
|
+
syncer.concurrency_level = 10
|
24
39
|
|
25
|
-
|
26
|
-
|
27
|
-
syncer.
|
28
|
-
syncer.bucket.should == 'my-bucket'
|
29
|
-
syncer.path.should == '/my_backups'
|
30
|
-
syncer.directories.should == ["/some/directory", "~/home/directory"]
|
31
|
-
syncer.mirror.should == true
|
32
|
-
syncer.additional_options.should == ['--opt-a', '--opt-b']
|
40
|
+
Parallel.expects(:each).with(anything, {:in_threads => 10}, anything)
|
41
|
+
|
42
|
+
syncer.perform!
|
33
43
|
end
|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
syncer.path.should == 'backups'
|
42
|
-
syncer.directories.should == []
|
43
|
-
syncer.mirror.should == false
|
44
|
-
syncer.additional_options.should == []
|
45
|
-
end
|
45
|
+
it "respects the concurrency_type setting with processors" do
|
46
|
+
syncer.concurrency_type = :processes
|
47
|
+
|
48
|
+
Parallel.expects(:each).with(anything, {:in_processes => 2}, anything)
|
49
|
+
|
50
|
+
syncer.perform!
|
46
51
|
end
|
47
52
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
syncer
|
62
|
-
syncer.access_key_id.should == 'some_access_key_id'
|
63
|
-
syncer.secret_access_key.should == 'some_secret_access_key'
|
64
|
-
syncer.bucket.should == 'some_bucket'
|
65
|
-
syncer.path.should == 'some_path'
|
66
|
-
syncer.directories.should == []
|
67
|
-
syncer.mirror.should == 'some_mirror'
|
68
|
-
syncer.additional_options.should == 'some_additional_options'
|
53
|
+
it "respects the parallel thread count" do
|
54
|
+
syncer.concurrency_type = :processes
|
55
|
+
syncer.concurrency_level = 10
|
56
|
+
|
57
|
+
Parallel.expects(:each).with(anything, {:in_processes => 10}, anything)
|
58
|
+
|
59
|
+
syncer.perform!
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'file exists locally' do
|
63
|
+
it "uploads a file if it does not exist remotely" do
|
64
|
+
files.expects(:create).with(:key => 'storage/tmp/foo', :body => content)
|
65
|
+
|
66
|
+
syncer.perform!
|
69
67
|
end
|
70
68
|
|
71
|
-
it
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
#s3.directories = 'cannot_have_a_default_value'
|
78
|
-
s3.mirror = 'old_mirror'
|
79
|
-
s3.additional_options = 'old_additional_options'
|
80
|
-
end
|
81
|
-
syncer = Backup::Syncer::S3.new do |s3|
|
82
|
-
s3.access_key_id = 'new_access_key_id'
|
83
|
-
s3.secret_access_key = 'new_secret_access_key'
|
84
|
-
s3.bucket = 'new_bucket'
|
85
|
-
s3.path = 'new_path'
|
86
|
-
s3.directories = 'new_directories'
|
87
|
-
s3.mirror = 'new_mirror'
|
88
|
-
s3.additional_options = 'new_additional_options'
|
89
|
-
end
|
90
|
-
|
91
|
-
syncer.access_key_id.should == 'new_access_key_id'
|
92
|
-
syncer.secret_access_key.should == 'new_secret_access_key'
|
93
|
-
syncer.bucket.should == 'new_bucket'
|
94
|
-
syncer.path.should == 'new_path'
|
95
|
-
syncer.directories.should == 'new_directories'
|
96
|
-
syncer.mirror.should == 'new_mirror'
|
97
|
-
syncer.additional_options.should == 'new_additional_options'
|
69
|
+
it "uploads a file if it exists remotely with a different MD5" do
|
70
|
+
files << stub('file', :key => 'storage/tmp/foo', :etag => 'abcdef123')
|
71
|
+
|
72
|
+
files.expects(:create).with(:key => 'storage/tmp/foo', :body => content)
|
73
|
+
|
74
|
+
syncer.perform!
|
98
75
|
end
|
99
|
-
end # context 'when setting configuration defaults'
|
100
|
-
end # describe '#initialize'
|
101
76
|
|
102
|
-
|
103
|
-
|
77
|
+
it "does nothing if the file exists remotely with the same MD5" do
|
78
|
+
files << stub('file', :key => 'storage/tmp/foo', :etag => '123abcdef')
|
104
79
|
|
105
|
-
|
106
|
-
syncer.expects(:utility).twice.with(:s3sync).returns('s3sync')
|
107
|
-
syncer.expects(:options).twice.returns('options_output')
|
108
|
-
end
|
80
|
+
files.expects(:create).never
|
109
81
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
# first directory
|
114
|
-
Backup::Logger.expects(:message).in_sequence(s).with(
|
115
|
-
"Syncer::S3 started syncing '/some/directory'."
|
116
|
-
)
|
117
|
-
syncer.expects(:run).in_sequence(s).with(
|
118
|
-
"s3sync options_output '/some/directory' 'my-bucket:my_backups'"
|
119
|
-
).returns('messages from stdout')
|
120
|
-
Backup::Logger.expects(:silent).in_sequence(s).with('messages from stdout')
|
121
|
-
|
122
|
-
# second directory
|
123
|
-
Backup::Logger.expects(:message).in_sequence(s).with(
|
124
|
-
"Syncer::S3 started syncing '~/home/directory'."
|
125
|
-
)
|
126
|
-
syncer.expects(:run).in_sequence(s).with(
|
127
|
-
"s3sync options_output '#{ File.expand_path('~/home/directory') }' " +
|
128
|
-
"'my-bucket:my_backups'"
|
129
|
-
).returns('messages from stdout')
|
130
|
-
Backup::Logger.expects(:silent).in_sequence(s).with('messages from stdout')
|
131
|
-
|
132
|
-
syncer.expects(:unset_environment_variables!).in_sequence(s)
|
82
|
+
syncer.perform!
|
83
|
+
end
|
133
84
|
|
134
|
-
|
135
|
-
|
136
|
-
|
85
|
+
it "skips the file if it no longer exists locally" do
|
86
|
+
File.stubs(:exist?).returns false
|
87
|
+
|
88
|
+
files.expects(:create).never
|
137
89
|
|
138
|
-
|
139
|
-
context 'when no block is given' do
|
140
|
-
it 'should return @directories' do
|
141
|
-
syncer.directories.should ==
|
142
|
-
['/some/directory', '~/home/directory']
|
90
|
+
syncer.perform!
|
143
91
|
end
|
144
|
-
end
|
145
92
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
syncer.directories.should == [
|
153
|
-
'/some/directory',
|
154
|
-
'~/home/directory',
|
155
|
-
'/new/path',
|
156
|
-
'~/new/home/path'
|
157
|
-
]
|
93
|
+
it "respects the given path" do
|
94
|
+
syncer.path = 'box'
|
95
|
+
|
96
|
+
files.expects(:create).with(:key => 'box/tmp/foo', :body => content)
|
97
|
+
|
98
|
+
syncer.perform!
|
158
99
|
end
|
159
|
-
end
|
160
|
-
end # describe '#directories'
|
161
100
|
|
162
|
-
|
163
|
-
|
164
|
-
syncer.add '/my/path'
|
165
|
-
syncer.directories.should ==
|
166
|
-
['/some/directory', '~/home/directory', '/my/path']
|
167
|
-
end
|
168
|
-
end
|
101
|
+
it "uploads the content of the local file" do
|
102
|
+
File.expects(:open).with('tmp/foo').returns content
|
169
103
|
|
170
|
-
|
171
|
-
|
172
|
-
syncer.send(:dest_path).should == 'my_backups'
|
173
|
-
end
|
104
|
+
syncer.perform!
|
105
|
+
end
|
174
106
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
107
|
+
it "creates the connection with the provided credentials" do
|
108
|
+
syncer.access_key_id = 'my-access'
|
109
|
+
syncer.secret_access_key = 'my-secret'
|
110
|
+
syncer.region = 'somewhere'
|
179
111
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
112
|
+
Fog::Storage.expects(:new).with(
|
113
|
+
:provider => 'AWS',
|
114
|
+
:aws_access_key_id => 'my-access',
|
115
|
+
:aws_secret_access_key => 'my-secret',
|
116
|
+
:region => 'somewhere'
|
117
|
+
).returns connection
|
185
118
|
|
186
|
-
|
187
|
-
context 'when @mirror is true' do
|
188
|
-
it 'should return the options with mirroring enabled' do
|
189
|
-
syncer.send(:options).should ==
|
190
|
-
'--verbose --recursive --delete --opt-a --opt-b'
|
119
|
+
syncer.perform!
|
191
120
|
end
|
192
|
-
end
|
193
121
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
122
|
+
it "uses the bucket with the given name" do
|
123
|
+
syncer.bucket = 'leaky'
|
124
|
+
|
125
|
+
connection.directories.expects(:get).with('leaky').returns(bucket)
|
126
|
+
|
127
|
+
syncer.perform!
|
199
128
|
end
|
200
|
-
end
|
201
129
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
130
|
+
it "creates the bucket if one does not exist" do
|
131
|
+
syncer.bucket = 'leaky'
|
132
|
+
syncer.region = 'elsewhere'
|
133
|
+
connection.directories.stubs(:get).returns nil
|
134
|
+
|
135
|
+
connection.directories.expects(:create).
|
136
|
+
with(:key => 'leaky', :location => 'elsewhere').returns(bucket)
|
137
|
+
|
138
|
+
syncer.perform!
|
139
|
+
end
|
140
|
+
|
141
|
+
it "iterates over each directory" do
|
142
|
+
syncer.directories << 'files'
|
143
|
+
|
144
|
+
Backup::Syncer::S3::SyncContext.any_instance.expects(:`).
|
145
|
+
with('find tmp -print0 | xargs -0 openssl md5 2> /dev/null').
|
146
|
+
returns 'MD5(tmp/foo)= 123abcdef'
|
147
|
+
Backup::Syncer::S3::SyncContext.any_instance.expects(:`).
|
148
|
+
with('find files -print0 | xargs -0 openssl md5 2> /dev/null').
|
149
|
+
returns 'MD5(tmp/foo)= 123abcdef'
|
150
|
+
|
151
|
+
syncer.perform!
|
207
152
|
end
|
208
153
|
end
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
154
|
+
|
155
|
+
context 'file does not exist locally' do
|
156
|
+
let(:file) { stub('file', :key => 'storage/tmp/foo',
|
157
|
+
:etag => '123abcdef') }
|
158
|
+
|
159
|
+
before :each do
|
160
|
+
Backup::Syncer::S3::SyncContext.any_instance.
|
161
|
+
stubs(:`).returns ''
|
162
|
+
files << file
|
163
|
+
File.stubs(:exist?).returns false
|
164
|
+
end
|
165
|
+
|
166
|
+
it "removes the remote file when mirroring is turned on" do
|
167
|
+
syncer.mirror = true
|
168
|
+
|
169
|
+
file.expects(:destroy).once
|
170
|
+
|
171
|
+
syncer.perform!
|
172
|
+
end
|
173
|
+
|
174
|
+
it "leaves the remote file when mirroring is turned off" do
|
175
|
+
syncer.mirror = false
|
176
|
+
|
177
|
+
file.expects(:destroy).never
|
178
|
+
|
179
|
+
syncer.perform!
|
180
|
+
end
|
181
|
+
|
182
|
+
it "does not remove files not under one of the specified directories" do
|
183
|
+
file.stubs(:key).returns 'unsynced/tmp/foo'
|
184
|
+
syncer.mirror = true
|
185
|
+
|
186
|
+
file.expects(:destroy).never
|
187
|
+
|
188
|
+
syncer.perform!
|
189
|
+
end
|
225
190
|
end
|
226
191
|
end
|
227
|
-
|
228
192
|
end
|
@@ -1,13 +1,25 @@
|
|
1
1
|
##
|
2
2
|
# Archive [Archive]
|
3
3
|
#
|
4
|
+
# Adding a file:
|
5
|
+
#
|
6
|
+
# archive.add "/path/to/a/file.rb"
|
7
|
+
#
|
8
|
+
# Adding an directory (including sub-directories):
|
9
|
+
#
|
10
|
+
# archive.add "/path/to/a/directory/"
|
11
|
+
#
|
12
|
+
# Excluding a file:
|
13
|
+
#
|
14
|
+
# archive.exclude "/path/to/an/excluded_file.rb"
|
15
|
+
#
|
16
|
+
# Excluding a directory (including sub-directories):
|
17
|
+
#
|
18
|
+
# archive.exclude "/path/to/an/excluded_directory/
|
19
|
+
#
|
4
20
|
archive :my_archive do |archive|
|
5
|
-
|
6
|
-
archive.add
|
7
|
-
|
8
|
-
archive.
|
9
|
-
# exclude a file
|
10
|
-
archive.exclude '/path/to/a/excluded_file.rb'
|
11
|
-
# exclude a folder (including sub-folders)
|
12
|
-
archive.exclude '/path/to/a/excluded_folder/'
|
21
|
+
archive.add "/path/to/a/file.rb"
|
22
|
+
archive.add "/path/to/a/folder/"
|
23
|
+
archive.exclude "/path/to/a/excluded_file.rb"
|
24
|
+
archive.exclude "/path/to/a/excluded_folder/"
|
13
25
|
end
|
@@ -8,11 +8,11 @@
|
|
8
8
|
db.host = "localhost"
|
9
9
|
db.port = 5432
|
10
10
|
db.ipv6 = false
|
11
|
-
db.only_collections = [
|
11
|
+
db.only_collections = ["only", "these" "collections"]
|
12
12
|
db.additional_options = []
|
13
13
|
db.lock = false
|
14
14
|
# Optional: Use to set the location of these utilities
|
15
15
|
# if they cannot be found by their name in your $PATH
|
16
|
-
# db.mongodump_utility =
|
17
|
-
# db.mongo_utility =
|
16
|
+
# db.mongodump_utility = "/opt/local/bin/mongodump"
|
17
|
+
# db.mongo_utility = "/opt/local/bin/mongo"
|
18
18
|
end
|
@@ -8,10 +8,10 @@
|
|
8
8
|
db.host = "localhost"
|
9
9
|
db.port = 3306
|
10
10
|
db.socket = "/tmp/mysql.sock"
|
11
|
-
db.skip_tables = [
|
12
|
-
db.only_tables = [
|
13
|
-
db.additional_options = [
|
11
|
+
db.skip_tables = ["skip", "these", "tables"]
|
12
|
+
db.only_tables = ["only", "these" "tables"]
|
13
|
+
db.additional_options = ["--quick", "--single-transaction"]
|
14
14
|
# Optional: Use to set the location of this utility
|
15
15
|
# if it cannot be found by name in your $PATH
|
16
|
-
# db.mysqldump_utility =
|
16
|
+
# db.mysqldump_utility = "/opt/local/bin/mysqldump"
|
17
17
|
end
|
@@ -8,10 +8,10 @@
|
|
8
8
|
db.host = "localhost"
|
9
9
|
db.port = 5432
|
10
10
|
db.socket = "/tmp/pg.sock"
|
11
|
-
db.skip_tables = [
|
12
|
-
db.only_tables = [
|
13
|
-
db.additional_options = [
|
11
|
+
db.skip_tables = ["skip", "these", "tables"]
|
12
|
+
db.only_tables = ["only", "these" "tables"]
|
13
|
+
db.additional_options = ["-xc", "-E=utf8"]
|
14
14
|
# Optional: Use to set the location of this utility
|
15
15
|
# if it cannot be found by name in your $PATH
|
16
|
-
# db.pg_dump_utility =
|
16
|
+
# db.pg_dump_utility = "/opt/local/bin/pg_dump"
|
17
17
|
end
|