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
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../spec_helper.rb', __FILE__)
4
+
5
+ describe Backup::Package do
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:package) { Backup::Package.new(model) }
8
+
9
+ before do
10
+ model.instance_variable_set(:@time, 'model_time')
11
+ end
12
+
13
+ describe '#initialize' do
14
+ it 'should set all variables' do
15
+ package.time.should == 'model_time'
16
+ package.trigger.should == 'test_trigger'
17
+ package.extension.should == 'tar'
18
+ package.chunk_suffixes.should == []
19
+ package.version.should == Backup::Version.current
20
+ end
21
+ end
22
+
23
+ describe '#filenames' do
24
+ context 'when the package files were not split' do
25
+ it 'should return an array with the single package filename' do
26
+ package.filenames.should == ['model_time.test_trigger.tar']
27
+ end
28
+
29
+ it 'should reflect changes in the extension' do
30
+ package.extension << '.enc'
31
+ package.filenames.should == ['model_time.test_trigger.tar.enc']
32
+ end
33
+ end
34
+
35
+ context 'when the package files were split' do
36
+ before { package.chunk_suffixes = ['aa', 'ab'] }
37
+ it 'should return an array of the package filenames' do
38
+ package.filenames.should == ['model_time.test_trigger.tar-aa',
39
+ 'model_time.test_trigger.tar-ab']
40
+ end
41
+
42
+ it 'should reflect changes in the extension' do
43
+ package.extension << '.enc'
44
+ package.filenames.should == ['model_time.test_trigger.tar.enc-aa',
45
+ 'model_time.test_trigger.tar.enc-ab']
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#basename' do
51
+ it 'should return the base filename for the package' do
52
+ package.basename.should == 'model_time.test_trigger.tar'
53
+ end
54
+
55
+ it 'should reflect changes in the extension' do
56
+ package.extension << '.enc'
57
+ package.basename.should == 'model_time.test_trigger.tar.enc'
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,154 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../spec_helper.rb', __FILE__)
4
+
5
+ describe 'Backup::Packager' do
6
+ let(:packager) { Backup::Packager }
7
+
8
+ describe '#package!' do
9
+ let(:model) { mock }
10
+ let(:package) { mock }
11
+ let(:encryptor) { mock }
12
+ let(:splitter) { mock }
13
+ let(:procedure) { mock }
14
+ let(:s) { sequence '' }
15
+
16
+ it 'should setup variables and perform packaging procedures' do
17
+ model.expects(:package).in_sequence(s).returns(package)
18
+ model.expects(:encryptor).in_sequence(s).returns(encryptor)
19
+ model.expects(:splitter).in_sequence(s).returns(splitter)
20
+
21
+ Backup::Logger.expects(:message).in_sequence(s).with(
22
+ 'Packaging the backup files...'
23
+ )
24
+ packager.expects(:procedure).in_sequence(s).returns(procedure)
25
+ procedure.expects(:call).in_sequence(s)
26
+ Backup::Logger.expects(:message).in_sequence(s).with(
27
+ 'Packaging Complete!'
28
+ )
29
+
30
+ packager.package!(model)
31
+
32
+ packager.instance_variable_get(:@package).should be(package)
33
+ packager.instance_variable_get(:@encryptor).should be(encryptor)
34
+ packager.instance_variable_get(:@splitter).should be(splitter)
35
+ end
36
+ end # describe '#package!'
37
+
38
+ describe '#procedure' do
39
+
40
+ module Fake
41
+ def self.stack_trace
42
+ @stack ||= []
43
+ end
44
+ class Encryptor
45
+ def encrypt_with
46
+ Fake.stack_trace << :encryptor_before
47
+ yield 'encryption_command', '.enc'
48
+ Fake.stack_trace << :encryptor_after
49
+ end
50
+ end
51
+ class Splitter
52
+ def split_with
53
+ Fake.stack_trace << :splitter_before
54
+ yield 'splitter_command'
55
+ Fake.stack_trace << :splitter_after
56
+ end
57
+ end
58
+ class Package
59
+ attr_accessor :trigger, :extension
60
+ def basename
61
+ 'base_filename.' + extension
62
+ end
63
+ end
64
+ end
65
+
66
+ let(:package) { Fake::Package.new }
67
+ let(:encryptor) { Fake::Encryptor.new }
68
+ let(:splitter) { Fake::Splitter.new }
69
+
70
+ before do
71
+ Fake.stack_trace.clear
72
+ packager.expects(:utility).with(:tar).returns('tar')
73
+ packager.instance_variable_set(:@package, package)
74
+ package.trigger = 'model_trigger'
75
+ package.extension = 'tar'
76
+ end
77
+
78
+ context 'when no encryptor or splitter are defined' do
79
+ it 'should package the backup without encryption into a single file' do
80
+ packager.instance_variable_set(:@encryptor, nil)
81
+ packager.instance_variable_set(:@splitter, nil)
82
+
83
+ packager.expects(:run).with(
84
+ "tar -cf - -C '#{ Backup::Config.tmp_path }' 'model_trigger'" +
85
+ " > #{ File.join(Backup::Config.tmp_path, 'base_filename.tar') }"
86
+ )
87
+ packager.send(:procedure).call
88
+ end
89
+ end
90
+
91
+ context 'when only an encryptor is configured' do
92
+ it 'should package the backup with encryption' do
93
+ packager.instance_variable_set(:@encryptor, encryptor)
94
+ packager.instance_variable_set(:@splitter, nil)
95
+
96
+ packager.expects(:run).with do |command|
97
+ Fake.stack_trace << :command_executed
98
+ command.should ==
99
+ "tar -cf - -C '#{ Backup::Config.tmp_path }' 'model_trigger'" +
100
+ " | encryption_command" +
101
+ " > #{ File.join(Backup::Config.tmp_path, 'base_filename.tar.enc') }"
102
+ end
103
+ packager.send(:procedure).call
104
+
105
+ Fake.stack_trace.should == [
106
+ :encryptor_before, :command_executed, :encryptor_after
107
+ ]
108
+ end
109
+ end
110
+
111
+ context 'when only a splitter is configured' do
112
+ it 'should package the backup without encryption through the splitter' do
113
+ packager.instance_variable_set(:@encryptor, nil)
114
+ packager.instance_variable_set(:@splitter, splitter)
115
+
116
+ packager.expects(:run).with do |command|
117
+ Fake.stack_trace << :command_executed
118
+ command.should ==
119
+ "tar -cf - -C '#{ Backup::Config.tmp_path }' 'model_trigger'" +
120
+ " | splitter_command"
121
+ end
122
+ packager.send(:procedure).call
123
+
124
+ Fake.stack_trace.should == [
125
+ :splitter_before, :command_executed, :splitter_after
126
+ ]
127
+ end
128
+ end
129
+
130
+ context 'when both an encryptor and a splitter are configured' do
131
+ it 'should package the backup with encryption through the splitter' do
132
+ packager.instance_variable_set(:@encryptor, encryptor)
133
+ packager.instance_variable_set(:@splitter, splitter)
134
+
135
+ packager.expects(:run).with do |command|
136
+ Fake.stack_trace << :command_executed
137
+ command.should ==
138
+ "tar -cf - -C '#{ Backup::Config.tmp_path }' 'model_trigger'" +
139
+ " | encryption_command | splitter_command"
140
+ end
141
+ packager.send(:procedure).call
142
+
143
+ Fake.stack_trace.should == [
144
+ :encryptor_before, :splitter_before,
145
+ :command_executed,
146
+ :splitter_after, :encryptor_after
147
+ ]
148
+ package.extension.should == 'tar.enc'
149
+ end
150
+ end
151
+
152
+ end # describe '#procedure'
153
+
154
+ end
@@ -9,28 +9,51 @@ require 'bundler/setup'
9
9
  # Load Backup
