backup-agoddard 3.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. data/.gitignore +8 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +28 -0
  4. data/Guardfile +23 -0
  5. data/LICENSE.md +24 -0
  6. data/README.md +478 -0
  7. data/backup.gemspec +32 -0
  8. data/bin/backup +11 -0
  9. data/lib/backup.rb +133 -0
  10. data/lib/backup/archive.rb +117 -0
  11. data/lib/backup/binder.rb +22 -0
  12. data/lib/backup/cleaner.rb +121 -0
  13. data/lib/backup/cli/helpers.rb +93 -0
  14. data/lib/backup/cli/utility.rb +255 -0
  15. data/lib/backup/compressor/base.rb +35 -0
  16. data/lib/backup/compressor/bzip2.rb +50 -0
  17. data/lib/backup/compressor/custom.rb +53 -0
  18. data/lib/backup/compressor/gzip.rb +50 -0
  19. data/lib/backup/compressor/lzma.rb +52 -0
  20. data/lib/backup/compressor/pbzip2.rb +59 -0
  21. data/lib/backup/config.rb +174 -0
  22. data/lib/backup/configuration.rb +33 -0
  23. data/lib/backup/configuration/helpers.rb +130 -0
  24. data/lib/backup/configuration/store.rb +24 -0
  25. data/lib/backup/database/base.rb +53 -0
  26. data/lib/backup/database/mongodb.rb +230 -0
  27. data/lib/backup/database/mysql.rb +160 -0
  28. data/lib/backup/database/postgresql.rb +144 -0
  29. data/lib/backup/database/redis.rb +136 -0
  30. data/lib/backup/database/riak.rb +67 -0
  31. data/lib/backup/dependency.rb +108 -0
  32. data/lib/backup/encryptor/base.rb +29 -0
  33. data/lib/backup/encryptor/gpg.rb +760 -0
  34. data/lib/backup/encryptor/open_ssl.rb +72 -0
  35. data/lib/backup/errors.rb +124 -0
  36. data/lib/backup/hooks.rb +68 -0
  37. data/lib/backup/logger.rb +152 -0
  38. data/lib/backup/model.rb +409 -0
  39. data/lib/backup/notifier/base.rb +81 -0
  40. data/lib/backup/notifier/campfire.rb +155 -0
  41. data/lib/backup/notifier/hipchat.rb +93 -0
  42. data/lib/backup/notifier/mail.rb +206 -0
  43. data/lib/backup/notifier/prowl.rb +65 -0
  44. data/lib/backup/notifier/pushover.rb +88 -0
  45. data/lib/backup/notifier/twitter.rb +70 -0
  46. data/lib/backup/package.rb +47 -0
  47. data/lib/backup/packager.rb +100 -0
  48. data/lib/backup/pipeline.rb +110 -0
  49. data/lib/backup/splitter.rb +75 -0
  50. data/lib/backup/storage/base.rb +99 -0
  51. data/lib/backup/storage/cloudfiles.rb +87 -0
  52. data/lib/backup/storage/cycler.rb +117 -0
  53. data/lib/backup/storage/dropbox.rb +178 -0
  54. data/lib/backup/storage/ftp.rb +119 -0
  55. data/lib/backup/storage/local.rb +82 -0
  56. data/lib/backup/storage/ninefold.rb +116 -0
  57. data/lib/backup/storage/rsync.rb +149 -0
  58. data/lib/backup/storage/s3.rb +94 -0
  59. data/lib/backup/storage/scp.rb +99 -0
  60. data/lib/backup/storage/sftp.rb +108 -0
  61. data/lib/backup/syncer/base.rb +46 -0
  62. data/lib/backup/syncer/cloud/base.rb +247 -0
  63. data/lib/backup/syncer/cloud/cloud_files.rb +78 -0
  64. data/lib/backup/syncer/cloud/s3.rb +68 -0
  65. data/lib/backup/syncer/rsync/base.rb +49 -0
  66. data/lib/backup/syncer/rsync/local.rb +55 -0
  67. data/lib/backup/syncer/rsync/pull.rb +36 -0
  68. data/lib/backup/syncer/rsync/push.rb +116 -0
  69. data/lib/backup/template.rb +46 -0
  70. data/lib/backup/version.rb +43 -0
  71. data/spec-live/.gitignore +6 -0
  72. data/spec-live/README +7 -0
  73. data/spec-live/backups/config.rb +83 -0
  74. data/spec-live/backups/config.yml.template +46 -0
  75. data/spec-live/backups/models.rb +184 -0
  76. data/spec-live/compressor/custom_spec.rb +30 -0
  77. data/spec-live/compressor/gzip_spec.rb +30 -0
  78. data/spec-live/encryptor/gpg_keys.rb +239 -0
  79. data/spec-live/encryptor/gpg_spec.rb +287 -0
  80. data/spec-live/notifier/mail_spec.rb +121 -0
  81. data/spec-live/spec_helper.rb +151 -0
  82. data/spec-live/storage/dropbox_spec.rb +151 -0
  83. data/spec-live/storage/local_spec.rb +83 -0
  84. data/spec-live/storage/scp_spec.rb +193 -0
  85. data/spec-live/syncer/cloud/s3_spec.rb +124 -0
  86. data/spec/archive_spec.rb +335 -0
  87. data/spec/cleaner_spec.rb +312 -0
  88. data/spec/cli/helpers_spec.rb +301 -0
  89. data/spec/cli/utility_spec.rb +411 -0
  90. data/spec/compressor/base_spec.rb +52 -0
  91. data/spec/compressor/bzip2_spec.rb +217 -0
  92. data/spec/compressor/custom_spec.rb +106 -0
  93. data/spec/compressor/gzip_spec.rb +217 -0
  94. data/spec/compressor/lzma_spec.rb +123 -0
  95. data/spec/compressor/pbzip2_spec.rb +165 -0
  96. data/spec/config_spec.rb +321 -0
  97. data/spec/configuration/helpers_spec.rb +247 -0
  98. data/spec/configuration/store_spec.rb +39 -0
  99. data/spec/configuration_spec.rb +62 -0
  100. data/spec/database/base_spec.rb +63 -0
  101. data/spec/database/mongodb_spec.rb +510 -0
  102. data/spec/database/mysql_spec.rb +411 -0
  103. data/spec/database/postgresql_spec.rb +353 -0
  104. data/spec/database/redis_spec.rb +334 -0
  105. data/spec/database/riak_spec.rb +176 -0
  106. data/spec/dependency_spec.rb +51 -0
  107. data/spec/encryptor/base_spec.rb +40 -0
  108. data/spec/encryptor/gpg_spec.rb +909 -0
  109. data/spec/encryptor/open_ssl_spec.rb +148 -0
  110. data/spec/errors_spec.rb +306 -0
  111. data/spec/hooks_spec.rb +35 -0
  112. data/spec/logger_spec.rb +367 -0
  113. data/spec/model_spec.rb +694 -0
  114. data/spec/notifier/base_spec.rb +104 -0
  115. data/spec/notifier/campfire_spec.rb +217 -0
  116. data/spec/notifier/hipchat_spec.rb +211 -0
  117. data/spec/notifier/mail_spec.rb +316 -0
  118. data/spec/notifier/prowl_spec.rb +138 -0
  119. data/spec/notifier/pushover_spec.rb +123 -0
  120. data/spec/notifier/twitter_spec.rb +153 -0
  121. data/spec/package_spec.rb +61 -0
  122. data/spec/packager_spec.rb +213 -0
  123. data/spec/pipeline_spec.rb +259 -0
  124. data/spec/spec_helper.rb +60 -0
  125. data/spec/splitter_spec.rb +120 -0
  126. data/spec/storage/base_spec.rb +166 -0
  127. data/spec/storage/cloudfiles_spec.rb +254 -0
  128. data/spec/storage/cycler_spec.rb +247 -0
  129. data/spec/storage/dropbox_spec.rb +480 -0
  130. data/spec/storage/ftp_spec.rb +271 -0
  131. data/spec/storage/local_spec.rb +259 -0
  132. data/spec/storage/ninefold_spec.rb +343 -0
  133. data/spec/storage/rsync_spec.rb +362 -0
  134. data/spec/storage/s3_spec.rb +245 -0
  135. data/spec/storage/scp_spec.rb +233 -0
  136. data/spec/storage/sftp_spec.rb +244 -0
  137. data/spec/syncer/base_spec.rb +109 -0
  138. data/spec/syncer/cloud/base_spec.rb +515 -0
  139. data/spec/syncer/cloud/cloud_files_spec.rb +181 -0
  140. data/spec/syncer/cloud/s3_spec.rb +174 -0
  141. data/spec/syncer/rsync/base_spec.rb +98 -0
  142. data/spec/syncer/rsync/local_spec.rb +149 -0
  143. data/spec/syncer/rsync/pull_spec.rb +98 -0
  144. data/spec/syncer/rsync/push_spec.rb +333 -0
  145. data/spec/version_spec.rb +21 -0
  146. data/templates/cli/utility/archive +25 -0
  147. data/templates/cli/utility/compressor/bzip2 +4 -0
  148. data/templates/cli/utility/compressor/custom +11 -0
  149. data/templates/cli/utility/compressor/gzip +4 -0
  150. data/templates/cli/utility/compressor/lzma +10 -0
  151. data/templates/cli/utility/compressor/pbzip2 +10 -0
  152. data/templates/cli/utility/config +32 -0
  153. data/templates/cli/utility/database/mongodb +18 -0
  154. data/templates/cli/utility/database/mysql +21 -0
  155. data/templates/cli/utility/database/postgresql +17 -0
  156. data/templates/cli/utility/database/redis +16 -0
  157. data/templates/cli/utility/database/riak +11 -0
  158. data/templates/cli/utility/encryptor/gpg +27 -0
  159. data/templates/cli/utility/encryptor/openssl +9 -0
  160. data/templates/cli/utility/model.erb +23 -0
  161. data/templates/cli/utility/notifier/campfire +12 -0
  162. data/templates/cli/utility/notifier/hipchat +15 -0
  163. data/templates/cli/utility/notifier/mail +22 -0
  164. data/templates/cli/utility/notifier/prowl +11 -0
  165. data/templates/cli/utility/notifier/pushover +11 -0
  166. data/templates/cli/utility/notifier/twitter +13 -0
  167. data/templates/cli/utility/splitter +7 -0
  168. data/templates/cli/utility/storage/cloud_files +22 -0
  169. data/templates/cli/utility/storage/dropbox +20 -0
  170. data/templates/cli/utility/storage/ftp +12 -0
  171. data/templates/cli/utility/storage/local +7 -0
  172. data/templates/cli/utility/storage/ninefold +9 -0
  173. data/templates/cli/utility/storage/rsync +11 -0
  174. data/templates/cli/utility/storage/s3 +19 -0
  175. data/templates/cli/utility/storage/scp +11 -0
  176. data/templates/cli/utility/storage/sftp +11 -0
  177. data/templates/cli/utility/syncer/cloud_files +46 -0
  178. data/templates/cli/utility/syncer/rsync_local +12 -0
  179. data/templates/cli/utility/syncer/rsync_pull +17 -0
  180. data/templates/cli/utility/syncer/rsync_push +17 -0
  181. data/templates/cli/utility/syncer/s3 +43 -0
  182. data/templates/general/links +11 -0
  183. data/templates/general/version.erb +2 -0
  184. data/templates/notifier/mail/failure.erb +9 -0
  185. data/templates/notifier/mail/success.erb +7 -0
  186. data/templates/notifier/mail/warning.erb +9 -0
  187. data/templates/storage/dropbox/authorization_url.erb +6 -0
  188. data/templates/storage/dropbox/authorized.erb +4 -0
  189. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  190. metadata +277 -0
