backup 3.0.20 → 3.0.21

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 (178) hide show
  1. data/Gemfile +1 -5
  2. data/Gemfile.lock +46 -50
  3. data/README.md +54 -27
  4. data/lib/backup.rb +16 -39
  5. data/lib/backup/archive.rb +42 -18
  6. data/lib/backup/cleaner.rb +110 -25
  7. data/lib/backup/cli/helpers.rb +17 -32
  8. data/lib/backup/cli/utility.rb +46 -107
  9. data/lib/backup/compressor/base.rb +14 -2
  10. data/lib/backup/compressor/bzip2.rb +10 -24
  11. data/lib/backup/compressor/gzip.rb +10 -24
  12. data/lib/backup/compressor/lzma.rb +10 -23
  13. data/lib/backup/compressor/pbzip2.rb +12 -32
  14. data/lib/backup/config.rb +171 -0
  15. data/lib/backup/configuration/compressor/base.rb +1 -2
  16. data/lib/backup/configuration/compressor/pbzip2.rb +4 -4
  17. data/lib/backup/configuration/database/base.rb +2 -1
  18. data/lib/backup/configuration/database/mongodb.rb +8 -0
  19. data/lib/backup/configuration/database/mysql.rb +4 -0
  20. data/lib/backup/configuration/database/postgresql.rb +4 -0
  21. data/lib/backup/configuration/database/redis.rb +4 -0
  22. data/lib/backup/configuration/database/riak.rb +5 -1
  23. data/lib/backup/configuration/encryptor/base.rb +1 -2
  24. data/lib/backup/configuration/encryptor/open_ssl.rb +1 -1
  25. data/lib/backup/configuration/helpers.rb +7 -2
  26. data/lib/backup/configuration/notifier/base.rb +4 -28
  27. data/lib/backup/configuration/storage/base.rb +1 -1
  28. data/lib/backup/configuration/storage/dropbox.rb +14 -4
  29. data/lib/backup/configuration/syncer/base.rb +10 -0
  30. data/lib/backup/configuration/syncer/rsync/base.rb +28 -0
  31. data/lib/backup/configuration/syncer/rsync/local.rb +11 -0
  32. data/lib/backup/configuration/syncer/rsync/pull.rb +11 -0
  33. data/lib/backup/configuration/syncer/rsync/push.rb +31 -0
  34. data/lib/backup/configuration/syncer/s3.rb +0 -4
  35. data/lib/backup/database/base.rb +25 -7
  36. data/lib/backup/database/mongodb.rb +112 -75
  37. data/lib/backup/database/mysql.rb +54 -29
  38. data/lib/backup/database/postgresql.rb +60 -42
  39. data/lib/backup/database/redis.rb +61 -39
  40. data/lib/backup/database/riak.rb +35 -11
  41. data/lib/backup/dependency.rb +4 -5
  42. data/lib/backup/encryptor/base.rb +13 -1
  43. data/lib/backup/encryptor/gpg.rb +39 -39
  44. data/lib/backup/encryptor/open_ssl.rb +28 -38
  45. data/lib/backup/logger.rb +20 -11
  46. data/lib/backup/model.rb +206 -163
  47. data/lib/backup/notifier/base.rb +27 -25
  48. data/lib/backup/notifier/campfire.rb +7 -13
  49. data/lib/backup/notifier/hipchat.rb +28 -28
  50. data/lib/backup/notifier/mail.rb +24 -26
  51. data/lib/backup/notifier/presently.rb +10 -18
  52. data/lib/backup/notifier/prowl.rb +9 -17
  53. data/lib/backup/notifier/twitter.rb +11 -18
  54. data/lib/backup/package.rb +47 -0
  55. data/lib/backup/packager.rb +81 -16
  56. data/lib/backup/splitter.rb +48 -35
  57. data/lib/backup/storage/base.rb +44 -172
  58. data/lib/backup/storage/cloudfiles.rb +31 -46
  59. data/lib/backup/storage/cycler.rb +117 -0
  60. data/lib/backup/storage/dropbox.rb +92 -76
  61. data/lib/backup/storage/ftp.rb +30 -40
  62. data/lib/backup/storage/local.rb +44 -45
  63. data/lib/backup/storage/ninefold.rb +55 -49
  64. data/lib/backup/storage/rsync.rb +49 -56
  65. data/lib/backup/storage/s3.rb +33 -44
  66. data/lib/backup/storage/scp.rb +21 -48
  67. data/lib/backup/storage/sftp.rb +26 -40
  68. data/lib/backup/syncer/base.rb +7 -0
  69. data/lib/backup/syncer/rsync/base.rb +78 -0
  70. data/lib/backup/syncer/rsync/local.rb +53 -0
  71. data/lib/backup/syncer/rsync/pull.rb +38 -0
  72. data/lib/backup/syncer/rsync/push.rb +113 -0
  73. data/lib/backup/syncer/s3.rb +42 -32
  74. data/lib/backup/version.rb +1 -1
  75. data/spec/archive_spec.rb +235 -69
  76. data/spec/cleaner_spec.rb +304 -0
  77. data/spec/cli/helpers_spec.rb +142 -1
  78. data/spec/cli/utility_spec.rb +338 -13
  79. data/spec/compressor/base_spec.rb +31 -0
  80. data/spec/compressor/bzip2_spec.rb +60 -35
  81. data/spec/compressor/gzip_spec.rb +60 -35
  82. data/spec/compressor/lzma_spec.rb +60 -35
  83. data/spec/compressor/pbzip2_spec.rb +98 -37
  84. data/spec/config_spec.rb +321 -0
  85. data/spec/configuration/base_spec.rb +4 -4
  86. data/spec/configuration/compressor/bzip2_spec.rb +1 -0
  87. data/spec/configuration/compressor/gzip_spec.rb +1 -0
  88. data/spec/configuration/compressor/lzma_spec.rb +1 -0
  89. data/spec/configuration/compressor/pbzip2_spec.rb +32 -0
  90. data/spec/configuration/database/base_spec.rb +2 -1
  91. data/spec/configuration/database/mongodb_spec.rb +26 -16
  92. data/spec/configuration/database/mysql_spec.rb +4 -0
  93. data/spec/configuration/database/postgresql_spec.rb +4 -0
  94. data/spec/configuration/database/redis_spec.rb +4 -0
  95. data/spec/configuration/database/riak_spec.rb +4 -0
  96. data/spec/configuration/encryptor/gpg_spec.rb +1 -0
  97. data/spec/configuration/encryptor/open_ssl_spec.rb +1 -0
  98. data/spec/configuration/notifier/base_spec.rb +32 -0
  99. data/spec/configuration/notifier/campfire_spec.rb +1 -0
  100. data/spec/configuration/notifier/hipchat_spec.rb +1 -0
  101. data/spec/configuration/notifier/mail_spec.rb +1 -0
  102. data/spec/configuration/notifier/presently_spec.rb +1 -0
  103. data/spec/configuration/notifier/prowl_spec.rb +1 -0
  104. data/spec/configuration/notifier/twitter_spec.rb +1 -0
  105. data/spec/configuration/storage/cloudfiles_spec.rb +1 -0
  106. data/spec/configuration/storage/dropbox_spec.rb +4 -3
  107. data/spec/configuration/storage/ftp_spec.rb +1 -0
  108. data/spec/configuration/storage/local_spec.rb +1 -0
  109. data/spec/configuration/storage/ninefold_spec.rb +1 -0
  110. data/spec/configuration/storage/rsync_spec.rb +3 -1
  111. data/spec/configuration/storage/s3_spec.rb +1 -0
  112. data/spec/configuration/storage/scp_spec.rb +1 -0
  113. data/spec/configuration/storage/sftp_spec.rb +1 -0
  114. data/spec/configuration/syncer/rsync/base_spec.rb +33 -0
  115. data/spec/configuration/syncer/rsync/local_spec.rb +10 -0
  116. data/spec/configuration/syncer/rsync/pull_spec.rb +10 -0
  117. data/spec/configuration/syncer/{rsync_spec.rb → rsync/push_spec.rb} +12 -15
  118. data/spec/configuration/syncer/s3_spec.rb +2 -3
  119. data/spec/database/base_spec.rb +35 -20
  120. data/spec/database/mongodb_spec.rb +298 -119
  121. data/spec/database/mysql_spec.rb +147 -72
  122. data/spec/database/postgresql_spec.rb +155 -100
  123. data/spec/database/redis_spec.rb +200 -97
  124. data/spec/database/riak_spec.rb +82 -24
  125. data/spec/dependency_spec.rb +49 -0
  126. data/spec/encryptor/base_spec.rb +30 -0
  127. data/spec/encryptor/gpg_spec.rb +105 -28
  128. data/spec/encryptor/open_ssl_spec.rb +85 -114
  129. data/spec/logger_spec.rb +74 -8
  130. data/spec/model_spec.rb +528 -220
  131. data/spec/notifier/base_spec.rb +89 -0
  132. data/spec/notifier/campfire_spec.rb +147 -119
  133. data/spec/notifier/hipchat_spec.rb +140 -145
  134. data/spec/notifier/mail_spec.rb +190 -248
  135. data/spec/notifier/presently_spec.rb +147 -282
  136. data/spec/notifier/prowl_spec.rb +79 -111
  137. data/spec/notifier/twitter_spec.rb +87 -106
  138. data/spec/package_spec.rb +61 -0
  139. data/spec/packager_spec.rb +154 -0
  140. data/spec/spec_helper.rb +36 -13
  141. data/spec/splitter_spec.rb +90 -41
  142. data/spec/storage/base_spec.rb +95 -239
  143. data/spec/storage/cloudfiles_spec.rb +185 -75
  144. data/spec/storage/cycler_spec.rb +239 -0
  145. data/spec/storage/dropbox_spec.rb +318 -87
  146. data/spec/storage/ftp_spec.rb +165 -152
  147. data/spec/storage/local_spec.rb +206 -54
  148. data/spec/storage/ninefold_spec.rb +264 -128
  149. data/spec/storage/rsync_spec.rb +244 -163
  150. data/spec/storage/s3_spec.rb +175 -64
  151. data/spec/storage/scp_spec.rb +156 -150
  152. data/spec/storage/sftp_spec.rb +153 -135
  153. data/spec/syncer/base_spec.rb +22 -0
  154. data/spec/syncer/rsync/base_spec.rb +118 -0
  155. data/spec/syncer/rsync/local_spec.rb +121 -0
  156. data/spec/syncer/rsync/pull_spec.rb +90 -0
  157. data/spec/syncer/rsync/push_spec.rb +327 -0
  158. data/spec/syncer/s3_spec.rb +180 -91
  159. data/templates/cli/utility/config +1 -1
  160. data/templates/cli/utility/database/mongodb +4 -0
  161. data/templates/cli/utility/database/mysql +3 -0
  162. data/templates/cli/utility/database/postgresql +3 -0
  163. data/templates/cli/utility/database/redis +3 -0
  164. data/templates/cli/utility/database/riak +3 -0
  165. data/templates/cli/utility/storage/dropbox +4 -1
  166. data/templates/cli/utility/syncer/rsync_local +12 -0
  167. data/templates/cli/utility/syncer/{rsync → rsync_pull} +2 -2
  168. data/templates/cli/utility/syncer/rsync_push +17 -0
  169. data/templates/storage/dropbox/authorization_url.erb +1 -1
  170. metadata +42 -17
  171. data/lib/backup/configuration/syncer/rsync.rb +0 -45
  172. data/lib/backup/finder.rb +0 -87
  173. data/lib/backup/storage/object.rb +0 -47
  174. data/lib/backup/syncer/rsync.rb +0 -152
  175. data/spec/backup_spec.rb +0 -11
  176. data/spec/finder_spec.rb +0 -91
  177. data/spec/storage/object_spec.rb +0 -74
  178. data/spec/syncer/rsync_spec.rb +0 -195