10
10
  require 'backup'
11
11
 
12
- ##
13
- # Use Mocha to mock with RSpec
14
- require 'rspec'
12
+ require 'timecop'
13
+
14
+ module Backup::ExampleHelpers
15
+ # ripped from MiniTest :)
16
+ # RSpec doesn't have a method for this? Am I missing something?
17
+ def capture_io
18
+ require 'stringio'
19
+
20
+ orig_stdout, orig_stderr = $stdout, $stderr
21
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
22
+ $stdout, $stderr = captured_stdout, captured_stderr
23
+
24
+ yield
25
+
26
+ return captured_stdout.string, captured_stderr.string
27
+ ensure
28
+ $stdout = orig_stdout
29
+ $stderr = orig_stderr
30
+ end
31
+ end
32
+
33
+ require 'rspec/autorun'
15
34
  RSpec.configure do |config|
35
+ ##
36
+ # Use Mocha to mock with RSpec
16
37
  config.mock_with :mocha
38
+
39
+ ##
40
+ # Example Helpers
41
+ config.include Backup::ExampleHelpers
42
+
43
+ ##
44
+ # Actions to perform before each example
17
45
  config.before(:each) do
18
- FileUtils.stubs(:mkdir_p)
46
+ FileUtils.collect_method(:noop).each do |method|
47
+ FileUtils.stubs(method).raises("Unexpected call to FileUtils.#{method}")
48
+ end
49
+ Open4.stubs(:popen4).raises('Unexpected call to CLI::Helpers.run()')
50
+
19
51
  [:message, :error, :warn, :normal, :silent].each do |message_type|
