sliday_backup 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +29 -0
- data/bin/sliday_backup +5 -0
- data/lib/sliday_backup.rb +147 -0
- data/lib/sliday_backup/archive.rb +170 -0
- data/lib/sliday_backup/binder.rb +22 -0
- data/lib/sliday_backup/cleaner.rb +116 -0
- data/lib/sliday_backup/cli.rb +374 -0
- data/lib/sliday_backup/cloud_io/base.rb +41 -0
- data/lib/sliday_backup/cloud_io/cloud_files.rb +298 -0
- data/lib/sliday_backup/cloud_io/s3.rb +260 -0
- data/lib/sliday_backup/compressor/base.rb +35 -0
- data/lib/sliday_backup/compressor/bzip2.rb +39 -0
- data/lib/sliday_backup/compressor/custom.rb +53 -0
- data/lib/sliday_backup/compressor/gzip.rb +74 -0
- data/lib/sliday_backup/config.rb +119 -0
- data/lib/sliday_backup/config/dsl.rb +103 -0
- data/lib/sliday_backup/config/helpers.rb +143 -0
- data/lib/sliday_backup/database/base.rb +86 -0
- data/lib/sliday_backup/database/mongodb.rb +187 -0
- data/lib/sliday_backup/database/mysql.rb +192 -0
- data/lib/sliday_backup/database/openldap.rb +95 -0
- data/lib/sliday_backup/database/postgresql.rb +133 -0
- data/lib/sliday_backup/database/redis.rb +179 -0
- data/lib/sliday_backup/database/riak.rb +82 -0
- data/lib/sliday_backup/database/sqlite.rb +57 -0
- data/lib/sliday_backup/encryptor/base.rb +29 -0
- data/lib/sliday_backup/encryptor/gpg.rb +747 -0
- data/lib/sliday_backup/encryptor/open_ssl.rb +77 -0
- data/lib/sliday_backup/errors.rb +58 -0
- data/lib/sliday_backup/logger.rb +199 -0
- data/lib/sliday_backup/logger/console.rb +51 -0
- data/lib/sliday_backup/logger/fog_adapter.rb +29 -0
- data/lib/sliday_backup/logger/logfile.rb +133 -0
- data/lib/sliday_backup/logger/syslog.rb +116 -0
- data/lib/sliday_backup/model.rb +479 -0
- data/lib/sliday_backup/notifier/base.rb +128 -0
- data/lib/sliday_backup/notifier/campfire.rb +63 -0
- data/lib/sliday_backup/notifier/command.rb +99 -0
- data/lib/sliday_backup/notifier/datadog.rb +107 -0
- data/lib/sliday_backup/notifier/flowdock.rb +103 -0
- data/lib/sliday_backup/notifier/hipchat.rb +112 -0
- data/lib/sliday_backup/notifier/http_post.rb +117 -0
- data/lib/sliday_backup/notifier/mail.rb +244 -0
- data/lib/sliday_backup/notifier/nagios.rb +69 -0
- data/lib/sliday_backup/notifier/pagerduty.rb +81 -0
- data/lib/sliday_backup/notifier/prowl.rb +68 -0
- data/lib/sliday_backup/notifier/pushover.rb +74 -0
- data/lib/sliday_backup/notifier/ses.rb +88 -0
- data/lib/sliday_backup/notifier/slack.rb +148 -0
- data/lib/sliday_backup/notifier/twitter.rb +58 -0
- data/lib/sliday_backup/notifier/zabbix.rb +63 -0
- data/lib/sliday_backup/package.rb +55 -0
- data/lib/sliday_backup/packager.rb +107 -0
- data/lib/sliday_backup/pipeline.rb +124 -0
- data/lib/sliday_backup/splitter.rb +76 -0
- data/lib/sliday_backup/storage/base.rb +69 -0
- data/lib/sliday_backup/storage/cloud_files.rb +158 -0
- data/lib/sliday_backup/storage/cycler.rb +75 -0
- data/lib/sliday_backup/storage/dropbox.rb +212 -0
- data/lib/sliday_backup/storage/ftp.rb +112 -0
- data/lib/sliday_backup/storage/local.rb +64 -0
- data/lib/sliday_backup/storage/qiniu.rb +65 -0
- data/lib/sliday_backup/storage/rsync.rb +248 -0
- data/lib/sliday_backup/storage/s3.rb +156 -0
- data/lib/sliday_backup/storage/scp.rb +67 -0
- data/lib/sliday_backup/storage/sftp.rb +82 -0
- data/lib/sliday_backup/storage/sliday_storage.rb +79 -0
- data/lib/sliday_backup/syncer/base.rb +70 -0
- data/lib/sliday_backup/syncer/cloud/base.rb +179 -0
- data/lib/sliday_backup/syncer/cloud/cloud_files.rb +83 -0
- data/lib/sliday_backup/syncer/cloud/local_file.rb +100 -0
- data/lib/sliday_backup/syncer/cloud/s3.rb +110 -0
- data/lib/sliday_backup/syncer/rsync/base.rb +54 -0
- data/lib/sliday_backup/syncer/rsync/local.rb +31 -0
- data/lib/sliday_backup/syncer/rsync/pull.rb +51 -0
- data/lib/sliday_backup/syncer/rsync/push.rb +205 -0
- data/lib/sliday_backup/template.rb +46 -0
- data/lib/sliday_backup/utilities.rb +224 -0
- data/lib/sliday_backup/version.rb +5 -0
- data/templates/cli/archive +28 -0
- data/templates/cli/compressor/bzip2 +4 -0
- data/templates/cli/compressor/custom +7 -0
- data/templates/cli/compressor/gzip +4 -0
- data/templates/cli/config +123 -0
- data/templates/cli/databases/mongodb +15 -0
- data/templates/cli/databases/mysql +18 -0
- data/templates/cli/databases/openldap +24 -0
- data/templates/cli/databases/postgresql +16 -0
- data/templates/cli/databases/redis +16 -0
- data/templates/cli/databases/riak +17 -0
- data/templates/cli/databases/sqlite +11 -0
- data/templates/cli/encryptor/gpg +27 -0
- data/templates/cli/encryptor/openssl +9 -0
- data/templates/cli/model +26 -0
- data/templates/cli/notifier/zabbix +15 -0
- data/templates/cli/notifiers/campfire +12 -0
- data/templates/cli/notifiers/command +32 -0
- data/templates/cli/notifiers/datadog +57 -0
- data/templates/cli/notifiers/flowdock +16 -0
- data/templates/cli/notifiers/hipchat +16 -0
- data/templates/cli/notifiers/http_post +32 -0
- data/templates/cli/notifiers/mail +24 -0
- data/templates/cli/notifiers/nagios +13 -0
- data/templates/cli/notifiers/pagerduty +12 -0
- data/templates/cli/notifiers/prowl +11 -0
- data/templates/cli/notifiers/pushover +11 -0
- data/templates/cli/notifiers/ses +15 -0
- data/templates/cli/notifiers/slack +22 -0
- data/templates/cli/notifiers/twitter +13 -0
- data/templates/cli/splitter +7 -0
- data/templates/cli/storages/cloud_files +11 -0
- data/templates/cli/storages/dropbox +20 -0
- data/templates/cli/storages/ftp +13 -0
- data/templates/cli/storages/local +8 -0
- data/templates/cli/storages/qiniu +12 -0
- data/templates/cli/storages/rsync +17 -0
- data/templates/cli/storages/s3 +16 -0
- data/templates/cli/storages/scp +15 -0
- data/templates/cli/storages/sftp +15 -0
- data/templates/cli/storages/sliday_storage +6 -0
- data/templates/cli/syncers/cloud_files +22 -0
- data/templates/cli/syncers/rsync_local +20 -0
- data/templates/cli/syncers/rsync_pull +28 -0
- data/templates/cli/syncers/rsync_push +28 -0
- data/templates/cli/syncers/s3 +27 -0
- data/templates/general/links +3 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +16 -0
- data/templates/notifier/mail/success.erb +16 -0
- data/templates/notifier/mail/warning.erb +16 -0
- data/templates/storage/dropbox/authorization_url.erb +6 -0
- data/templates/storage/dropbox/authorized.erb +4 -0
- data/templates/storage/dropbox/cache_file_written.erb +10 -0
- metadata +1079 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SlidayBackup
|
4
|
+
class Logger
|
5
|
+
class Syslog
|
6
|
+
class Options
|
7
|
+
##
|
8
|
+
# Enables logging to the system's Syslog compatible logger.
|
9
|
+
#
|
10
|
+
# This may also be enabled using +--syslog+ on the command line.
|
11
|
+
#
|
12
|
+
# If +--no-syslog+ is used on the command line, this will be
|
13
|
+
# disabled and any settings here will be ignored.
|
14
|
+
#
|
15
|
+
# @param [Boolean, nil]
|
16
|
+
# @return [Boolean, nil] Default: +false+
|
17
|
+
attr_reader :enabled
|
18
|
+
|
19
|
+
##
|
20
|
+
# Specify the identification string to be used with Syslog.
|
21
|
+
#
|
22
|
+
# @param [String]
|
23
|
+
# @return [String] Default: 'backup'
|
24
|
+
attr_accessor :ident
|
25
|
+
|
26
|
+
##
|
27
|
+
# Specify the options to be used with Syslog.
|
28
|
+
#
|
29
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more info.
|
30
|
+
# http://rdoc.info/stdlib/syslog/Syslog.open
|
31
|
+
#
|
32
|
+
# Note that setting this to +nil+ will cause this to default
|
33
|
+
# to a setting of +Syslog::LOG_PID | Syslog::LOG_CONS+
|
34
|
+
#
|
35
|
+
# @param [Integer]
|
36
|
+
# @return [Integer] Default: +Syslog::LOG_PID+
|
37
|
+
attr_accessor :options
|
38
|
+
|
39
|
+
##
|
40
|
+
# Specify the facility to be used with Syslog.
|
41
|
+
#
|
42
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more info.
|
43
|
+
# http://rdoc.info/stdlib/syslog/Syslog.open
|
44
|
+
#
|
45
|
+
# Note that setting this to +nil+ will cause this to default
|
46
|
+
# to a setting of +Syslog::LOG_USER+
|
47
|
+
#
|
48
|
+
# @param [Integer]
|
49
|
+
# @return [Integer] Default: +Syslog::LOG_LOCAL0+
|
50
|
+
attr_accessor :facility
|
51
|
+
|
52
|
+
##
|
53
|
+
# Specify the priority level to be used for +:info+ messages.
|
54
|
+
#
|
55
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more info.
|
56
|
+
# http://rdoc.info/stdlib/syslog/Syslog.log
|
57
|
+
#
|
58
|
+
# @param [Integer]
|
59
|
+
# @return [Integer] Default: +Syslog::LOG_INFO+
|
60
|
+
attr_accessor :info
|
61
|
+
|
62
|
+
##
|
63
|
+
# Specify the priority level to be used for +:warn+ messages.
|
64
|
+
#
|
65
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more info.
|
66
|
+
# http://rdoc.info/stdlib/syslog/Syslog.log
|
67
|
+
#
|
68
|
+
# @param [Integer]
|
69
|
+
# @return [Integer] Default: +Syslog::LOG_WARNING+
|
70
|
+
attr_accessor :warn
|
71
|
+
|
72
|
+
##
|
73
|
+
# Specify the priority level to be used for +:error+ messages.
|
74
|
+
#
|
75
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more info.
|
76
|
+
# http://rdoc.info/stdlib/syslog/Syslog.log
|
77
|
+
#
|
78
|
+
# @param [Integer]
|
79
|
+
# @return [Integer] Default: +Syslog::LOG_ERR+
|
80
|
+
attr_accessor :error
|
81
|
+
|
82
|
+
def initialize
|
83
|
+
@enabled = false
|
84
|
+
@ident = 'backup'
|
85
|
+
@options = ::Syslog::LOG_PID
|
86
|
+
@facility = ::Syslog::LOG_LOCAL0
|
87
|
+
@info = ::Syslog::LOG_INFO
|
88
|
+
@warn = ::Syslog::LOG_WARNING
|
89
|
+
@error = ::Syslog::LOG_ERR
|
90
|
+
end
|
91
|
+
|
92
|
+
def enabled?
|
93
|
+
!!enabled
|
94
|
+
end
|
95
|
+
|
96
|
+
def enabled=(val)
|
97
|
+
@enabled = val unless enabled.nil?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def initialize(options)
|
102
|
+
@options = options
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Message lines are sent without formatting (timestamp, level),
|
107
|
+
# since Syslog will provide it's own timestamp and priority.
|
108
|
+
def log(message)
|
109
|
+
level = @options.send(message.level)
|
110
|
+
::Syslog.open(@options.ident, @options.options, @options.facility) do |s|
|
111
|
+
message.lines.each {|line| s.log(level, '%s', line) }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,479 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SlidayBackup
|
4
|
+
class Model
|
5
|
+
class Error < SlidayBackup::Error; end
|
6
|
+
class FatalError < SlidayBackup::FatalError; end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
##
|
10
|
+
# The SlidayBackup::Model.all class method keeps track of all the models
|
11
|
+
# that have been instantiated. It returns the @all class variable,
|
12
|
+
# which contains an array of all the models
|
13
|
+
def all
|
14
|
+
@all ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Return an Array of Models matching the given +trigger+.
|
19
|
+
def find_by_trigger(trigger)
|
20
|
+
trigger = trigger.to_s
|
21
|
+
if trigger.include?('*')
|
22
|
+
regex = /^#{ trigger.gsub('*', '(.*)') }$/
|
23
|
+
all.select {|model| regex =~ model.trigger }
|
24
|
+
else
|
25
|
+
all.select {|model| trigger == model.trigger }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Allows users to create preconfigured models.
|
30
|
+
def preconfigure(&block)
|
31
|
+
@preconfigure ||= block
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# used for testing
|
37
|
+
def reset!
|
38
|
+
@all = @preconfigure = nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# The trigger (stored as a String) is used as an identifier
|
44
|
+
# for initializing the backup process
|
45
|
+
attr_reader :trigger
|
46
|
+
|
47
|
+
##
|
48
|
+
# The label (stored as a String) is used for a more friendly user output
|
49
|
+
attr_reader :label
|
50
|
+
|
51
|
+
##
|
52
|
+
# Array of configured Database objects.
|
53
|
+
attr_reader :databases
|
54
|
+
|
55
|
+
##
|
56
|
+
# Array of configured Archive objects.
|
57
|
+
attr_reader :archives
|
58
|
+
|
59
|
+
##
|
60
|
+
# Array of configured Notifier objects.
|
61
|
+
attr_reader :notifiers
|
62
|
+
|
63
|
+
##
|
64
|
+
# Array of configured Storage objects.
|
65
|
+
attr_reader :storages
|
66
|
+
|
67
|
+
##
|
68
|
+
# Array of configured Syncer objects.
|
69
|
+
attr_reader :syncers
|
70
|
+
|
71
|
+
##
|
72
|
+
# The configured Compressor, if any.
|
73
|
+
attr_reader :compressor
|
74
|
+
|
75
|
+
##
|
76
|
+
# The configured Encryptor, if any.
|
77
|
+
attr_reader :encryptor
|
78
|
+
|
79
|
+
##
|
80
|
+
# The configured Splitter, if any.
|
81
|
+
attr_reader :splitter
|
82
|
+
|
83
|
+
##
|
84
|
+
# The final backup Package this model will create.
|
85
|
+
attr_reader :package
|
86
|
+
|
87
|
+
##
|
88
|
+
# The time when the backup initiated (in format: 2011.02.20.03.29.59)
|
89
|
+
attr_reader :time
|
90
|
+
|
91
|
+
##
|
92
|
+
# The time when the backup initiated (as a Time object)
|
93
|
+
attr_reader :started_at
|
94
|
+
|
95
|
+
##
|
96
|
+
# The time when the backup finished (as a Time object)
|
97
|
+
attr_reader :finished_at
|
98
|
+
|
99
|
+
##
|
100
|
+
# Result of this model's backup process.
|
101
|
+
#
|
102
|
+
# 0 = Job was successful
|
103
|
+
# 1 = Job was successful, but issued warnings
|
104
|
+
# 2 = Job failed, additional triggers may be performed
|
105
|
+
# 3 = Job failed, additional triggers will not be performed
|
106
|
+
attr_reader :exit_status
|
107
|
+
|
108
|
+
##
|
109
|
+
# Exception raised by either a +before+ hook or one of the model's
|
110
|
+
# procedures that caused the model to fail. An exception raised by an
|
111
|
+
# +after+ hook would not be stored here. Therefore, it is possible for
|
112
|
+
# this to be +nil+ even if #exit_status is 2 or 3.
|
113
|
+
attr_reader :exception
|
114
|
+
|
115
|
+
def initialize(trigger, label, &block)
|
116
|
+
@trigger = trigger.to_s
|
117
|
+
@label = label.to_s
|
118
|
+
@package = Package.new(self)
|
119
|
+
|
120
|
+
@databases = []
|
121
|
+
@archives = []
|
122
|
+
@storages = []
|
123
|
+
@notifiers = []
|
124
|
+
@syncers = []
|
125
|
+
|
126
|
+
instance_eval(&self.class.preconfigure) if self.class.preconfigure
|
127
|
+
instance_eval(&block) if block_given?
|
128
|
+
|
129
|
+
# trigger all defined databases to generate their #dump_filename
|
130
|
+
# so warnings may be logged if `backup perform --check` is used
|
131
|
+
databases.each {|db| db.send(:dump_filename) }
|
132
|
+
|
133
|
+
Model.all << self
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Adds an Archive. Multiple Archives may be added to the model.
|
138
|
+
def archive(name, &block)
|
139
|
+
@archives << Archive.new(self, name, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Adds an Database. Multiple Databases may be added to the model.
|
144
|
+
def database(name, database_id = nil, &block)
|
145
|
+
@databases << get_class_from_scope(Database, name).
|
146
|
+
new(self, database_id, &block)
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Adds an Storage. Multiple Storages may be added to the model.
|
151
|
+
def store_with(name, storage_id = nil, &block)
|
152
|
+
@storages << get_class_from_scope(Storage, name).
|
153
|
+
new(self, storage_id, &block)
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Adds an Syncer. Multiple Syncers may be added to the model.
|
158
|
+
def sync_with(name, syncer_id = nil, &block)
|
159
|
+
@syncers << get_class_from_scope(Syncer, name).new(syncer_id, &block)
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Adds an Notifier. Multiple Notifiers may be added to the model.
|
164
|
+
def notify_by(name, &block)
|
165
|
+
@notifiers << get_class_from_scope(Notifier, name).new(self, &block)
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Adds an Encryptor. Only one Encryptor may be added to the model.
|
170
|
+
# This will be used to encrypt the final backup package.
|
171
|
+
def encrypt_with(name, &block)
|
172
|
+
@encryptor = get_class_from_scope(Encryptor, name).new(&block)
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Adds an Compressor. Only one Compressor may be added to the model.
|
177
|
+
# This will be used to compress each individual Archive and Database
|
178
|
+
# stored within the final backup package.
|
179
|
+
def compress_with(name, &block)
|
180
|
+
@compressor = get_class_from_scope(Compressor, name).new(&block)
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Adds a Splitter to split the final backup package into multiple files.
|
185
|
+
#
|
186
|
+
# +chunk_size+ is specified in MiB and must be given as an Integer.
|
187
|
+
# +suffix_length+ controls the number of characters used in the suffix
|
188
|
+
# (and the maximum number of chunks possible).
|
189
|
+
# ie. 1 (-a, -b), 2 (-aa, -ab), 3 (-aaa, -aab)
|
190
|
+
def split_into_chunks_of(chunk_size, suffix_length = 3)
|
191
|
+
if chunk_size.is_a?(Integer) && suffix_length.is_a?(Integer)
|
192
|
+
@splitter = Splitter.new(self, chunk_size, suffix_length)
|
193
|
+
else
|
194
|
+
raise Error, <<-EOS
|
195
|
+
Invalid arguments for #split_into_chunks_of()
|
196
|
+
+chunk_size+ (and optional +suffix_length+) must be Integers.
|
197
|
+
EOS
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Defines a block of code to run before the model's procedures.
|
203
|
+
#
|
204
|
+
# Warnings logged within the before hook will elevate the model's
|
205
|
+
# exit_status to 1 and cause warning notifications to be sent.
|
206
|
+
#
|
207
|
+
# Raising an exception will abort the model and cause failure notifications
|
208
|
+
# to be sent. If the exception is a StandardError, exit_status will be 2.
|
209
|
+
# If the exception is not a StandardError, exit_status will be 3.
|
210
|
+
#
|
211
|
+
# If any exception is raised, any defined +after+ hook will be skipped.
|
212
|
+
def before(&block)
|
213
|
+
@before = block if block
|
214
|
+
@before
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Defines a block of code to run after the model's procedures.
|
219
|
+
#
|
220
|
+
# This code is ensured to run, even if the model failed, **unless** a
|
221
|
+
# +before+ hook raised an exception and aborted the model.
|
222
|
+
#
|
223
|
+
# The code block will be passed the model's current exit_status:
|
224
|
+
#
|
225
|
+
# `0`: Success, no warnings.
|
226
|
+
# `1`: Success, but warnings were logged.
|
227
|
+
# `2`: Failure, but additional models/triggers will still be processed.
|
228
|
+
# `3`: Failure, no additional models/triggers will be processed.
|
229
|
+
#
|
230
|
+
# The model's exit_status may be elevated based on the after hook's
|
231
|
+
# actions, but will never be decreased.
|
232
|
+
#
|
233
|
+
# Warnings logged within the after hook may elevate the model's
|
234
|
+
# exit_status to 1 and cause warning notifications to be sent.
|
235
|
+
#
|
236
|
+
# Raising an exception may elevate the model's exit_status and cause
|
237
|
+
# failure notifications to be sent. If the exception is a StandardError,
|
238
|
+
# the exit_status will be elevated to 2. If the exception is not a
|
239
|
+
# StandardError, the exit_status will be elevated to 3.
|
240
|
+
def after(&block)
|
241
|
+
@after = block if block
|
242
|
+
@after
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# Performs the backup process
|
247
|
+
#
|
248
|
+
# Once complete, #exit_status will indicate the result of this process.
|
249
|
+
#
|
250
|
+
# If any errors occur during the backup process, all temporary files will
|
251
|
+
# be left in place. If the error occurs before Packaging, then the
|
252
|
+
# temporary folder (tmp_path/trigger) will remain and may contain all or
|
253
|
+
# some of the configured Archives and/or Database dumps. If the error
|
254
|
+
# occurs after Packaging, but before the Storages complete, then the final
|
255
|
+
# packaged files (located in the root of tmp_path) will remain.
|
256
|
+
#
|
257
|
+
# *** Important ***
|
258
|
+
# If an error occurs and any of the above mentioned temporary files remain,
|
259
|
+
# those files *** will be removed *** before the next scheduled backup for
|
260
|
+
# the same trigger.
|
261
|
+
def perform!
|
262
|
+
@started_at = Time.now.utc
|
263
|
+
@time = package.time = started_at.strftime("%Y.%m.%d.%H.%M.%S")
|
264
|
+
|
265
|
+
log!(:started)
|
266
|
+
before_hook
|
267
|
+
|
268
|
+
procedures.each do |procedure|
|
269
|
+
procedure.is_a?(Proc) ? procedure.call : procedure.each(&:perform!)
|
270
|
+
end
|
271
|
+
|
272
|
+
syncers.each(&:perform!)
|
273
|
+
|
274
|
+
rescue Interrupt
|
275
|
+
@interrupted = true
|
276
|
+
raise
|
277
|
+
|
278
|
+
rescue Exception => err
|
279
|
+
@exception = err
|
280
|
+
|
281
|
+
ensure
|
282
|
+
unless @interrupted
|
283
|
+
set_exit_status
|
284
|
+
@finished_at = Time.now.utc
|
285
|
+
log!(:finished)
|
286
|
+
after_hook
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
##
|
291
|
+
# The duration of the backup process (in format: HH:MM:SS)
|
292
|
+
def duration
|
293
|
+
return unless finished_at
|
294
|
+
elapsed_time(started_at, finished_at)
|
295
|
+
end
|
296
|
+
|
297
|
+
private
|
298
|
+
|
299
|
+
##
|
300
|
+
# Returns an array of procedures that will be performed if any
|
301
|
+
# Archives or Databases are configured for the model.
|
302
|
+
def procedures
|
303
|
+
return [] unless databases.any? || archives.any?
|
304
|
+
|
305
|
+
[lambda { prepare! }, databases, archives,
|
306
|
+
lambda { package! }, lambda { store! }, lambda { clean! }]
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# Clean any temporary files and/or package files left over
|
311
|
+
# from the last time this model/trigger was performed.
|
312
|
+
# Logs warnings if files exist and are cleaned.
|
313
|
+
def prepare!
|
314
|
+
Cleaner.prepare(self)
|
315
|
+
end
|
316
|
+
|
317
|
+
##
|
318
|
+
# After all the databases and archives have been dumped and stored,
|
319
|
+
# these files will be bundled in to a .tar archive (uncompressed),
|
320
|
+
# which may be optionally Encrypted and/or Split into multiple "chunks".
|
321
|
+
# All information about this final archive is stored in the @package.
|
322
|
+
# Once complete, the temporary folder used during packaging is removed.
|
323
|
+
def package!
|
324
|
+
Packager.package!(self)
|
325
|
+
Cleaner.remove_packaging(self)
|
326
|
+
end
|
327
|
+
|
328
|
+
##
|
329
|
+
# Attempts to use all configured Storages, even if some of them result in exceptions.
|
330
|
+
# Returns true or raises first encountered exception.
|
331
|
+
def store!
|
332
|
+
storage_results = storages.map do |storage|
|
333
|
+
begin
|
334
|
+
storage.perform!
|
335
|
+
rescue => ex
|
336
|
+
ex
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
first_exception, *other_exceptions = storage_results.select { |result| result.is_a? Exception }
|
341
|
+
|
342
|
+
if first_exception
|
343
|
+
other_exceptions.each do |exception|
|
344
|
+
Logger.error exception.to_s
|
345
|
+
Logger.error exception.backtrace.join('\n')
|
346
|
+
end
|
347
|
+
raise first_exception
|
348
|
+
else
|
349
|
+
true
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
##
|
354
|
+
# Removes the final package file(s) once all configured Storages have run.
|
355
|
+
def clean!
|
356
|
+
Cleaner.remove_package(package)
|
357
|
+
end
|
358
|
+
|
359
|
+
##
|
360
|
+
# Returns the class/model specified by +name+ inside of +scope+.
|
361
|
+
# +scope+ should be a Class/Module.
|
362
|
+
# +name+ may be Class/Module or String representation
|
363
|
+
# of any namespace which exists under +scope+.
|
364
|
+
#
|
365
|
+
# The 'SlidayBackup::Config::DSL' namespace is stripped from +name+,
|
366
|
+
# since this is the namespace where we define module namespaces
|
367
|
+
# for use with Model's DSL methods.
|
368
|
+
#
|
369
|
+
# Examples:
|
370
|
+
# get_class_from_scope(SlidayBackup::Database, 'MySQL')
|
371
|
+
# returns the class SlidayBackup::Database::MySQL
|
372
|
+
#
|
373
|
+
# get_class_from_scope(SlidayBackup::Syncer, SlidayBackup::Config::RSync::Local)
|
374
|
+
# returns the class SlidayBackup::Syncer::RSync::Local
|
375
|
+
#
|
376
|
+
def get_class_from_scope(scope, name)
|
377
|
+
klass = scope
|
378
|
+
name = name.to_s.sub(/^SlidayBackup::Config::DSL::/, '')
|
379
|
+
name.split('::').each do |chunk|
|
380
|
+
klass = klass.const_get(chunk)
|
381
|
+
end
|
382
|
+
klass
|
383
|
+
end
|
384
|
+
|
385
|
+
##
|
386
|
+
# Sets or updates the model's #exit_status.
|
387
|
+
def set_exit_status
|
388
|
+
@exit_status = if exception
|
389
|
+
exception.is_a?(StandardError) ? 2 : 3
|
390
|
+
else
|
391
|
+
Logger.has_warnings? ? 1 : 0
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
##
|
396
|
+
# Runs the +before+ hook.
|
397
|
+
# Any exception raised will be wrapped and re-raised, where it will be
|
398
|
+
# handled by #perform the same as an exception raised while performing
|
399
|
+
# the model's #procedures. Only difference is that an exception raised
|
400
|
+
# here will prevent any +after+ hook from being run.
|
401
|
+
def before_hook
|
402
|
+
return unless before
|
403
|
+
|
404
|
+
Logger.info 'Before Hook Starting...'
|
405
|
+
before.call
|
406
|
+
Logger.info 'Before Hook Finished.'
|
407
|
+
|
408
|
+
rescue Exception => err
|
409
|
+
@before_hook_failed = true
|
410
|
+
ex = err.is_a?(StandardError) ? Error : FatalError
|
411
|
+
raise ex.wrap(err, 'Before Hook Failed!')
|
412
|
+
end
|
413
|
+
|
414
|
+
##
|
415
|
+
# Runs the +after+ hook.
|
416
|
+
# Any exception raised here will be logged only and the model's
|
417
|
+
# #exit_status will be elevated if neccessary.
|
418
|
+
def after_hook
|
419
|
+
return unless after && !@before_hook_failed
|
420
|
+
|
421
|
+
Logger.info 'After Hook Starting...'
|
422
|
+
after.call(exit_status)
|
423
|
+
Logger.info 'After Hook Finished.'
|
424
|
+
|
425
|
+
set_exit_status # in case hook logged warnings
|
426
|
+
|
427
|
+
rescue Exception => err
|
428
|
+
fatal = !err.is_a?(StandardError)
|
429
|
+
ex = fatal ? FatalError : Error
|
430
|
+
Logger.error ex.wrap(err, 'After Hook Failed!')
|
431
|
+
# upgrade exit_status if needed
|
432
|
+
(@exit_status = fatal ? 3 : 2) unless exit_status == 3
|
433
|
+
end
|
434
|
+
|
435
|
+
##
|
436
|
+
# Logs messages when the model starts and finishes.
|
437
|
+
#
|
438
|
+
# #exception will be set here if #exit_status is > 1,
|
439
|
+
# since log(:finished) is called before the +after+ hook.
|
440
|
+
def log!(action)
|
441
|
+
case action
|
442
|
+
when :started
|
443
|
+
Logger.info "Performing SlidayBackup for '#{ label } (#{ trigger })'!\n" +
|
444
|
+
"[ backup #{ VERSION } : #{ RUBY_DESCRIPTION } ]"
|
445
|
+
|
446
|
+
when :finished
|
447
|
+
if exit_status > 1
|
448
|
+
ex = exit_status == 2 ? Error : FatalError
|
449
|
+
err = ex.wrap(exception, "SlidayBackup for #{ label } (#{ trigger }) Failed!")
|
450
|
+
Logger.error err
|
451
|
+
Logger.error "\nBacktrace:\n\s\s" + err.backtrace.join("\n\s\s") + "\n\n"
|
452
|
+
|
453
|
+
Cleaner.warnings(self)
|
454
|
+
else
|
455
|
+
msg = "SlidayBackup for '#{ label } (#{ trigger })' "
|
456
|
+
if exit_status == 1
|
457
|
+
msg << "Completed Successfully (with Warnings) in #{ duration }"
|
458
|
+
Logger.warn msg
|
459
|
+
else
|
460
|
+
msg << "Completed Successfully in #{ duration }"
|
461
|
+
Logger.info msg
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
##
|
468
|
+
# Returns a string representing the elapsed time in HH:MM:SS.
|
469
|
+
def elapsed_time(start_time, finish_time)
|
470
|
+
duration = finish_time.to_i - start_time.to_i
|
471
|
+
hours = duration / 3600
|
472
|
+
remainder = duration - (hours * 3600)
|
473
|
+
minutes = remainder / 60
|
474
|
+
seconds = remainder - (minutes * 60)
|
475
|
+
'%02d:%02d:%02d' % [hours, minutes, seconds]
|
476
|
+
end
|
477
|
+
|
478
|
+
end
|
479
|
+
end
|