@@ -3,118 +3,228 @@
3
3
  require File.expand_path('../../spec_helper.rb', __FILE__)
4
4
 
5
5
  describe Backup::Storage::CloudFiles do
6
-
7
- let(:cf) do
8
- Backup::Storage::CloudFiles.new do |cf|
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:storage) do
8
+ Backup::Storage::CloudFiles.new(model) do |cf|
9
9
  cf.username = 'my_username'
10
10
  cf.api_key = 'my_api_key'
11
- cf.container = 'my_container'
12
- cf.path = 'backups'
13
- cf.keep = 20
14
11
  cf.auth_url = 'lon.auth.api.rackspacecloud.com'
12
+ cf.container = 'my_container'
13
+ cf.keep = 5
15
14
  end
16
15
  end
17
16
 
18
- before do
19
- Backup::Configuration::Storage::CloudFiles.clear_defaults!
20
- end
21
-
22
- it 'should have defined the configuration properly' do
23
- cf.username.should == 'my_username'
24
- cf.api_key.should == 'my_api_key'
25
- cf.container.should == 'my_container'
26
- cf.path.should == 'backups'
27
- cf.keep.should == 20
28
- cf.auth_url.should == 'lon.auth.api.rackspacecloud.com'
29
- end
30
-
31
- it 'should use the defaults if a particular attribute has not been defined' do
32
- Backup::Configuration::Storage::CloudFiles.defaults do |cf|
33
- cf.username = 'my_username'
34
- cf.api_key = 'my_api_key'
17
+ describe '#initialize' do
18
+ it 'should set the correct values' do
19
+ storage.username.should == 'my_username'
20
+ storage.api_key.should == 'my_api_key'
21
+ storage.auth_url.should == 'lon.auth.api.rackspacecloud.com'
22
+ storage.container.should == 'my_container'
23
+ storage.servicenet.should == false
24
+ storage.path.should == 'backups'
25
+
26
+ storage.storage_id.should be_nil
27
+ storage.keep.should == 5
35
28
  end
