backup 3.0.19 → 3.0.20

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 (188) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +9 -8
  3. data/Gemfile.lock +19 -1
  4. data/Guardfile +13 -9
  5. data/README.md +93 -31
  6. data/backup.gemspec +3 -3
  7. data/bin/backup +6 -283
  8. data/lib/backup.rb +101 -72
  9. data/lib/backup/archive.rb +21 -9
  10. data/lib/backup/binder.rb +22 -0
  11. data/lib/backup/cleaner.rb +36 -0
  12. data/lib/backup/cli/helpers.rb +103 -0
  13. data/lib/backup/cli/utility.rb +308 -0
  14. data/lib/backup/compressor/base.rb +2 -2
  15. data/lib/backup/compressor/pbzip2.rb +76 -0
  16. data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
  17. data/lib/backup/configuration/database/riak.rb +25 -0
  18. data/lib/backup/configuration/encryptor/open_ssl.rb +6 -0
  19. data/lib/backup/configuration/helpers.rb +5 -18
  20. data/lib/backup/configuration/notifier/base.rb +13 -0
  21. data/lib/backup/configuration/notifier/hipchat.rb +41 -0
  22. data/lib/backup/configuration/notifier/mail.rb +38 -0
  23. data/lib/backup/configuration/notifier/prowl.rb +23 -0
  24. data/lib/backup/configuration/storage/cloudfiles.rb +4 -0
  25. data/lib/backup/configuration/storage/dropbox.rb +8 -4
  26. data/lib/backup/database/base.rb +10 -2
  27. data/lib/backup/database/mongodb.rb +16 -19
  28. data/lib/backup/database/mysql.rb +2 -2
  29. data/lib/backup/database/postgresql.rb +2 -2
  30. data/lib/backup/database/redis.rb +15 -7
  31. data/lib/backup/database/riak.rb +45 -0
  32. data/lib/backup/dependency.rb +21 -7
  33. data/lib/backup/encryptor/base.rb +1 -1
  34. data/lib/backup/encryptor/open_ssl.rb +20 -5
  35. data/lib/backup/errors.rb +124 -0
  36. data/lib/backup/finder.rb +11 -3
  37. data/lib/backup/logger.rb +121 -82
  38. data/lib/backup/model.rb +103 -44
  39. data/lib/backup/notifier/base.rb +50 -0
  40. data/lib/backup/notifier/campfire.rb +32 -52
  41. data/lib/backup/notifier/hipchat.rb +99 -0
  42. data/lib/backup/notifier/mail.rb +100 -61
  43. data/lib/backup/notifier/presently.rb +31 -40
  44. data/lib/backup/notifier/prowl.rb +73 -0
  45. data/lib/backup/notifier/twitter.rb +29 -39
  46. data/lib/backup/packager.rb +25 -0
  47. data/lib/backup/splitter.rb +62 -0
  48. data/lib/backup/storage/base.rb +178 -18
  49. data/lib/backup/storage/cloudfiles.rb +34 -28
  50. data/lib/backup/storage/dropbox.rb +64 -67
  51. data/lib/backup/storage/ftp.rb +48 -40
  52. data/lib/backup/storage/local.rb +33 -28
  53. data/lib/backup/storage/ninefold.rb +40 -26
  54. data/lib/backup/storage/object.rb +8 -6
  55. data/lib/backup/storage/rsync.rb +61 -51
  56. data/lib/backup/storage/s3.rb +29 -27
  57. data/lib/backup/storage/scp.rb +56 -36
  58. data/lib/backup/storage/sftp.rb +49 -33
  59. data/lib/backup/syncer/base.rb +1 -1
  60. data/lib/backup/syncer/rsync.rb +1 -1
  61. data/lib/backup/template.rb +46 -0
  62. data/lib/backup/version.rb +1 -1
  63. data/spec/archive_spec.rb +34 -9
  64. data/spec/backup_spec.rb +1 -1
  65. data/spec/cli/helpers_spec.rb +35 -0
  66. data/spec/cli/utility_spec.rb +38 -0
  67. data/spec/compressor/bzip2_spec.rb +1 -1
  68. data/spec/compressor/gzip_spec.rb +1 -1
  69. data/spec/compressor/lzma_spec.rb +1 -1
  70. data/spec/compressor/pbzip2_spec.rb +63 -0
  71. data/spec/configuration/base_spec.rb +1 -1
  72. data/spec/configuration/compressor/bzip2_spec.rb +1 -1
  73. data/spec/configuration/compressor/gzip_spec.rb +1 -1
  74. data/spec/configuration/compressor/lzma_spec.rb +1 -1
  75. data/spec/configuration/database/base_spec.rb +1 -1
  76. data/spec/configuration/database/mongodb_spec.rb +1 -1
  77. data/spec/configuration/database/mysql_spec.rb +1 -1
  78. data/spec/configuration/database/postgresql_spec.rb +1 -1
  79. data/spec/configuration/database/redis_spec.rb +1 -1
  80. data/spec/configuration/database/riak_spec.rb +31 -0
  81. data/spec/configuration/encryptor/gpg_spec.rb +1 -1
  82. data/spec/configuration/encryptor/open_ssl_spec.rb +4 -1
  83. data/spec/configuration/notifier/campfire_spec.rb +1 -1
  84. data/spec/configuration/notifier/hipchat_spec.rb +43 -0
  85. data/spec/configuration/notifier/mail_spec.rb +34 -22
  86. data/spec/configuration/notifier/presently_spec.rb +1 -1
  87. data/spec/configuration/notifier/prowl_spec.rb +28 -0
  88. data/spec/configuration/notifier/twitter_spec.rb +1 -1
  89. data/spec/configuration/storage/cloudfiles_spec.rb +19 -16
  90. data/spec/configuration/storage/dropbox_spec.rb +1 -1
  91. data/spec/configuration/storage/ftp_spec.rb +1 -1
  92. data/spec/configuration/storage/local_spec.rb +1 -1
  93. data/spec/configuration/storage/ninefold_spec.rb +1 -1
  94. data/spec/configuration/storage/rsync_spec.rb +1 -1
  95. data/spec/configuration/storage/s3_spec.rb +1 -1
  96. data/spec/configuration/storage/scp_spec.rb +1 -1
  97. data/spec/configuration/storage/sftp_spec.rb +1 -1
  98. data/spec/configuration/syncer/rsync_spec.rb +1 -1
  99. data/spec/configuration/syncer/s3_spec.rb +1 -1
  100. data/spec/database/base_spec.rb +10 -1
  101. data/spec/database/mongodb_spec.rb +34 -7
  102. data/spec/database/mysql_spec.rb +8 -7
  103. data/spec/database/postgresql_spec.rb +8 -7
  104. data/spec/database/redis_spec.rb +39 -9
  105. data/spec/database/riak_spec.rb +50 -0
  106. data/spec/encryptor/gpg_spec.rb +1 -1
  107. data/spec/encryptor/open_ssl_spec.rb +77 -20
  108. data/spec/errors_spec.rb +306 -0
  109. data/spec/finder_spec.rb +91 -0
  110. data/spec/logger_spec.rb +254 -33
  111. data/spec/model_spec.rb +120 -15
  112. data/spec/notifier/campfire_spec.rb +127 -52
  113. data/spec/notifier/hipchat_spec.rb +193 -0
  114. data/spec/notifier/mail_spec.rb +290 -74
  115. data/spec/notifier/presently_spec.rb +290 -73
  116. data/spec/notifier/prowl_spec.rb +149 -0
  117. data/spec/notifier/twitter_spec.rb +106 -41
  118. data/spec/spec_helper.rb +8 -2
  119. data/spec/splitter_spec.rb +71 -0
  120. data/spec/storage/base_spec.rb +280 -19
  121. data/spec/storage/cloudfiles_spec.rb +38 -22
  122. data/spec/storage/dropbox_spec.rb +17 -13
  123. data/spec/storage/ftp_spec.rb +145 -55
  124. data/spec/storage/local_spec.rb +6 -6
  125. data/spec/storage/ninefold_spec.rb +70 -29
  126. data/spec/storage/object_spec.rb +44 -44
  127. data/spec/storage/rsync_spec.rb +186 -63
  128. data/spec/storage/s3_spec.rb +23 -24
  129. data/spec/storage/scp_spec.rb +116 -41
  130. data/spec/storage/sftp_spec.rb +124 -46
  131. data/spec/syncer/rsync_spec.rb +3 -3
  132. data/spec/syncer/s3_spec.rb +1 -1
  133. data/spec/version_spec.rb +1 -1
  134. data/templates/cli/utility/archive +13 -0
  135. data/{lib/templates → templates/cli/utility}/compressor/bzip2 +1 -1
  136. data/{lib/templates → templates/cli/utility}/compressor/gzip +1 -1
  137. data/{lib/templates → templates/cli/utility}/compressor/lzma +0 -0
  138. data/templates/cli/utility/compressor/pbzip2 +7 -0
  139. data/templates/cli/utility/config +31 -0
  140. data/{lib/templates → templates/cli/utility}/database/mongodb +1 -1
  141. data/{lib/templates → templates/cli/utility}/database/mysql +1 -1
  142. data/{lib/templates → templates/cli/utility}/database/postgresql +1 -1
  143. data/{lib/templates → templates/cli/utility}/database/redis +1 -1
  144. data/templates/cli/utility/database/riak +8 -0
  145. data/{lib/templates → templates/cli/utility}/encryptor/gpg +1 -1
  146. data/templates/cli/utility/encryptor/openssl +9 -0
  147. data/templates/cli/utility/model.erb +23 -0
  148. data/{lib/templates → templates/cli/utility}/notifier/campfire +2 -1
  149. data/templates/cli/utility/notifier/hipchat +15 -0
  150. data/{lib/templates → templates/cli/utility}/notifier/mail +6 -1
  151. data/{lib/templates → templates/cli/utility}/notifier/presently +1 -0
  152. data/templates/cli/utility/notifier/prowl +11 -0
  153. data/{lib/templates → templates/cli/utility}/notifier/twitter +2 -1
  154. data/templates/cli/utility/splitter +7 -0
  155. data/templates/cli/utility/storage/cloudfiles +12 -0
  156. data/{lib/templates → templates/cli/utility}/storage/dropbox +1 -1
  157. data/{lib/templates → templates/cli/utility}/storage/ftp +0 -0
  158. data/templates/cli/utility/storage/local +7 -0
  159. data/{lib/templates → templates/cli/utility}/storage/ninefold +1 -1
  160. data/templates/cli/utility/storage/rsync +11 -0
  161. data/{lib/templates → templates/cli/utility}/storage/s3 +0 -2
  162. data/templates/cli/utility/storage/scp +11 -0
  163. data/templates/cli/utility/storage/sftp +11 -0
  164. data/{lib/templates → templates/cli/utility}/syncer/rsync +1 -1
  165. data/{lib/templates → templates/cli/utility}/syncer/s3 +1 -1
  166. data/templates/general/links +11 -0
  167. data/templates/general/version.erb +2 -0
  168. data/templates/notifier/mail/failure.erb +9 -0
  169. data/templates/notifier/mail/success.erb +7 -0
  170. data/templates/notifier/mail/warning.erb +9 -0
  171. data/templates/storage/dropbox/authorization_url.erb +6 -0
  172. data/templates/storage/dropbox/authorized.erb +4 -0
  173. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  174. metadata +81 -45
  175. data/lib/backup/cli.rb +0 -110
  176. data/lib/backup/exception/command_failed.rb +0 -8
  177. data/lib/backup/exception/command_not_found.rb +0 -8
  178. data/lib/backup/notifier/binder.rb +0 -32
  179. data/lib/backup/notifier/templates/notify_failure.erb +0 -33
  180. data/lib/backup/notifier/templates/notify_success.erb +0 -16
  181. data/lib/templates/archive +0 -7
  182. data/lib/templates/encryptor/openssl +0 -8
  183. data/lib/templates/readme +0 -15
  184. data/lib/templates/storage/cloudfiles +0 -11
  185. data/lib/templates/storage/local +0 -7
  186. data/lib/templates/storage/rsync +0 -11
  187. data/lib/templates/storage/scp +0 -11
  188. data/lib/templates/storage/sftp +0 -11
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.dirname(__FILE__) + '/../spec_helper'
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
4
 
