backup 3.0.21 → 3.0.22

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 (46) hide show
  1. data/Gemfile.lock +3 -1
  2. data/README.md +4 -3
  3. data/lib/backup.rb +8 -4
  4. data/lib/backup/config.rb +1 -1
  5. data/lib/backup/configuration/syncer/cloud.rb +23 -0
  6. data/lib/backup/configuration/syncer/cloud_files.rb +30 -0
  7. data/lib/backup/configuration/syncer/s3.rb +5 -11
  8. data/lib/backup/dependency.rb +6 -0
  9. data/lib/backup/notifier/twitter.rb +1 -1
  10. data/lib/backup/syncer/base.rb +25 -0
  11. data/lib/backup/syncer/cloud.rb +187 -0
  12. data/lib/backup/syncer/cloud_files.rb +56 -0
  13. data/lib/backup/syncer/rsync/base.rb +0 -26
  14. data/lib/backup/syncer/s3.rb +21 -102
  15. data/lib/backup/version.rb +1 -1
  16. data/spec/cli/utility_spec.rb +2 -2
  17. data/spec/configuration/syncer/cloud_files_spec.rb +44 -0
  18. data/spec/configuration/syncer/s3_spec.rb +0 -4
  19. data/spec/notifier/twitter_spec.rb +3 -3
  20. data/spec/syncer/cloud_files_spec.rb +192 -0
  21. data/spec/syncer/s3_spec.rb +155 -191
  22. data/templates/cli/utility/archive +20 -8
  23. data/templates/cli/utility/database/mongodb +3 -3
  24. data/templates/cli/utility/database/mysql +4 -4
  25. data/templates/cli/utility/database/postgresql +4 -4
  26. data/templates/cli/utility/database/redis +1 -1
  27. data/templates/cli/utility/encryptor/openssl +2 -2
  28. data/templates/cli/utility/notifier/campfire +3 -3
  29. data/templates/cli/utility/notifier/hipchat +6 -6
  30. data/templates/cli/utility/notifier/mail +7 -7
  31. data/templates/cli/utility/notifier/presently +4 -4
  32. data/templates/cli/utility/notifier/prowl +2 -2
  33. data/templates/cli/utility/notifier/twitter +4 -4
  34. data/templates/cli/utility/storage/cloud_files +22 -0
  35. data/templates/cli/utility/storage/dropbox +15 -10
  36. data/templates/cli/utility/storage/ftp +4 -4
  37. data/templates/cli/utility/storage/local +1 -1
  38. data/templates/cli/utility/storage/ninefold +3 -3
  39. data/templates/cli/utility/storage/rsync +4 -4
  40. data/templates/cli/utility/storage/s3 +6 -6
  41. data/templates/cli/utility/storage/scp +4 -4
  42. data/templates/cli/utility/storage/sftp +4 -4
  43. data/templates/cli/utility/syncer/cloud_files +48 -0
  44. data/templates/cli/utility/syncer/s3 +31 -1
  45. metadata +69 -39
  46. data/templates/cli/utility/storage/cloudfiles +0 -12
@@ -4,19 +4,6 @@ module Backup
4
4
  module Syncer
5
5
  module RSync
6
6
  class Base < Syncer::Base
7
-
8
- ##
9
- # Path to store the synced files/directories to
10
- attr_accessor :path
11
-
12
- ##
13
- # Directories to sync
14
- attr_writer :directories
15
-
16
- ##
17
- # Flag for mirroring the files/directories
18
- attr_accessor :mirror
19
-
20
7
  ##
21
8
  # Additional options for the rsync cli
22
9
  attr_accessor :additional_options
@@ -33,19 +20,6 @@ module Backup
33
20
  @additional_options ||= Array.new
34
21
  end
35
22
 
36
- ##
37
- # Syntactical suger for the DSL for adding directories
38
- def directories(&block)
39
- return @directories unless block_given?
40
- instance_eval(&block)
41
- end
42
-
43
- ##
44
- # Adds a path to the @directories array
45
- def add(path)
46
- @directories << path
47
- end
48
-
49
23
  private
50
24
 
51
25
  ##
@@ -2,127 +2,46 @@
2
2
 
3
3
  module Backup
4
4
  module Syncer
5
- class S3 < Base
5
+ class S3 < Cloud
6
6
 
7
7
  ##
8
8
  # Amazon Simple Storage Service (S3) Credentials