36
29
 
37
- cf = Backup::Storage::CloudFiles.new do |cf|
38
- cf.container = 'my_container'
39
- cf.path = 'my/backups'
30
+ it 'should set a storage_id if given' do
31
+ cf = Backup::Storage::CloudFiles.new(model, 'my storage_id')
32
+ cf.storage_id.should == 'my storage_id'
40
33
  end
41
34
 
42
- cf.username.should == 'my_username'
43
- cf.api_key.should == 'my_api_key'
44
- cf.container.should == 'my_container'
45
- cf.path.should == 'my/backups'
46
- end
35
+ context 'when setting configuration defaults' do
36
+ after { Backup::Configuration::Storage::CloudFiles.clear_defaults! }
37
+
38
+ it 'should use the configured defaults' do
39
+ Backup::Configuration::Storage::CloudFiles.defaults do |cf|
40
+ cf.username = 'some_username'
41
+ cf.api_key = 'some_api_key'
42
+ cf.auth_url = 'some_auth_url'
43
+ cf.container = 'some_container'
44
+ cf.servicenet = true
45
+ cf.path = 'some_path'
46
+ cf.keep = 15
47
+ end
48
+ storage = Backup::Storage::CloudFiles.new(model)
49
+ storage.username.should == 'some_username'
50
+ storage.api_key.should == 'some_api_key'
51
+ storage.auth_url.should == 'some_auth_url'
52
+ storage.container.should == 'some_container'
53
+ storage.servicenet.should == true
54
+ storage.path.should == 'some_path'
55
+
56
+ storage.storage_id.should be_nil
57
+ storage.keep.should == 15
58
+ end
59
+
60
+ it 'should override the configured defaults' do
61
+ Backup::Configuration::Storage::CloudFiles.defaults do |cf|
62
+ cf.username = 'old_username'
63
+ cf.api_key = 'old_api_key'
64
+ cf.auth_url = 'old_auth_url'
65
+ cf.container = 'old_container'
66
+ cf.servicenet = true
67
+ cf.path = 'old_path'
68
+ cf.keep = 15
69
+ end
70
+ storage = Backup::Storage::CloudFiles.new(model) do |cf|
71
+ cf.username = 'new_username'
72
+ cf.api_key = 'new_api_key'
73
+ cf.auth_url = 'new_auth_url'
74
+ cf.container = 'new_container'
75
+ cf.servicenet = false
76
+ cf.path = 'new_path'
77
+ cf.keep = 10
78
+ end
79
+
80
+ storage.username.should == 'new_username'
81
+ storage.api_key.should == 'new_api_key'
82
+ storage.auth_url.should == 'new_auth_url'
83
+ storage.container.should == 'new_container'
84
+ storage.servicenet.should == false
85
+ storage.path.should == 'new_path'
86
+
87
+ storage.storage_id.should be_nil
88
+ storage.keep.should == 10
89
+ end
90
+ end # context 'when setting configuration defaults'
91
+
92
+ end # describe '#initialize'
47
93
 
