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
  describe Backup::Storage::Ninefold do
6
6
 
@@ -39,21 +39,30 @@ describe Backup::Storage::Ninefold do
39
39
  ninefold.keep.should == 500 # comes from the default configuration
40
40
  end
41
41
 
42
+ describe '#provider' do
43
+ it 'should be Ninefold' do
44
+ ninefold.provider.should == 'Ninefold'
45
+ end
46
+ end
47
+
48
+ describe '#perform' do
49
+ it 'should invoke transfer! and cycle!' do
50
+ ninefold.expects(:transfer!)
51
+ ninefold.expects(:cycle!)
52
+ ninefold.perform!
53
+ end
54
+ end
55
+
42
56
  describe '#connection' do
43
- it 'should establish a connection to Ninefold using the provided credentials' do
44
- Fog::Storage.expects(:new).with({
57
+ it 'should establish and re-use a connection to Ninefold' do
58
+ Fog::Storage.expects(:new).once.with({
45
59
  :provider => 'Ninefold',
46
60
  :ninefold_storage_token => 'my_storage_token',
47
61
  :ninefold_storage_secret => 'my_storage_secret'
48
- })
62
+ }).returns(true)
49
63
 
50
64
  ninefold.send(:connection)
51
- end
52
- end
53
-
54
- describe '#provider' do
55
- it 'should be Ninefold' do
56
- ninefold.provider.should == 'Ninefold'
65
+ ninefold.send(:connection)
57
66
  end
58
67
  end
59
68
 
@@ -64,7 +73,7 @@ describe Backup::Storage::Ninefold do
64
73
  let(:files) { mock('Fog::Storage::Ninefold::Files') }
65
74
 
66
75
  before do
67
- Fog::Storage.stubs(:new).returns(connection)
76
+ Fog::Storage.expects(:new).once.returns(connection)
68
77
  connection.stubs(:directories).returns(directories)
69
78
  directory.stubs(:files).returns(files)
70
79
  end
@@ -74,11 +83,10 @@ describe Backup::Storage::Ninefold do
74
83
  Backup::Model.new('blah', 'blah') {}
75
84
  file = mock("Backup::Storage::Ninefold::File")
76
85
  File.expects(:open).with("#{File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER}")}.tar").returns(file)
77
- ninefold.expects(:remote_file).returns("#{ Backup::TIME }.#{ Backup::TRIGGER }.tar").twice
78
86
 
79
- directories.expects(:get).with('backups/myapp').returns(directory)
87
+ directories.expects(:get).with("backups/myapp/#{ Backup::TIME }").returns(directory)
80
88
  files.expects(:create) do |options|
81
- options[:key].should == "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"
89
+ options[:key].should == "#{ Backup::TRIGGER }.tar"
82
90
  options[:body].should == file
83
91
  end
84
92
 
@@ -91,15 +99,14 @@ describe Backup::Storage::Ninefold do
91
99
  Backup::Model.new('blah', 'blah') {}
92
100
  file = mock("Backup::Storage::Ninefold::File")
93
101
  File.expects(:open).with("#{File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER}")}.tar").returns(file)
94
- ninefold.expects(:remote_file).returns("#{ Backup::TIME }.#{ Backup::TRIGGER }.tar").twice
95
102
 
96
- directories.expects(:get).with('backups/myapp').returns(nil)
103
+ directories.expects(:get).with("backups/myapp/#{ Backup::TIME }").returns(nil)
97
104
  directories.expects(:create) { |options|
98
- options[:key].should == 'backups/myapp'
105
+ options[:key].should == "backups/myapp/#{ Backup::TIME }"
99
106
  }.returns(directory)
100
107
 
101
108
  files.expects(:create) do |options|
102
- options[:key].should == "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"
109
+ options[:key].should == "#{ Backup::TRIGGER }.tar"
103
110
  options[:body].should == file
104
111
  end
105
112
 
@@ -116,27 +123,61 @@ describe Backup::Storage::Ninefold do
116
123
  let(:file) { mock('Fog::Storage::Ninefold::File') }
117
124
 
118
125
  before do
119
- Fog::Storage.stubs(:new).returns(connection)
126
+ Fog::Storage.expects(:new).once.returns(connection)
120
127
  connection.stubs(:directories).returns(directories)