9
9
  attr_accessor :access_key_id, :secret_access_key
10
10
 
11
11
  ##
12
- # Amazon S3 bucket name and path to sync to
13
- attr_accessor :bucket, :path
12
+ # The S3 bucket to store files to
13
+ attr_accessor :bucket
14
14
 
15
15
  ##
16
- # Directories to sync
17
- attr_writer :directories
18
-
19
- ##
20
- # Flag to enable mirroring
21
- attr_accessor :mirror
22
-
23
- ##
24
- # Additional options for the s3sync cli
25
- attr_accessor :additional_options
26
-
27
- ##
28
- # Instantiates a new S3 Syncer object and sets the default configuration
29
- # specified in the Backup::Configuration::Syncer::S3.
30
- # Then it sets the object defaults if particular properties weren't set.
31
- # Finally it'll evaluate the users configuration file and overwrite
32
- # anything that's been defined
33
- def initialize(&block)
34
- load_defaults!
35
-
36
- @path ||= 'backups'
37
- @directories = Array.new
38
- @mirror ||= false
39
- @additional_options ||= []
40
-
41
- instance_eval(&block) if block_given?
42
- end
43
-
44
- ##
45
- # Sets the Amazon S3 credentials for S3Sync, performs the S3Sync
46
- # operation, then unsets the credentials (back to nil values)
47
- def perform!
48
- set_environment_variables!
49
-
50
- @directories.each do |directory|
51
- Logger.message("#{ syncer_name } started syncing '#{ directory }'.")
52
- Logger.silent(
53
- run("#{ utility(:s3sync) } #{ options } " +
54
- "'#{ File.expand_path(directory) }' '#{ bucket }:#{ dest_path }'")
55
- )
56
- end
57
-
58
- unset_environment_variables!
59
- end
60
-
61
- ##
62
- # Syntactical suger for the DSL for adding directories
63
- def directories(&block)
64
- return @directories unless block_given?
65
- instance_eval(&block)
66
- end
67
-
68
- ##
69
- # Adds a path to the @directories array
70
- def add(path)
71
- @directories << path
72
- end
16
+ # The AWS region of the specified S3 bucket
17
+ attr_accessor :region
73
18
 
74
19
  private
75
20
 
76
21
  ##