48
- describe '#perform' do
49
- it 'should invoke transfer! and cycle!' do
50
- cf.expects(:transfer!)
51
- cf.expects(:cycle!)
52
- cf.perform!
94
+ describe '#provider' do
95
+ it 'should set the Fog provider' do
96
+ storage.send(:provider).should == 'Rackspace'
53
97
  end
54
98
  end
55
99
 
56
100
  describe '#connection' do
57
- it 'should establish and re-use a connection to Rackspace Cloud Files' do
58
- Fog::Storage.expects(:new).once.with({
59
- :provider => 'Rackspace',
60
- :rackspace_username => 'my_username',
61
- :rackspace_api_key => 'my_api_key',
62
- :rackspace_auth_url => 'lon.auth.api.rackspacecloud.com',
63
- :rackspace_servicenet => false
64
- }).returns(true)
65
-
66
- cf.send(:connection)
67
- cf.send(:connection)
101
+ let(:connection) { mock }
102
+
103
+ context 'when @servicenet is set to false' do
104
+ it 'should create a new standard connection' do
105
+ Fog::Storage.expects(:new).once.with(
106
+ :provider => 'Rackspace',
107
+ :rackspace_username => 'my_username',
108
+ :rackspace_api_key => 'my_api_key',
109
+ :rackspace_auth_url => 'lon.auth.api.rackspacecloud.com',
110
+ :rackspace_servicenet => false
111
+ ).returns(connection)
112
+ storage.send(:connection).should == connection
113
+ end
68
114
  end