121
128
  directory.stubs(:files).returns(files)
122
129
  end
123
130
 
124
131
  it 'should remove the file from the bucket' do
125
- ninefold.expects(:remote_file).returns("#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
126
-
127
- directories.expects(:get).with('backups/myapp').returns(directory)
128
- files.expects(:get).with("#{ Backup::TIME }.#{ Backup::TRIGGER }.tar").returns(file)
132
+ directories.expects(:get).
133
+ with("backups/myapp/#{ Backup::TIME }").
134
+ returns(directory)
135
+ files.expects(:get).
136
+ with("#{ Backup::TRIGGER }.tar").
137
+ returns(file)
129
138
  file.expects(:destroy)
139
+ directory.expects(:destroy)
130
140
 
131
141
  ninefold.send(:remove!)
132
142
  end
133
- end
134
143
 
135
- describe '#perform' do
136
- it 'should invoke transfer! and cycle!' do
137
- ninefold.expects(:transfer!)
138
- ninefold.expects(:cycle!)
139
- ninefold.perform!
144
+ it 'should raise an error if remote_path does not exist' do
145
+ directories.expects(:get).
146
+ with("backups/myapp/#{ Backup::TIME }").
147
+ returns(nil)
148
+ files.expects(:get).never
149
+ file.expects(:destroy).never
150
+ directory.expects(:destroy).never
151
+
152
+ expect do
153
+ ninefold.send(:remove!)
154
+ end.to raise_error(
155
+ Backup::Errors::Storage::Ninefold::NotFoundError,
156
+ "Storage::Ninefold::NotFoundError: " +
157
+ "Directory at 'backups/myapp/#{Backup::TIME}' not found"
158
+ )
159
+
160
+ end
161
+
162
+ it 'should raise an error if remote_file does not exist' do
163
+ directories.expects(:get).
164
+ with("backups/myapp/#{ Backup::TIME }").
165
+ returns(directory)
166
+ files.expects(:get).
167
+ with("#{ Backup::TRIGGER }.tar").
168
+ returns(nil)
169
+ file.expects(:destroy).never
170
+ directory.expects(:destroy).never
171
+
172
+ expect do
173
+ ninefold.send(:remove!)
174
+ end.to raise_error(
175
+ Backup::Errors::Storage::Ninefold::NotFoundError,
176
+ "Storage::Ninefold::NotFoundError: " +
177
+ "'#{Backup::TRIGGER}.tar' not found in 'backups/myapp/#{Backup::TIME}'"
178
+ )
179
+
140
180
  end
141
181
  end
182
+
142
183
  end
@@ -1,74 +1,74 @@
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::Object do
6
- let(:object) { Backup::Storage::Object.new(:s3) }
7
6
 
8
- it do
9
- object.storage_file.should == File.join(Backup::DATA_PATH, Backup::TRIGGER, 's3.yml')
7
+ describe '#initialize' do
8
+
9
+ it 'uses storage type only as YAML filename if no storage_id' do
10
+ [nil, '', ' '].each do |storage_id|
11
+ object = Backup::Storage::Object.new(:s3, storage_id)
12
+ object.storage_file.should == File.join(Backup::DATA_PATH, Backup::TRIGGER, 's3.yml')
13
+ end
14
+ end
15
+
16
+ it 'appends optional storage_id' do
17
+ object = Backup::Storage::Object.new(:s3, 'foo')
18
+ object.storage_file.should == File.join(Backup::DATA_PATH, Backup::TRIGGER, 's3-foo.yml')
19
+ end
20
+
21
+ it 'sanitizes user-defined storage_id for use as filename' do
22
+ [ ['Backup Server #1', 'Backup_Server__1'],
23
+ [' {Special} Storage ', '_Special__Storage'],
24
+ ['Cloud (!@$%^&*) #9', 'Cloud____________9'] ].each do |input, sanitized|
25
+ object = Backup::Storage::Object.new(:s3, input)
26
+ object.storage_file.should == File.join(Backup::DATA_PATH, Backup::TRIGGER, "s3-#{sanitized}.yml")
27
+ end
28
+ end
29
+
10
30
  end
11
31
 
12
32
  describe '#load' do
33
+ let(:storage_object) { Backup::Storage::Object.new(:s3, nil) }
34
+
13
35
  it 'should return an array with objects' do
36
+ loaded_objects = YAML.load([Backup::Storage::S3.new, Backup::Storage::S3.new].to_yaml)
37
+ sorted_objects = loaded_objects.sort {|a,b| b.time <=> a.time }
38
+
14
39
  File.expects(:exist?).returns(true)
15
40
  YAML.expects(:load_file).with(
16
41
  File.join(Backup::DATA_PATH, Backup::TRIGGER, 's3.yml')
17
- ).returns(YAML.load([Backup::Storage::S3.new, Backup::Storage::S3.new].to_yaml))
42
+ ).returns(loaded_objects)
18
43
 