20
52
  Backup::Logger.stubs(message_type)
21
53
  end
22
- Backup::Model.extension = 'tar'
23
54
  end
24
55
  end
25
56
 
26
- Backup.send(:remove_const, :TRIGGER) if defined? Backup::TRIGGER
27
- Backup.send(:remove_const, :TIME) if defined? Backup::TIME
28
-
29
- module Backup
30
- TRIGGER = 'myapp'
31
- TIME = Time.now.strftime("%Y.%m.%d.%H.%M.%S")
32
- end
33
-
34
57
  unless @_put_ruby_version
35
58
  puts @_put_ruby_version = "\n\nRuby version: #{RUBY_DESCRIPTION}\n\n"
36
59
  end
@@ -3,69 +3,118 @@
3
3
  require File.expand_path('../spec_helper.rb', __FILE__)
4
4
 
5
5
  describe Backup::Splitter do
6
- let(:model) { mock("Backup::Model") }
7
- let(:splitter) { Backup::Splitter.new(model) }
8
-
9
- before do
10
- splitter.stubs(:chunks).returns([
11
- "/some/file.tar.gz.enc-ad",
12
- "/some/file.tar.gz.enc-ac",
13
- "/some/file.tar.gz.enc-ab",
14
- "/some/file.tar.gz.enc-aa",
15
- ])
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:splitter) { Backup::Splitter.new(model, 250) }
8
+ let(:package) { mock }
9
+
10
+ describe '#initialize' do
11
+ it 'should set instance variables' do
12
+ splitter.instance_variable_get(:@model).should be(model)
13
+ splitter.instance_variable_get(:@chunk_size).should be(250)
14
+ end
16
15
  end
17
16
 
18
- describe "#chunk_suffixes" do
19
- it "should return an array of chunk suffixes (ordered in alphabetical order)" do
20
- splitter.send(:chunk_suffixes).should == ["aa", "ab", "ac", "ad"]
17
+ describe '#split_with' do
18
+ it 'should yield the split command, performing before/after methods' do
19
+ s = sequence ''
20
+ given_block = mock
21
+ block = lambda {|arg| given_block.got(arg) }
22
+ splitter.instance_variable_set(:@split_command, 'split command')
23
+
24
+ splitter.expects(:before_packaging).in_sequence(s)
25
+ given_block.expects(:got).in_sequence(s).with('split command')
26
+ splitter.expects(:after_packaging).in_sequence(s)
27
+
28
+ splitter.split_with(&block)
21
29
  end
22
30
  end
23
31
 