69
115
 
70
- context 'with LAN (servicenet) enabled' do
71
- it 'should establish and re-use a connection to Rackspace Cloud Files' do
72
- Fog::Storage.expects(:new).once.with({
116
+ context 'when @servicenet is set to true' do
117
+ before do
118
+ storage.servicenet = true
119
+ end
120
+
121
+ it 'should create a new servicenet connection' do
122
+ Fog::Storage.expects(:new).once.with(
73
123
  :provider => 'Rackspace',
74
124
  :rackspace_username => 'my_username',
75
125
  :rackspace_api_key => 'my_api_key',
76
126
  :rackspace_auth_url => 'lon.auth.api.rackspacecloud.com',
77
127
  :rackspace_servicenet => true
78
- }).returns(true)
79
-
80
- cf.servicenet = true
81
- cf.send(:connection)
82
- cf.send(:connection)
128
+ ).returns(connection)
129
+ storage.send(:connection).should == connection
83
130
  end
84
131
  end
85
- end
86
132
 
87
- describe '#provider' do
88
- it 'should be Rackspace' do
89
- cf.provider == 'Rackspace'
133
+ it 'should return an existing connection' do
134
+ Fog::Storage.expects(:new).once.returns(connection)
135
+ storage.send(:connection).should == connection
136
+ storage.send(:connection).should == connection
90
137
  end
91
- end
138
+
139
+ end # describe '#connection'
92
140
 
93
141
  describe '#transfer!' do
94
- let(:connection) { mock('Fog::Storage') }
142
+ let(:connection) { mock }
143
+ let(:package) { mock }
144
+ let(:file) { mock }
145
+ let(:s) { sequence '' }
146
+
95
147
  before do
96
- Fog::Storage.expects(:new).once.returns(connection)
148
+ storage.instance_variable_set(:@package, package)
149
+ storage.stubs(:storage_name).returns('Storage::CloudFiles')
150
+ storage.stubs(:local_path).returns('/local/path')
151
+ storage.stubs(:connection).returns(connection)
97
152
  end
98
153
 
99
- it 'should transfer the provided file to the container' do
100
- Backup::Model.new('blah', 'blah') {}
101
- file = mock("Backup::Storage::CloudFiles::File")
102
- File.expects(:open).with("#{File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER}")}.tar").returns(file)
103
- connection.expects(:put_object).with('my_container', "backups/myapp/#{ Backup::TIME }/#{ Backup::TRIGGER }.tar", file)
104
- cf.send(:transfer!)
154
+ it 'should transfer the package files' do
155
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
156
+ returns('remote/path')
157
+ storage.expects(:files_to_transfer_for).in_sequence(s).with(package).
158
+ multiple_yields(
159
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
160
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
161
+ )
162
+ # first yield
163
+ Backup::Logger.expects(:message).in_sequence(s).with(
164
+ "Storage::CloudFiles started transferring " +
165
+ "'2011.12.31.11.00.02.backup.tar.enc-aa'."
166
+ )
167
+ File.expects(:open).in_sequence(s).with(
168
+ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-aa'), 'r'
169
+ ).yields(file)
170
+ connection.expects(:put_object).in_sequence(s).with(
171
+ 'my_container', File.join('remote/path', 'backup.tar.enc-aa'), file
172
+ )
173
+ # second yield
174
+ Backup::Logger.expects(:message).in_sequence(s).with(
175
+ "Storage::CloudFiles started transferring " +
176
+ "'2011.12.31.11.00.02.backup.tar.enc-ab'."
177
+ )
178
+ File.expects(:open).in_sequence(s).with(
179
+ File.join('/local/path', '2011.12.31.11.00.02.backup.tar.enc-ab'), 'r'
180
+ ).yields(file)
181
+ connection.expects(:put_object).in_sequence(s).with(
182
+ 'my_container', File.join('remote/path', 'backup.tar.enc-ab'), file
183
+ )
184
+
185
+ storage.send(:transfer!)
105
186
  end