19
- objects = object.load
44
+ objects = storage_object.load
20
45
  objects.should be_an(Array)
21
46
  objects.first.should be_an_instance_of(Backup::Storage::S3)
22
47
  end
23
48
 
24
- describe 'loading them sorted by time descending (newest backup is first in the array)' do
25
- it do
26
- obj_1 = Backup::Storage::S3.new; obj_1.time = '2007.00.00.00.00.00'
27
- obj_2 = Backup::Storage::S3.new; obj_2.time = '2009.00.00.00.00.00'
28
- obj_3 = Backup::Storage::S3.new; obj_3.time = '2011.00.00.00.00.00'
49
+ it 'should load them sorted by time descending (newest backup is first in the array)' do
50
+ obj_1 = Backup::Storage::S3.new; obj_1.time = '2007.00.00.00.00.00'
51
+ obj_2 = Backup::Storage::S3.new; obj_2.time = '2009.00.00.00.00.00'
52
+ obj_3 = Backup::Storage::S3.new; obj_3.time = '2011.00.00.00.00.00'
29
53
 
30
- File.expects(:exist?).returns(true)
31
- YAML.expects(:load_file).with(
32
- File.join(Backup::DATA_PATH, Backup::TRIGGER, 's3.yml')
33
- ).returns(YAML.load([obj_1, obj_2, obj_3].to_yaml))
54
+ File.stubs(:exist?).returns(true)
55
+ File.stubs(:zero?).returns(false)
34
56
 
35
- objects = object.load
36
- objects[0].time.should == '2011.00.00.00.00.00'
37
- objects[1].time.should == '2009.00.00.00.00.00'
38
- objects[2].time.should == '2007.00.00.00.00.00'
39
- end
40
-
41
- it do
42
- obj_3 = Backup::Storage::S3.new; obj_3.time = '2007.00.00.00.00.00'
43
- obj_2 = Backup::Storage::S3.new; obj_2.time = '2009.00.00.00.00.00'
44
- obj_1 = Backup::Storage::S3.new; obj_1.time = '2011.00.00.00.00.00'
45
-
46
- File.expects(:exist?).returns(true)
47
- YAML.expects(:load_file).with(
48
- File.join(Backup::DATA_PATH, Backup::TRIGGER, 's3.yml')
49
- ).returns(YAML.load([obj_1, obj_2, obj_3].to_yaml))
50
-
51
- objects = object.load
52
- objects[0].time.should == '2011.00.00.00.00.00'
53
- objects[1].time.should == '2009.00.00.00.00.00'
54
- objects[2].time.should == '2007.00.00.00.00.00'
55
- end
57
+ [obj_1, obj_2, obj_3].permutation.each do |perm|
58
+ loaded_objects = YAML.load(perm.to_yaml)
59
+ sorted_objects = loaded_objects.sort {|a,b| b.time <=> a.time }
56
60
 
57
- it do
58
- obj_3 = Backup::Storage::S3.new; obj_3.time = '2007.00.00.00.00.00'
59
- obj_1 = Backup::Storage::S3.new; obj_1.time = '2009.00.00.00.00.00'
60
- obj_2 = Backup::Storage::S3.new; obj_2.time = '2011.00.00.00.00.00'
61
-
62
- File.expects(:exist?).at_least_once.returns(true)
63
61
  YAML.expects(:load_file).with(
64
62
  File.join(Backup::DATA_PATH, Backup::TRIGGER, 's3.yml')
65
- ).returns(YAML.load([obj_1, obj_2, obj_3].to_yaml))
63
+ ).returns(loaded_objects)
66
64
 
