backup 3.0.16 → 3.0.18

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 (81) hide show
  1. data/.travis.yml +10 -0
  2. data/Gemfile.lock +50 -47
  3. data/Guardfile +3 -3
  4. data/README.md +136 -81
  5. data/backup.gemspec +3 -2
  6. data/bin/backup +36 -15
  7. data/lib/backup.rb +30 -20
  8. data/lib/backup/cli.rb +30 -2
  9. data/lib/backup/compressor/lzma.rb +63 -0
  10. data/lib/backup/configuration/compressor/lzma.rb +23 -0
  11. data/lib/backup/configuration/helpers.rb +10 -4
  12. data/lib/backup/configuration/notifier/mail.rb +5 -0
  13. data/lib/backup/configuration/storage/dropbox.rb +19 -4
  14. data/lib/backup/configuration/storage/ftp.rb +4 -0
  15. data/lib/backup/configuration/storage/local.rb +17 -0
  16. data/lib/backup/configuration/storage/ninefold.rb +20 -0
  17. data/lib/backup/configuration/storage/rsync.rb +4 -0
  18. data/lib/backup/database/postgresql.rb +12 -3
  19. data/lib/backup/database/redis.rb +5 -1
  20. data/lib/backup/dependency.rb +11 -12
  21. data/lib/backup/encryptor/gpg.rb +2 -0
  22. data/lib/backup/exception/command_failed.rb +8 -0
  23. data/lib/backup/finder.rb +49 -9
  24. data/lib/backup/notifier/mail.rb +7 -1
  25. data/lib/backup/notifier/twitter.rb +1 -1
  26. data/lib/backup/storage/dropbox.rb +93 -16
  27. data/lib/backup/storage/ftp.rb +10 -3
  28. data/lib/backup/storage/local.rb +78 -0
  29. data/lib/backup/storage/ninefold.rb +96 -0
  30. data/lib/backup/storage/rsync.rb +37 -20
  31. data/lib/backup/storage/s3.rb +1 -1
  32. data/lib/backup/storage/scp.rb +1 -1
  33. data/lib/backup/syncer/rsync.rb +1 -1
  34. data/lib/backup/version.rb +1 -1
  35. data/lib/templates/compressor/lzma +7 -0
  36. data/lib/templates/storage/dropbox +2 -2
  37. data/lib/templates/storage/ftp +8 -7
  38. data/lib/templates/storage/local +7 -0
  39. data/lib/templates/storage/ninefold +9 -0
  40. data/lib/templates/storage/rsync +1 -0
  41. data/spec/archive_spec.rb +0 -1
  42. data/spec/compressor/bzip2_spec.rb +0 -1
  43. data/spec/compressor/gzip_spec.rb +0 -1
  44. data/spec/compressor/lzma_spec.rb +58 -0
  45. data/spec/configuration/compressor/bzip2_spec.rb +28 -0
  46. data/spec/configuration/compressor/lzma_spec.rb +28 -0
  47. data/spec/configuration/database/mongodb_spec.rb +16 -0
  48. data/spec/configuration/database/mysql_spec.rb +17 -0
  49. data/spec/configuration/database/postgresql_spec.rb +17 -0
  50. data/spec/configuration/database/redis_spec.rb +16 -0
  51. data/spec/configuration/notifier/campfire_spec.rb +11 -0
  52. data/spec/configuration/notifier/mail_spec.rb +20 -0
  53. data/spec/configuration/notifier/presently_spec.rb +34 -0
  54. data/spec/configuration/notifier/twitter_spec.rb +12 -0
  55. data/spec/configuration/storage/dropbox_spec.rb +0 -6
  56. data/spec/configuration/storage/ftp_spec.rb +15 -12
  57. data/spec/configuration/storage/local_spec.rb +28 -0
  58. data/spec/configuration/storage/ninefold_spec.rb +31 -0
  59. data/spec/configuration/storage/rsync_spec.rb +2 -0
  60. data/spec/database/mongodb_spec.rb +0 -1
  61. data/spec/database/mysql_spec.rb +0 -1
  62. data/spec/database/postgresql_spec.rb +31 -11
  63. data/spec/database/redis_spec.rb +9 -4
  64. data/spec/encryptor/gpg_spec.rb +1 -1
  65. data/spec/encryptor/open_ssl_spec.rb +0 -1
  66. data/spec/logger_spec.rb +32 -24
  67. data/spec/model_spec.rb +15 -15
  68. data/spec/spec_helper.rb +8 -4
  69. data/spec/storage/base_spec.rb +0 -4
  70. data/spec/storage/cloudfiles_spec.rb +0 -1
  71. data/spec/storage/dropbox_spec.rb +44 -14
  72. data/spec/storage/ftp_spec.rb +26 -15
  73. data/spec/storage/local_spec.rb +83 -0
  74. data/spec/storage/ninefold_spec.rb +142 -0
  75. data/spec/storage/object_spec.rb +1 -1
  76. data/spec/storage/rsync_spec.rb +17 -7
  77. data/spec/storage/s3_spec.rb +4 -3
  78. data/spec/storage/scp_spec.rb +0 -1
  79. data/spec/storage/sftp_spec.rb +0 -1
  80. data/spec/syncer/rsync_spec.rb +8 -8
  81. metadata +62 -36