106
- end
187
+ end # describe '#transfer!'
107
188
 
108
189
  describe '#remove!' do
109
- let(:connection) { mock('Fog::Storage') }
190
+ let(:package) { mock }
191
+ let(:connection) { mock }
192
+ let(:s) { sequence '' }
193
+
110
194
  before do
111
- Fog::Storage.expects(:new).once.returns(connection)
195
+ storage.stubs(:storage_name).returns('Storage::CloudFiles')
196
+ storage.stubs(:connection).returns(connection)
112
197
  end
113
198
 
114
- it 'should remove the file from the container' do
115
- connection.expects(:delete_object).with('my_container', "backups/myapp/#{ Backup::TIME }/#{ Backup::TRIGGER }.tar")
116
- cf.send(:remove!)
199
+ it 'should remove the package files' do
200
+ storage.expects(:remote_path_for).in_sequence(s).with(package).
201
+ returns('remote/path')
202
+ storage.expects(:transferred_files_for).in_sequence(s).with(package).
203
+ multiple_yields(
204
+ ['2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'],
205
+ ['2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab']
206
+ )
207
+ # first yield
208
+ Backup::Logger.expects(:message).in_sequence(s).with(
209
+ "Storage::CloudFiles started removing " +
210
+ "'2011.12.31.11.00.02.backup.tar.enc-aa' " +
211
+ "from container 'my_container'."
212
+ )
213
+ connection.expects(:delete_object).in_sequence(s).with(
214
+ 'my_container', File.join('remote/path', 'backup.tar.enc-aa')
215
+ )
216
+ # second yield
217
+ Backup::Logger.expects(:message).in_sequence(s).with(
218
+ "Storage::CloudFiles started removing " +
219
+ "'2011.12.31.11.00.02.backup.tar.enc-ab' " +
220
+ "from container 'my_container'."
221
+ )
222
+ connection.expects(:delete_object).in_sequence(s).with(
223
+ 'my_container', File.join('remote/path', 'backup.tar.enc-ab')
224
+ )
225
+
226
+ storage.send(:remove!, package)
117
227
  end
118
- end
228
+ end # describe '#remove!'
119
229
 
120
230
  end