5
5
  ##
6
6
  # available S3 regions:
@@ -50,38 +50,46 @@ describe Backup::Storage::S3 do
50
50
  s3.keep.should == 500 # comes from the default configuration
51
51
  end
52
52
 
53
+ describe '#provider' do
54
+ it 'should be AWS' do
55
+ s3.provider == 'AWS'
56
+ end
57
+ end
58
+
59
+ describe '#perform' do
60
+ it 'should invoke transfer! and cycle!' do
61
+ s3.expects(:transfer!)
62
+ s3.expects(:cycle!)
63
+ s3.perform!
64
+ end
65
+ end
66
+
53
67
  describe '#connection' do
54
- it 'should establish a connection to Amazon S3 using the provided credentials' do
55
- Fog::Storage.expects(:new).with({
68
+ it 'should establish and re-use a connection to Amazon S3' do
69
+ Fog::Storage.expects(:new).once.with({
56
70
  :provider => 'AWS',
57
71
  :aws_access_key_id => 'my_access_key_id',
58
72
  :aws_secret_access_key => 'my_secret_access_key',
59
73
  :region => 'us-east-1'
60
- })
74
+ }).returns(true)
61
75
 
62
76
  s3.send(:connection)
63
- end
64
- end
65
-
66
- describe '#provider' do
67
- it 'should be AWS' do
68
- s3.provider == 'AWS'
77
+ s3.send(:connection)
69
78
  end