77
- # Return @path with preceeding '/' slash removed
78
- def dest_path
79
- @dest_path ||= @path.sub(/^\//, '')
80
- end
81
-
82
- ##
83
- # Returns all the specified S3Sync options,
84
- # concatenated, ready for the CLI
85
- def options
86
- ([verbose_option, recursive_option, mirror_option] +
87
- additional_options).compact.join("\s")
88
- end
89
-
90
- ##
91
- # Returns S3Sync syntax for enabling mirroring
92
- def mirror_option
93
- '--delete' if @mirror
94
- end
95
-
96
- ##
97
- # Returns S3Sync syntax for syncing recursively
98
- def recursive_option
99
- '--recursive'
22
+ # Established and creates a new Fog storage object for S3.
23
+ def connection
24
+ @connection ||= Fog::Storage.new(
25
+ :provider => provider,
26
+ :aws_access_key_id => access_key_id,
27
+ :aws_secret_access_key => secret_access_key,
28
+ :region => region
29
+ )
100
30
  end
101
31
 
102
32
  ##
103
- # Returns S3Sync syntax for making output verbose
104
- def verbose_option
105
- '--verbose'
33
+ # Creates a new @repository_object (bucket). Fetches it from S3
34
+ # if it already exists, otherwise it will create it first and fetch use that instead.
35
+ def repository_object
36
+ @repository_object ||= connection.directories.get(bucket) ||
37
+ connection.directories.create(:key => bucket, :location => region)
106
38
  end
107
39
 
108
40
  ##
109
- # In order for S3Sync to know what credentials to use, we have to set the
110
- # AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables, these
111
- # evironment variables will be used by S3Sync
112
- def set_environment_variables!
113
- ENV['AWS_ACCESS_KEY_ID'] = access_key_id
114
- ENV['AWS_SECRET_ACCESS_KEY'] = secret_access_key
115
- ENV['AWS_CALLING_FORMAT'] = 'SUBDOMAIN'
41
+ # This is the provider that Fog uses for the Cloud Files
42
+ def provider
43
+ "AWS"
116
44
  end
117
-
118
- ##
119
- # Sets the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY back to nil
120
- def unset_environment_variables!
121
- ENV['AWS_ACCESS_KEY_ID'] = nil
122
- ENV['AWS_SECRET_ACCESS_KEY'] = nil
123
- ENV['AWS_CALLING_FORMAT'] = nil
124
- end
125
-
126
45
  end
127
46
  end
128
47
  end
@@ -13,7 +13,7 @@ module Backup
13
13
  # Defines the minor version
14
14
  # PATCH:
15
15
  # Defines the patch version
16
- MAJOR, MINOR, PATCH = 3, 0, 21
16
+ MAJOR, MINOR, PATCH = 3, 0, 22
17
17
 
18
18
  ##
19
19
  # Returns the major version ( big release based off of multiple minor releases )
@@ -187,8 +187,8 @@ describe 'Backup::CLI::Utility' do
187
187
  --trigger=TRIGGER
188
188
  [--config-path=CONFIG_PATH] # Path to your Backup configuration directory
189
189
  [--databases=DATABASES] # (mongodb, mysql, postgresql, redis, riak)
190
- [--storages=STORAGES] # (cloudfiles, dropbox, ftp, local, ninefold, rsync, s3, scp, sftp)
191
- [--syncers=SYNCERS] # (rsync_local, rsync_pull, rsync_push, s3)
190
+ [--storages=STORAGES] # (cloud_files, dropbox, ftp, local, ninefold, rsync, s3, scp, sftp)
191
+ [--syncers=SYNCERS] # (cloud_files, rsync_local, rsync_pull, rsync_push, s3)
192
192
  [--encryptors=ENCRYPTORS] # (gpg, openssl)
193
193
  [--compressors=COMPRESSORS] # (bzip2, gzip, lzma, pbzip2)
194
194
  [--notifiers=NOTIFIERS] # (campfire, hipchat, mail, presently, prowl, twitter)
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../../spec_helper.rb', __FILE__)
4
+
5
+ describe Backup::Configuration::Syncer::CloudFiles do
6
+ before do
7
+ Backup::Configuration::Syncer::CloudFiles.defaults do |cf|
8
+ cf.username = 'my-username'
9
+ cf.api_key = 'my-api-key'
10
+ cf.container = 'my-container'
11
+ cf.auth_url = 'my-auth-url'
12
+ cf.servicenet = true
13
+ cf.path = '/backups/'
14
+ cf.mirror = true
15
+ end
16
+ end
17
+ after { Backup::Configuration::Syncer::CloudFiles.clear_defaults! }
18
+
19
+ it 'should set the default cloud files configuration' do
20
+ cf = Backup::Configuration::Syncer::CloudFiles
21
+ cf.username.should == 'my-username'
22
+ cf.api_key.should == 'my-api-key'
23
+ cf.container.should == 'my-container'
24
+ cf.auth_url.should == 'my-auth-url'
25
+ cf.servicenet.should == true
26
+ cf.path.should == '/backups/'
27
+ cf.mirror.should == true
28
+ end
29
+
30
+ describe '#clear_defaults!' do
31
+ it 'should clear all the defaults, resetting them to nil' do
32
+ Backup::Configuration::Syncer::CloudFiles.clear_defaults!
33
+
34
+ cf = Backup::Configuration::Syncer::CloudFiles
35
+ cf.username.should == nil
36
+ cf.api_key.should == nil
37
+ cf.container.should == nil
38
+ cf.auth_url.should == nil
39
+ cf.servicenet.should == nil
40
+ cf.path.should == nil
41
+ cf.mirror.should == nil
42
+ end
43
+ end
44
+ end
@@ -9,9 +9,7 @@ describe Backup::Configuration::Syncer::S3 do
9
9
  s3.secret_access_key = 'my_secret_access_key'
10
10
  s3.bucket = 'my-bucket'
11
11
  s3.path = '/backups/'
12
- #s3.directories = 'cannot_have_a_default_value'
13
12
  s3.mirror = true
14
- s3.additional_options = ['--exclude="*.rb"']
15
13
  end
16
14
  end
17
15
  after { Backup::Configuration::Syncer::S3.clear_defaults! }
@@ -23,7 +21,6 @@ describe Backup::Configuration::Syncer::S3 do
23
21
  s3.bucket.should == 'my-bucket'
24
22
  s3.path.should == '/backups/'
25
23
  s3.mirror.should == true
26
- s3.additional_options.should == ['--exclude="*.rb"']
27
24
  end
28
25
 
29
26
  describe '#clear_defaults!' do
@@ -36,7 +33,6 @@ describe Backup::Configuration::Syncer::S3 do
36
33
  s3.bucket.should == nil
37
34
  s3.path.should == nil
38
35
  s3.mirror.should == nil
39
- s3.additional_options.should == nil
40
36
  end
41
37
  end
42
38
  end
@@ -88,7 +88,7 @@ describe Backup::Notifier::Twitter do
88
88
  context 'when status is :success' do
89
89
  it 'should send Success message' do
90
90
  notifier.expects(:send_message).with(
91
- '[Backup::Success] test label (test_trigger)'
91
+ "[Backup::Success] test label (test_trigger) (@ #{notifier.instance_variable_get("@model").time})"
92
92
  )
93
93
  notifier.send(:notify!, :success)
94
94
  end
@@ -97,7 +97,7 @@ describe Backup::Notifier::Twitter do
97
97
  context 'when status is :warning' do
98
98
  it 'should send Warning message' do
99
99
  notifier.expects(:send_message).with(
100
- '[Backup::Warning] test label (test_trigger)'
100
+ "[Backup::Warning] test label (test_trigger) (@ #{notifier.instance_variable_get("@model").time})"
101
101
  )
102
102
  notifier.send(:notify!, :warning)
103
103
  end
@@ -106,7 +106,7 @@ describe Backup::Notifier::Twitter do
106
106
  context 'when status is :failure' do
107
107
  it 'should send Failure message' do
108
108
  notifier.expects(:send_message).with(
109
- '[Backup::Failure] test label (test_trigger)'
109
+ "[Backup::Failure] test label (test_trigger) (@ #{notifier.instance_variable_get("@model").time})"
110
110
  )
111
111
  notifier.send(:notify!, :failure)
112
112
  end
@@ -0,0 +1,192 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../../spec_helper.rb', __FILE__)
3
+
4
+ class Parallel; end
5
+
6
+ describe Backup::Syncer::CloudFiles do
7
+ describe '#perform!' do
8
+ let(:syncer) { Backup::Syncer::CloudFiles.new }
9
+ let(:connection) { stub('connection',
10
+ :directories => stub('directories', :get => container)) }
11
+ let(:container) { stub('container', :files => files) }
12
+ let(:files) { [] }
13
+ let(:content) { stub('content') }
14
+
15
+ before :each do
16
+ Fog::Storage.stubs(:new).returns connection
17
+ File.stubs(:open).returns content
18
+ File.stubs(:exist?).returns true
19
+ files.stubs(:create).returns true
20
+
21
+ syncer.directories << 'tmp'
22
+ syncer.path = 'storage'
23
+ Backup::Syncer::S3::SyncContext.any_instance.
24
+ stubs(:`).returns 'MD5(tmp/foo)= 123abcdef'
25
+ end
26
+
27
+ it "respects the concurrency_type setting with threads" do
28
+ syncer.concurrency_type = :threads
29
+
30
+ Parallel.expects(:each).with(anything, {:in_threads => 2}, anything)
31
+
32
+ syncer.perform!
33
+ end
34
+
35
+ it "respects the parallel thread count" do
36
+ syncer.concurrency_type = :threads
37
+ syncer.concurrency_level = 10
38
+
39
+ Parallel.expects(:each).with(anything, {:in_threads => 10}, anything)
40
+
41
+ syncer.perform!
42
+ end
43
+
44
+ it "respects the concurrency_type setting with processors" do
45
+ syncer.concurrency_type = :processes
46
+
47
+ Parallel.expects(:each).with(anything, {:in_processes => 2}, anything)
48
+
49
+ syncer.perform!
50
+ end
51
+
52
+ it "respects the parallel thread count" do
53
+ syncer.concurrency_type = :processes
54
+ syncer.concurrency_level = 10
55
+
56
+ Parallel.expects(:each).with(anything, {:in_processes => 10}, anything)
57
+
58
+ syncer.perform!
59
+ end
60
+
61
+ context 'file exists locally' do
62
+ it "uploads a file if it does not exist remotely" do
63
+ files.expects(:create).with(:key => 'storage/tmp/foo', :body => content)
64
+
65
+ syncer.perform!
66
+ end
67
+
68
+ it "uploads a file if it exists remotely with a different MD5" do
69
+ files << stub('file', :key => 'storage/tmp/foo', :etag => 'abcdef123')
70
+
71
+ files.expects(:create).with(:key => 'storage/tmp/foo', :body => content)
72
+
73
+ syncer.perform!
74
+ end
75
+
76
+ it "does nothing if the file exists remotely with the same MD5" do
77
+ files << stub('file', :key => 'storage/tmp/foo', :etag => '123abcdef')
78
+
79
+ files.expects(:create).never
80
+
81
+ syncer.perform!
82
+ end
83
+
84
+ it "skips the file if it no longer exists locally" do
85
+ File.stubs(:exist?).returns false
86
+
87
+ files.expects(:create).never
88
+
89
+ syncer.perform!
90
+ end
91
+
92
+ it "respects the given path" do
93
+ syncer.path = 'box'
94
+
95
+ files.expects(:create).with(:key => 'box/tmp/foo', :body => content)
96
+
97
+ syncer.perform!
98
+ end
99
+
100
+ it "uploads the content of the local file" do
101
+ File.expects(:open).with('tmp/foo').returns content
102
+
103
+ syncer.perform!
104
+ end
105
+
106
+ it "creates the connection with the provided credentials" do
107
+ syncer.api_key = 'my-key'
108
+ syncer.username = 'my-name'
109
+ syncer.auth_url = 'my-auth'
110
+ syncer.servicenet = 'my-servicenet'
111
+
112
+ Fog::Storage.expects(:new).with(
113
+ :provider => 'Rackspace',
114
+ :rackspace_api_key => 'my-key',
115
+ :rackspace_username => 'my-name',
116
+ :rackspace_auth_url => 'my-auth',
117
+ :rackspace_servicenet => 'my-servicenet'
118
+ ).returns connection
119
+
120
+ syncer.perform!
121
+ end
122
+
123
+ it "uses the container with the given name" do
124
+ syncer.container = 'leaky'
125
+
126
+ connection.directories.expects(:get).with('leaky').returns(container)
127
+
128
+ syncer.perform!
129
+ end
130
+
131
+ it "creates the container if one does not exist" do
132
+ syncer.container = 'leaky'
133
+ connection.directories.stubs(:get).returns nil
134
+
135
+ connection.directories.expects(:create).
136
+ with(:key => 'leaky').returns(container)
137
+
138
+ syncer.perform!
139
+ end
140
+
141
+ it "iterates over each directory" do
142
+ syncer.directories << 'files'
143
+
144
+ Backup::Syncer::CloudFiles::SyncContext.any_instance.expects(:`).
145
+ with('find tmp -print0 | xargs -0 openssl md5 2> /dev/null').
146
+ returns 'MD5(tmp/foo)= 123abcdef'
147
+ Backup::Syncer::CloudFiles::SyncContext.any_instance.expects(:`).
148
+ with('find files -print0 | xargs -0 openssl md5 2> /dev/null').
149
+ returns 'MD5(tmp/foo)= 123abcdef'
150
+
151
+ syncer.perform!
152
+ end
153
+ end
154
+
155
+ context 'file does not exist locally' do
156
+ let(:file) { stub('file', :key => 'storage/tmp/foo',
157
+ :etag => '123abcdef') }
158
+
159
+ before :each do
160
+ Backup::Syncer::CloudFiles::SyncContext.any_instance.
161
+ stubs(:`).returns ''
162
+ files << file
163
+ File.stubs(:exist?).returns false
164
+ end
165
+
166
+ it "removes the remote file when mirroring is turned on" do
167
+ syncer.mirror = true
168
+
169
+ file.expects(:destroy).once
170
+
171
+ syncer.perform!
172
+ end
173
+
174
+ it "leaves the remote file when mirroring is turned off" do
175
+ syncer.mirror = false
176
+
177
+ file.expects(:destroy).never
178
+
179
+ syncer.perform!
180
+ end
181
+
182
+ it "does not remove files not under one of the specified directories" do
183
+ file.stubs(:key).returns 'unsynced/tmp/foo'
184
+ syncer.mirror = true
185
+
186
+ file.expects(:destroy).never
187
+
188
+ syncer.perform!
189
+ end
190
+ end
191
+ end
192
+ end