@@ -0,0 +1,239 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
+
5
+ describe 'Backup::Storage::Cycler' do
6
+ let(:cycler) { Backup::Storage::Cycler }
7
+ let(:storage) { mock }
8
+ let(:package) { mock }
9
+ let(:storage_file) { mock }
10
+ let(:pkg_a) { mock }
11
+ let(:pkg_b) { mock }
12
+ let(:pkg_c) { mock }
13
+ let(:s) { sequence '' }
14
+
15
+ describe '#cycle!' do
16
+ it 'should setup variables and initiate cycling' do
17
+ cycler.expects(:storage_file).in_sequence(s).returns(storage_file)
18
+ cycler.expects(:update_storage_file!).in_sequence(s)
19
+ cycler.expects(:remove_packages!).in_sequence(s)
20
+ cycler.cycle!(storage, package)
21
+ cycler.instance_variable_get(:@storage).should be(storage)
22
+ cycler.instance_variable_get(:@package).should be(package)
23
+ cycler.instance_variable_get(:@storage_file).should be(storage_file)
24
+ end
25
+ end
26
+
27
+ describe 'update_storage_file!' do
28
+ before do
29
+ storage.stubs(:keep).returns(2)
30
+ cycler.instance_variable_set(:@storage, storage)
31
+ cycler.instance_variable_set(:@package, package)
32
+ cycler.instance_variable_set(:@storage_file, storage_file)
33
+ end
34
+
35
+ it 'should remove entries and set @packages_to_remove' do
36
+ cycler.expects(:yaml_load).in_sequence(s).returns([pkg_a, pkg_b, pkg_c])
37
+ cycler.expects(:yaml_save).in_sequence(s).with([package, pkg_a])
38
+ cycler.send(:update_storage_file!)
39
+ cycler.instance_variable_get(:@packages_to_remove).should == [pkg_b, pkg_c]
40
+ end
41
+ end
42
+
43
+ describe '#remove_packages!' do
44
+ before do
45
+ cycler.instance_variable_set(:@storage, storage)
46
+ cycler.instance_variable_set(:@packages_to_remove, [pkg_a, pkg_b, pkg_c])
47
+ end
48
+
49
+ it 'should call the @storage to remove the old packages' do
50
+ storage.expects(:remove!).in_sequence(s).with(pkg_a)
51
+ storage.expects(:remove!).in_sequence(s).with(pkg_b)
52
+ storage.expects(:remove!).in_sequence(s).with(pkg_c)
53
+ cycler.send(:remove_packages!)
54
+ end
55
+
56
+ context 'when errors occur removing packages' do
57
+ before do
58
+ pkg_b.stubs(:trigger).returns('pkg_trigger')
59
+ pkg_b.stubs(:time).returns('pkg_time')
60
+ pkg_b.stubs(:filenames).returns(['file1', 'file2'])
61
+ end
62
+
63
+ it 'should warn and continue' do
64
+ storage.expects(:remove!).in_sequence(s).with(pkg_a)
65
+ storage.expects(:remove!).in_sequence(s).with(pkg_b).raises('error message')
66
+ Backup::Logger.expects(:warn).with do |err|
67
+ err.should be_an_instance_of Backup::Errors::Storage::CyclerError
68
+ err.message.should == 'Storage::CyclerError: ' +
69
+ "There was a problem removing the following package:\n" +
70
+ " Trigger: pkg_trigger :: Dated: pkg_time\n" +
71
+ " Package included the following 2 file(s):\n" +
72
+ " file1\n" +
73
+ " file2\n" +
74
+ " Reason: RuntimeError\n" +
75
+ " error message"
76
+ end
77
+ storage.expects(:remove!).in_sequence(s).with(pkg_c)
78
+
79
+ expect do
80
+ cycler.send(:remove_packages!)
81
+ end.not_to raise_error
82
+ end
83
+ end
84
+
85
+ end # describe '#remove_packages!'
86
+
87
+ describe '#storage_file' do
88
+ before do
89
+ cycler.instance_variable_set(:@storage, storage)
90
+ storage.stubs(:class).returns('Backup::Storage::S3')
91
+ cycler.instance_variable_set(:@package, package)
92
+ package.stubs(:trigger).returns('pkg_trigger')
93
+ end
94
+
95
+ context 'when the @storage.storage_id is not set' do
96
+ before { storage.stubs(:storage_id).returns(nil) }
97
+ it 'should return the path to the YAML storage file with no suffix' do
98
+ cycler.send(:storage_file).should ==
99
+ File.join(Backup::Config.data_path, 'pkg_trigger', 'S3.yml')
100
+ end
101
+ end
102
+
103
+ context 'when the @storage.storage_id is set' do
104
+ before { storage.stubs(:storage_id).returns('Storage #1') }
105
+ it 'should sanitize the storage_id to use as a filename suffix' do
106
+ cycler.send(:storage_file).should ==
107
+ File.join(Backup::Config.data_path, 'pkg_trigger', 'S3-Storage__1.yml')
108
+ end
109
+ end
110
+
111
+ end # describe '#storage_file'
112
+
113
+ describe '#yaml_load' do
114
+ let(:obj_a) { mock }
115
+ let(:obj_b) { mock }
116
+ let(:obj_c) { mock }
117
+ let(:unsorted_objects) { [obj_a, obj_c, obj_b] }
118
+ let(:sorted_objects) { [obj_c, obj_b, obj_a] }
119
+
120
+ before do
121
+ cycler.instance_variable_set(:@storage_file, storage_file)
122
+ obj_a.instance_variable_set(:@time, '2012.01.01.07.00.00')
123
+ obj_b.instance_variable_set(:@time, '2012.01.01.08.00.00')
124
+ obj_c.instance_variable_set(:@time, '2012.01.01.09.00.00')
125
+ end
126
+
127
+ context 'when the storage file exists' do
128
+ before { File.expects(:exist?).with(storage_file).returns(true) }
129
+ context 'when the file is not empty' do
130
+ before { File.expects(:zero?).with(storage_file).returns(false) }
131
+ it 'should return YAML deserialized objects in an array, sorted by time DESC' do
132
+ YAML.expects(:load_file).with(storage_file).returns(unsorted_objects)
133
+ cycler.expects(:check_upgrade).with(sorted_objects).returns(sorted_objects)
134
+ cycler.send(:yaml_load).should be(sorted_objects)
135
+ end
136
+ end
137
+ context 'when the file is empty' do
138
+ before { File.expects(:zero?).with(storage_file).returns(true) }
139
+ it 'should return an empty array' do
140
+ cycler.send(:yaml_load).should == []
141
+ end
142
+ end
143
+ end
144
+
145
+ context 'when the storage file does not exist' do
146
+ before { File.expects(:exist?).with(storage_file).returns(false) }
147
+ it 'should return an empty array' do
148
+ cycler.send(:yaml_load).should == []
149
+ end
150
+ end
151
+ end # describe '#yaml_load'
152
+
153
+ describe '#yaml_save' do
154
+ let(:file) { mock }
155
+ let(:pkgs) { [ [1, 2, 3], [4, 5, 6] ] }
156
+ let(:pkgs_to_yaml) { pkgs.to_yaml }
157
+
158
+ it 'should save the given packages to the storage file in YAML format' do
159
+ cycler.instance_variable_set(:@storage_file, storage_file)
160
+ File.expects(:open).with(storage_file, 'w').yields(file)
161
+ file.expects(:write).with(pkgs_to_yaml)
162
+ cycler.send(:yaml_save, pkgs)
163
+ end
164
+ end # describe '#yaml_save'
165
+
166
+ describe '#check_upgrade' do
167
+ let(:yaml_v3_0_19) do
168
+ <<-EOF.gsub(/^ +/,' ').gsub(/^ (-\W\W)/, "\\1")
169
+ ---
170
+ - !ruby/object:Backup::Storage::Local
171
+ time: 2011.11.01.12.01.00
172
+ remote_file: 2011.11.01.12.01.00.backup.tar.gz
173
+ - !ruby/object:Backup::Storage::Local
174
+ time: 2011.11.01.12.02.00
175
+ remote_file: 2011.11.01.12.02.00.backup.tar.gz
176
+ - !ruby/object:Backup::Storage::Local
177
+ time: 2011.11.01.12.03.00
178
+ remote_file: 2011.11.01.12.03.00.backup.tar.gz
179
+ EOF
180
+ end
181
+ let(:yaml_v3_0_20) do
182
+ <<-EOF.gsub(/^ +/,' ').gsub(/^ (-\W\W)/, "\\1")
183
+ ---
184
+ - !ruby/object:Backup::Storage::Local
185
+ time: 2011.12.01.12.01.00
186
+ chunk_suffixes: []
187
+ filename: 2011.12.01.12.01.00.backup.tar.gz
188
+ version: 3.0.20
189
+ - !ruby/object:Backup::Storage::Local
190
+ time: 2011.12.01.12.02.00
191
+ chunk_suffixes: []
192
+ filename: 2011.12.01.12.02.00.backup.tar.gz
193
+ version: 3.0.20
194
+ - !ruby/object:Backup::Storage::Local
195
+ time: 2011.12.01.12.03.00
196
+ chunk_suffixes:
197
+ - aa
198
+ - ab
199
+ filename: 2011.12.01.12.03.00.backup.tar.gz
200
+ version: 3.0.20
201
+ EOF
202
+ end
203
+
204
+ before do
205
+ model = Backup::Model.new('foo', 'foo')
206
+ cycler.instance_variable_set(:@storage, storage)
207
+ storage.instance_variable_set(:@model, model)
208
+ end
209
+
210
+ it 'should upgrade v3.0.20 objects' do
211
+ objects = YAML.load(yaml_v3_0_20)
212
+ packages = cycler.send(:check_upgrade, objects)
213
+
214
+ packages.all? {|pkg| pkg.is_a? Backup::Package }.should be_true
215
+ packages.all? {|pkg| pkg.extension == 'tar.gz' }.should be_true
216
+
217
+ packages[0].time.should == '2011.12.01.12.01.00'
218
+ packages[0].chunk_suffixes.should == []
219
+ packages[1].time.should == '2011.12.01.12.02.00'
220
+ packages[1].chunk_suffixes.should == []
221
+ packages[2].time.should == '2011.12.01.12.03.00'
222
+ packages[2].chunk_suffixes.should == ['aa', 'ab']
223
+ end
224
+
225
+ it 'should upgrade v3.0.19 objects' do
226
+ objects = YAML.load(yaml_v3_0_19)
227
+ packages = cycler.send(:check_upgrade, objects)
228
+
229
+ packages.all? {|pkg| pkg.is_a? Backup::Package }.should be_true
230
+ packages.all? {|pkg| pkg.extension == 'tar.gz' }.should be_true
231
+ packages.all? {|pkg| pkg.chunk_suffixes == [] }.should be_true
232
+
233
+ packages[0].time.should == '2011.11.01.12.01.00'
234
+ packages[1].time.should == '2011.11.01.12.02.00'
235
+ packages[2].time.should == '2011.11.01.12.03.00'
236
+ end
237
+ end # describe '#check_upgrade'
238
+
239
+ end