24
- describe "#bytes_representation_of" do
25
- it "should convert megabytes to bytes" do
26
- splitter.send(:bytes_representation_of, 50).should == 52428800
32
+ # Note: using a 'M' suffix for the byte size is not OSX compatible
33
+ describe '#before_packaging' do
34
+ before do
35
+ model.instance_variable_set(:@package, package)
36
+ splitter.expects(:utility).with(:split).returns('split')
37
+ package.expects(:basename).returns('base_filename')
38
+ end
39
+
40
+ it 'should set @package and @split_command' do
41
+ Backup::Logger.expects(:message).with(
42
+ 'Splitter configured with a chunk size of 250MB.'
43
+ )
44
+ splitter.send(:before_packaging)
45
+
46
+ splitter.instance_variable_get(:@package).should be(package)
47
+
48
+ split_suffix = File.join(Backup::Config.tmp_path, 'base_filename-')
49
+ splitter.instance_variable_get(:@split_command).should ==
50
+ "split -b 250m - '#{ split_suffix }'"
27
51
  end
28
52
  end
29
53
 
30
- describe "#split!" do
54
+ describe '#after_packaging' do
31
55
  before do
32
- model.stubs(:file).returns("/some/file.tar.gz.enc")
56
+ splitter.instance_variable_set(:@package, package)
33
57
  end
34
58
 
35
- [nil, true, false, "123", :sym].each do |value|
36
- it "should not split: chunk_size must be an integer" do
37
- model.stubs(:chunk_size).returns(value)
59
+ context 'when splitting occurred during packaging' do
60
+ before do
61
+ splitter.expects(:chunk_suffixes).returns(['aa', 'ab'])
62
+ end
38
63
 
39
- File.expects(:size).never
40
- splitter.expects(:bytes_representation_of).never
41
- splitter.expects(:run).never
42
- splitter.split!
64
+ it 'should set the chunk_suffixes for the package' do
65
+ package.expects(:chunk_suffixes=).with(['aa', 'ab'])
66
+ splitter.send(:after_packaging)
43
67
  end
44
68
  end
45
69
 
46
- it "should not split: chunk size is 300mb, file is 300mb" do
47
- File.expects(:size).with(model.file).returns(52428800) # 300mb
48
- model.stubs(:chunk_size).returns(300)
70
+ context 'when splitting did not occur during packaging' do
71
+ before do
72
+ splitter.expects(:chunk_suffixes).returns(['aa'])
73
+ package.expects(:basename).twice.returns('base_filename')
74
+ end
49
75
 
50
- splitter.expects(:run).never
51
- splitter.split!
76
+ it 'should remove the suffix from the only package file' do
77
+ package.expects(:chunk_suffixes=).never
78
+ FileUtils.expects(:mv).with(
79
+ File.join(Backup::Config.tmp_path, 'base_filename-aa'),
80
+ File.join(Backup::Config.tmp_path, 'base_filename')
81
+ )
82
+ splitter.send(:after_packaging)
83
+ end
84
+ end
85
+ end # describe '#after_packaging'
86
+
87
+ describe '#chunk_suffixes' do
88
+ before do
89
+ splitter.expects(:chunks).returns(
90
+ ['/path/to/file.tar-aa', '/path/to/file.tar-ab']
91
+ )
52
92
  end
53
93
 
54
- it "should not split: chunk size is 300mb, file size is 200mb" do
55
- File.expects(:size).with(model.file).returns(209715200) # 200mb
56
- model.stubs(:chunk_size).returns(300)
94
+ it 'should return an array of chunk suffixes' do
95
+ splitter.send(:chunk_suffixes).should == ['aa', 'ab']
96
+ end
97
+ end
57
98
 
58
- splitter.expects(:run).never
59
- splitter.split!
99
+ describe '#chunks' do
100
+ before do
101
+ splitter.instance_variable_set(:@package, package)
102
+ package.expects(:basename).returns('base_filename')
103
+ FileUtils.unstub(:touch)
60
104
  end
61
105
 
