backup 3.0.19 → 3.0.20

Sign up to get free protection for your applications and to get access to all the features.
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