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
@@ -24,44 +24,42 @@ module Backup
24
24
  # use passive mode?
25
25
  attr_accessor :passive_mode
26
26
 
27
- ##
28
- # Creates a new instance of the FTP storage object
29
- # First it sets the defaults (if any exist) and then evaluates
30
- # the configuration block which may overwrite these defaults
31
- def initialize(&block)
32
- load_defaults!
33
-
34
- @port ||= 21
35
- @path ||= 'backups'
36
- @passive_mode ||= false
37
-
38
- instance_eval(&block) if block_given?
39
-
40
- @time = TIME
41
- @path = path.sub(/^\~\//, '')
42
- end
43
-
44
27
  ##
45
28
  # This is the remote path to where the backup files will be stored
46
29
  def remote_path
47
- File.join(path, TRIGGER)
30
+ File.join(path, TRIGGER, @time)
48
31
  end
49
32
 
50
33
  ##
51
34
  # Performs the backup transfer
52
35
  def perform!
36
+ super
53
37
  transfer!
54
38
  cycle!
55
39
  end
56
40
 
57
41
  private
58
42
 
43
+ ##
44
+ # Set configuration defaults before evaluating configuration block,
45
+ # after setting defaults from Storage::Base
46
+ def pre_configure
47
+ super
48
+ @port ||= 21
49
+ @path ||= 'backups'
50
+ @passive_mode ||= false
51
+ end
52
+
53
+ ##
54
+ # Adjust configuration after evaluating configuration block,
55
+ # after adjustments from Storage::Base
56
+ def post_configure
57
+ super
58
+ @path = path.sub(/^\~\//, '')
59
+ end
60
+
59
61
  ##
60
62
  # Establishes a connection to the remote server and returns the Net::FTP object.
61
- # Not doing any instance variable caching because this object gets persisted in YAML
62
- # format to a file and will issues. This, however has no impact on performance since it only
63
- # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
64
- # background anyway so even if it were a bit slower it shouldn't matter.
65
63
  #
66
64
  # Note *
67
65
  # Since the FTP port is defined as a constant in the Net::FTP class, and might be required
@@ -71,31 +69,41 @@ module Backup
71
69
  Net::FTP.send(:remove_const, :FTP_PORT)
72
70
  end; Net::FTP.send(:const_set, :FTP_PORT, port)
73
71
 
74
- ftp = Net::FTP.new(ip, username, password)
75
- ftp.passive = true if passive_mode
76
- ftp
72
+ Net::FTP.open(ip, username, password) do |ftp|
73
+ ftp.passive = true if passive_mode
74
+ yield ftp
75
+ end
77
76
  end
78
77
 
79
78
  ##
80
79
  # Transfers the archived file to the specified remote server
81
80
  def transfer!
82
- Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
83
- create_remote_directories!
84
- connection.put(
85
- File.join(local_path, local_file),
86
- File.join(remote_path, remote_file)
87
- )
81
+ connection do |ftp|
82
+ create_remote_directories(ftp)
83
+
84
+ files_to_transfer do |local_file, remote_file|
85
+ Logger.message "#{storage_name} started transferring " +
86
+ "'#{ local_file }' to '#{ ip }'."
87
+ ftp.put(
88
+ File.join(local_path, local_file),
89
+ File.join(remote_path, remote_file)
90
+ )
91
+ end
92
+ end
88
93
  end
89
94
 
90
95
  ##
91
96
  # Removes the transferred archive file from the server
92
97
  def remove!
93
- begin
94
- connection.delete(
95
- File.join(remote_path, remote_file)
96
- )
97
- rescue Net::FTPPermError
98
- Logger.warn "Could not remove file \"#{ File.join(remote_path, remote_file) }\"."
98
+ connection do |ftp|
99
+ transferred_files do |local_file, remote_file|
100
+ Logger.message "#{storage_name} started removing " +
101
+ "'#{ local_file }' from '#{ ip }'."
102
+
103
+ ftp.delete(File.join(remote_path, remote_file))
104
+ end
105
+
106
+ ftp.rmdir(remote_path)
99
107
  end
100
108
  end
101
109
 
@@ -105,13 +113,13 @@ module Backup
105
113
  # paths to directories that don't yet exist when creating new directories.
106
114
  # Instead, we split the parts up in to an array (for each '/') and loop through
107
115
  # that to create the directories one by one. Net::FTP raises an exception when
108
- # the directory it's trying ot create already exists, so we have rescue it
109
- def create_remote_directories!
116
+ # the directory it's trying to create already exists, so we have rescue it
117
+ def create_remote_directories(ftp)
110
118
  path_parts = Array.new
111
119
  remote_path.split('/').each do |path_part|
112
120
  path_parts << path_part
113
121
  begin
114
- connection.mkdir(path_parts.join('/'))
122
+ ftp.mkdir(path_parts.join('/'))
115
123
  rescue Net::FTPPermError; end
116
124
  end
117
125
  end
@@ -12,53 +12,64 @@ module Backup
12
12
  # Path to store backups to
13
13
  attr_accessor :path
14
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
15
  ##
31
16
  # This is the remote path to where the backup files will be stored.
32
17
  # Eventhough it says "remote", it's actually the "local" path, but
33
18
  # the naming is necessary for compatibility reasons
34
19
  def remote_path
35
- File.join(path, TRIGGER)
20
+ File.join(path, TRIGGER, @time)
36
21
  end
37
22
 
38
23
  ##
39
24
  # Performs the backup transfer
40
25
  def perform!
26
+ super
41
27
  transfer!
42
28
  cycle!
43
29
  end
44
30
 
45
31
  private
46
32
 
33
+ ##
34
+ # Set configuration defaults before evaluating configuration block,
35
+ # after setting defaults from Storage::Base
36
+ def pre_configure
37
+ super
38
+ @path ||= "#{ENV['HOME']}/backups"
39
+ end
40
+
41
+ ##
42
+ # Adjust configuration after evaluating configuration block,
43
+ # after adjustments from Storage::Base
44
+ def post_configure
45
+ super
46
+ @path = File.expand_path(path)
47
+ end
48
+
47
49
  ##
48
50
  # Transfers the archived file to the specified local path
49
51
  def transfer!
50
- Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
51
52
  create_local_directories!
52
- FileUtils.cp(
53
- File.join(local_path, local_file),
54
- File.join(remote_path, remote_file)
55
- )
53
+
54
+ files_to_transfer do |local_file, remote_file|
55
+ Logger.message "#{storage_name} started transferring '#{ local_file }'."
56
+ FileUtils.cp(
57
+ File.join(local_path, local_file),
58
+ File.join(remote_path, remote_file)
59
+ )
60
+ end
56
61
  end
57
62
 
58
63
  ##
59
64
  # Removes the transferred archive file from the local path
60
65
  def remove!
61
- FileUtils.rm(File.join(remote_path, remote_file))
66
+ messages = []
67
+ transferred_files do |local_file, remote_file|
68
+ messages << "#{storage_name} started removing '#{ local_file }'."
69
+ end
70
+ Logger.message messages.join("\n")
71
+
72
+ FileUtils.rm_r(remote_path)
62
73
  end
63
74
 
64
75
  ##
@@ -67,12 +78,6 @@ module Backup
67
78
  FileUtils.mkdir_p(remote_path)
68
79
  end
69
80
 
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
81
  end
77
82
  end
78
83
  end
@@ -16,25 +16,10 @@ module Backup
16
16
  # Ninefold directory path
17
17
  attr_accessor :path
18
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
19
  ##
35
20
  # This is the remote path to where the backup files will be stored
36
21
  def remote_path
37
- File.join(path, TRIGGER).sub(/^\//, '')
22
+ File.join(path, TRIGGER, @time).sub(/^\//, '')
38
23
  end
39
24
 
40
25
  ##
@@ -46,12 +31,28 @@ module Backup
46
31
  ##
47
32
  # Performs the backup transfer
48
33
  def perform!
34
+ super
49
35
  transfer!
50
36
  cycle!
51
37
  end
52
38
 
53
39
  private
54
40
 
41
+ ##
42
+ # Set configuration defaults before evaluating configuration block,
43
+ # after setting defaults from Storage::Base
44
+ def pre_configure
45
+ super
46
+ @path ||= 'backups'
47
+ end
48
+
49
+ ##
50
+ # Adjust configuration after evaluating configuration block,
51
+ # after adjustments from Storage::Base
52
+ def post_configure
53
+ super
54
+ end
55
+
55
56
  ##
56
57
  # Establishes a connection to Amazon S3 and returns the Fog object.
57
58
  # Not doing any instance variable caching because this object gets persisted in YAML
@@ -59,7 +60,7 @@ module Backup
59
60
  # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
60
61
  # background anyway so even if it were a bit slower it shouldn't matter.
61
62
  def connection
62
- Fog::Storage.new(
63
+ @connection ||= Fog::Storage.new(
63
64
  :provider => provider,
64
65
  :ninefold_storage_token => storage_token,
65
66
  :ninefold_storage_secret => storage_secret
@@ -69,26 +70,39 @@ module Backup
69
70
  ##
70
71
  # Transfers the archived file to the specified directory
71
72
  def transfer!
72
- begin
73
- Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
74
- directory = connection.directories.get remote_path
73
+ files_to_transfer do |local_file, remote_file|
74
+ Logger.message "#{storage_name} started transferring '#{ local_file }'."
75
+ directory = connection.directories.get(remote_path)
75
76
  directory ||= connection.directories.create(:key => remote_path)
76
77
  directory.files.create(
77
78
  :key => remote_file,
78
79
  :body => File.open(File.join(local_path, local_file))
79
80
  )
80
- rescue Excon::Errors::NotFound
81
- raise "An error occurred while trying to transfer the file."
82
81
  end
83
82
  end
84
83
 
85
84
  ##
86
85
  # Removes the transferred archive file from the Amazon S3 bucket
87
86
  def remove!
88
- begin
89
- directory = connection.directories.get remote_path
90
- directory.files.get(remote_file).destroy
91
- rescue Excon::Errors::SocketError; end
87
+ if directory = connection.directories.get(remote_path)
88
+ transferred_files do |local_file, remote_file|
89
+ Logger.message "#{storage_name} started removing " +
90
+ "'#{ local_file }' from Ninefold.'"
91
+
92
+ if file = directory.files.get(remote_file)
93
+ file.destroy
94
+ else
95
+ # Note: Fog-0.11.0 will return nil if remote_file is not found
96
+ raise Errors::Storage::Ninefold::NotFoundError,
97
+ "'#{remote_file}' not found in '#{remote_path}'", caller(1)
98
+ end
99
+ end
100
+ directory.destroy
101
+ else
102
+ # Note: Fog-0.11.0 will return nil if remote_path is not found
103
+ raise Errors::Storage::Ninefold::NotFoundError,
104
+ "Directory at '#{remote_path}' not found", caller(1)
105
+ end
92
106
  end
93
107
 
94
108
  end
@@ -11,8 +11,10 @@ module Backup
11
11
  ##
12
12
  # Instantiates a new Backup::Storage::Object and stores the
13
13
  # full path to the storage file (yaml) in the @storage_file attribute
14
- def initialize(type)
15
- @storage_file = File.join(DATA_PATH, TRIGGER, "#{type}.yml")
14
+ def initialize(type, storage_id)
15
+ suffix = storage_id.to_s.strip.gsub(/[\W\s]/, '_')
16
+ filename = suffix.empty? ? type : "#{type}-#{suffix}"
17
+ @storage_file = File.join(DATA_PATH, TRIGGER, "#{filename}.yml")
16
18
  end
17
19
 
18
20
  ##
@@ -24,11 +26,11 @@ module Backup
24
26
  # descending. The newest backup storage object comes in Backup::Storage::Object.load[0]
25
27
  # and the oldest in Backup::Storage::Object.load[-1]
26
28
  def load
27
- if File.exist?(storage_file)
28
- YAML.load_file(storage_file).sort { |a,b| b.time <=> a.time }
29
- else
30
- []
29
+ objects = []
30
+ if File.exist?(storage_file) and not File.zero?(storage_file)
31
+ objects = YAML.load_file(storage_file).sort { |a,b| b.time <=> a.time }
31
32
  end
33
+ objects
32
34
  end
33
35
 
34
36
  ##
@@ -11,7 +11,7 @@ Backup::Dependency.load('net-ssh')
11
11
  module Backup
12
12
  module Storage
13
13
  class RSync < Base
14
- include Backup::CLI
14
+ include Backup::CLI::Helpers
15
15
 
16
16
  ##
17
17
  # Server credentials
@@ -29,24 +29,6 @@ module Backup
29
29
  # Flag to use local backups
30
30
  attr_accessor :local
31
31
 
32
- ##
33
- # Creates a new instance of the RSync storage object
34
- # First it sets the defaults (if any exist) and then evaluates
35
- # the configuration block which may overwrite these defaults
36
- def initialize(&block)
37
- load_defaults!
38
-
39
- @port ||= 22
40
- @path ||= 'backups'
41
- @local ||= false
42
-
43
- instance_eval(&block) if block_given?
44
- write_password_file!
45
-
46
- @time = TIME
47
- @path = path.sub(/^\~\//, '')
48
- end
49
-
50
32
  ##
51
33
  # This is the remote path to where the backup files will be stored
52
34
  def remote_path
@@ -56,60 +38,67 @@ module Backup
56
38
  ##
57
39
  # Performs the backup transfer
58
40
  def perform!
41
+ super
42
+ write_password_file!
59
43
  transfer!
44
+ ensure
60
45
  remove_password_file!
61
46
  end
62
47
 
63
- ##
64
- # Returns Rsync syntax for defining a port to connect to
65
- def port
66
- "-e 'ssh -p #{@port}'"
67
- end
48
+ private
68
49
 
69
50
  ##
70
- # Returns Rsync syntax for using a password file
71
- def password
72
- "--password-file='#{@password_file.path}'" unless @password.nil?
51
+ # Set configuration defaults before evaluating configuration block,
52
+ # after setting defaults from Storage::Base
53
+ def pre_configure
54
+ super
55
+ @port ||= 22
56
+ @path ||= 'backups'
57
+ @local ||= false
73
58
  end
74
59
 
75
60
  ##
76
- # RSync options
77
- # -z = Compresses the bytes that will be transferred to reduce bandwidth usage
78
- def options
79
- "-z"
61
+ # Adjust configuration after evaluating configuration block,
62
+ # after adjustments from Storage::Base
63
+ def post_configure
64
+ super
65
+ @path = path.sub(/^\~\//, '')
80
66
  end
81
67
 
82
- private
83
-
84
68
  ##
85
69
  # Establishes a connection to the remote server and returns the Net::SSH object.
86
- # Not doing any instance variable caching because this object gets persisted in YAML
87
- # format to a file and will issues. This, however has no impact on performance since it only
88
- # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
89
- # background anyway so even if it were a bit slower it shouldn't matter.
90
70
  def connection
91
- Net::SSH.start(ip, username, :password => @password, :port => @port)
71
+ Net::SSH.start(ip, username, :password => password, :port => port) do |ssh|
72
+ yield ssh
73
+ end
92
74
  end
93
75
 
94
76
  ##
95
77
  # Transfers the archived file to the specified remote server
96
78
  def transfer!
97
- Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
98
79
  create_remote_directories!
99
- if @local
100
- run("#{ utility(:rsync) } '#{ File.join(local_path, local_file) }' '#{ File.join(remote_path, TIME+'.'+remote_file[20..-1]) }'")
80
+
81
+ Logger.message "#{storage_name} started transferring " +
82
+ "'#{ filename }' to '#{ ip }'."
83
+
84
+ if local
85
+ run(
86
+ "#{ utility(:rsync) } '#{ File.join(local_path, filename) }' " +
87
+ "'#{ File.join(remote_path, filename[20..-1]) }'"
88
+ )
101
89
  else
102
- run("#{ utility(:rsync) } #{ options } #{ port } #{ password } '#{ File.join(local_path, local_file) }' '#{ username }@#{ ip }:#{ File.join(remote_path, remote_file[20..-1]) }'")
90
+ run(
91
+ "#{ utility(:rsync) } #{ rsync_options } #{ rsync_port } " +
92
+ "#{ rsync_password_file } '#{ File.join(local_path, filename) }' " +
93
+ "'#{ username }@#{ ip }:#{ File.join(remote_path, filename[20..-1]) }'"
94
+ )
103
95
  end
104
96
  end
105
97
 
106
98
  ##
107
- # Removes the transferred archive file from the server
99
+ # Note: RSync::Storage doesn't cycle
108
100
  def remove!
109
- response = connection.exec!("rm #{ File.join(remote_path, remote_file) }")
110
- if response =~ /No such file or directory/
111
- Logger.warn "Could not remove file \"#{ File.join(remote_path, remote_file) }\"."
112
- end
101
+ nil
113
102
  end
114
103
 
115
104
  ##
@@ -119,7 +108,9 @@ module Backup
119
108
  if @local
120
109
  mkdir(remote_path)
121
110
  else
122
- connection.exec!("mkdir -p '#{ remote_path }'")
111
+ connection do |ssh|
112
+ ssh.exec!("mkdir -p '#{ remote_path }'")
113
+ end
123
114
  end
124
115
  end
125
116
 
@@ -127,9 +118,9 @@ module Backup
127
118
  # Writes the provided password to a temporary file so that
128
119
  # the rsync utility can read the password from this file
129
120
  def write_password_file!
130
- unless @password.nil?
121
+ unless password.nil?
131
122
  @password_file = Tempfile.new('backup-rsync-password')
132
- @password_file.write(@password)
123
+ @password_file.write(password)
133
124
  @password_file.close
134
125
  end
135
126
  end
@@ -138,7 +129,26 @@ module Backup
138
129
  # Removes the previously created @password_file
139
130
  # (temporary file containing the password)
140
131
  def remove_password_file!
141
- @password_file.unlink unless @password.nil?
132
+ @password_file.delete if @password_file
133
+ end
134
+
135
+ ##
136
+ # Returns Rsync syntax for using a password file
137
+ def rsync_password_file
138
+ "--password-file='#{@password_file.path}'" if @password_file
139
+ end
140
+
141
+ ##
142
+ # Returns Rsync syntax for defining a port to connect to
143
+ def rsync_port
144
+ "-e 'ssh -p #{port}'"
145
+ end
146
+
147
+ ##
148
+ # RSync options
149
+ # -z = Compresses the bytes that will be transferred to reduce bandwidth usage
150
+ def rsync_options
151
+ "-z"
142
152
  end
143
153
 
144
154
  end