67
- objects = object.load
65
+ objects = storage_object.load
68
66
  objects[0].time.should == '2011.00.00.00.00.00'
69
67
  objects[1].time.should == '2009.00.00.00.00.00'
70
68
  objects[2].time.should == '2007.00.00.00.00.00'
71
69
  end
72
70
  end
71
+
73
72
  end
73
+
74
74
  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::RSync do
6
6
 
@@ -19,13 +19,12 @@ describe Backup::Storage::RSync do
19
19
  end
20
20
 
21
21
  it 'should have defined the configuration properly' do
22
- rsync.username.should == 'my_username'
23
- rsync.send(:password).should =~ /backup-rsync-password/
24
- rsync.ip.should == '123.45.678.90'
25
- rsync.port.should == "-e 'ssh -p 22'"
26
- rsync.path.should == 'backups/'
27
-
28
- File.read(rsync.instance_variable_get('@password_file').path).should == 'my_password'
22
+ rsync.username.should == 'my_username'
23
+ rsync.password.should == 'my_password'
24
+ rsync.ip.should == '123.45.678.90'
25
+ rsync.port.should == 22
26
+ rsync.path.should == 'backups/'
27
+ rsync.send(:rsync_port).should == "-e 'ssh -p 22'"
29
28
  end
30
29
 
31
30
  it 'should use the defaults if a particular attribute has not been defined' do
@@ -40,101 +39,225 @@ describe Backup::Storage::RSync do
40
39
  rsync.ip = '123.45.678.90'
41
40
  end
42
41
 
43
- rsync.username.should == 'my_default_username'
44
- rsync.send(:password).should =~ /backup-rsync-password/
45
- rsync.ip.should == '123.45.678.90'
46
- rsync.port.should == "-e 'ssh -p 22'"
47
-
48
- File.read(rsync.instance_variable_get('@password_file').path).should == 'my_password'
42
+ rsync.username.should == 'my_default_username'
43
+ rsync.password.should == 'my_password'
44
+ rsync.ip.should == '123.45.678.90'
45
+ rsync.port.should == 22
46
+ rsync.send(:rsync_port).should == "-e 'ssh -p 22'"
49
47
  end
50
48
 
51
49
  it 'should have its own defaults' do
52
50
  rsync = Backup::Storage::RSync.new
53
- rsync.port.should == "-e 'ssh -p 22'"
51
+ rsync.port.should == 22
54
52
  rsync.path.should == 'backups'
55
53
  rsync.local.should == false
54
+ rsync.send(:rsync_port).should == "-e 'ssh -p 22'"
55
+ end
56
+
57
+ describe '#perform' do
58
+ it 'should invoke transfer!' do
59
+ s = sequence ''
60
+ rsync.expects(:write_password_file!).in_sequence(s)
61
+ rsync.expects(:transfer!).in_sequence(s)
62
+ rsync.expects(:remove_password_file!).in_sequence(s)
63
+
64
+ rsync.perform!
65
+ end
66
+
67
+ it 'should ensure any password file is removed' do
68
+ s = sequence ''
69
+ rsync.expects(:write_password_file!).in_sequence(s)
70
+ rsync.expects(:transfer!).in_sequence(s).raises(Exception)
71
+ rsync.expects(:remove_password_file!).in_sequence(s)
72
+
73
+ expect do
74
+ rsync.perform!
75
+ end.to raise_error
76
+ end
56
77
  end
57
78
 
58
79
  describe '#connection' do
59
- it 'should establish a connection to the remote server using the provided ip address and credentials' do
60
- Net::SSH.expects(:start).with('123.45.678.90', 'my_username', :password => 'my_password', :port => 22)
61
- rsync.send(:connection)
80
+ it 'should establish a connection to the remote server' do
81
+ connection = mock
82
+ Net::SSH.expects(:start).with(
83
+ '123.45.678.90',
84
+ 'my_username',
85
+ :password => 'my_password',
86
+ :port => 22
87
+ ).yields(connection)
88
+
89
+ rsync.send(:connection) do |ssh|
90
+ ssh.should be connection
91
+ end
62
92
  end
63
93
  end
64
94
 
65
95
  describe '#transfer!' do