@@ -0,0 +1,247 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
+
5
+ describe 'Backup::Configuration::Helpers' do
6
+
7
+ before do
8
+ module Backup
9
+ class Foo
10
+ include Backup::Configuration::Helpers
11
+ attr_accessor :accessor, :accessor_two
12
+ attr_reader :reader
13
+
14
+ attr_deprecate :removed,
15
+ :version => '1.1'
16
+
17
+ attr_deprecate :removed_with_message,
18
+ :version => '1.2',
19
+ :message => 'This has no replacement.'
20
+
21
+ attr_deprecate :removed_with_action,
22
+ :version => '1.3',
23
+ :action => lambda {|klass, val|
24
+ klass.accessor = val ? '1' : '0'
25
+ klass.accessor_two = 'updated'
26
+ }
27
+
28
+ attr_deprecate :removed_with_action_and_message,
29
+ :version => '1.4',
30
+ :message => "Updating accessors.",
31
+ :action => lambda {|klass, val|
32
+ klass.accessor = val ? '1' : '0'
33
+ klass.accessor_two = 'updated'
34
+ }
35
+ end
36
+ end
37
+ end
38
+
39
+ after do
40
+ Backup.send(:remove_const, 'Foo')
41
+ end
42
+
43
+ describe '.defaults' do
44
+ let(:configuration) { mock }
45
+
46
+ before do
47
+ Backup::Configuration::Store.expects(:new).once.returns(configuration)
48
+ end
49
+
50
+ it 'should return the Configuration::Store for the class' do
51
+ Backup::Foo.defaults.should be(configuration)
52
+ end
53
+
54
+ it 'should yield the Configuration::Store for the class' do
55
+ Backup::Foo.defaults do |config|
56
+ config.should be(configuration)
57
+ end
58
+ end
59
+
60
+ it 'should cache the Configuration::Store for the class' do
61
+ Backup::Foo.instance_variable_get(:@configuration).should be_nil
62
+ Backup::Foo.defaults.should be(configuration)
63
+ Backup::Foo.instance_variable_get(
64
+ :@configuration).should be(configuration)
65
+ Backup::Foo.defaults.should be(configuration)
66
+ end
67
+ end
68
+
69
+ describe '.clear_defaults!' do
70
+ it 'should clear all defaults set' do
71
+ Backup::Foo.defaults do |config|
72
+ config.accessor = 'foo'
73
+ end
74
+ Backup::Foo.defaults.accessor.should == 'foo'
75
+
76
+ Backup::Foo.clear_defaults!
77
+ Backup::Foo.defaults.accessor.should be_nil
78
+ end
79
+ end
80
+
81
+ describe '.deprecations' do
82
+ it 'should return @deprecations' do
83
+ Backup::Foo.deprecations.should be_a(Hash)
84
+ Backup::Foo.deprecations.keys.count.should be(4)
85
+ end
86
+
87
+ it 'should set @deprecations to an empty hash if not set' do
88
+ Backup::Foo.send(:remove_instance_variable, :@deprecations)
89
+ Backup::Foo.deprecations.should == {}
90
+ end
91
+ end
92
+
93
+ describe '.attr_deprecate' do
94
+ before do
95
+ Backup::Foo.send(:remove_instance_variable, :@deprecations)
96
+ end
97
+
98
+ it 'should add deprected attributes' do
99
+ Backup::Foo.send(:attr_deprecate, :attr1)
100
+ Backup::Foo.send(:attr_deprecate, :attr2,
101
+ :version => '2')
102
+ Backup::Foo.send(:attr_deprecate, :attr3,
103
+ :version => '3',
104
+ :message => 'attr3 message')
105
+ Backup::Foo.send(:attr_deprecate, :attr4,
106
+ :version => '4',
107
+ :message => 'attr4 message',
108
+ :action => 'attr4 action')
109
+
110
+ Backup::Foo.deprecations.should == {
111
+ :attr1 => { :version => nil,
112
+ :message => nil,
113
+ :action => nil },
114
+ :attr2 => { :version => '2',
115
+ :message => nil,
116
+ :action => nil },
117
+ :attr3 => { :version => '3',
118
+ :message => 'attr3 message',
119
+ :action => nil },
120
+ :attr4 => { :version => '4',
121
+ :message => 'attr4 message',
122
+ :action => 'attr4 action' }
123
+ }
124
+ end
125
+ end
126
+
127
+ describe '.log_deprecation_warning' do
128
+ context 'when no message given' do
129
+ it 'should log a warning that the attribute has been removed' do
130
+ Backup::Logger.expects(:warn).with do |err|
131
+ err.message.should ==
132
+ "ConfigurationError: [DEPRECATION WARNING]\n" +
133
+ " Backup::Foo#removed has been deprecated as of backup v.1.1"
134
+ end
135
+
136
+ deprecation = Backup::Foo.deprecations[:removed]
137
+ Backup::Foo.log_deprecation_warning(:removed, deprecation)
138
+ end
139
+ end
140
+
141
+ context 'when a message is given' do
142
+ it 'should log warning with the message' do
143
+ Backup::Logger.expects(:warn).with do |err|
144
+ err.message.should ==
145
+ "ConfigurationError: [DEPRECATION WARNING]\n" +
146
+ " Backup::Foo#removed_with_message has been deprecated " +
147
+ "as of backup v.1.2\n" +
148
+ " This has no replacement."
149
+ end
150
+
151
+ deprecation = Backup::Foo.deprecations[:removed_with_message]
152
+ Backup::Foo.log_deprecation_warning(:removed_with_message, deprecation)
153
+
154
+ end
155
+ end
156
+ end # describe '.log_deprecation_warning'
157
+
158
+ describe '#load_defaults!' do
159
+ let(:klass) { Backup::Foo.new }
160
+
161
+ it 'should load default values set for the class' do
162
+ Backup::Foo.defaults do |config|
163
+ config.accessor = 'foo'
164
+ end
165
+
166
+ klass.send(:load_defaults!)
167
+ klass.accessor.should == 'foo'
168
+ end
169
+
170
+ it 'should raise an error if defaults are set for attribute readers' do
171
+ Backup::Foo.defaults do |config|
172
+ config.reader = 'foo'
173
+ end
174
+
175
+ expect do
176
+ klass.send(:load_defaults!)
177
+ end.to raise_error(NoMethodError, /Backup::Foo/)
178
+ end
179
+
180
+ it 'should raise an error if defaults were set for invalid accessors' do
181
+ Backup::Foo.defaults do |config|
182
+ config.foobar = 'foo'
183
+ end
184
+
185
+ expect do
186
+ klass.send(:load_defaults!)
187
+ end.to raise_error(NoMethodError, /Backup::Foo/)
188
+ end
189
+ end
190
+
191
+ describe '#method_missing' do
192
+ context 'when the method is a deprecated method' do
193
+ before do
194
+ Backup::Logger.expects(:warn).with(
195
+ instance_of(Backup::Errors::ConfigurationError)
196
+ )
197
+ end
198
+
199
+ context 'when an :action is specified' do
200
+ it 'should call the :action' do
201
+ value = [true, false].shuffle.first
202
+ expected_value = value ? '1' : '0'
203
+
204
+ klass = Backup::Foo.new
205
+ klass.removed_with_action = value
206
+
207
+ klass.accessor.should == expected_value
208
+ # lambda additionally sets :accessor_two
209
+ klass.accessor_two.should == 'updated'
210
+ end
211
+ end
212
+
213
+ context 'when no :action is specified' do
214
+ it 'should only log the warning' do
215
+ Backup::Foo.any_instance.expects(:accessor=).never
216
+
217
+ klass = Backup::Foo.new
218
+ klass.removed = 'foo'
219
+
220
+ klass.accessor.should be_nil
221
+ end
222
+ end
223
+ end
224
+
225
+ context 'when the method is not a deprecated method' do
226
+ it 'should raise a NoMethodError' do
227
+ Backup::Logger.expects(:warn).never
228
+
229
+ klass = Backup::Foo.new
230
+ expect do
231
+ klass.foobar = 'attr_value'
232
+ end.to raise_error(NoMethodError)
233
+ end
234
+ end
235
+
236
+ context 'when the method is not a set operation' do
237
+ it 'should raise a NoMethodError' do
238
+ Backup::Logger.expects(:warn).never
239
+
240
+ klass = Backup::Foo.new
241
+ expect do
242
+ klass.removed
243
+ end.to raise_error(NoMethodError)
244
+ end
245
+ end
246
+ end # describe '#method_missing'
247
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
+
5
+ describe 'Backup::Configuration::Store' do
6
+ let(:store) { Backup::Configuration::Store.new }
7
+
8
+ before do
9
+ store.foo = 'one'
10
+ store.bar = 'two'
11
+ end
12
+
13
+ it 'should be a subclass of OpenStruct' do
14
+ Backup::Configuration::Store.superclass.should == OpenStruct
15
+ end
16
+
17
+ it 'should return nil for unset attributes' do
18
+ store.foobar.should be_nil
19
+ end
20
+
21
+ describe '#_attribues' do
22
+ it 'should return an array of attribute names' do
23
+ store._attributes.should be_an Array
24
+ store._attributes.count.should be(2)
25
+ store._attributes.should include(:foo, :bar)
26
+ end
27
+ end
28
+
29
+ describe '#reset!' do
30
+ it 'should clear all attributes set' do
31
+ store.reset!
32
+ store._attributes.should be_an Array
33
+ store._attributes.should be_empty
34
+ store.foo.should be_nil
35
+ store.bar.should be_nil
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../spec_helper.rb', __FILE__)
4
+
5
+ describe 'Backup::Configuration' do
6
+
7
+ after do
8
+ Backup::Configuration.send(:remove_const, 'Foo')
9
+ end
10
+
11
+ it 'should create modules for missing constants' do
12
+ Backup::Configuration::Foo.class.should == Module
13
+ end
14
+
15
+ describe 'a generated module' do
16
+
17
+ before do
18
+ module Backup
19
+ class Foo; end
20
+ end
21
+ end
22
+
23
+ after do
24
+ Backup.send(:remove_const, 'Foo')
25
+ end
26
+
27
+ it 'should create modules for missing constants' do
28
+ Backup::Configuration::Foo::A::B.class.should == Module
29
+ end
30
+
31
+ it 'should pass calls to .defaults to the proper class' do
32
+ Backup::Logger.expects(:warn)
33
+ Backup::Foo.expects(:defaults)
34
+ Backup::Configuration::Foo.defaults
35
+ end
36
+
37
+ it 'should pass a given block to .defaults to the proper class' do
38
+ Backup::Logger.expects(:warn)
39
+ configuration = mock
40
+ Backup::Foo.expects(:defaults).yields(configuration)
41
+ configuration.expects(:foo=).with('bar')
42
+
43
+ Backup::Configuration::Foo.defaults do |config|
44
+ config.foo = 'bar'
45
+ end
46
+ end
47
+
48
+ it 'should log a deprecation warning' do
49
+ Backup::Foo.stubs(:defaults)
50
+ Backup::Logger.expects(:warn).with do |err|
51
+ err.message.should ==
52
+ "ConfigurationError: [DEPRECATION WARNING]\n" +
53
+ " Backup::Configuration::Foo.defaults is being deprecated.\n" +
54
+ " To set pre-configured defaults for Backup::Foo, use:\n" +
55
+ " Backup::Foo.defaults"
56
+ end
57
+ Backup::Configuration::Foo.defaults
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
+
5
+ describe Backup::Database::Base do
6
+ let(:model) { Backup::Model.new('foo', 'foo') }
7
+ let(:db) { Backup::Database::Base.new(model) }
8
+
9
+ it 'should include CLI::Helpers' do
10
+ Backup::Database::Base.
11
+ include?(Backup::CLI::Helpers).should be_true
12
+ end
13
+
14
+ it 'should include Configuration::Helpers' do
15
+ Backup::Database::Base.
16
+ include?(Backup::Configuration::Helpers).should be_true
17
+ end
18
+
19
+ describe '#initialize' do
20
+ it 'should load pre-configured defaults' do
21
+ Backup::Database::Base.any_instance.expects(:load_defaults!)
22
+ db
23
+ end
24
+
25
+ it 'should set a reference to the model' do
26
+ db.instance_variable_get(:@model).should == model
27
+ end
28
+ end
29
+
30
+ describe '#perform!' do
31
+ it 'should invoke prepare! and log!' do
32
+ s = sequence ''
33
+ db.expects(:prepare!).in_sequence(s)
34
+ db.expects(:log!).in_sequence(s)
35
+
36
+ db.perform!
37
+ end
38
+ end
39
+
40
+ describe '#prepare!' do
41
+ it 'should set and create #dump_path' do
42
+ model = stub(:trigger => 'test_trigger')
43
+ db.instance_variable_set(:@model, model)
44
+ FileUtils.expects(:mkdir_p).with(
45
+ File.join(Backup::Config.tmp_path, 'test_trigger', 'databases', 'Base')
46
+ )
47
+ db.send(:prepare!)
48
+ db.instance_variable_get(:@dump_path).should ==
49
+ File.join(Backup::Config.tmp_path, 'test_trigger', 'databases', 'Base')
50
+ end
51
+ end
52
+
53
+ describe '#log!' do
54
+ it 'should use #database_name' do
55
+ db.stubs(:name).returns('database_name')
56
+ Backup::Logger.expects(:message).with(
57
+ "Database::Base started dumping and archiving 'database_name'."
58
+ )
59
+
60
+ db.send(:log!)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,510 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
+
5
+ describe Backup::Database::MongoDB do
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:db) do
8
+ Backup::Database::MongoDB.new(model) do |db|
9
+ db.name = 'mydatabase'
10
+ db.username = 'someuser'
11
+ db.password = 'secret'
12
+ db.host = 'localhost'
13
+ db.port = 123
14
+
15
+ db.ipv6 = true
16
+ db.only_collections = ['users', 'pirates']
17
+ db.additional_options = ['--query', '--foo']
18
+ db.mongodump_utility = '/path/to/mongodump'
19
+ db.mongo_utility = '/path/to/mongo'
20
+ db.lock = true
21
+ end
22
+ end
23
+
24
+ it 'should be a subclass of Database::Base' do
25
+ Backup::Database::MongoDB.superclass.
26
+ should == Backup::Database::Base
27
+ end
28
+
29
+ describe '#initialize' do
30
+
31
+ it 'should load pre-configured defaults through Base' do
32
+ Backup::Database::MongoDB.any_instance.expects(:load_defaults!)
33
+ db
34
+ end
35
+
36
+ it 'should pass the model reference to Base' do
37
+ db.instance_variable_get(:@model).should == model
38
+ end
39
+
40
+ context 'when no pre-configured defaults have been set' do
41
+ context 'when options are specified' do
42
+ it 'should use the given values' do
43
+ db.name.should == 'mydatabase'
44
+ db.username.should == 'someuser'
45
+ db.password.should == 'secret'
46
+ db.host.should == 'localhost'
47
+ db.port.should == 123
48
+
49
+ db.ipv6.should == true
50
+ db.only_collections.should == ['users', 'pirates']
51
+ db.additional_options.should == ['--query', '--foo']
52
+ db.mongodump_utility.should == '/path/to/mongodump'
53
+ db.mongo_utility.should == '/path/to/mongo'
54
+ db.lock.should == true
55
+ end
56
+ end
57
+
58
+ context 'when options are not specified' do
59
+ before do
60
+ Backup::Database::MongoDB.any_instance.expects(:utility).
61
+ with(:mongodump).returns('/real/mongodump')
62
+ Backup::Database::MongoDB.any_instance.expects(:utility).
63
+ with(:mongo).returns('/real/mongo')
64
+ end
65
+
66
+ it 'should provide default values' do
67
+ db = Backup::Database::MongoDB.new(model)
68
+
69
+ db.name.should be_nil
70
+ db.username.should be_nil
71
+ db.password.should be_nil
72
+ db.host.should be_nil
73
+ db.port.should be_nil
74
+
75
+ db.ipv6.should be_false
76
+ db.only_collections.should == []
77
+ db.additional_options.should == []
78
+ db.mongodump_utility.should == '/real/mongodump'
79
+ db.mongo_utility.should == '/real/mongo'
80
+ db.lock.should be_false
81
+ end
82
+ end
83
+ end # context 'when no pre-configured defaults have been set'
84
+
85
+ context 'when pre-configured defaults have been set' do
86
+ before do
87
+ Backup::Database::MongoDB.defaults do |db|
88
+ db.name = 'db_name'
89
+ db.username = 'db_username'
90
+ db.password = 'db_password'
91
+ db.host = 'db_host'
92
+ db.port = 789
93
+
94
+ db.ipv6 = 'default_ipv6'
95
+ db.only_collections = ['collection']
96
+ db.additional_options = ['--opt']
97
+ db.mongodump_utility = '/default/path/to/mongodump'
98
+ db.mongo_utility = '/default/path/to/mongo'
99
+ db.lock = 'default_lock'
100
+ end
101
+ end
102
+
103
+ after { Backup::Database::MongoDB.clear_defaults! }
104
+
105
+ context 'when options are specified' do
106
+ it 'should override the pre-configured defaults' do
107
+ db.name.should == 'mydatabase'
108
+ db.username.should == 'someuser'
109
+ db.password.should == 'secret'
110
+ db.host.should == 'localhost'
111
+ db.port.should == 123
112
+
113
+ db.ipv6.should == true
114
+ db.only_collections.should == ['users', 'pirates']
115
+ db.additional_options.should == ['--query', '--foo']
116
+ db.mongodump_utility.should == '/path/to/mongodump'
117
+ db.mongo_utility.should == '/path/to/mongo'
118
+ db.lock.should == true
119
+ end
120
+ end
121
+
122
+ context 'when options are not specified' do
123
+ it 'should use the pre-configured defaults' do
124
+ db = Backup::Database::MongoDB.new(model)
125
+
126
+ db.name.should == 'db_name'
127
+ db.username.should == 'db_username'
128
+ db.password.should == 'db_password'
129
+ db.host.should == 'db_host'
130
+ db.port.should == 789
131
+
132
+ db.ipv6.should == 'default_ipv6'
133
+ db.only_collections.should == ['collection']
134
+ db.additional_options.should == ['--opt']
135
+ db.mongodump_utility.should == '/default/path/to/mongodump'
136
+ db.mongo_utility.should == '/default/path/to/mongo'
137
+ db.lock.should == 'default_lock'
138
+ end
139
+ end
140
+ end # context 'when no pre-configured defaults have been set'
141
+ end # describe '#initialize'
142
+
143
+ describe '#perform!' do
144
+ let(:s) { sequence '' }
145
+
146
+ before do
147
+ # superclass actions
148
+ db.expects(:prepare!).in_sequence(s)
149
+ db.expects(:log!).in_sequence(s)
150
+ end
151
+
152
+ context 'when #lock is set to false' do
153
+ before { db.lock = false }
154
+
155
+ context 'when #only_collections has not been specified' do
156
+ before { db.only_collections = [] }
157
+ it 'should dump everything without locking' do
158
+ db.expects(:lock_database).never
159
+ db.expects(:unlock_database).never
160
+ db.expects(:specific_collection_dump!).never
161
+
162
+ db.expects(:dump!).in_sequence(s)
163
+ db.expects(:package!).in_sequence(s)
164
+ db.perform!
165
+ end
166
+ end
167
+
168
+ context 'when #only_collections has been specified' do
169
+ it 'should dump specific collections without locking' do
170
+ db.expects(:lock_database).never
171
+ db.expects(:unlock_database).never
172
+ db.expects(:dump!).never
173
+
174
+ db.expects(:specific_collection_dump!).in_sequence(s)
175
+ db.expects(:package!).in_sequence(s)
176
+ db.perform!
177
+ end
178
+ end
179
+
180
+ end # context 'when #lock is set to false'
181
+
182
+ context 'when #lock is set to true' do
183
+
184
+ context 'when #only_collections has not been specified' do
185
+ before { db.only_collections = [] }
186
+ it 'should dump everything while locking the database' do
187
+ db.expects(:specific_collection_dump!).never
188
+
189
+ db.expects(:lock_database).in_sequence(s)
190
+ db.expects(:dump!).in_sequence(s)
191
+ db.expects(:unlock_database).in_sequence(s)
192
+ db.expects(:package!).in_sequence(s)
193
+ db.perform!
194
+ end
195
+ end
196
+
197
+ context 'when #only_collections has been specified' do
198
+ it 'should dump specific collections without locking' do
199
+ db.expects(:lock_database).never
200
+ db.expects(:unlock_database).never
201
+ db.expects(:dump!).never
202
+
203
+ db.expects(:lock_database).in_sequence(s)
204
+ db.expects(:specific_collection_dump!).in_sequence(s)
205
+ db.expects(:unlock_database).in_sequence(s)
206
+ db.expects(:package!).in_sequence(s)
207
+ db.perform!
208
+ end
209
+ end
210
+
211
+ end # context 'when #lock is set to true'
212
+
213
+ context 'when errors occur' do
214
+ it 'should re-raise error and skip package!' do
215
+ db.lock = false
216
+
217
+ db.expects(:specific_collection_dump!).in_sequence(s).
218
+ raises('Test Error Message')
219
+ db.expects(:package!).never
220
+
221
+ expect do
222
+ db.perform!
223
+ end.to raise_error(
224
+ Backup::Errors::Database::MongoDBError,
225
+ "Database::MongoDBError: Database Dump Failed!\n" +
226
+ " Reason: RuntimeError\n" +
227
+ " Test Error Message"
228
+ )
229
+ end
230
+
231
+ it 'should ensure database is unlocked' do
232
+ db.expects(:lock_database).in_sequence(s)
233
+ db.expects(:specific_collection_dump!).in_sequence(s).
234
+ raises('Test Error Message')
235
+ db.expects(:unlock_database).in_sequence(s)
236
+ db.expects(:package!).never
237
+
238
+ expect do
239
+ db.perform!
240
+ end.to raise_error(
241
+ Backup::Errors::Database::MongoDBError,
242
+ "Database::MongoDBError: Database Dump Failed!\n" +
243
+ " Reason: RuntimeError\n" +
244
+ " Test Error Message"
245
+ )
246
+ end
247
+ end
248
+
249
+ end # describe '#perform!'
250
+
251
+ describe '#dump!' do
252
+ it 'should run the mongodb dump command' do
253
+ db.expects(:mongodump).returns(:dump_command)
254
+ db.expects(:run).with(:dump_command)
255
+ db.send(:dump!)
256
+ end
257
+ end
258
+
259
+ describe '#specific_collection_dump!' do
260
+ it 'should run the mongodb dump command for each collection' do
261
+ db.expects(:mongodump).twice.returns('dump_command')
262
+ db.expects(:run).with("dump_command --collection='users'")
263
+ db.expects(:run).with("dump_command --collection='pirates'")
264
+ db.send(:specific_collection_dump!)
265
+ end
266
+ end
267
+
268
+ describe '#mongodump' do
269
+ before do
270
+ db.instance_variable_set(:@dump_path, '/path/to/dump/folder')
271
+ end
272
+
273
+ it 'should return the mongodb dump command string' do
274
+ db.send(:mongodump).should == "/path/to/mongodump " +
275
+ "--db='mydatabase' --username='someuser' --password='secret' " +
276
+ "--host='localhost' --port='123' --ipv6 " +
277
+ "--query --foo --out='/path/to/dump/folder'"
278
+ end
279
+ end
280
+
281
+ describe '#package!' do
282
+ let(:compressor) { mock }
283
+ let(:pipeline) { mock }
284
+ let(:timestamp) { Time.now.to_i.to_s[-5, 5] }
285
+ let(:s) { sequence '' }
286
+
287
+ context 'when a compressor is configured' do
288
+ before do
289
+ Timecop.freeze(Time.now)
290
+ db.instance_variable_set(:@dump_path, '/path/to/dump/folder')
291
+ db.expects(:utility).with(:tar).returns('tar')
292
+ model.expects(:compressor).twice.returns(compressor)
293
+ compressor.expects(:compress_with).yields('compressor_command', '.gz')
294
+ Backup::Pipeline.expects(:new).returns(pipeline)
295
+ end
296
+
297
+ context 'when pipeline command succeeds' do
298
+ it 'should package the dump directory, then remove it' do
299
+
300
+ Backup::Logger.expects(:message).in_sequence(s).with(
301
+ "Database::MongoDB started compressing and packaging:\n" +
302
+ " '/path/to/dump/folder'"
303
+ )
304
+
305
+ pipeline.expects(:<<).in_sequence(s).with(
306
+ "tar -cf - -C '/path/to/dump' 'folder'"
307
+ )
308
+ pipeline.expects(:<<).in_sequence(s).with('compressor_command')
309
+ pipeline.expects(:<<).in_sequence(s).with(
310
+ "cat > /path/to/dump/folder-#{ timestamp }.tar.gz"
311
+ )
312
+
313
+ pipeline.expects(:run).in_sequence(s)
314
+ pipeline.expects(:success?).in_sequence(s).returns(true)
315
+ Backup::Logger.expects(:message).in_sequence(s).with(
316
+ "Database::MongoDB completed compressing and packaging:\n" +
317
+ " '/path/to/dump/folder-#{ timestamp }.tar.gz'"
318
+ )
319
+ FileUtils.expects(:rm_rf).in_sequence(s).with('/path/to/dump/folder')
320
+
321
+ db.send(:package!)
322
+ end
323
+ end #context 'when pipeline command succeeds'
324
+
325
+ context 'when pipeline command fails' do
326
+ before do
327
+ pipeline.stubs(:<<)
328
+ pipeline.expects(:run)
329
+ pipeline.expects(:success?).returns(false)
330
+ pipeline.expects(:error_messages).returns('pipeline_errors')
331
+ end
332
+
333
+ it 'should raise an error' do
334
+ Backup::Logger.expects(:message).with(
335
+ "Database::MongoDB started compressing and packaging:\n" +
336
+ " '/path/to/dump/folder'"
337
+ )
338
+
339
+ expect do
340
+ db.send(:package!)
341
+ end.to raise_error(
342
+ Backup::Errors::Database::PipelineError,
343
+ "Database::PipelineError: Database::MongoDB " +
344
+ "Failed to create compressed dump package:\n" +
345
+ " '/path/to/dump/folder-#{ timestamp }.tar.gz'\n" +
346
+ " pipeline_errors"
347
+ )
348
+ end
349
+ end # context 'when pipeline command fails'
350
+ end
351
+
352
+ context 'when a compressor is not configured' do
353
+ before do
354
+ model.expects(:compressor).returns(nil)
355
+ end
356
+
357
+ it 'should return nil' do
358
+ Backup::Pipeline.expects(:new).never
359
+ db.send(:package!).should be_nil
360
+ end
361
+ end
362
+ end # describe '#package!'
363
+
364
+ describe '#database' do
365
+ context 'when a database name is given' do
366
+ it 'should return the command string for the database' do
367
+ db.send(:database).should == "--db='mydatabase'"
368
+ end
369
+ end
370
+
371
+ context 'when no database name is given' do
372
+ it 'should return nil' do
373
+ db.name = nil
374
+ db.send(:database).should be_nil
375
+ end
376
+ end
377
+ end
378
+
379
+ describe '#credential_options' do
380
+ it 'should return the command string for the user credentials' do
381
+ db.send(:credential_options).should ==
382
+ "--username='someuser' --password='secret'"
383
+ end
384
+ end
385
+
386
+ describe '#connectivity_options' do
387
+ it 'should return the command string for the connectivity options' do
388
+ db.send(:connectivity_options).should == "--host='localhost' --port='123'"
389
+ end
390
+ end
391
+
392
+ describe '#ipv6_option' do
393
+ context 'when #ipv6 is set true' do
394
+ it 'should return the command string for the ipv6 option' do
395
+ db.send(:ipv6_option).should == '--ipv6'
396
+ end
397
+ end
398
+
399
+ context 'when #ipv6 is set false' do
400
+ it 'should return and empty string' do
401
+ db.ipv6 = false
402
+ db.send(:ipv6_option).should == ''
403
+ end
404
+ end
405
+ end
406
+
407
+ describe '#user_options' do
408
+ context 'when #additional_options are set' do
409
+ it 'should return the command string for the options' do
410
+ db.send(:user_options).should == '--query --foo'
411
+ end
412
+ end
413
+
414
+ context 'when #additional_options are not set' do
415
+ it 'should return an empty string' do
416
+ db.additional_options = []
417
+ db.send(:user_options).should == ''
418
+ end
419
+ end
420
+ end
421
+
422
+ describe '#dump_directory' do
423
+ it 'should return the command string for the dump path' do
424
+ db.instance_variable_set(:@dump_path, '/path/to/dump/folder')
425
+ db.send(:dump_directory).should == "--out='/path/to/dump/folder'"
426
+ end
427
+ end
428
+
429
+ describe '#lock_database' do
430
+ it 'should return the command to lock the database' do
431
+ db.stubs(:mongo_uri).returns(:mongo_uri_output)
432
+ db.expects(:run).with(
433
+ " echo 'use admin\n" +
434
+ ' db.runCommand({"fsync" : 1, "lock" : 1})\' | /path/to/mongo mongo_uri_output' +
435
+ "\n"
436
+ )
437
+ db.send(:lock_database)
438
+ end
439
+ end
440
+
441
+ describe '#unlock_database' do
442
+ it 'should return the command to unlock the database' do
443
+ db.stubs(:mongo_uri).returns(:mongo_uri_output)
444
+ db.expects(:run).with(
445
+ " echo 'use admin\n" +
446
+ ' db.$cmd.sys.unlock.findOne()\' | /path/to/mongo mongo_uri_output' +
447
+ "\n"
448
+ )
449
+ db.send(:unlock_database)
450
+ end
451
+ end
452
+
453
+ describe '#mongo_uri' do
454
+ before do
455
+ db.stubs(:credential_options).returns(:credential_options_output)
456
+ db.stubs(:ipv6_option).returns(:ipv6_option_output)
457
+ end
458
+
459
+ context 'when a database name is given' do
460
+ it 'should return the URI specifying the database' do
461
+ db.send(:mongo_uri).should ==
462
+ "localhost:123/mydatabase credential_options_output ipv6_option_output"
463
+ end
464
+ end
465
+
466
+ context 'when no database name is given' do
467
+ it 'should return the URI without specifying the database' do
468
+ db.name = nil
469
+ db.send(:mongo_uri).should ==
470
+ "localhost:123 credential_options_output ipv6_option_output"
471
+ end
472
+ end
473
+ end
474
+
475
+ describe 'deprecations' do
476
+ describe '#utility_path' do
477
+ before do
478
+ Backup::Database::MongoDB.any_instance.stubs(:utility)
479
+ Backup::Logger.expects(:warn).with {|err|
480
+ err.should be_an_instance_of Backup::Errors::ConfigurationError
481
+ err.message.should match(
482
+ /Use MongoDB#mongodump_utility instead/
483
+ )
484
+ }
485
+ end
486
+ after do
487
+ Backup::Database::MongoDB.clear_defaults!
488
+ end
489
+
490
+ context 'when set directly' do
491
+ it 'should issue a deprecation warning and set the replacement value' do
492
+ mongodb = Backup::Database::MongoDB.new(model) do |db|
493
+ db.utility_path = 'foo'
494
+ end
495
+ mongodb.mongodump_utility.should == 'foo'
496
+ end
497
+ end
498
+
499
+ context 'when set as a default' do
500
+ it 'should issue a deprecation warning and set the replacement value' do
501
+ mongodb = Backup::Database::MongoDB.defaults do |db|
502
+ db.utility_path = 'foo'
503
+ end
504
+ mongodb = Backup::Database::MongoDB.new(model)
505
+ mongodb.mongodump_utility.should == 'foo'
506
+ end
507
+ end
508
+ end # describe '#utility_path'
509
+ end
510
+ end