70
79
  end
71
80
 
72
81
  describe '#transfer!' do
73
82
  let(:connection) { mock('Fog::Storage') }
74
83
  before do
75
- Fog::Storage.stubs(:new).returns(connection)
84
+ Fog::Storage.expects(:new).once.returns(connection)
76
85
  end
77
86
 
78
87
  it 'should transfer the provided file to the bucket' do
79
88
  Backup::Model.new('blah', 'blah') {}
80
89
  file = mock("Backup::Storage::S3::File")
81
90
  File.expects(:open).with("#{File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER}")}.tar").returns(file)
82
- s3.expects(:remote_file).returns("#{ Backup::TIME }.#{ Backup::TRIGGER }.tar").twice
83
91
  connection.expects(:sync_clock)
84
- connection.expects(:put_object).with('my-bucket', "backups/myapp/#{ Backup::TIME }.#{ Backup::TRIGGER }.tar", file)
92
+ connection.expects(:put_object).with('my-bucket', "backups/myapp/#{ Backup::TIME }/#{ Backup::TRIGGER }.tar", file)
85
93
  s3.send(:transfer!)
86
94
  end
87
95
  end
@@ -89,23 +97,14 @@ describe Backup::Storage::S3 do
89
97
  describe '#remove!' do
90
98
  let(:connection) { mock('Fog::Storage') }
