backup_checksum 3.0.23

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 (244) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +28 -0
  4. data/Gemfile.lock +130 -0
  5. data/Guardfile +21 -0
  6. data/LICENSE.md +24 -0
  7. data/README.md +476 -0
  8. data/backup_checksum.gemspec +32 -0
  9. data/bin/backup +11 -0
  10. data/lib/backup.rb +217 -0
  11. data/lib/backup/archive.rb +117 -0
  12. data/lib/backup/binder.rb +22 -0
  13. data/lib/backup/checksum/base.rb +44 -0
  14. data/lib/backup/checksum/shasum.rb +16 -0
  15. data/lib/backup/cleaner.rb +121 -0
  16. data/lib/backup/cli/helpers.rb +88 -0
  17. data/lib/backup/cli/utility.rb +247 -0
  18. data/lib/backup/compressor/base.rb +29 -0
  19. data/lib/backup/compressor/bzip2.rb +50 -0
  20. data/lib/backup/compressor/gzip.rb +47 -0
  21. data/lib/backup/compressor/lzma.rb +50 -0
  22. data/lib/backup/compressor/pbzip2.rb +56 -0
  23. data/lib/backup/config.rb +173 -0
  24. data/lib/backup/configuration/base.rb +15 -0
  25. data/lib/backup/configuration/checksum/base.rb +9 -0
  26. data/lib/backup/configuration/checksum/shasum.rb +9 -0
  27. data/lib/backup/configuration/compressor/base.rb +9 -0
  28. data/lib/backup/configuration/compressor/bzip2.rb +23 -0
  29. data/lib/backup/configuration/compressor/gzip.rb +23 -0
  30. data/lib/backup/configuration/compressor/lzma.rb +23 -0
  31. data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
  32. data/lib/backup/configuration/database/base.rb +19 -0
  33. data/lib/backup/configuration/database/mongodb.rb +49 -0
  34. data/lib/backup/configuration/database/mysql.rb +42 -0
  35. data/lib/backup/configuration/database/postgresql.rb +41 -0
  36. data/lib/backup/configuration/database/redis.rb +39 -0
  37. data/lib/backup/configuration/database/riak.rb +29 -0
  38. data/lib/backup/configuration/encryptor/base.rb +9 -0
  39. data/lib/backup/configuration/encryptor/gpg.rb +17 -0
  40. data/lib/backup/configuration/encryptor/open_ssl.rb +32 -0
  41. data/lib/backup/configuration/helpers.rb +52 -0
  42. data/lib/backup/configuration/notifier/base.rb +28 -0
  43. data/lib/backup/configuration/notifier/campfire.rb +25 -0
  44. data/lib/backup/configuration/notifier/hipchat.rb +41 -0
  45. data/lib/backup/configuration/notifier/mail.rb +112 -0
  46. data/lib/backup/configuration/notifier/presently.rb +25 -0
  47. data/lib/backup/configuration/notifier/prowl.rb +23 -0
  48. data/lib/backup/configuration/notifier/twitter.rb +21 -0
  49. data/lib/backup/configuration/storage/base.rb +18 -0
  50. data/lib/backup/configuration/storage/cloudfiles.rb +25 -0
  51. data/lib/backup/configuration/storage/dropbox.rb +58 -0
  52. data/lib/backup/configuration/storage/ftp.rb +29 -0
  53. data/lib/backup/configuration/storage/local.rb +17 -0
  54. data/lib/backup/configuration/storage/ninefold.rb +20 -0
  55. data/lib/backup/configuration/storage/rsync.rb +29 -0
  56. data/lib/backup/configuration/storage/s3.rb +25 -0
  57. data/lib/backup/configuration/storage/scp.rb +25 -0
  58. data/lib/backup/configuration/storage/sftp.rb +25 -0
  59. data/lib/backup/configuration/syncer/base.rb +10 -0
  60. data/lib/backup/configuration/syncer/cloud.rb +23 -0
  61. data/lib/backup/configuration/syncer/cloud_files.rb +30 -0
  62. data/lib/backup/configuration/syncer/rsync/base.rb +28 -0
  63. data/lib/backup/configuration/syncer/rsync/local.rb +11 -0
  64. data/lib/backup/configuration/syncer/rsync/pull.rb +11 -0
  65. data/lib/backup/configuration/syncer/rsync/push.rb +31 -0
  66. data/lib/backup/configuration/syncer/s3.rb +23 -0
  67. data/lib/backup/database/base.rb +59 -0
  68. data/lib/backup/database/mongodb.rb +232 -0
  69. data/lib/backup/database/mysql.rb +163 -0
  70. data/lib/backup/database/postgresql.rb +146 -0
  71. data/lib/backup/database/redis.rb +139 -0
  72. data/lib/backup/database/riak.rb +69 -0
  73. data/lib/backup/dependency.rb +114 -0
  74. data/lib/backup/encryptor/base.rb +29 -0
  75. data/lib/backup/encryptor/gpg.rb +80 -0
  76. data/lib/backup/encryptor/open_ssl.rb +72 -0
  77. data/lib/backup/errors.rb +124 -0
  78. data/lib/backup/logger.rb +152 -0
  79. data/lib/backup/model.rb +386 -0
  80. data/lib/backup/notifier/base.rb +81 -0
  81. data/lib/backup/notifier/campfire.rb +168 -0
  82. data/lib/backup/notifier/hipchat.rb +99 -0
  83. data/lib/backup/notifier/mail.rb +206 -0
  84. data/lib/backup/notifier/presently.rb +88 -0
  85. data/lib/backup/notifier/prowl.rb +65 -0
  86. data/lib/backup/notifier/twitter.rb +70 -0
  87. data/lib/backup/package.rb +51 -0
  88. data/lib/backup/packager.rb +108 -0
  89. data/lib/backup/pipeline.rb +107 -0
  90. data/lib/backup/splitter.rb +75 -0
  91. data/lib/backup/storage/base.rb +119 -0
  92. data/lib/backup/storage/cloudfiles.rb +87 -0
  93. data/lib/backup/storage/cycler.rb +117 -0
  94. data/lib/backup/storage/dropbox.rb +181 -0
  95. data/lib/backup/storage/ftp.rb +119 -0
  96. data/lib/backup/storage/local.rb +82 -0
  97. data/lib/backup/storage/ninefold.rb +116 -0
  98. data/lib/backup/storage/rsync.rb +149 -0
  99. data/lib/backup/storage/s3.rb +94 -0
  100. data/lib/backup/storage/scp.rb +99 -0
  101. data/lib/backup/storage/sftp.rb +108 -0
  102. data/lib/backup/syncer/base.rb +42 -0
  103. data/lib/backup/syncer/cloud.rb +190 -0
  104. data/lib/backup/syncer/cloud_files.rb +56 -0
  105. data/lib/backup/syncer/rsync/base.rb +52 -0
  106. data/lib/backup/syncer/rsync/local.rb +53 -0
  107. data/lib/backup/syncer/rsync/pull.rb +38 -0
  108. data/lib/backup/syncer/rsync/push.rb +113 -0
  109. data/lib/backup/syncer/s3.rb +47 -0
  110. data/lib/backup/template.rb +46 -0
  111. data/lib/backup/version.rb +43 -0
  112. data/spec/archive_spec.rb +335 -0
  113. data/spec/cleaner_spec.rb +304 -0
  114. data/spec/cli/helpers_spec.rb +176 -0
  115. data/spec/cli/utility_spec.rb +363 -0
  116. data/spec/compressor/base_spec.rb +31 -0
  117. data/spec/compressor/bzip2_spec.rb +83 -0
  118. data/spec/compressor/gzip_spec.rb +83 -0
  119. data/spec/compressor/lzma_spec.rb +83 -0
  120. data/spec/compressor/pbzip2_spec.rb +124 -0
  121. data/spec/config_spec.rb +321 -0
  122. data/spec/configuration/base_spec.rb +35 -0
  123. data/spec/configuration/compressor/bzip2_spec.rb +29 -0
  124. data/spec/configuration/compressor/gzip_spec.rb +29 -0
  125. data/spec/configuration/compressor/lzma_spec.rb +29 -0
  126. data/spec/configuration/compressor/pbzip2_spec.rb +32 -0
  127. data/spec/configuration/database/base_spec.rb +17 -0
  128. data/spec/configuration/database/mongodb_spec.rb +56 -0
  129. data/spec/configuration/database/mysql_spec.rb +53 -0
  130. data/spec/configuration/database/postgresql_spec.rb +53 -0
  131. data/spec/configuration/database/redis_spec.rb +50 -0
  132. data/spec/configuration/database/riak_spec.rb +35 -0
  133. data/spec/configuration/encryptor/gpg_spec.rb +26 -0
  134. data/spec/configuration/encryptor/open_ssl_spec.rb +35 -0
  135. data/spec/configuration/notifier/base_spec.rb +32 -0
  136. data/spec/configuration/notifier/campfire_spec.rb +32 -0
  137. data/spec/configuration/notifier/hipchat_spec.rb +44 -0
  138. data/spec/configuration/notifier/mail_spec.rb +71 -0
  139. data/spec/configuration/notifier/presently_spec.rb +35 -0
  140. data/spec/configuration/notifier/prowl_spec.rb +29 -0
  141. data/spec/configuration/notifier/twitter_spec.rb +35 -0
  142. data/spec/configuration/storage/cloudfiles_spec.rb +41 -0
  143. data/spec/configuration/storage/dropbox_spec.rb +38 -0
  144. data/spec/configuration/storage/ftp_spec.rb +44 -0
  145. data/spec/configuration/storage/local_spec.rb +29 -0
  146. data/spec/configuration/storage/ninefold_spec.rb +32 -0
  147. data/spec/configuration/storage/rsync_spec.rb +41 -0
  148. data/spec/configuration/storage/s3_spec.rb +38 -0
  149. data/spec/configuration/storage/scp_spec.rb +41 -0
  150. data/spec/configuration/storage/sftp_spec.rb +41 -0
  151. data/spec/configuration/syncer/cloud_files_spec.rb +44 -0
  152. data/spec/configuration/syncer/rsync/base_spec.rb +33 -0
  153. data/spec/configuration/syncer/rsync/local_spec.rb +10 -0
  154. data/spec/configuration/syncer/rsync/pull_spec.rb +10 -0
  155. data/spec/configuration/syncer/rsync/push_spec.rb +43 -0
  156. data/spec/configuration/syncer/s3_spec.rb +38 -0
  157. data/spec/database/base_spec.rb +54 -0
  158. data/spec/database/mongodb_spec.rb +428 -0
  159. data/spec/database/mysql_spec.rb +335 -0
  160. data/spec/database/postgresql_spec.rb +278 -0
  161. data/spec/database/redis_spec.rb +260 -0
  162. data/spec/database/riak_spec.rb +108 -0
  163. data/spec/dependency_spec.rb +49 -0
  164. data/spec/encryptor/base_spec.rb +30 -0
  165. data/spec/encryptor/gpg_spec.rb +134 -0
  166. data/spec/encryptor/open_ssl_spec.rb +129 -0
  167. data/spec/errors_spec.rb +306 -0
  168. data/spec/logger_spec.rb +363 -0
  169. data/spec/model_spec.rb +649 -0
  170. data/spec/notifier/base_spec.rb +89 -0
  171. data/spec/notifier/campfire_spec.rb +199 -0
  172. data/spec/notifier/hipchat_spec.rb +188 -0
  173. data/spec/notifier/mail_spec.rb +280 -0
  174. data/spec/notifier/presently_spec.rb +181 -0
  175. data/spec/notifier/prowl_spec.rb +117 -0
  176. data/spec/notifier/twitter_spec.rb +132 -0
  177. data/spec/package_spec.rb +61 -0
  178. data/spec/packager_spec.rb +225 -0
  179. data/spec/pipeline_spec.rb +257 -0
  180. data/spec/spec_helper.rb +59 -0
  181. data/spec/splitter_spec.rb +120 -0
  182. data/spec/storage/base_spec.rb +160 -0
  183. data/spec/storage/cloudfiles_spec.rb +230 -0
  184. data/spec/storage/cycler_spec.rb +239 -0
  185. data/spec/storage/dropbox_spec.rb +370 -0
  186. data/spec/storage/ftp_spec.rb +247 -0
  187. data/spec/storage/local_spec.rb +235 -0
  188. data/spec/storage/ninefold_spec.rb +319 -0
  189. data/spec/storage/rsync_spec.rb +345 -0
  190. data/spec/storage/s3_spec.rb +221 -0
  191. data/spec/storage/scp_spec.rb +209 -0
  192. data/spec/storage/sftp_spec.rb +220 -0
  193. data/spec/syncer/base_spec.rb +22 -0
  194. data/spec/syncer/cloud_files_spec.rb +192 -0
  195. data/spec/syncer/rsync/base_spec.rb +118 -0
  196. data/spec/syncer/rsync/local_spec.rb +121 -0
  197. data/spec/syncer/rsync/pull_spec.rb +90 -0
  198. data/spec/syncer/rsync/push_spec.rb +327 -0
  199. data/spec/syncer/s3_spec.rb +192 -0
  200. data/spec/version_spec.rb +21 -0
  201. data/templates/cli/utility/archive +25 -0
  202. data/templates/cli/utility/compressor/bzip2 +7 -0
  203. data/templates/cli/utility/compressor/gzip +7 -0
  204. data/templates/cli/utility/compressor/lzma +7 -0
  205. data/templates/cli/utility/compressor/pbzip2 +7 -0
  206. data/templates/cli/utility/config +31 -0
  207. data/templates/cli/utility/database/mongodb +18 -0
  208. data/templates/cli/utility/database/mysql +21 -0
  209. data/templates/cli/utility/database/postgresql +17 -0
  210. data/templates/cli/utility/database/redis +16 -0
  211. data/templates/cli/utility/database/riak +11 -0
  212. data/templates/cli/utility/encryptor/gpg +12 -0
  213. data/templates/cli/utility/encryptor/openssl +9 -0
  214. data/templates/cli/utility/model.erb +23 -0
  215. data/templates/cli/utility/notifier/campfire +12 -0
  216. data/templates/cli/utility/notifier/hipchat +15 -0
  217. data/templates/cli/utility/notifier/mail +22 -0
  218. data/templates/cli/utility/notifier/presently +13 -0
  219. data/templates/cli/utility/notifier/prowl +11 -0
  220. data/templates/cli/utility/notifier/twitter +13 -0
  221. data/templates/cli/utility/splitter +7 -0
  222. data/templates/cli/utility/storage/cloud_files +22 -0
  223. data/templates/cli/utility/storage/dropbox +20 -0
  224. data/templates/cli/utility/storage/ftp +12 -0
  225. data/templates/cli/utility/storage/local +7 -0
  226. data/templates/cli/utility/storage/ninefold +9 -0
  227. data/templates/cli/utility/storage/rsync +11 -0
  228. data/templates/cli/utility/storage/s3 +19 -0
  229. data/templates/cli/utility/storage/scp +11 -0
  230. data/templates/cli/utility/storage/sftp +11 -0
  231. data/templates/cli/utility/syncer/cloud_files +48 -0
  232. data/templates/cli/utility/syncer/rsync_local +12 -0
  233. data/templates/cli/utility/syncer/rsync_pull +17 -0
  234. data/templates/cli/utility/syncer/rsync_push +17 -0
  235. data/templates/cli/utility/syncer/s3 +45 -0
  236. data/templates/general/links +11 -0
  237. data/templates/general/version.erb +2 -0
  238. data/templates/notifier/mail/failure.erb +9 -0
  239. data/templates/notifier/mail/success.erb +7 -0
  240. data/templates/notifier/mail/warning.erb +9 -0
  241. data/templates/storage/dropbox/authorization_url.erb +6 -0
  242. data/templates/storage/dropbox/authorized.erb +4 -0
  243. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  244. metadata +311 -0