62
- it "should split: chunk size is 300mb, file size is 400mb" do
63
- File.expects(:size).with(model.file).returns(419430400) # 400mb
64
- model.stubs(:chunk_size).returns(300)
106
+ it 'should return a sorted array of chunked file paths' do
107
+ Dir.mktmpdir do |dir|
108
+ Backup::Config.expects(:tmp_path).returns(dir)
109
+ FileUtils.touch(File.join(dir, 'base_filename-aa'))
110
+ FileUtils.touch(File.join(dir, 'base_filename-ab'))
65
111
 
66
- splitter.expects(:utility).with(:split).returns("split")
67
- splitter.expects(:run).with("split -b 300m '/some/file.tar.gz.enc' '/some/file.tar.gz.enc-'")
68
- splitter.split!
112
+ splitter.send(:chunks).should == [
113
+ File.join(dir, 'base_filename-aa'),
114
+ File.join(dir, 'base_filename-ab')
115
+ ]
116
+ end
69
117
  end
70
118
  end
119
+
71
120
  end
@@ -3,288 +3,144 @@
3
3
  require File.expand_path('../../spec_helper.rb', __FILE__)
4
4
 
5
5
  describe Backup::Storage::Base do
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:package) { mock }
8
+ let(:base) { Backup::Storage::Base.new(model) }
6
9
 
7
10
  describe '#initialize' do
8
-
9
- after do
10
- Backup::Configuration::Storage::Base.clear_defaults!
11
- end
12
-
13
- it 'should create a new storage object with default values' do
14
- base = Backup::Storage::Base.new
11
+ it 'should set instance variables' do
12
+ base.instance_variable_get(:@model).should be(model)
13
+ base.instance_variable_defined?(:@storage_id).should be_true
14
+ base.instance_variable_get(:@storage_id).should be_nil
15
15
  base.keep.should be_nil
16
- base.time.should == Backup::TIME
17
16
  end
18
17
 
19
- it 'should set configured defaults' do
20
- Backup::Configuration::Storage::Base.defaults do |base|
21
- base.keep = 5
18
+ context 'when given a storage_id' do
19
+ it 'should set @storage_id' do
20
+ base = Backup::Storage::Base.new(model, 'my storage id')
21
+ base.instance_variable_get(:@storage_id).should == 'my storage id'
22
22
  end
23
-
24
- base = Backup::Storage::Base.new
25
- base.keep.should == 5
26
- base.time.should == Backup::TIME
27
23
  end
28
24
 
29
- it 'should override the configuration defaults with the configure block' do
30
- Backup::Configuration::Storage::Base.defaults do |base|
31
- base.keep = 5
32
- end
33
-
34
- base = Backup::Storage::Base.new do |base|
35
- base.keep = 10
25
+ context 'when configuration defaults are set' do
26
+ after { Backup::Configuration::Storage::Base.clear_defaults! }
27
+ it 'should use the defaults' do
28
+ Backup::Configuration::Storage::Base.defaults do |base|
29
+ base.keep = 5
30
+ end
31
+ base = Backup::Storage::Base.new(model)
32
+ base.keep.should be(5)
36
33
  end
37
- base.keep.should == 10
38
- base.time.should == Backup::TIME
39
34
  end
35
+ end # describe '#initialize'
40
36
 
41
- it 'should store the configuration block' do
42
- config_block = lambda {|base| base.keep = 10 }
43
- base = Backup::Storage::Base.new(&config_block)
37
+ describe '#perform!' do
38
+ before do
39
+ model.instance_variable_set(:@package, package)
40
+ end
44
41
 
45
- base.keep.should == 10
46
- base.configure_block.should be config_block
42
+ it 'should call #transfer!, then #cycle!' do
43
+ s = sequence ''
44
+ base.expects(:transfer!).in_sequence(s)
45
+ base.expects(:cycle!).in_sequence(s)
46
+ base.perform!
47
+ base.instance_variable_get(:@package).should be(package)
47
48
  end
49
+ end
48
50
 
49
- it 'should set the storage_id using an optional block parameter' do
50
- base = Backup::Storage::Base.new('my storage_id') do |base|
51
- base.keep = 10
51
+ describe '#storage_name' do
52
+ context 'when given a storage_id' do
53
+ before { base.storage_id = 'storage id' }
54
+ it 'should return a log-friendly name with the storage_id' do
55
+ base.send(:storage_name).should == 'Storage::Base (storage id)'
52
56
  end
