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.
Files changed (46) hide show
  1. data/Gemfile.lock +3 -1
  2. data/README.md +4 -3
  3. data/lib/backup.rb +8 -4
  4. data/lib/backup/config.rb +1 -1
  5. data/lib/backup/configuration/syncer/cloud.rb +23 -0
  6. data/lib/backup/configuration/syncer/cloud_files.rb +30 -0
  7. data/lib/backup/configuration/syncer/s3.rb +5 -11
  8. data/lib/backup/dependency.rb +6 -0
  9. data/lib/backup/notifier/twitter.rb +1 -1
  10. data/lib/backup/syncer/base.rb +25 -0
  11. data/lib/backup/syncer/cloud.rb +187 -0
  12. data/lib/backup/syncer/cloud_files.rb +56 -0
  13. data/lib/backup/syncer/rsync/base.rb +0 -26
  14. data/lib/backup/syncer/s3.rb +21 -102
  15. data/lib/backup/version.rb +1 -1
  16. data/spec/cli/utility_spec.rb +2 -2
  17. data/spec/configuration/syncer/cloud_files_spec.rb +44 -0
  18. data/spec/configuration/syncer/s3_spec.rb +0 -4
  19. data/spec/notifier/twitter_spec.rb +3 -3
  20. data/spec/syncer/cloud_files_spec.rb +192 -0
  21. data/spec/syncer/s3_spec.rb +155 -191
  22. data/templates/cli/utility/archive +20 -8
  23. data/templates/cli/utility/database/mongodb +3 -3
  24. data/templates/cli/utility/database/mysql +4 -4
  25. data/templates/cli/utility/database/postgresql +4 -4
  26. data/templates/cli/utility/database/redis +1 -1
  27. data/templates/cli/utility/encryptor/openssl +2 -2
  28. data/templates/cli/utility/notifier/campfire +3 -3
  29. data/templates/cli/utility/notifier/hipchat +6 -6
  30. data/templates/cli/utility/notifier/mail +7 -7
  31. data/templates/cli/utility/notifier/presently +4 -4
  32. data/templates/cli/utility/notifier/prowl +2 -2
  33. data/templates/cli/utility/notifier/twitter +4 -4
  34. data/templates/cli/utility/storage/cloud_files +22 -0
  35. data/templates/cli/utility/storage/dropbox +15 -10
  36. data/templates/cli/utility/storage/ftp +4 -4
  37. data/templates/cli/utility/storage/local +1 -1
  38. data/templates/cli/utility/storage/ninefold +3 -3
  39. data/templates/cli/utility/storage/rsync +4 -4
  40. data/templates/cli/utility/storage/s3 +6 -6
  41. data/templates/cli/utility/storage/scp +4 -4
  42. data/templates/cli/utility/storage/sftp +4 -4
  43. data/templates/cli/utility/syncer/cloud_files +48 -0
  44. data/templates/cli/utility/syncer/s3 +31 -1
  45. metadata +69 -39
  46. data/templates/cli/utility/storage/cloudfiles +0 -12
@@ -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
- let(:syncer) do
7
- Backup::Syncer::S3.new do |s3|
8
- s3.access_key_id = 'my_access_key_id'
9
- s3.secret_access_key = 'my_secret_access_key'
10
- s3.bucket = 'my-bucket'
11
- s3.path = "/my_backups"
12
-
13
- s3.directories do |directory|
14
- directory.add "/some/directory"
15
- directory.add "~/home/directory"
16
- end
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
- s3.mirror = true
19
- s3.additional_options = ['--opt-a', '--opt-b']
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
- describe '#initialize' do
36
+ it "respects the parallel thread count" do
37
+ syncer.concurrency_type = :threads
38
+ syncer.concurrency_level = 10
24
39
 