66
- let(:connection) { mock('Net::SCP') }
96
+ let(:local_file) { File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }
97
+ let(:remote_file) { "#{ Backup::TRIGGER }/#{ Backup::TRIGGER }.tar" }
98
+ let(:pwdfile) { stub(:path => 'path/to/password/file') }
67
99
 
68
100
  before do
69
- Net::SSH.stubs(:start).returns(connection)
70
- rsync.stubs(:create_remote_directories!)
101
+ rsync.expects(:create_remote_directories!)
102
+ rsync.expects(:utility).returns('rsync')
103
+ Backup::Logger.expects(:message).with(
104
+ "Storage::RSync started transferring '#{rsync.filename}' to '#{rsync.ip}'."
105
+ )
71
106
  end
72
107
 
73
- it 'should transfer the provided file to the path' do
74
- Backup::Model.new('blah', 'blah') {}
75
- file = mock("Backup::Storage::RSync::File")
108
+ context 'when performing a remote transfer' do
109
+ context 'when a password is set' do
110
+ before do
111
+ rsync.stubs(:write_password_file!)
112
+ rsync.instance_variable_set(:@password_file, pwdfile)
113
+ end
76
114
 
77
- rsync.expects(:create_remote_directories!)
78
- rsync.expects(:utility).returns('rsync')
79
- rsync.expects(:run).with("rsync -z -e 'ssh -p 22' --password-file='#{rsync.instance_variable_get('@password_file').path}' '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }' 'my_username@123.45.678.90:backups/#{ Backup::TRIGGER }/#{ Backup::TRIGGER }.tar'")
115
+ it 'should transfer the provided file to the path' do
116
+ rsync.expects(:run).with(
117
+ "rsync -z -e 'ssh -p 22' --password-file='path/to/password/file' " +
118
+ "'#{local_file}' " +
119
+ "'my_username@123.45.678.90:backups/#{remote_file}'"
120
+ )
80
121
 
81
- rsync.send(:transfer!)
82
- end
122
+ rsync.send(:transfer!)
123
+ end
124
+ end
83
125
 
84
- it 'should not provide the --password-file option' do
85
- Backup::Model.new('blah', 'blah') {}
86
- file = mock("Backup::Storage::RSync::File")
126
+ context 'when no password is set' do
127
+ before { rsync.password = nil }
87
128
 
88
- rsync.password = nil
89
- rsync.expects(:create_remote_directories!)
90
- rsync.expects(:utility).returns('rsync')
91
- rsync.expects(:run).with("rsync -z -e 'ssh -p 22' '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }' 'my_username@123.45.678.90:backups/#{ Backup::TRIGGER }/#{ Backup::TRIGGER }.tar'")
129
+ it 'should not provide the --password-file option' do
130
+ rsync.expects(:run).with(
131
+ "rsync -z -e 'ssh -p 22' " +
132
+ "'#{local_file}' " +
133
+ "'my_username@123.45.678.90:backups/#{remote_file}'"
134
+ )
92
135
 
93
- rsync.send(:transfer!)
94
- end
136
+ rsync.send(:transfer!)
137
+ end
138
+ end
139
+
140
+ end # context 'when performing a remote transfer'
141
+
142
+ context 'when performing a local transfer' do
143
+ before { rsync.local = true }
144
+
145
+ it 'should save a local copy of backups' do
146
+ rsync.expects(:run).with(
147
+ "rsync '#{local_file}' 'backups/#{remote_file}'"
148
+ )
149
+ rsync.send(:transfer!)
150
+ end
151
+ end # context 'when performing a local transfer'
95
152
  end
96
153
 
97
154
  describe '#remove!' do
98
- let(:connection) { mock('Net::SCP') }
155
+ it 'should return nil' do
156
+ rsync.send(:remove!).should == nil
157
+ end
158
+ end
99
159
 
100
- before do
101
- Net::SSH.stubs(:start).returns(connection)
160
+ describe '#create_remote_directories!' do
161
+
162
+ context 'when rsync.local is false' do
163
+ it 'should create directories on the remote server' do
164
+ ssh = mock
165
+ rsync.expects(:mkdir).never
166
+ rsync.expects(:connection).yields(ssh)
167
+ ssh.expects(:exec!).with("mkdir -p '#{rsync.remote_path}'")
168
+
169
+ rsync.send(:create_remote_directories!)
170
+ end
102
171
  end