53
- base.keep.should == 10
54
- base.time.should == Backup::TIME
55
- base.storage_id.should == 'my storage_id'
56
57
  end
57
58
 
58
- end # describe '#inititalize'
59
-
60
- describe '#clean!' do
61
-
62
- context 'when clearing instance variables for cycling' do
63
- it 'version stamps the object' do
64
- cycle_required_variables = %w[ @filename @time @chunk_suffixes ]
65
- expected_variables = (cycle_required_variables + ['@version']).sort
66
-
67
- base = Backup::Storage::Base.new do |config|
68
- config.storage_id = 'bar_id'
69
- config.keep = 3
70
- end
71
- base.time = Time.utc(2011, 12, 2)
72
- base.chunk_suffixes = ['aa', 'ab']
73
- base.instance_variable_set(:@filename, 'foo.tar')
74
- base.version.should be_nil
75
-
76
- base.send(:clean!)
77
- base.instance_variables.map(&:to_s).sort.should == expected_variables
78
- base.time.should == Time.utc(2011, 12, 2)
79
- base.chunk_suffixes.should == ['aa', 'ab']
80
- base.filename.should == 'foo.tar'
81
- base.version.should == Backup::Version.current
59
+ context 'when not given a storage_id' do
60
+ it 'should return a log-friendly name without a storage_id' do
61
+ base.send(:storage_name).should == 'Storage::Base'
82
62
  end
83
63
  end
84
-
85
64
  end
86
65
 
87
- describe '#update!' do
66
+ describe '#local_path' do
67
+ it 'should return the configured tmp_path' do
68
+ base.send(:local_path).should == Backup::Config.tmp_path
69
+ end
70
+ end
88
71
 
89
- it 'updates the storage object with the given configure_block' do
90
- base_a = Backup::Storage::Base.new do |config|
91
- config.storage_id = 'foo_id'
92
- config.keep = 5
93
- end
94
- base_b = Backup::Storage::Base.new do |config|
95
- config.storage_id = 'bar_id'
96
- config.keep = 3
97
- end
98
- base_b.expects(:upgrade_if_needed!)
72
+ describe '#remote_path_for' do
73
+ before do
74
+ package.expects(:trigger).returns('test_trigger')
75
+ package.expects(:time).returns('backup_time')
76
+ base.expects(:path).returns('base/remote/path')
77
+ end
99
78
 
100
- base_b.send(:update!, base_a.configure_block)
101
- base_b.storage_id.should == 'foo_id'
102
- base_b.keep.should == 5
79
+ it 'should return the remote_path for the given package' do
80
+ base.send(:remote_path_for, package).should ==
81
+ File.join('base/remote/path', 'test_trigger', 'backup_time')
103
82
  end
83
+ end
104
84
 
105
- it 'updates YAML loaded objects, which were `cleaned` for cycling' do
106
- base = Backup::Storage::Base.new do |config|
107
- config.storage_id = 'foo_id'
108
- config.keep = 5
109
- end
110
- base.time.should == Backup::TIME
85
+ describe '#files_to_transfer_for' do
86
+ let(:given_block) { mock }
87
+ before do
88
+ package.stubs(:filenames).returns(
89
+ ['2011.12.31.11.00.02.backup.tar.enc-aa',
90
+ '2011.12.31.11.00.02.backup.tar.enc-ab']
91
+ )
92
+ end
111
93
 
112
- loaded = Backup::Storage::Base.new do |config|
113
- config.storage_id = 'bar_id'
114
- config.keep = 3
94
+ it 'should yield the full filename and the filename without the timestamp' do
95
+ given_block.expects(:got).with(
96
+ '2011.12.31.11.00.02.backup.tar.enc-aa', 'backup.tar.enc-aa'
97
+ )
98
+ given_block.expects(:got).with(
99
+ '2011.12.31.11.00.02.backup.tar.enc-ab', 'backup.tar.enc-ab'
100
+ )
101
+ base.send(:files_to_transfer_for, package) do |local_file, remote_file|
102
+ given_block.got(local_file, remote_file)
115
103
  end