91
99
  before do
92
- Fog::Storage.stubs(:new).returns(connection)
100
+ Fog::Storage.expects(:new).once.returns(connection)
93
101
  end
94
102
 
95
103
  it 'should remove the file from the bucket' do
96
- s3.expects(:remote_file).returns("#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
97
104
  connection.expects(:sync_clock)
98
- connection.expects(:delete_object).with('my-bucket', "backups/myapp/#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
105
+ connection.expects(:delete_object).with('my-bucket', "backups/myapp/#{ Backup::TIME }/#{ Backup::TRIGGER }.tar")
99
106
  s3.send(:remove!)
100
107
  end
101
108
  end
102
109
 
103
- describe '#perform' do
104
- it 'should invoke transfer! and cycle!' do
105
- s3.expects(:transfer!)
106
- s3.expects(:cycle!)
107
- s3.perform!
108
- end
109
- end
110
-
111
110
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.dirname(__FILE__) + '/../spec_helper'
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
4
 
5
5
  describe Backup::Storage::SCP do
6
6
 
@@ -52,76 +52,151 @@ describe Backup::Storage::SCP do
52
52
  scp.path.should == 'backups'
53
53
  end
54
54
 
55
+ describe '#perform' do
56
+ it 'should invoke transfer! and cycle!' do
57
+ scp.expects(:transfer!)
58
+ scp.expects(:cycle!)
59
+ scp.perform!
60
+ end
61
+ end
62
+
55
63
  describe '#connection' do
56
- it 'should establish a connection to the remote server using the provided ip address and credentials' do
57
- Net::SSH.expects(:start).with('123.45.678.90', 'my_username', :password => 'my_password', :port => 22)
58
- scp.send(:connection)
64
+ it 'should establish a connection to the remote server' do
65
+ connection = mock
66
+ Net::SSH.expects(:start).with(
67
+ '123.45.678.90',
68
+ 'my_username',
69
+ :password => 'my_password',
70
+ :port => 22
71
+ ).yields(connection)
72
+
73
+ scp.send(:connection) do |ssh|
74
+ ssh.should be connection
75
+ end
59
76
  end
60
77
  end
61
78
 
62
79
  describe '#transfer!' do
63
- let(:connection) { mock('Net::SCP') }
64
80
 
65
81
  before do
66
- Net::SSH.stubs(:start).returns(connection)
67
- scp.stubs(:create_remote_directories!)
82
+ scp.stubs(:storage_name).returns('Storage::SCP')
68
83
  end
69
84
 
70
- it 'should transfer the provided file to the path' do
71
- Backup::Model.new('blah', 'blah') {}
72
- file = mock("Backup::Storage::SCP::File")
85
+ context 'when file chunking is not used' do
86
+ it 'should create remote paths and transfer using a single connection' do
87
+ ssh, ssh_scp = mock, mock
88
+ local_file = "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"
89
+ remote_file = "#{ Backup::TRIGGER }.tar"
73
90
 
74
- scp.expects(:create_remote_directories!)
91
+ scp.expects(:connection).yields(ssh)
92
+ scp.expects(:create_remote_directories).with(ssh)
75
93
 
76
- ssh_scp = mock('Net::SSH::SCP')
77
- connection.expects(:scp).returns(ssh_scp)
94
+ Backup::Logger.expects(:message).with(
95
+ "Storage::SCP started transferring '#{local_file}' to '#{scp.ip}'."
96
+ )
78
97
 
79
- ssh_scp.expects(:upload!).with(
80
- File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"),
81
- File.join('backups/myapp', "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
82
- )
98
+ ssh.expects(:scp).returns(ssh_scp)
99
+ ssh_scp.expects(:upload!).with(
100
+ File.join(Backup::TMP_PATH, local_file),
101
+ File.join('backups/myapp', Backup::TIME, remote_file)
102
+ )
83
103
 
84
- scp.send(:transfer!)
104
+ scp.send(:transfer!)
105
+ end
85
106
  end
86
- end
107
+
108
+ context 'when file chunking is used' do
109
+ it 'should transfer all the provided files using a single connection' do
110
+ s = sequence ''
111
+ ssh, ssh_scp = mock, mock
112
+
113
+ scp.expects(:connection).in_sequence(s).yields(ssh)
114
+ scp.expects(:create_remote_directories).in_sequence(s).with(ssh)
115
+
116
+ scp.expects(:files_to_transfer).in_sequence(s).multiple_yields(
117
+ ['local_file1', 'remote_file1'], ['local_file2', 'remote_file2']
118
+ )
119
+
120
+ Backup::Logger.expects(:message).in_sequence(s).with(
121
+ "Storage::SCP started transferring 'local_file1' to '#{scp.ip}'."
122
+ )
123
+ ssh.expects(:scp).in_sequence(s).returns(ssh_scp)
124
+ ssh_scp.expects(:upload!).in_sequence(s).with(
125
+ File.join(Backup::TMP_PATH, 'local_file1'),
126
+ File.join('backups/myapp', Backup::TIME, 'remote_file1')
127
+ )
128
+
129
+ Backup::Logger.expects(:message).in_sequence(s).with(
130
+ "Storage::SCP started transferring 'local_file2' to '#{scp.ip}'."
131
+ )
132
+ ssh.expects(:scp).in_sequence(s).returns(ssh_scp)
133
+ ssh_scp.expects(:upload!).in_sequence(s).with(
134
+ File.join(Backup::TMP_PATH, 'local_file2'),
135
+ File.join('backups/myapp', Backup::TIME, 'remote_file2')
136
+ )
137
+
138
+ scp.send(:transfer!)
139
+ end
140
+ end
141
+
142
+ end # describe '#transfer!'
87
143
 
88
144
  describe '#remove!' do
89
- let(:connection) { mock('Net::SCP') }
90
145
 
91
146
  before do
92
- Net::SSH.stubs(:start).returns(connection)
147
+ scp.stubs(:storage_name).returns('Storage::SCP')
93
148
  end
94
149
 
95
- it 'should remove the file from the remote server path' do
96
- connection.expects(:exec!).with("rm backups/myapp/#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
150
+ it 'should remove all remote files with a single logger call' do
151
+ ssh = mock
152
+
153
+ scp.expects(:transferred_files).multiple_yields(
154
+ ['local_file1', 'remote_file1'], ['local_file2', 'remote_file2']
155
+ )
156
+
157
+ Backup::Logger.expects(:message).with(
158
+ "Storage::SCP started removing 'local_file1' from '#{scp.ip}'.\n" +
159
+ "Storage::SCP started removing 'local_file2' from '#{scp.ip}'."
160
+ )
161
+
162
+ scp.expects(:connection).yields(ssh)
163
+ ssh.expects(:exec!).with("rm -r 'backups/myapp/#{ Backup::TIME }'")
164
+
97
165
  scp.send(:remove!)
98
166
  end
99
- end
100
167
 
101
- describe '#create_remote_directories!' do
102
- let(:connection) { mock('Net::SSH') }
168
+ it 'should raise an error if Net::SSH reports errors' do
169
+ ssh = mock
103
170
 
104
- before do
105
- Net::SSH.stubs(:start).returns(connection)
171
+ scp.expects(:transferred_files)
172
+ Backup::Logger.expects(:message)
173
+
174
+ scp.expects(:connection).yields(ssh)
175
+ ssh.expects(:exec!).yields('', :stderr, 'error message')
176
+
177
+ expect do
178
+ scp.send(:remove!)
179
+ end.to raise_error(
180
+ Backup::Errors::Storage::SCP::SSHError,
181
+ "Storage::SCP::SSHError: Net::SSH reported the following errors:\n" +
182
+ " error message"
183
+ )
106
184
  end
107
185
 
186
+ end # describe '#remove!'
187
+
188
+ describe '#create_remote_directories' do
108
189
  it 'should properly create remote directories one by one' do
190
+ ssh = mock
109
191
  scp.path = 'backups/some_other_folder/another_folder'
110
192
 
111
- connection.expects(:exec!).with("mkdir 'backups'")
112
- connection.expects(:exec!).with("mkdir 'backups/some_other_folder'")
113
- connection.expects(:exec!).with("mkdir 'backups/some_other_folder/another_folder'")
114
- connection.expects(:exec!).with("mkdir 'backups/some_other_folder/another_folder/myapp'")
193
+ ssh.expects(:exec!).with("mkdir 'backups'")
194
+ ssh.expects(:exec!).with("mkdir 'backups/some_other_folder'")
195
+ ssh.expects(:exec!).with("mkdir 'backups/some_other_folder/another_folder'")
196
+ ssh.expects(:exec!).with("mkdir 'backups/some_other_folder/another_folder/myapp'")
197
+ ssh.expects(:exec!).with("mkdir 'backups/some_other_folder/another_folder/myapp/#{ Backup::TIME }'")
115
198
 
116
- scp.send(:create_remote_directories!)
117
- end
118
- end
119
-
120
- describe '#perform' do
121
- it 'should invoke transfer! and cycle!' do
122
- scp.expects(:transfer!)
123
- scp.expects(:cycle!)
124
- scp.perform!
199
+ scp.send(:create_remote_directories, ssh)
125
200
  end
126
201
  end
127
202
 
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.dirname(__FILE__) + '/../spec_helper'
3
+ require File.expand_path('../../spec_helper.rb', __FILE__)
4
4
 
5
5
  describe Backup::Storage::SFTP do
6
6
 
@@ -52,72 +52,150 @@ describe Backup::Storage::SFTP do
52
52
  sftp.path.should == 'backups'
53
53
  end
54
54
 
55
- describe '#connection' do
56
- it 'should establish a connection to the remote server using the provided ip address and credentials' do
57
- Net::SFTP.expects(:start).with('123.45.678.90', 'my_username', :password => 'my_password', :port => 22)
58
- sftp.send(:connection)
55
+ describe '#perform' do
56
+ it 'should invoke transfer! and cycle!' do
57
+ sftp.expects(:transfer!)
58
+ sftp.expects(:cycle!)
59
+ sftp.perform!
59
60
  end
60
61
  end
61
62
 
63
+ describe '#connection' do
64
+ it 'should establish a connection to the remote server' do
65
+ connection = mock
66
+ Net::SFTP.expects(:start).with(
67
+ '123.45.678.90',
68
+ 'my_username',
69
+ :password => 'my_password',
70
+ :port => 22
71
+ ).yields(connection)
72
+
73
+ sftp.send(:connection) do |conn|
74
+ conn.should be connection
75
+ end
76
+ end
77
+ end # describe '#connection'
78
+
62
79
  describe '#transfer!' do
63
- let(:connection) { mock('Fog::Storage') }
80
+ let(:connection) { mock }
64
81
 
65
82
  before do
66
- Net::SFTP.stubs(:start).returns(connection)
67
- sftp.stubs(:create_remote_directories!)
83
+ sftp.stubs(:storage_name).returns('Storage::SFTP')
68
84
  end
69
85
 
70
- it 'should transfer the provided file to the path' do
71
- Backup::Model.new('blah', 'blah') {}
72
- file = mock("Backup::Storage::SFTP::File")
86
+ context 'when file chunking is not used' do
87
+ it 'should create remote paths and transfer using a single connection' do
88
+ local_file = "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"
89
+ remote_file = "#{ Backup::TRIGGER }.tar"
73
90
 
74
- sftp.expects(:create_remote_directories!)
75
- connection.expects(:upload!).with(
76
- File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"),
77
- File.join('backups/myapp', "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
78
- )
91
+ sftp.expects(:connection).yields(connection)
92
+ sftp.expects(:create_remote_directories).with(connection)
79
93
 
80
- sftp.send(:transfer!)
81
- end
82
- end
94
+ Backup::Logger.expects(:message).with(
95
+ "Storage::SFTP started transferring '#{local_file}' to '#{sftp.ip}'."
96
+ )
83
97
 
84
- describe '#remove!' do
85
- let(:connection) { mock('Net::SFTP') }
98
+ connection.expects(:upload!).with(
99
+ File.join(Backup::TMP_PATH, local_file),
100
+ File.join('backups/myapp', Backup::TIME, remote_file)
101
+ )
86
102
 
87
- before do
88
- Net::SFTP.stubs(:start).returns(connection)
103
+ sftp.send(:transfer!)
104
+ end
89
105
  end
90
106
 
91
- it 'should remove the file from the remote server path' do
92
- connection.expects(:remove!).with("backups/myapp/#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
93
- sftp.send(:remove!)
107
+ context 'when file chunking is used' do
108
+ it 'should transfer all the provided files using a single connection' do
109
+ s = sequence ''
110
+
111
+ sftp.expects(:connection).in_sequence(s).yields(connection)
112
+ sftp.expects(:create_remote_directories).in_sequence(s).with(connection)
113
+
114
+ sftp.expects(:files_to_transfer).in_sequence(s).multiple_yields(
115
+ ['local_file1', 'remote_file1'], ['local_file2', 'remote_file2']
116
+ )
117
+
118
+ Backup::Logger.expects(:message).in_sequence(s).with(
119
+ "Storage::SFTP started transferring 'local_file1' to '#{sftp.ip}'."
120
+ )
121
+ connection.expects(:upload!).in_sequence(s).with(
122
+ File.join(Backup::TMP_PATH, 'local_file1'),
123
+ File.join('backups/myapp', Backup::TIME, 'remote_file1')
124
+ )
125
+
126
+ Backup::Logger.expects(:message).in_sequence(s).with(
127
+ "Storage::SFTP started transferring 'local_file2' to '#{sftp.ip}'."
128
+ )
129
+ connection.expects(:upload!).in_sequence(s).with(
130
+ File.join(Backup::TMP_PATH, 'local_file2'),
131
+ File.join('backups/myapp', Backup::TIME, 'remote_file2')
132
+ )
133
+
134
+ sftp.send(:transfer!)
135
+ end
94
136
  end
95
- end
137
+ end # describe '#transfer'
138
+
139
+ describe '#remove!' do
140
+ it 'should remove all remote files with a single FTP connection' do
141
+ s = sequence ''
142
+ connection = mock
143
+ remote_path = "backups/myapp/#{ Backup::TIME }"
144
+ sftp.stubs(:storage_name).returns('Storage::SFTP')
96
145
 
97
- describe '#create_remote_directories!' do
98
- let(:connection) { mock('Net::SFTP') }
146
+ sftp.expects(:connection).in_sequence(s).yields(connection)
99
147
 
100
- before do
101
- Net::SFTP.stubs(:start).returns(connection)
102
- end
148
+ sftp.expects(:transferred_files).in_sequence(s).multiple_yields(
149
+ ['local_file1', 'remote_file1'], ['local_file2', 'remote_file2']
150
+ )
103
151
 
104
- it 'should properly create remote directories one by one' do
105
- sftp.path = 'backups/some_other_folder/another_folder'
152
+ Backup::Logger.expects(:message).in_sequence(s).with(
153
+ "Storage::SFTP started removing 'local_file1' from '#{sftp.ip}'."
154
+ )
155
+ connection.expects(:remove!).in_sequence(s).with(
156
+ File.join(remote_path, 'remote_file1')
157
+ )
106
158
 
107
- connection.expects(:mkdir!).with('backups')
108
- connection.expects(:mkdir!).with('backups/some_other_folder')
109
- connection.expects(:mkdir!).with('backups/some_other_folder/another_folder')
110
- connection.expects(:mkdir!).with('backups/some_other_folder/another_folder/myapp')
159
+ Backup::Logger.expects(:message).in_sequence(s).with(
160
+ "Storage::SFTP started removing 'local_file2' from '#{sftp.ip}'."
161
+ )
162
+ connection.expects(:remove!).in_sequence(s).with(
163
+ File.join(remote_path, 'remote_file2')
164
+ )
111
165
 
112
- sftp.send(:create_remote_directories!)
113
- end
114
- end
166
+ connection.expects(:rmdir!).in_sequence(s).with(remote_path)
115
167
 
116
- describe '#perform' do
117
- it 'should invoke transfer! and cycle!' do
118
- sftp.expects(:transfer!)
119
- sftp.expects(:cycle!)
120
- sftp.perform!
168
+ sftp.send(:remove!)
169
+ end
170
+ end # describe '#remove!'
171
+
172
+ describe '#create_remote_directories' do
173
+ let(:connection) { mock }
174
+
175
+ context 'while properly creating remote directories one by one' do
176
+ it 'should rescue any SFTP::StatusExceptions' do
177
+ s = sequence ''
178
+ sftp.path = '~/backups/some_other_folder/another_folder'
179
+ sftp_response = stub(:code => 11, :message => nil)
180
+ sftp_status_exception = Net::SFTP::StatusException.new(sftp_response)
181
+
182
+ connection.expects(:mkdir!).in_sequence(s).
183
+ with("~").raises(sftp_status_exception)
184
+ connection.expects(:mkdir!).in_sequence(s).
185
+ with("~/backups").raises(sftp_status_exception)
186
+ connection.expects(:mkdir!).in_sequence(s).
187
+ with("~/backups/some_other_folder")
188
+ connection.expects(:mkdir!).in_sequence(s).
189
+ with("~/backups/some_other_folder/another_folder")
190
+ connection.expects(:mkdir!).in_sequence(s).
191
+ with("~/backups/some_other_folder/another_folder/myapp")
192
+ connection.expects(:mkdir!).in_sequence(s).
193
+ with("~/backups/some_other_folder/another_folder/myapp/#{ Backup::TIME }")
194
+
195
+ expect do
196
+ sftp.send(:create_remote_directories, connection)
197
+ end.not_to raise_error
198
+ end
121
199
  end
122
200
  end
123
201