@@ -20,6 +20,10 @@ module Backup
20
20
  # Path to store backups to
21
21
  attr_accessor :path
22
22
 
23
+ ##
24
+ # use passive mode?
25
+ attr_accessor :passive_mode
26
+
23
27
  ##
24
28
  # Creates a new instance of the FTP storage object
25
29
  # First it sets the defaults (if any exist) and then evaluates
@@ -27,8 +31,9 @@ module Backup
27
31
  def initialize(&block)
28
32
  load_defaults!
29
33
 
30
- @port ||= 21
31
- @path ||= 'backups'
34
+ @port ||= 21
35
+ @path ||= 'backups'
36
+ @passive_mode ||= false
32
37
 
33
38
  instance_eval(&block) if block_given?
34
39
 
@@ -66,7 +71,9 @@ module Backup
66
71
  Net::FTP.send(:remove_const, :FTP_PORT)
67
72
  end; Net::FTP.send(:const_set, :FTP_PORT, port)
68
73
 
69
- Net::FTP.new(ip, username, password)
74
+ ftp = Net::FTP.new(ip, username, password)
75
+ ftp.passive = true if passive_mode
76
+ ftp
70
77
  end
71
78
 
72
79
  ##
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Load the Ruby FileUtils library
5
+ require 'fileutils'
6
+
7
+ module Backup
8
+ module Storage
9
+ class Local < Base
10
+
11
+ ##
12
+ # Path to store backups to
13
+ attr_accessor :path
14
+
15
+ ##
16
+ # Creates a new instance of the Local storage object
17
+ # First it sets the defaults (if any exist) and then evaluates
18
+ # the configuration block which may overwrite these defaults
19
+ def initialize(&block)
20
+ load_defaults!
21
+
22
+ @path ||= "#{ENV['HOME']}/backups"
23
+
24
+ instance_eval(&block) if block_given?
25
+
26
+ @time = TIME
27
+ fix_path!
28
+ end
29
+
30
+ ##
31
+ # This is the remote path to where the backup files will be stored.
32
+ # Eventhough it says "remote", it's actually the "local" path, but
33
+ # the naming is necessary for compatibility reasons
34
+ def remote_path
35
+ File.join(path, TRIGGER)
36
+ end
37
+
38
+ ##
39
+ # Performs the backup transfer
40
+ def perform!
41
+ transfer!
42
+ cycle!
43
+ end
44
+
45
+ private
46
+
47
+ ##
48
+ # Transfers the archived file to the specified local path
49
+ def transfer!
50
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
51
+ create_local_directories!
52
+ FileUtils.cp(
53
+ File.join(local_path, local_file),
54
+ File.join(remote_path, remote_file)
55
+ )
56
+ end
57
+
58
+ ##
59
+ # Removes the transferred archive file from the local path
60
+ def remove!
61
+ FileUtils.rm(File.join(remote_path, remote_file))
62
+ end
63
+
64
+ ##
65
+ # Creates the path to where the backups are stored if it doesn't exist yet
66
+ def create_local_directories!
67
+ FileUtils.mkdir_p(remote_path)
68
+ end
69
+
70
+ ##
71
+ # Replaces ~/ with the full path to the users $HOME directory
72
+ def fix_path!
73
+ @path = path.sub(/^\~\//, "#{ENV['HOME']}/")
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Fog gem when the Backup::Storage::Ninefold class is loaded
5
+ Backup::Dependency.load('fog')
6
+
7
+ module Backup
8
+ module Storage
9
+ class Ninefold < Base
10
+
11
+ ##
12
+ # Ninefold Credentials
13
+ attr_accessor :storage_token, :storage_secret
14
+
15
+ ##
16
+ # Ninefold directory path
17
+ attr_accessor :path
18
+
19
+ ##
20
+ # Creates a new instance of the Ninefold storage object
21
+ # First it sets the defaults (if any exist) and then evaluates
22
+ # the configuration block which may overwrite these defaults
23
+ #
24
+ def initialize(&block)
25
+ load_defaults!
26
+
27
+ @path ||= 'backups'
28
+
29
+ instance_eval(&block) if block_given?
30
+
31
+ @time = TIME
32
+ end
33
+
34
+ ##
35
+ # This is the remote path to where the backup files will be stored
36
+ def remote_path
37
+ File.join(path, TRIGGER).sub(/^\//, '')
38
+ end
39
+
40
+ ##
41
+ # This is the provider that Fog uses for the Ninefold storage
42
+ def provider
43
+ 'Ninefold'
44
+ end
45
+
46
+ ##
47
+ # Performs the backup transfer
48
+ def perform!
49
+ transfer!
50
+ cycle!
51
+ end
52
+
53
+ private
54
+
55
+ ##
56
+ # Establishes a connection to Amazon S3 and returns the Fog object.
57
+ # Not doing any instance variable caching because this object gets persisted in YAML
58
+ # format to a file and will issues. This, however has no impact on performance since it only
59
+ # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
60
+ # background anyway so even if it were a bit slower it shouldn't matter.
61
+ def connection
62
+ Fog::Storage.new(
63
+ :provider => provider,
64
+ :ninefold_storage_token => storage_token,
65
+ :ninefold_storage_secret => storage_secret
66
+ )
67
+ end
68
+
69
+ ##
70
+ # Transfers the archived file to the specified directory
71
+ def transfer!
72
+ begin
73
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
74
+ directory = connection.directories.get remote_path
75
+ directory ||= connection.directories.create(:key => remote_path)
76
+ directory.files.create(
77
+ :key => remote_file,
78
+ :body => File.open(File.join(local_path, local_file))
79
+ )
80
+ rescue Excon::Errors::NotFound
81
+ raise "An error occurred while trying to transfer the file."
82
+ end
83
+ end
84
+
85
+ ##
86
+ # Removes the transferred archive file from the Amazon S3 bucket
87
+ def remove!
88
+ begin
89
+ directory = connection.directories.get remote_path
90
+ directory.files.get(remote_file).destroy
91
+ rescue Excon::Errors::SocketError; end
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -25,6 +25,10 @@ module Backup
25
25
  # Path to store backups to
26
26
  attr_accessor :path
27
27
 
28
+ ##
29
+ # Flag to use local backups
30
+ attr_accessor :local
31
+
28
32
  ##
29
33
  # Creates a new instance of the RSync storage object
30
34
  # First it sets the defaults (if any exist) and then evaluates
@@ -32,8 +36,9 @@ module Backup
32
36
  def initialize(&block)
33
37
  load_defaults!
34
38
 
35
- @port ||= 22
36
- @path ||= 'backups'
39
+ @port ||= 22
40
+ @path ||= 'backups'
41
+ @local ||= false
37
42
 
38
43
  instance_eval(&block) if block_given?
39
44
  write_password_file!
@@ -55,6 +60,25 @@ module Backup
55
60
  remove_password_file!
56
61
  end
57
62
 
63
+ ##
64
+ # Returns Rsync syntax for defining a port to connect to
65
+ def port
66
+ "-e 'ssh -p #{@port}'"
67
+ end
68
+
69
+ ##
70
+ # Returns Rsync syntax for using a password file
71
+ def password
72
+ "--password-file='#{@password_file.path}'" unless @password.nil?
73
+ end
74
+
75
+ ##
76
+ # RSync options
77
+ # -z = Compresses the bytes that will be transferred to reduce bandwidth usage
78
+ def options
79
+ "-z"
80
+ end
81
+
58
82
  private
59
83
 
60
84
  ##
@@ -64,7 +88,7 @@ module Backup
64
88
  # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
65
89
  # background anyway so even if it were a bit slower it shouldn't matter.
66
90
  def connection
67
- Net::SSH.start(ip, username, :password => @password, :port => port)
91
+ Net::SSH.start(ip, username, :password => @password, :port => @port)
68
92
  end
69
93
 
70
94
  ##
@@ -72,7 +96,11 @@ module Backup
72
96
  def transfer!
73
97
  Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
74
98
  create_remote_directories!
75
- run("#{ utility(:rsync) } #{ options } #{ password } '#{ File.join(local_path, local_file) }' '#{ username }@#{ ip }:#{ File.join(remote_path, remote_file[20..-1]) }'")
99
+ if @local
100
+ run("#{ utility(:rsync) } '#{ File.join(local_path, local_file) }' '#{ File.join(remote_path, TIME+'.'+remote_file[20..-1]) }'")
101
+ else
102
+ run("#{ utility(:rsync) } #{ options } #{ port } #{ password } '#{ File.join(local_path, local_file) }' '#{ username }@#{ ip }:#{ File.join(remote_path, remote_file[20..-1]) }'")
103
+ end
76
104
  end
77
105
 
78
106
  ##
@@ -88,22 +116,11 @@ module Backup
88
116
  # Creates (if they don't exist yet) all the directories on the remote
89
117
  # server in order to upload the backup file.
90
118
  def create_remote_directories!
91
- connection.exec!("mkdir -p '#{ remote_path }'")
92
- end
93
-
94
- ##
95
- # RSync options
96
- # -z = Compresses the bytes that will be transferred to reduce bandwidth usage
97
- # --port = the port to connect to through SSH
98
- # -Phv = debug options
99
- def options
100
- "-z --port='#{ port }'"
101
- end
102
-
103
- ##
104
- # Returns Rsync syntax for using a password file
105
- def password
106
- "--password-file='#{@password_file.path}'" unless @password.nil?
119
+ if @local
120
+ mkdir(remote_path)
121
+ else
122
+ connection.exec!("mkdir -p '#{ remote_path }'")
123
+ end
107
124
  end
108
125
 
109
126
  ##
@@ -77,7 +77,7 @@ module Backup
77
77
  # Transfers the archived file to the specified Amazon S3 bucket
78
78
  def transfer!
79
79
  begin
80
- Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
80
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\" to bucket \"#{ bucket }\"")
81
81
  connection.sync_clock
82
82
  connection.put_object(
83
83
  bucket,
@@ -55,7 +55,7 @@ module Backup
55
55
  private
56
56
 
57
57
  ##
58
- # Establishes a connection to the remote server and returns the Net::SCP object.
58
+ # Establishes a connection to the remote server and returns the Net::SSH object.
59
59
  # Not doing any instance variable caching because this object gets persisted in YAML
60
60
  # format to a file and will issues. This, however has no impact on performance since it only
61
61
  # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
@@ -100,7 +100,7 @@ module Backup
100
100
  ##
101
101
  # Returns Rsync syntax for defining a port to connect to
102
102
  def port
103
- "--port='#{@port}'"
103
+ "-e 'ssh -p #{@port}'"
104
104
  end
105
105
 
106
106
  ##
@@ -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, 16
16
+ MAJOR, MINOR, PATCH = 3, 0, 18
17
17
 
18
18
  ##
19
19
  # Returns the major version ( big release based off of multiple minor releases )
@@ -0,0 +1,7 @@
1
+ ##
2
+ # Lzma [Compressor]
3
+ #
4
+ compress_with Lzma do |compression|
5
+ compression.best = true
6
+ compression.fast = false
7
+ end
@@ -1,9 +1,9 @@
1
1
  ##
2
2
  # Dropbox File Hosting Service [Storage]
3
+ # Note: Initial backup must be performed manually to authorize
4
+ # this machine with your Dropbox account.
3
5
  #
4
6
  store_with Dropbox do |db|
5
- db.email = 'my@email.com'
6
- db.password = 'my_password'
7
7
  db.api_key = 'my_api_key'
8
8
  db.api_secret = 'my_api_secret'
9
9
  db.timeout = 300
@@ -2,10 +2,11 @@
2
2
  # FTP (File Transfer Protocol) [Storage]
3
3
  #
4
4
  store_with FTP do |server|
5
- server.username = 'my_username'
6
- server.password = 'my_password'
7
- server.ip = '123.45.678.90'
8
- server.port = 21
9
- server.path = '~/backups/'
10
- server.keep = 5
11
- end
5
+ server.username = 'my_username'
6
+ server.password = 'my_password'
7
+ server.ip = '123.45.678.90'
8
+ server.port = 21
9
+ server.path = '~/backups/'
10
+ server.keep = 5
11
+ server.passive_mode = false
12
+ end
@@ -0,0 +1,7 @@
1
+ ##
2
+ # Local (Copy) [Storage]
3
+ #
4
+ store_with Local do |local|
5
+ local.path = '~/backups/'
6
+ local.keep = 5
7
+ end
@@ -0,0 +1,9 @@
1
+ ##
2
+ # Ninefold Cloud Storage [Storage]
3
+ #
4
+ store_with Ninefold do |nf|
5
+ nf.storage_token = 'my_storage_token'
6
+ nf.storage_secret = 'my_storage_secret'
7
+ nf.path = '/path/to/my/backups'
8
+ nf.keep = 10
9
+ end
@@ -7,4 +7,5 @@
7
7
  server.ip = '123.45.678.90'
8
8
  server.port = 22
9
9
  server.path = '~/backups/'
10
+ server.local = false
10
11
  end
@@ -58,7 +58,6 @@ describe Backup::Archive do
58
58
  describe '#perform!' do
59
59
  before do
60
60
  [:mkdir, :run, :utility].each { |method| archive.stubs(method) }
61
- Backup::Logger.stubs(:message)
62
61
  end
63
62
 
64
63
  context 'when both paths were added and paths that should be excluded were added' do
@@ -22,7 +22,6 @@ describe Backup::Compressor::Bzip2 do
22
22
  describe '#perform!' do
23
23
  before do
24
24
  [:run, :utility].each { |method| compressor.stubs(method) }
25
- Backup::Logger.stubs(:message)
26
25
  end
27
26
 
28
27
  it 'should perform the compression' do
@@ -22,7 +22,6 @@ describe Backup::Compressor::Gzip do
22
22
  describe '#perform!' do
23
23
  before do
24
24
  [:run, :utility].each { |method| compressor.stubs(method) }
25
- Backup::Logger.stubs(:message)
26
25
  end
27
26
 
28
27
  it 'should perform the compression' do
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require File.dirname(__FILE__) + '/../spec_helper'
4
+
5
+ describe Backup::Compressor::Lzma do
6
+ let(:compressor) { Backup::Compressor::Lzma.new }
7
+
8
+ before do
9
+ Backup::Model.extension = 'tar'
10
+ end
11
+
12
+ describe 'the options' do
13
+ it do
14
+ compressor.send(:best).should == []
15
+ end
16
+
17
+ it do
18
+ compressor.send(:fast).should == []
19
+ end
20
+ end
21
+
22
+ describe '#perform!' do
23
+ before do
24
+ [:run, :utility].each { |method| compressor.stubs(method) }
25
+ end
26
+
27
+ it 'should perform the compression' do
28
+ compressor.expects(:utility).with(:lzma).returns(:lzma)
29
+ compressor.expects(:run).with("lzma '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }'")
30
+ compressor.perform!
31
+ end
32
+
33
+ it 'should perform the compression with the --best and --fast options' do
34
+ compressor = Backup::Compressor::Lzma.new do |c|
35
+ c.best = true
36
+ c.fast = true
37
+ end
38
+
39
+ compressor.stubs(:utility).returns(:lzma)
40
+ compressor.expects(:run).with("lzma --best --fast '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }'")
41
+ compressor.perform!
42
+ end
43
+
44
+ it 'should set the class variable @extension (Backup::Model.extension) to .lzma' do
45
+ compressor.stubs(:utility).returns(:lzma)
46
+ compressor.expects(:run)
47
+
48
+ Backup::Model.extension.should == 'tar'
49
+ compressor.perform!
50
+ Backup::Model.extension.should == 'tar.lzma'
51
+ end
52
+
53
+ it 'should log' do
54
+ Backup::Logger.expects(:message).with("Backup::Compressor::Lzma started compressing the archive.")
55
+ compressor.perform!
56
+ end
57
+ end
58
+ end