@@ -0,0 +1,88 @@
1
+ # encoding: utf-8
2
+
3
+ Backup::Dependency.load('httparty')
4
+
5
+ module Backup
6
+ module Notifier
7
+ class Presently < Base
8
+
9
+ ##
10
+ # Presently subdomain
11
+ attr_accessor :subdomain
12
+
13
+ ##
14
+ # Presently credentials
15
+ attr_accessor :user_name, :password
16
+
17
+ ##
18
+ # Group id
19
+ attr_accessor :group_id
20
+
21
+ def initialize(model, &block)
22
+ super(model)
23
+
24
+ instance_eval(&block) if block_given?
25
+ end
26
+
27
+ private
28
+
29
+ ##
30
+ # Notify the user of the backup operation results.
31
+ # `status` indicates one of the following:
32
+ #
33
+ # `:success`
34
+ # : The backup completed successfully.
35
+ # : Notification will be sent if `on_success` was set to `true`
36
+ #
37
+ # `:warning`
38
+ # : The backup completed successfully, but warnings were logged
39
+ # : Notification will be sent, including a copy of the current
40
+ # : backup log, if `on_warning` was set to `true`
41
+ #
42
+ # `:failure`
43
+ # : The backup operation failed.
44
+ # : Notification will be sent, including the Exception which caused
45
+ # : the failure, the Exception's backtrace, a copy of the current
46
+ # : backup log and other information if `on_failure` was set to `true`
47
+ #
48
+ def notify!(status)
49
+ name = case status
50
+ when :success then 'Success'
51
+ when :warning then 'Warning'
52
+ when :failure then 'Failure'
53
+ end
54
+ message = "[Backup::%s] #{@model.label} (#{@model.trigger})" % name
55
+ send_message(message)
56
+ end
57
+
58
+ def send_message(message)
59
+ client = Client.new(subdomain, user_name, password, group_id)
60
+ client.update(message)
61
+ end
62
+
63
+ class Client
64
+ include HTTParty
65
+
66
+ attr_accessor :subdomain, :user_name, :password, :group_id
67
+
68
+ def initialize(subdomain, user_name, password, group_id)
69
+ @subdomain = subdomain
70
+ @user_name = user_name
71
+ @password = password
72
+ @group_id = group_id
73
+
74
+ self.class.base_uri "https://#{subdomain}.presently.com"
75
+ self.class.basic_auth user_name, password
76
+ end
77
+
78
+ def update(message)
79
+ message = "d @#{group_id} #{message}" if group_id
80
+ self.class.post "/api/twitter/statuses/update.json", :body => {
81
+ :status => message,
82
+ :source => "Backup Notifier"
83
+ }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Prowler gem when using Prowler notifications
5
+ Backup::Dependency.load('prowler')
6
+
7
+ module Backup
8
+ module Notifier
9
+ class Prowl < Base
10
+
11
+ ##
12
+ # Application name
13
+ # Tell something like your server name. Example: "Server1 Backup"
14
+ attr_accessor :application
15
+
16
+ ##
17
+ # API-Key
18
+ # Create a Prowl account and request an API key on prowlapp.com.
19
+ attr_accessor :api_key
20
+
21
+ def initialize(model, &block)
22
+ super(model)
23
+
24
+ instance_eval(&block) if block_given?
25
+ end
26
+
27
+ private
28
+
29
+ ##
30
+ # Notify the user of the backup operation results.
31
+ # `status` indicates one of the following:
32
+ #
33
+ # `:success`
34
+ # : The backup completed successfully.
35
+ # : Notification will be sent if `on_success` was set to `true`
36
+ #
37
+ # `:warning`
38
+ # : The backup completed successfully, but warnings were logged
39
+ # : Notification will be sent, including a copy of the current
40
+ # : backup log, if `on_warning` was set to `true`
41
+ #
42
+ # `:failure`
43
+ # : The backup operation failed.
44
+ # : Notification will be sent, including the Exception which caused
45
+ # : the failure, the Exception's backtrace, a copy of the current
46
+ # : backup log and other information if `on_failure` was set to `true`
47
+ #
48
+ def notify!(status)
49
+ name = case status
50
+ when :success then 'Success'
51
+ when :warning then 'Warning'
52
+ when :failure then 'Failure'
53
+ end
54
+ message = '[Backup::%s]' % name
55
+ send_message(message)
56
+ end
57
+
58
+ def send_message(message)
59
+ client = Prowler.new(:application => application, :api_key => api_key)
60
+ client.notify(message, "#{@model.label} (#{@model.trigger})")
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Twitter gem when using Twitter notifications
5
+ Backup::Dependency.load('twitter')
6
+
7
+ module Backup
8
+ module Notifier
9
+ class Twitter < Base
10
+
11
+ ##
12
+ # Twitter consumer key credentials
13
+ attr_accessor :consumer_key, :consumer_secret
14
+
15
+ ##
16
+ # OAuth credentials
17
+ attr_accessor :oauth_token, :oauth_token_secret
18
+
19
+ def initialize(model, &block)
20
+ super(model)
21
+
22
+ instance_eval(&block) if block_given?
23
+ end
24
+
25
+ private
26
+
27
+ ##
28
+ # Notify the user of the backup operation results.
29
+ # `status` indicates one of the following:
30
+ #
31
+ # `:success`
32
+ # : The backup completed successfully.
33
+ # : Notification will be sent if `on_success` was set to `true`
34
+ #
35
+ # `:warning`
36
+ # : The backup completed successfully, but warnings were logged
37
+ # : Notification will be sent, including a copy of the current
38
+ # : backup log, if `on_warning` was set to `true`
39
+ #
40
+ # `:failure`
41
+ # : The backup operation failed.
42
+ # : Notification will be sent, including the Exception which caused
43
+ # : the failure, the Exception's backtrace, a copy of the current
44
+ # : backup log and other information if `on_failure` was set to `true`
45
+ #
46
+ def notify!(status)
47
+ name = case status
48
+ when :success then 'Success'
49
+ when :warning then 'Warning'
50
+ when :failure then 'Failure'
51
+ end
52
+ message = "[Backup::%s] #{@model.label} (#{@model.trigger}) (@ #{@model.time})" % name
53
+ send_message(message)
54
+ end
55
+
56
+ def send_message(message)
57
+ ::Twitter.configure do |config|
58
+ config.consumer_key = @consumer_key
59
+ config.consumer_secret = @consumer_secret
60
+ config.oauth_token = @oauth_token
61
+ config.oauth_token_secret = @oauth_token_secret
62
+ end
63
+
64
+ client = ::Twitter::Client.new
65
+ client.update(message)
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ class Package
5
+
6
+ ##
7
+ # The time when the backup initiated (in format: 2011.02.20.03.29.59)
8
+ attr_reader :time
9
+
10
+ ##
11
+ # The trigger which initiated the backup process
12
+ attr_reader :trigger
13
+
14
+ ##
15
+ # Extension for the final archive file(s)
16
+ attr_accessor :extension
17
+
18
+ ##
19
+ # Set by the Splitter if the final archive was "chunked"
20
+ attr_accessor :chunk_suffixes
21
+
22
+ ##
23
+ # The version of Backup used to create the package
24
+ attr_reader :version
25
+
26
+ def initialize(model)
27
+ @time = model.time
28
+ @trigger = model.trigger
29
+ @extension = 'tar'
30
+ @chunk_suffixes = Array.new
31
+ @version = Backup::Version.current
32
+ end
33
+
34
+ def checksum_name
35
+ basename+".sum"
36
+ end
37
+
38
+ def filenames
39
+ if chunk_suffixes.empty?
40
+ [basename]
41
+ else
42
+ chunk_suffixes.map {|suffix| "#{ basename }-#{ suffix }" }
43
+ end
44
+ end
45
+
46
+ def basename
47
+ "#{ time }.#{ trigger }.#{ extension }"
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Packager
5
+ class << self
6
+ include Backup::CLI::Helpers
7
+
8
+ ##
9
+ # Build the final package for the backup model.
10
+ def package!(model)
11
+ @package = model.package
12
+ @encryptor = model.encryptor
13
+ @splitter = model.splitter
14
+ @checksum_creator = model.checksum_creator
15
+ @pipeline = Pipeline.new
16
+
17
+ Logger.message "Packaging the backup files..."
18
+ procedure.call
19
+
20
+ if @pipeline.success?
21
+ Logger.message "Packaging Complete!"
22
+ else
23
+ raise Errors::Packager::PipelineError,
24
+ "Failed to Create Backup Package\n" +
25
+ @pipeline.error_messages
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ ##
32
+ # Builds a chain of nested Procs which adds each command to a Pipeline
33
+ # needed to package the final command to package the backup.
34
+ # This is done so that the Encryptor and Splitter have the ability
35
+ # to perform actions before and after the final command is executed.
36
+ # No Encryptors currently utilize this, however the Splitter does.
37
+ def procedure
38
+ stack = []
39
+
40
+ ##
41
+ # Initial `tar` command to package the temporary backup folder.
42
+ # The command's output will then be either piped to the Encryptor
43
+ # or the Splitter (if no Encryptor), or through `cat` into the final
44
+ # output file if neither are configured.
45
+ @pipeline << "#{ utility(:tar) } -cf - " +
46
+ "-C '#{ Config.tmp_path }' '#{ @package.trigger }'"
47
+
48
+ ##
49
+ # If an Encryptor was configured, it will be called first
50
+ # to add the encryption utility command to be piped through,
51
+ # and amend the final package extension.
52
+ # It's output will then be either piped into a Splitter,
53
+ # or through `cat` into the final output file.
54
+ if @encryptor
55
+ stack << lambda do
56
+ @encryptor.encrypt_with do |command, ext|
57
+ @pipeline << command
58
+ @package.extension << ext
59
+ stack.shift.call
60
+ end
61
+ end
62
+ end
63
+
64
+ ##
65
+ # If a Splitter was configured, the `split` utility command will be
66
+ # added to the Pipeline to split the final output into multiple files.
67
+ # Once the Proc executing the Pipeline has completed and returns back
68
+ # to the Splitter, it will check the final output files to determine
69
+ # if the backup was indeed split.
70
+ # If so, it will set the package's chunk_suffixes. If not, it will
71
+ # remove the '-aa' suffix from the only file created by `split`.
72
+ #
73
+ # If no Splitter was configured, the final file output will be
74
+ # piped through `cat` into the final output file.
75
+ if @splitter
76
+ stack << lambda do
77
+ @splitter.split_with do |command|
78
+ @pipeline << command
79
+ stack.shift.call
80
+ end
81
+ end
82
+ else
83
+ stack << lambda do
84
+ outfile = File.join(Config.tmp_path, @package.basename)
85
+ @pipeline << "cat > #{ outfile }"
86
+
87
+ if @checksum_creator
88
+ @checksum_creator.checksum_with outfile do |cmd|
89
+ @pipeline << cmd
90
+ end
91
+ end
92
+
93
+ stack.shift.call
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Last Proc to be called runs the Pipeline the procedure built.
99
+ # Once complete, the call stack will unwind back through the
100
+ # preceeding Procs in the stack (if any)
101
+ stack << lambda { @pipeline.run }
102
+
103
+ stack.shift
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ class Pipeline
5
+ include Backup::CLI::Helpers
6
+
7
+ attr_reader :stderr, :errors
8
+
9
+ def initialize
10
+ @commands = []
11
+ @errors = []
12
+ @stderr = ''
13
+ end
14
+
15
+ ##
16
+ # Adds a command to be executed in the pipeline.
17
+ # Each command will be run in the order in which it was added,
18
+ # with it's output being piped to the next command.
19
+ def <<(command)
20
+ @commands << command
21
+ end
22
+
23
+ ##
24
+ # Runs the command line from `#pipeline` and collects STDOUT/STDERR.
25
+ # STDOUT is then parsed to determine the exit status of each command.
26
+ # For each command with a non-zero exit status, a SystemCallError is
27
+ # created and added to @errors. All STDERR output is set in @stderr.
28
+ #
29
+ # Note that there is no accumulated STDOUT from the commands,
30
+ # and the last command added to the pipeline should *not* attempt to
31
+ # write to STDOUT, as this will raise an Exception.
32
+ #
33
+ # Use `#success?` to determine if all commands in the pipeline succeeded.
34
+ # If `#success?` returns `false`, use `#error_messages` to get an error report.
35
+ def run
36
+ Open4.popen4(pipeline) do |pid, stdin, stdout, stderr|
37
+ pipestatus = stdout.read.gsub("\n", '').split(':').sort
38
+ pipestatus.each do |status|
39
+ index, exitstatus = status.split('|').map(&:to_i)
40
+ if exitstatus > 0
41
+ command = command_name(@commands[index])
42
+ @errors << SystemCallError.new(
43
+ "'#{ command }' returned exit code: #{ exitstatus }", exitstatus
44
+ )
45
+ end
46
+ end
47
+ @stderr = stderr.read.strip
48
+ end
49
+ Logger.warn(stderr_messages) if success? && stderr_messages
50
+ rescue Exception => e
51
+ raise Errors::Pipeline::ExecutionError.wrap(e)
52
+ end
53
+
54
+ def success?
55
+ @errors.empty?
56
+ end
57
+
58
+ ##
59
+ # Returns a multi-line String, reporting all STDERR messages received
60
+ # from the commands in the pipeline (if any), along with the SystemCallError
61
+ # (Errno) message for each command which had a non-zero exit status.
62
+ #
63
+ # Each error is wrapped by Backup::Errors to provide formatting.
64
+ def error_messages
65
+ @error_messages ||= (stderr_messages || '') +
66
+ "The following system errors were returned:\n" +
67
+ @errors.map {|err| Errors::Error.wrap(err).message }.join("\n")
68
+ end
69
+
70
+ private
71
+
72
+ ##
73
+ # Each command is added as part of the pipeline, grouped with an `echo`
74
+ # command to pass along the command's index in @commands and it's exit status.
75
+ # The command's STDERR is redirected to FD#4, and the `echo` command to
76
+ # report the "index|exit status" it redirected to FD#3.
77
+ # Each command's STDOUT will be connected to the STDIN of the next subshell.
78
+ # The entire pipeline is run within a container group, which redirects
79
+ # FD#3 to STDOUT and FD#4 to STDERR so these can be collected.
80
+ # FD#1 is closed so any attempt by the last command in the pipeline to
81
+ # write to STDOUT will raise an error, as we don't want this to interfere
82
+ # with collecting the exit statuses.
83
+ #
84
+ # There is no guarantee as to the order of this output, which is why the
85
+ # command's index in @commands is passed along with it's exit status.
86
+ # And, if multiple commands output messages on STDERR, those messages
87
+ # may be interleaved. Interleaving of the "index|exit status" outputs
88
+ # should not be an issue, given the small byte size of the data being written.
89
+ def pipeline
90
+ parts = []
91
+ @commands.each_with_index do |command, index|
92
+ parts << %Q[( #{ command } 2>&4; echo "#{ index }|$?:" >&3 )]
93
+ end
94
+ "( #{ parts.join(' | ') } ) 3>&1- 4>&2"
95
+ end
96
+
97
+ def stderr_messages
98
+ @stderr_messages ||= @stderr.empty? ? false : <<-EOS.gsub(/^ +/, ' ')
99
+ Pipeline STDERR Messages:
100
+ (Note: may be interleaved if multiple commands returned error messages)
101
+
102
+ #{ @stderr }
103
+ EOS
104
+ end
105
+
106
+ end
107
+ end