116
- loaded.time = Time.utc(2011, 12, 2)
117
- loaded.chunk_suffixes = ['aa', 'ab']
118
- loaded.instance_variable_set(:@filename, 'foo.tar')
119
-
120
- loaded.send(:clean!)
121
- loaded.storage_id.should == nil
122
- loaded.keep.should == nil
123
- loaded.version.should == Backup::Version.current
124
-
125
- loaded.send(:update!, base.configure_block)
126
- loaded.storage_id.should == 'foo_id'
127
- loaded.keep.should == 5
128
- loaded.time.should == Time.utc(2011, 12, 2)
129
- loaded.chunk_suffixes.should == ['aa', 'ab']
130
- loaded.filename.should == 'foo.tar'
131
- loaded.version.should == Backup::Version.current
132
104
  end
133
105
 
134
- end # describe '#update!'
135
-
136
- describe '#upgrade_if_needed!' do
137
-
138
- it 'upgrades <= 3.0.19' do
139
- v_3_0_19_yaml = <<-EOF.gsub(/^\s+/,' ').gsub(/^ -/,'-')
140
- ---
141
- !ruby/object:Backup::Storage::SCP
142
- username: a_user
143
- password: a_password
144
- ip: an_address
145
- port: a_port
146
- path: a_path
147
- keep: a_few
148
- time: 2011.11.01.01.02.03
149
- local_file: 2011.11.01.01.02.03.a_backup.tar
150
- remote_file: 2011.11.01.01.02.03.a_backup.tar
151
- EOF
152
- loaded = YAML.load(v_3_0_19_yaml)
153
- cycle_required_variables = %w[ @filename @time @chunk_suffixes ]
154
- expected_variables = (cycle_required_variables + ['@version']).sort
155
-
156
- loaded.send(:upgrade_if_needed!)
157
- loaded.instance_variables.map(&:to_s).sort.should == expected_variables
158
- loaded.filename.should == '2011.11.01.01.02.03.a_backup.tar'
159
- loaded.time.should == '2011.11.01.01.02.03'
160
- loaded.chunk_suffixes.should == []
161
- loaded.version.should == Backup::Version.current
106
+ it 'should have an alias method called #transferred_files_for' do
107
+ base.method(:transferred_files_for).should ==
108
+ base.method(:files_to_transfer_for)
162
109
  end
163
-
164
- end # describe '#upgrade_if_needed!'
110
+ end
165
111
 
166
112
  describe '#cycle!' do
167
- let(:base) { Backup::Storage::Base.new {} }
168
-
169
- it 'updates loaded objects and adds current object' do
170
- s = sequence ''
171
- storage_object = mock
172
- loaded_object = mock
173
-
174
- Backup::Storage::Object.expects(:new).in_sequence(s).
175
- with('Base', nil).returns(storage_object)
176
- storage_object.expects(:load).in_sequence(s).
177
- returns([loaded_object])
178
- loaded_object.expects(:update!).in_sequence(s).
179
- with(base.configure_block)
180
- objects = [base, loaded_object]
181
- objects.each {|object| object.expects(:clean!).in_sequence(s) }
182
- storage_object.expects(:write).in_sequence(s).
183
- with(objects)
184
-
185
- base.keep = 2
186
- base.send(:cycle!)
113
+ before do
114
+ base.stubs(:storage_name).returns('Storage Name')
115
+ base.instance_variable_set(:@package, package)
187
116
  end
188
117
 
189
- context 'when removing old stored objects' do
190
-
191
- it 'should warn and continue on errors' do
192
- num_to_load = 6
193
- num_to_keep = 3
194
- obj_to_fail = 2 # we're removing 3, so fail the 2nd one.
195
-
118
+ context 'when keep is set and > 0' do
119
+ before { base.keep = 1 }
120
+ it 'should cycle' do
196
121
  s = sequence ''