25
- it 'should have defined the configuration properly' do
26
- syncer.access_key_id.should == 'my_access_key_id'
27
- syncer.secret_access_key.should == 'my_secret_access_key'
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
- context 'when options are not set' do
36
- it 'should use default values' do
37
- syncer = Backup::Syncer::S3.new
38
- syncer.access_key_id.should == nil
39
- syncer.secret_access_key.should == nil
40
- syncer.bucket.should == nil
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
- context 'when setting configuration defaults' do
49
- after { Backup::Configuration::Syncer::S3.clear_defaults! }
50
-
51
- it 'should use the configured defaults' do
52
- Backup::Configuration::Syncer::S3.defaults do |s3|
53
- s3.access_key_id = 'some_access_key_id'
54
- s3.secret_access_key = 'some_secret_access_key'
55
- s3.bucket = 'some_bucket'
56
- s3.path = 'some_path'
57
- #s3.directories = 'cannot_have_a_default_value'
58
- s3.mirror = 'some_mirror'
59
- s3.additional_options = 'some_additional_options'
60
- end
61
- syncer = Backup::Syncer::S3.new
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 'should override the configured defaults' do
72
- Backup::Configuration::Syncer::S3.defaults do |s3|
73
- s3.access_key_id = 'old_access_key_id'
74
- s3.secret_access_key = 'old_secret_access_key'
75
- s3.bucket = 'old_bucket'
76
- s3.path = 'old_path'
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
- describe '#perform!' do
103
- let(:s) { sequence '' }
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
- before do
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
- it 'should sync two directories' do
111
- syncer.expects(:set_environment_variables!).in_sequence(s)
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
- syncer.perform!
135
- end
136
- end # describe '#perform!'
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
- describe '#directories' do
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
- context 'when a block is given' do
147
- it 'should evalute the block, allowing #add to add directories' do
148
- syncer.directories do
149
- add '/new/path'
150
- add '~/new/home/path'
151
- end
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
- describe '#add' do
163
- it 'should add the given path to @directories' do
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
- describe '#dest_path' do
171
- it 'should remove any preceeding "/" from @path' do
172
- syncer.send(:dest_path).should == 'my_backups'
173
- end
104
+ syncer.perform!
105
+ end
174
106
 
175
- it 'should set @dest_path' do
176
- syncer.send(:dest_path)
177
- syncer.instance_variable_get(:@dest_path).should == 'my_backups'
178
- end
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
- it 'should return @dest_path if already set' do
181
- syncer.instance_variable_set(:@dest_path, 'foo')
182
- syncer.send(:dest_path).should == 'foo'
183
- end
184
- end
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
- describe '#options' do
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
- context 'when @mirror is false' do
195
- before { syncer.mirror = false }
196
- it 'should return the options without mirroring enabled' do
197
- syncer.send(:options).should ==
198
- '--verbose --recursive --opt-a --opt-b'
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
- context 'with no additional options' do
203
- before { syncer.additional_options = [] }
204
- it 'should return the options without additional options' do
205
- syncer.send(:options).should ==
206
- '--verbose --recursive --delete'
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
- end # describe '#options'
210
-
211
- describe 'changing environment variables' do
212
- before { @env = ENV }
213
- after { ENV.replace(@env) }
214
-
215
- it 'should set and unset environment variables' do
216
- syncer.send(:set_environment_variables!)
217
- ENV['AWS_ACCESS_KEY_ID'].should == 'my_access_key_id'
218
- ENV['AWS_SECRET_ACCESS_KEY'].should == 'my_secret_access_key'
219
- ENV['AWS_CALLING_FORMAT'].should == 'SUBDOMAIN'
220
-
221
- syncer.send(:unset_environment_variables!)
222
- ENV['AWS_ACCESS_KEY_ID'].should == nil
223
- ENV['AWS_SECRET_ACCESS_KEY'].should == nil
224
- ENV['AWS_CALLING_FORMAT'].should == nil
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
- # add a file
6
- archive.add '/path/to/a/file.rb'
7
- # add a folder (including sub-folders)
8
- archive.add '/path/to/a/folder/'
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 = ['only', 'these' '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 = '/opt/local/bin/mongodump'
17
- # db.mongo_utility = '/opt/local/bin/mongo'
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 = ['skip', 'these', 'tables']
12
- db.only_tables = ['only', 'these' 'tables']
13
- db.additional_options = ['--quick', '--single-transaction']
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 = '/opt/local/bin/mysqldump'
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 = ['skip', 'these', 'tables']
12
- db.only_tables = ['only', 'these' 'tables']
13
- db.additional_options = ['-xc', '-E=utf8']
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 = '/opt/local/bin/pg_dump'
16
+ # db.pg_dump_utility = "/opt/local/bin/pg_dump"
17
17
  end