103
172
 
104
- it 'should remove the file from the remote server path' do
105
- connection.expects(:exec!).with("rm backups/myapp/#{ Backup::TIME }.#{ Backup::TRIGGER }.tar")
106
- rsync.send(:remove!)
173
+ context 'when rsync.local is true' do
174
+ before { rsync.local = true }
175
+ it 'should create directories locally' do
176
+ rsync.expects(:mkdir).with(rsync.remote_path)
177
+ rsync.expects(:connection).never
178
+
179
+ rsync.send(:create_remote_directories!)
180
+ end
107
181
  end
182
+
108
183
  end
109
184
 
110
- describe '#create_remote_directories!' do
111
- let(:connection) { mock('Net::SSH') }
185
+ describe '#write_password_file!' do
112
186
 
113
187
  before do
114
- Net::SSH.stubs(:start).returns(connection)
188
+ rsync.instance_variable_defined?(:@password_file).should be_false
115
189
  end
116
190
 
117
- it 'should properly create remote directories one by one' do
118
- rsync.path = 'backups/some_other_folder/another_folder'
119
- connection.expects(:exec!).with("mkdir -p 'backups/some_other_folder/another_folder/myapp'")
120
- rsync.send(:create_remote_directories!)
191
+ context 'when a password is set' do
192
+ it 'should write the password file' do
193
+ rsync.send(:write_password_file!)
194
+ password_file = rsync.instance_variable_get(:@password_file)
195
+ password_file.should respond_to(:path)
196
+ File.read(password_file.path).should == 'my_password'
197
+ end
121
198
  end
122
- end
123
199
 
124
- describe '#perform' do
125
- it 'should invoke transfer!' do
126
- rsync.expects(:transfer!)
127
- rsync.perform!
200
+ context 'when a password is not set' do
201
+ before { rsync.password = nil }
202
+ it 'should return nil' do
203
+ rsync.send(:write_password_file!).should be_nil
204
+ end
205
+ end
206
+
207
+ end # describe '#write_password_file!'
208
+
209
+ describe '#remove_password_file!' do
210
+ let(:pwdfile) { mock }
211
+
212
+ context 'when @password_file is set' do
213
+ before do
214
+ rsync.instance_variable_set(:@password_file, pwdfile)
215
+ end
216
+
217
+ it 'should remove the password file' do
218
+ pwdfile.expects(:delete)
219
+ rsync.send(:remove_password_file!)
220
+ end
221
+ end
222
+
223
+ context 'when @password_file is not set' do
224
+ it 'should return nil' do
225
+ rsync.send(:remove_password_file!).should be_nil
226
+ end
227
+ end
228
+
229
+ end # describe '#remove_password_file!'
230
+
231
+ describe '#rsync_password_file' do
232
+ let(:pwdfile) { stub(:path => 'path/to/password/file') }
233
+
234
+ context 'when @password_file is set' do
235
+ before do
236
+ rsync.instance_variable_set(:@password_file, pwdfile)
237
+ end
238
+
239
+ it 'should return the password file string for the rsync command' do
240
+ rsync.send(:rsync_password_file).should == "--password-file='path/to/password/file'"
241
+ end
242
+ end
243
+
244
+ context 'when a password is not set' do
245
+ it 'should return nil' do
246
+ rsync.send(:rsync_password_file).should be_nil
247
+ end
248
+ end
249
+
250
+ end # describe '#password_file'
251
+
252
+ describe '#rsync_port' do
253
+ it 'should return the port string for the rsync command' do
254
+ rsync.send(:rsync_port).should == "-e 'ssh -p 22'"
128
255
  end
129
256
  end
130
257
 
131
- describe '#local backups' do
132
- it 'should save a local copy of backups' do
133
- rsync.expects(:create_remote_directories!)
134
- rsync.local = true
135
- rsync.expects(:utility).returns('rsync')
136
- rsync.expects(:run).with("rsync '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }' 'backups/#{ Backup::TRIGGER }/#{ Backup::TIME }.#{ Backup::TRIGGER }.tar'")
137
- rsync.send(:transfer!)
258
+ describe '#rsync_options' do
259
+ it 'should return the options string for the rsync command' do
260
+ rsync.send(:rsync_options).should == "-z"
138
261
  end
139
262
  end
140
263