197
- storage_object = mock
198
- raised_error = StandardError.new
199
- loaded_objects = []
200
- (1..num_to_load).each do |n|
201
- instance_eval <<-EOS
202
- loaded_object#{n} = mock
203
- loaded_object#{n}.stubs(:filename).returns("file#{n}")
204
- loaded_objects << loaded_object#{n}
205
- EOS
206
- end
122
+ Backup::Logger.expects(:message).in_sequence(s).
123
+ with('Storage Name: Cycling Started...')
124
+ Backup::Storage::Cycler.expects(:cycle!).in_sequence(s).
125
+ with(base, package)
126
+ Backup::Logger.expects(:message).in_sequence(s).
127
+ with('Storage Name: Cycling Complete!')
207
128
 
208
- Backup::Storage::Object.expects(:new).in_sequence(s).
209
- with('Base', nil).returns(storage_object)
210
- storage_object.expects(:load).in_sequence(s).
211
- returns(loaded_objects)
212
- loaded_objects.each do |loaded_object|
213
- loaded_object.expects(:update!).in_sequence(s).
214
- with(base.configure_block)
215
- end
216
- objects = [base] + loaded_objects
217
-
218
- objects_to_remove = objects[num_to_keep..-1]
219
- objects_to_remove.each_with_index do |object_to_remove, i|
220
- Backup::Logger.expects(:message).in_sequence(s).
221
- with {|msg| msg.include?(object_to_remove.filename) }
222
- if i == obj_to_fail
223
- object_to_remove.expects(:remove!).in_sequence(s).
224
- raises(raised_error)
225
- Backup::Errors::Storage::CycleError.expects(:wrap).in_sequence(s).
226
- with(raised_error, "#{base.storage_name} failed to remove " +
227
- "'#{object_to_remove.filename}'").
228
- returns(:wrapped_error)
229
- Backup::Logger.expects(:warn).in_sequence(s).
230
- with(:wrapped_error)
231
- else
232
- object_to_remove.expects(:remove!).in_sequence(s)
233
- end
234
- end
235
-
236
- objects = objects - objects_to_remove
237
- objects.each {|object| object.expects(:clean!).in_sequence(s) }
238
- storage_object.expects(:write).in_sequence(s).
239
- with(objects)
240
-
241
- base.keep = num_to_keep
242
129
  base.send(:cycle!)
243
130
  end
244
-
245
131
  end
246
132
 
247
- end
248
-
249
- describe '#chunks' do
250
-
251
- it 'returns sorted filenames for chunk_suffixes' do
252
- base = Backup::Storage::Base.new
253
- base.chunk_suffixes = ["aa", "ad", "ae", "ab", "ac"]
254
- base.stubs(:filename).returns("file.tar")
255
- base.chunks.should == [
256
- "#{base.filename}-aa",
257
- "#{base.filename}-ab",
258
- "#{base.filename}-ac",
259
- "#{base.filename}-ad",
260
- "#{base.filename}-ae",
261
- ]
262
-
263
- base.chunk_suffixes.should == ["aa", "ad", "ae", "ab", "ac"]
264
- end
265
-
266
- end
267
-
268
- describe '#storage_name' do
269
- let(:s3) { Backup::Storage::S3.new {} }
270
-
271
- describe 'returns storage class name with the Backup:: namespace removed' do
272
-
273
- context 'when storage_id is set' do
274
- before { s3.storage_id = 'my storage' }
275
-
276
- it 'appends the storage_id' do
277
- s3.storage_name.should == 'Storage::S3 (my storage)'
278
- end
133
+ context 'when keep is not set or == 0' do
134
+ it 'should return nil when not set' do
135
+ base.keep = nil
136
+ base.send(:cycle!).should be_nil
279
137
  end
280
138
 
281
- context 'when storage_id is not set' do
282
- it 'does not append the storage_id' do
283
- s3.storage_name.should == 'Storage::S3'
284
- end
139
+ it 'should return nil when == 0' do
140
+ base.keep = 0
141
+ base.send(:cycle!).should be_nil
285
142
  end
286
-
287
143
  end
288
-
289
144
  end
145
+
290
146
  end