backup 3.5.1 → 3.6.0
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.
- checksums.yaml +4 -4
- data/lib/backup/cli.rb +29 -6
- data/lib/backup/errors.rb +46 -81
- data/lib/backup/model.rb +184 -143
- data/lib/backup/notifier/base.rb +60 -29
- data/lib/backup/notifier/campfire.rb +23 -105
- data/lib/backup/notifier/hipchat.rb +19 -17
- data/lib/backup/notifier/mail.rb +64 -59
- data/lib/backup/notifier/prowl.rb +27 -19
- data/lib/backup/notifier/pushover.rb +26 -36
- data/lib/backup/notifier/twitter.rb +13 -15
- data/lib/backup/pipeline.rb +1 -3
- data/lib/backup/storage/s3.rb +0 -3
- data/lib/backup/utilities.rb +9 -8
- data/lib/backup/version.rb +1 -1
- data/templates/notifier/mail/failure.erb +2 -0
- data/templates/notifier/mail/success.erb +4 -0
- data/templates/notifier/mail/warning.erb +2 -0
- metadata +16 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d68dc83802f92387efeb5d0bfd6e89c7d5d32e49
|
4
|
+
data.tar.gz: a9c414e7eb6a1c329b49329d3c4fc5391ab78f84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f801689681baae58593843de6828116bc688ece381912e47d6b74e8354f61bd437a13e0b36bf106331124de108788c626dd0fa1d16e09f9c04fff4834c9a747
|
7
|
+
data.tar.gz: ed9473697e3a81910ada2857f7c3533b341bc8ff0a9a6863db58d16cc408a2e29042ab4284bf0e44979dea0fa7dfebe68e48de10cc8b6a41a7c13ed62a321c1d
|
data/lib/backup/cli.rb
CHANGED
@@ -156,15 +156,38 @@ module Backup
|
|
156
156
|
exit(3)
|
157
157
|
end
|
158
158
|
|
159
|
-
|
160
|
-
|
161
|
-
warnings = errors = false
|
162
|
-
models.each do |model|
|
159
|
+
until models.empty?
|
160
|
+
model = models.shift
|
163
161
|
model.perform!
|
164
|
-
|
165
|
-
|
162
|
+
|
163
|
+
case model.exit_status
|
164
|
+
when 1
|
165
|
+
warnings = true
|
166
|
+
when 2
|
167
|
+
errors = true
|
168
|
+
unless models.empty?
|
169
|
+
Logger.info Errors::ModelError.new(<<-EOS)
|
170
|
+
Backup will now continue...
|
171
|
+
The following triggers will now be processed:
|
172
|
+
(#{ models.map {|m| m.trigger }.join(', ') })
|
173
|
+
EOS
|
174
|
+
end
|
175
|
+
when 3
|
176
|
+
fatal = true
|
177
|
+
unless models.empty?
|
178
|
+
Logger.error Errors::ModelFatalError.new(<<-EOS)
|
179
|
+
Backup will now exit.
|
180
|
+
The following triggers will not be processed:
|
181
|
+
(#{ models.map {|m| m.trigger }.join(', ') })
|
182
|
+
EOS
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
model.notifiers.each(&:perform!)
|
187
|
+
exit(3) if fatal
|
166
188
|
Logger.clear!
|
167
189
|
end
|
190
|
+
|
168
191
|
exit(errors ? 2 : 1) if errors || warnings
|
169
192
|
end
|
170
193
|
|
data/lib/backup/errors.rb
CHANGED
@@ -5,13 +5,21 @@ module Backup
|
|
5
5
|
# - automatically defines module namespaces referenced under Backup::Errors
|
6
6
|
# - any constant name referenced that ends with 'Error' will be created
|
7
7
|
# as a subclass of Backup::Errors::Error
|
8
|
+
# - any constant name referenced that ends with 'FatalError' will be created
|
9
|
+
# as a subclass of Backup::Errors::FatalError
|
10
|
+
#
|
8
11
|
# e.g.
|
9
12
|
# err = Backup::Errors::Foo::Bar::FooError.new('error message')
|
10
13
|
# err.message => "Foo::Bar::FooError: error message"
|
11
14
|
#
|
15
|
+
# err = Backup::Errors::Foo::Bar::FooFatalError.new('error message')
|
16
|
+
# err.message => "Foo::Bar::FooFatalError: error message"
|
17
|
+
#
|
12
18
|
module ErrorsHelper
|
13
19
|
def const_missing(const)
|
14
|
-
if const.to_s.end_with?('
|
20
|
+
if const.to_s.end_with?('FatalError')
|
21
|
+
module_eval("class #{const} < Backup::Errors::FatalError; end")
|
22
|
+
elsif const.to_s.end_with?('Error')
|
15
23
|
module_eval("class #{const} < Backup::Errors::Error; end")
|
16
24
|
else
|
17
25
|
module_eval("module #{const}; extend Backup::ErrorsHelper; end")
|
@@ -20,104 +28,61 @@ module Backup
|
|
20
28
|
end
|
21
29
|
end
|
22
30
|
|
23
|
-
##
|
24
|
-
# provides cascading errors with formatted messages
|
25
|
-
# see the specs for details
|
26
|
-
#
|
27
|
-
# e.g.
|
28
|
-
# module Backup
|
29
|
-
# begin
|
30
|
-
# begin
|
31
|
-
# begin
|
32
|
-
# raise Errors::ZoneAError, 'an error occurred in Zone A'
|
33
|
-
# rescue => err
|
34
|
-
# raise Errors::ZoneBError.wrap(err, <<-EOS)
|
35
|
-
# an error occurred in Zone B
|
36
|
-
#
|
37
|
-
# the following error should give a reason
|
38
|
-
# EOS
|
39
|
-
# end
|
40
|
-
# rescue => err
|
41
|
-
# raise Errors::ZoneCError.wrap(err)
|
42
|
-
# end
|
43
|
-
# rescue => err
|
44
|
-
# puts Errors::ZoneDError.wrap(err, 'an error occurred in Zone D')
|
45
|
-
# end
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# Outputs:
|
49
|
-
# ZoneDError: an error occurred in Zone D
|
50
|
-
# Reason: ZoneCError
|
51
|
-
# ZoneBError: an error occurred in Zone B
|
52
|
-
#
|
53
|
-
# the following error should give a reason
|
54
|
-
# Reason: ZoneAError
|
55
|
-
# an error occurred in Zone A
|
56
|
-
#
|
57
31
|
module Errors
|
58
32
|
extend ErrorsHelper
|
59
33
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
new(msg, orig_err)
|
64
|
-
end
|
34
|
+
# Provides cascading errors with formatted messages.
|
35
|
+
# See the specs for details.
|
36
|
+
module NestedExceptions
|
65
37
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
38
|
+
def self.included(klass)
|
39
|
+
klass.extend Module.new {
|
40
|
+
def wrap(wrapped_exception, msg = nil)
|
41
|
+
new(msg, wrapped_exception)
|
42
|
+
end
|
43
|
+
}
|
69
44
|
end
|
70
45
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
msg <<
|
81
|
-
|
46
|
+
def initialize(obj = nil, wrapped_exception = nil)
|
47
|
+
@wrapped_exception = wrapped_exception
|
48
|
+
msg = (obj.respond_to?(:to_str) ? obj.to_str : obj.to_s).
|
49
|
+
gsub(/^ */, ' ').strip
|
50
|
+
msg = clean_name(self.class.name) + (msg.empty? ? '' : ": #{ msg }")
|
51
|
+
|
52
|
+
if wrapped_exception
|
53
|
+
msg << "\n--- Wrapped Exception ---\n"
|
54
|
+
class_name = clean_name(wrapped_exception.class.name)
|
55
|
+
msg << class_name + ': ' unless
|
56
|
+
wrapped_exception.message.start_with? class_name
|
57
|
+
msg << wrapped_exception.message
|
82
58
|
end
|
83
59
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def msg_prefix
|
90
|
-
@msg_prefix ||= class_name + ': '
|
60
|
+
super(msg)
|
61
|
+
set_backtrace(wrapped_exception.backtrace) if wrapped_exception
|
91
62
|
end
|
92
63
|
|
93
|
-
def
|
94
|
-
|
95
|
-
end
|
64
|
+
def exception(obj = nil)
|
65
|
+
return self if obj.nil? || equal?(obj)
|
96
66
|
|
97
|
-
|
98
|
-
|
67
|
+
ex = self.class.new(obj, @wrapped_exception)
|
68
|
+
ex.set_backtrace(backtrace) unless ex.backtrace
|
69
|
+
ex
|
99
70
|
end
|
100
71
|
|
101
|
-
|
102
|
-
return unless @orig_err
|
72
|
+
private
|
103
73
|
|
104
|
-
|
105
|
-
|
74
|
+
def clean_name(name)
|
75
|
+
name.sub(/^Backup::Errors::/, '')
|
106
76
|
end
|
107
77
|
|
108
|
-
|
109
|
-
return unless @orig_err
|
110
|
-
return @orig_err_msg unless @orig_err_msg.nil?
|
78
|
+
end
|
111
79
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
false : format_msg(msg)
|
116
|
-
end
|
80
|
+
class Error < StandardError
|
81
|
+
include NestedExceptions
|
82
|
+
end
|
117
83
|
|
118
|
-
|
119
|
-
|
120
|
-
end
|
84
|
+
class FatalError < Exception
|
85
|
+
include NestedExceptions
|
121
86
|
end
|
122
87
|
|
123
88
|
end
|
data/lib/backup/model.rb
CHANGED
@@ -34,35 +34,35 @@ module Backup
|
|
34
34
|
attr_reader :label
|
35
35
|
|
36
36
|
##
|
37
|
-
#
|
37
|
+
# Array of configured Database objects.
|
38
38
|
attr_reader :databases
|
39
39
|
|
40
40
|
##
|
41
|
-
#
|
41
|
+
# Array of configured Archive objects.
|
42
42
|
attr_reader :archives
|
43
43
|
|
44
44
|
##
|
45
|
-
#
|
45
|
+
# Array of configured Notifier objects.
|
46
46
|
attr_reader :notifiers
|
47
47
|
|
48
48
|
##
|
49
|
-
#
|
49
|
+
# Array of configured Storage objects.
|
50
50
|
attr_reader :storages
|
51
51
|
|
52
52
|
##
|
53
|
-
#
|
53
|
+
# Array of configured Syncer objects.
|
54
54
|
attr_reader :syncers
|
55
55
|
|
56
56
|
##
|
57
|
-
#
|
57
|
+
# The configured Compressor, if any.
|
58
58
|
attr_reader :compressor
|
59
59
|
|
60
60
|
##
|
61
|
-
#
|
61
|
+
# The configured Encryptor, if any.
|
62
62
|
attr_reader :encryptor
|
63
63
|
|
64
64
|
##
|
65
|
-
#
|
65
|
+
# The configured Splitter, if any.
|
66
66
|
attr_reader :splitter
|
67
67
|
|
68
68
|
##
|
@@ -74,17 +74,31 @@ module Backup
|
|
74
74
|
attr_reader :time
|
75
75
|
|
76
76
|
##
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
77
|
+
# Result of this model's backup process.
|
78
|
+
#
|
79
|
+
# 0 = Job was successful
|
80
|
+
# 1 = Job was successful, but issued warnings
|
81
|
+
# 2 = Job failed, additional triggers may be performed
|
82
|
+
# 3 = Job failed, additional triggers will not be performed
|
83
|
+
attr_reader :exit_status
|
84
|
+
|
85
|
+
##
|
86
|
+
# Exception raised by either a +before+ hook or one of the model's
|
87
|
+
# procedures that caused the model to fail. An exception raised by an
|
88
|
+
# +after+ hook would not be stored here. Therefore, it is possible for
|
89
|
+
# this to be +nil+ even if #exit_status is 2 or 3.
|
90
|
+
attr_reader :exception
|
91
|
+
|
80
92
|
def initialize(trigger, label, &block)
|
81
93
|
@trigger = trigger.to_s
|
82
94
|
@label = label.to_s
|
83
95
|
@package = Package.new(self)
|
84
96
|
|
85
|
-
|
86
|
-
|
87
|
-
|
97
|
+
@databases = []
|
98
|
+
@archives = []
|
99
|
+
@storages = []
|
100
|
+
@notifiers = []
|
101
|
+
@syncers = []
|
88
102
|
|
89
103
|
instance_eval(&block) if block_given?
|
90
104
|
|
@@ -96,31 +110,27 @@ module Backup
|
|
96
110
|
end
|
97
111
|
|
98
112
|
##
|
99
|
-
# Adds an
|
100
|
-
# to store during the backup process
|
113
|
+
# Adds an Archive. Multiple Archives may be added to the model.
|
101
114
|
def archive(name, &block)
|
102
115
|
@archives << Archive.new(self, name, &block)
|
103
116
|
end
|
104
117
|
|
105
118
|
##
|
106
|
-
# Adds
|
107
|
-
# to dump during the backup process
|
119
|
+
# Adds an Database. Multiple Databases may be added to the model.
|
108
120
|
def database(name, database_id = nil, &block)
|
109
121
|
@databases << get_class_from_scope(Database, name).
|
110
122
|
new(self, database_id, &block)
|
111
123
|
end
|
112
124
|
|
113
125
|
##
|
114
|
-
# Adds
|
115
|
-
# methods to use during the backup process
|
126
|
+
# Adds an Storage. Multiple Storages may be added to the model.
|
116
127
|
def store_with(name, storage_id = nil, &block)
|
117
128
|
@storages << get_class_from_scope(Storage, name).
|
118
129
|
new(self, storage_id, &block)
|
119
130
|
end
|
120
131
|
|
121
132
|
##
|
122
|
-
# Adds
|
123
|
-
# methods to use during the backup process
|
133
|
+
# Adds an Syncer. Multiple Syncers may be added to the model.
|
124
134
|
def sync_with(name, syncer_id = nil, &block)
|
125
135
|
##
|
126
136
|
# Warn user of DSL changes
|
@@ -147,29 +157,29 @@ module Backup
|
|
147
157
|
end
|
148
158
|
|
149
159
|
##
|
150
|
-
# Adds
|
151
|
-
# to use during the backup process
|
160
|
+
# Adds an Notifier. Multiple Notifiers may be added to the model.
|
152
161
|
def notify_by(name, &block)
|
153
162
|
@notifiers << get_class_from_scope(Notifier, name).new(self, &block)
|
154
163
|
end
|
155
164
|
|
156
165
|
##
|
157
|
-
# Adds an
|
166
|
+
# Adds an Encryptor. Only one Encryptor may be added to the model.
|
167
|
+
# This will be used to encrypt the final backup package.
|
158
168
|
def encrypt_with(name, &block)
|
159
169
|
@encryptor = get_class_from_scope(Encryptor, name).new(&block)
|
160
170
|
end
|
161
171
|
|
162
172
|
##
|
163
|
-
# Adds
|
173
|
+
# Adds an Compressor. Only one Compressor may be added to the model.
|
174
|
+
# This will be used to compress each individual Archive and Database
|
175
|
+
# stored within the final backup package.
|
164
176
|
def compress_with(name, &block)
|
165
177
|
@compressor = get_class_from_scope(Compressor, name).new(&block)
|
166
178
|
end
|
167
179
|
|
168
180
|
##
|
169
|
-
# Adds a
|
170
|
-
#
|
171
|
-
# The +chunk_size+ (in megabytes) will later determine
|
172
|
-
# in how many chunks the backup needs to be split into
|
181
|
+
# Adds a Splitter with the given +chunk_size+ in MB.
|
182
|
+
# This will split the final backup package into multiple files.
|
173
183
|
def split_into_chunks_of(chunk_size)
|
174
184
|
if chunk_size.is_a?(Integer)
|
175
185
|
@splitter = Splitter.new(self, chunk_size)
|
@@ -182,79 +192,97 @@ module Backup
|
|
182
192
|
end
|
183
193
|
|
184
194
|
##
|
185
|
-
#
|
186
|
-
|
187
|
-
#
|
188
|
-
#
|
189
|
-
|
190
|
-
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
# Optionally encrypts the packaged file with the configured encryptor
|
200
|
-
##
|
201
|
-
# [Compression]
|
202
|
-
# Optionally compresses the each Archive and Database dump with the configured compressor
|
203
|
-
##
|
204
|
-
# [Splitting]
|
205
|
-
# Optionally splits the backup file in to multiple smaller chunks before transferring them
|
206
|
-
##
|
207
|
-
# [Storages]
|
208
|
-
# Runs all (if any) storage objects to store the backups to remote locations
|
209
|
-
# and (if configured) it'll cycle the files on the remote location to limit the
|
210
|
-
# amount of backups stored on each individual location
|
211
|
-
##
|
212
|
-
# [Syncers]
|
213
|
-
# Runs all (if any) sync objects to store the backups to remote locations.
|
214
|
-
# A Syncer does not go through the process of packaging, compressing, encrypting backups.
|
215
|
-
# A Syncer directly transfers data from the filesystem to the remote location
|
195
|
+
# Defines a block of code to run before the model's procedures.
|
196
|
+
#
|
197
|
+
# Warnings logged within the before hook will elevate the model's
|
198
|
+
# exit_status to 1 and cause warning notifications to be sent.
|
199
|
+
#
|
200
|
+
# Raising an exception will abort the model and cause failure notifications
|
201
|
+
# to be sent. If the exception is a StandardError, exit_status will be 2.
|
202
|
+
# If the exception is not a StandardError, exit_status will be 3.
|
203
|
+
#
|
204
|
+
# If any exception is raised, any defined +after+ hook will be skipped.
|
205
|
+
def before(&block)
|
206
|
+
@before ||= block
|
207
|
+
end
|
208
|
+
|
216
209
|
##
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
210
|
+
# Defines a block of code to run after the model's procedures.
|
211
|
+
#
|
212
|
+
# This code is ensured to run, even if the model failed, **unless** a
|
213
|
+
# +before+ hook raised an exception and aborted the model.
|
214
|
+
#
|
215
|
+
# The code block will be passed the model's current exit_status:
|
216
|
+
#
|
217
|
+
# `0`: Success, no warnings.
|
218
|
+
# `1`: Success, but warnings were logged.
|
219
|
+
# `2`: Failure, but additional models/triggers will still be processed.
|
220
|
+
# `3`: Failure, no additional models/triggers will be processed.
|
221
|
+
#
|
222
|
+
# The model's exit_status may be elevated based on the after hook's
|
223
|
+
# actions, but will never be decreased.
|
224
|
+
#
|
225
|
+
# Warnings logged within the after hook may elevate the model's
|
226
|
+
# exit_status to 1 and cause warning notifications to be sent.
|
227
|
+
#
|
228
|
+
# Raising an exception may elevate the model's exit_status and cause
|
229
|
+
# failure notifications to be sent. If the exception is a StandardError,
|
230
|
+
# the exit_status will be elevated to 2. If the exception is not a
|
231
|
+
# StandardError, the exit_status will be elevated to 3.
|
232
|
+
def after(&block)
|
233
|
+
@after ||= block
|
234
|
+
end
|
235
|
+
|
220
236
|
##
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
# If
|
226
|
-
#
|
227
|
-
#
|
237
|
+
# Performs the backup process
|
238
|
+
#
|
239
|
+
# Once complete, #exit_status will indicate the result of this process.
|
240
|
+
#
|
241
|
+
# If any errors occur during the backup process, all temporary files will
|
242
|
+
# be left in place. If the error occurs before Packaging, then the
|
243
|
+
# temporary folder (tmp_path/trigger) will remain and may contain all or
|
244
|
+
# some of the configured Archives and/or Database dumps. If the error
|
245
|
+
# occurs after Packaging, but before the Storages complete, then the final
|
228
246
|
# packaged files (located in the root of tmp_path) will remain.
|
229
|
-
# *** Important *** If an error occurs and any of the above mentioned temporary files remain,
|
230
|
-
# those files *** will be removed *** before the next scheduled backup for the same trigger.
|
231
247
|
#
|
248
|
+
# *** Important ***
|
249
|
+
# If an error occurs and any of the above mentioned temporary files remain,
|
250
|
+
# those files *** will be removed *** before the next scheduled backup for
|
251
|
+
# the same trigger.
|
232
252
|
def perform!
|
233
253
|
@started_at = Time.now
|
234
254
|
@time = package.time = @started_at.strftime("%Y.%m.%d.%H.%M.%S")
|
235
|
-
log!(:started)
|
236
255
|
|
237
|
-
|
256
|
+
log!(:started)
|
257
|
+
before_hook
|
238
258
|
|
239
|
-
|
240
|
-
|
241
|
-
(procedure.call; next) if procedure.is_a?(Proc)
|
242
|
-
procedure.each(&:perform!)
|
243
|
-
end
|
259
|
+
procedures.each do |procedure|
|
260
|
+
procedure.is_a?(Proc) ? procedure.call : procedure.each(&:perform!)
|
244
261
|
end
|
245
262
|
|
246
263
|
syncers.each(&:perform!)
|
247
|
-
notifiers.each(&:perform!)
|
248
|
-
log!(:finished)
|
249
264
|
|
250
265
|
rescue Exception => err
|
251
|
-
|
252
|
-
|
253
|
-
|
266
|
+
@exception = err
|
267
|
+
|
268
|
+
ensure
|
269
|
+
set_exit_status
|
270
|
+
log!(:finished)
|
271
|
+
after_hook
|
254
272
|
end
|
255
273
|
|
256
274
|
private
|
257
275
|
|
276
|
+
##
|
277
|
+
# Returns an array of procedures that will be performed if any
|
278
|
+
# Archives or Databases are configured for the model.
|
279
|
+
def procedures
|
280
|
+
return [] unless databases.any? || archives.any?
|
281
|
+
|
282
|
+
[lambda { prepare! }, databases, archives,
|
283
|
+
lambda { package! }, storages, lambda { clean! }]
|
284
|
+
end
|
285
|
+
|
258
286
|
##
|
259
287
|
# Clean any temporary files and/or package files left over
|
260
288
|
# from the last time this model/trigger was performed.
|
@@ -280,18 +308,6 @@ module Backup
|
|
280
308
|
Cleaner.remove_package(package)
|
281
309
|
end
|
282
310
|
|
283
|
-
##
|
284
|
-
# Returns an array of procedures
|
285
|
-
def procedures
|
286
|
-
[databases, archives, lambda { package! }, storages, lambda { clean! }]
|
287
|
-
end
|
288
|
-
|
289
|
-
##
|
290
|
-
# Returns an Array of the names (String) of the procedure instance variables
|
291
|
-
def procedure_instance_variables
|
292
|
-
[:@databases, :@archives, :@storages, :@notifiers, :@syncers]
|
293
|
-
end
|
294
|
-
|
295
311
|
##
|
296
312
|
# Returns the class/model specified by +name+ inside of +scope+.
|
297
313
|
# +scope+ should be a Class/Module.
|
@@ -319,43 +335,84 @@ module Backup
|
|
319
335
|
end
|
320
336
|
|
321
337
|
##
|
322
|
-
#
|
323
|
-
def
|
338
|
+
# Sets or updates the model's #exit_status.
|
339
|
+
def set_exit_status
|
340
|
+
@exit_status = if exception
|
341
|
+
exception.is_a?(StandardError) ? 2 : 3
|
342
|
+
else
|
343
|
+
Logger.has_warnings? ? 1 : 0
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
##
|
348
|
+
# Runs the +before+ hook.
|
349
|
+
# Any exception raised will be wrapped and re-raised, where it will be
|
350
|
+
# handled by #perform the same as an exception raised while performing
|
351
|
+
# the model's #procedures. Only difference is that an exception raised
|
352
|
+
# here will prevent any +after+ hook from being run.
|
353
|
+
def before_hook
|
354
|
+
return unless before
|
355
|
+
|
356
|
+
Logger.info 'Before Hook Starting...'
|
357
|
+
before.call
|
358
|
+
Logger.info 'Before Hook Finished.'
|
359
|
+
|
360
|
+
rescue Exception => err
|
361
|
+
@before_hook_failed = true
|
362
|
+
ex = err.is_a?(StandardError) ?
|
363
|
+
Errors::Model::HookError : Errors::Model::HookFatalError
|
364
|
+
raise ex.wrap(err, 'Before Hook Failed!')
|
365
|
+
end
|
366
|
+
|
367
|
+
##
|
368
|
+
# Runs the +after+ hook.
|
369
|
+
# Any exception raised here will be logged only and the model's
|
370
|
+
# #exit_status will be elevated if neccessary.
|
371
|
+
def after_hook
|
372
|
+
return unless after && !@before_hook_failed
|
373
|
+
|
374
|
+
Logger.info 'After Hook Starting...'
|
375
|
+
after.call(exit_status)
|
376
|
+
Logger.info 'After Hook Finished.'
|
377
|
+
|
378
|
+
set_exit_status # in case hook logged warnings
|
379
|
+
|
380
|
+
rescue Exception => err
|
381
|
+
fatal = !err.is_a?(StandardError)
|
382
|
+
ex = fatal ? Errors::Model::HookFatalError : Errors::Model::HookError
|
383
|
+
Logger.error ex.wrap(err, 'After Hook Failed!')
|
384
|
+
# upgrade exit_status if needed
|
385
|
+
(@exit_status = fatal ? 3 : 2) unless exit_status == 3
|
386
|
+
end
|
387
|
+
|
388
|
+
##
|
389
|
+
# Logs messages when the model starts and finishes.
|
390
|
+
#
|
391
|
+
# #exception will be set here if #exit_status is > 1,
|
392
|
+
# since log(:finished) is called before the +after+ hook.
|
393
|
+
def log!(action)
|
324
394
|
case action
|
325
395
|
when :started
|
326
|
-
Logger.info "Performing Backup for '#{label} (#{trigger})'!\n" +
|
396
|
+
Logger.info "Performing Backup for '#{ label } (#{ trigger })'!\n" +
|
327
397
|
"[ backup #{ VERSION } : #{ RUBY_DESCRIPTION } ]"
|
328
398
|
|
329
399
|
when :finished
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
Logger.
|
334
|
-
|
335
|
-
Logger.info msg % 'Successfully'
|
336
|
-
end
|
337
|
-
|
338
|
-
when :failure
|
339
|
-
err = Errors::ModelError.wrap(exception, <<-EOS)
|
340
|
-
Backup for #{label} (#{trigger}) Failed!
|
341
|
-
An Error occured which has caused this Backup to abort before completion.
|
342
|
-
EOS
|
343
|
-
Logger.error err
|
344
|
-
Logger.error "\nBacktrace:\n\s\s" + err.backtrace.join("\n\s\s") + "\n\n"
|
400
|
+
if exit_status > 1
|
401
|
+
ex = exit_status == 2 ? Errors::ModelError : Errors::ModelFatalError
|
402
|
+
err = ex.wrap(exception, "Backup for #{ label } (#{ trigger }) Failed!")
|
403
|
+
Logger.error err
|
404
|
+
Logger.error "\nBacktrace:\n\s\s" + err.backtrace.join("\n\s\s") + "\n\n"
|
345
405
|
|
346
|
-
|
347
|
-
|
348
|
-
if exception.is_a?(StandardError)
|
349
|
-
Logger.info Errors::ModelError.new(<<-EOS)
|
350
|
-
If you have other Backup jobs (triggers) configured to run,
|
351
|
-
Backup will now attempt to continue...
|
352
|
-
EOS
|
406
|
+
Cleaner.warnings(self)
|
353
407
|
else
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
408
|
+
msg = "Backup for '#{ label } (#{ trigger })' "
|
409
|
+
if exit_status == 1
|
410
|
+
msg << "Completed Successfully (with Warnings) in #{ elapsed_time }"
|
411
|
+
Logger.warn msg
|
412
|
+
else
|
413
|
+
msg << "Completed Successfully in #{ elapsed_time }"
|
414
|
+
Logger.info msg
|
415
|
+
end
|
359
416
|
end
|
360
417
|
end
|
361
418
|
end
|
@@ -371,21 +428,5 @@ module Backup
|
|
371
428
|
'%02d:%02d:%02d' % [hours, minutes, seconds]
|
372
429
|
end
|
373
430
|
|
374
|
-
##
|
375
|
-
# Sends notifications when a backup fails.
|
376
|
-
# Errors are logged and rescued, since the error that caused the
|
377
|
-
# backup to fail could have been an error with a notifier.
|
378
|
-
def send_failure_notifications
|
379
|
-
notifiers.each do |n|
|
380
|
-
begin
|
381
|
-
n.perform!(true)
|
382
|
-
rescue Exception => err
|
383
|
-
Logger.error Errors::ModelError.wrap(err, <<-EOS)
|
384
|
-
#{ n.class } Failed to send notification of backup failure.
|
385
|
-
EOS
|
386
|
-
end
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
431
|
end
|
391
432
|
end
|