forked from backup/backup
-
Notifications
You must be signed in to change notification settings - Fork 0
/
model.rb
249 lines (216 loc) · 7.99 KB
/
model.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# encoding: utf-8
module Backup
class Model
include Backup::CLI
##
# The trigger is used as an identifier for
# initializing the backup process
attr_accessor :trigger
##
# The label is used for a more friendly user output
attr_accessor :label
##
# The databases attribute holds an array of database objects
attr_accessor :databases
##
# The archives attr_accessor holds an array of archive objects
attr_accessor :archives
##
# The encryptors attr_accessor holds an array of encryptor objects
attr_accessor :encryptors
##
# The compressors attr_accessor holds an array of compressor objects
attr_accessor :compressors
##
# The notifiers attr_accessor holds an array of notifier objects
attr_accessor :notifiers
##
# The storages attribute holds an array of storage objects
attr_accessor :storages
##
# The time when the backup initiated (in format: 2011.02.20.03.29.59)
attr_accessor :time
class << self
##
# The Backup::Model.all class method keeps track of all the models
# that have been instantiated. It returns the @all class variable,
# which contains an array of all the models
attr_accessor :all
##
# Contains the current file extension (this changes from time to time after a file
# gets compressed or encrypted so we can keep track of the correct file when new
# extensions get appended to the current file name)
attr_accessor :extension
##
# Contains the currently-in-use model. This attribute should get set by Backup::Finder.
# Use Backup::Model.current to retrieve the actual data of the model
attr_accessor :current
##
# Returns the full path to the current file (including the current extension).
# To just return the filename and extension without the path, use File.basename(Backup::Model.file)
def file
File.join(TMP_PATH, "#{ TIME }.#{ TRIGGER }.#{ Backup::Model.extension }")
end
##
# Returns the temporary trigger path of the current model
# e.g. /Users/Michael/tmp/backup/my_trigger
def tmp_path
File.join(TMP_PATH, TRIGGER)
end
end
##
# Accessible through "Backup::Model.all", it stores an array of Backup::Model instances.
# Everytime a new Backup::Model gets instantiated it gets pushed into this array
@all = Array.new
##
# Contains the current file extension (should change after each compression or encryption)
@extension = 'tar'
##
# Takes a trigger, label and the configuration block and instantiates the model.
# The TIME (time of execution) gets stored in the @time attribute.
# After the instance has evaluated the configuration block and properly set the
# configuration for the model, it will append the newly created "model" instance
# to the @all class variable (Array) so it can be accessed by Backup::Finder
# and any other location
def initialize(trigger, label, &block)
@trigger = trigger
@label = label
@time = TIME
@databases = Array.new
@archives = Array.new
@encryptors = Array.new
@compressors = Array.new
@storages = Array.new
@notifiers = Array.new
instance_eval(&block)
Backup::Model.all << self
end
##
# Adds a database to the array of databases
# to dump during the backup process
def database(database, &block)
@databases << Backup::Database.const_get(
last_constant(database)
).new(&block)
end
##
# Adds an archive to the array of archives
# to store during the backup process
def archive(name, &block)
@archives << Backup::Archive.new(name, &block)
end
##
# Adds an encryptor to the array of encryptors
# to use during the backup process
def encrypt_with(name, &block)
@encryptors << Backup::Encryptor.const_get(
last_constant(name)
).new(&block)
end
##
# Adds a compressor to the array of compressors
# to use during the backup process
def compress_with(name, &block)
@compressors << Backup::Compressor.const_get(
last_constant(name)
).new(&block)
end
##
# Adds a notifier to the array of notifiers
# to use during the backup process
def notify_by(name, &block)
@notifiers << Backup::Notifier.const_get(
last_constant(name)
).new(&block)
end
##
# Adds a storage method to the array of storage
# methods to use during the backup process
def store_with(storage, &block)
@storages << Backup::Storage.const_get(
last_constant(storage)
).new(&block)
end
##
# Performs the backup process
##
# [Databases]
# Runs all (if any) database objects to dump the databases
##
# [Archives]
# Runs all (if any) archive objects to package all their
# paths in to a single tar file and places it in the backup folder
##
# [Package]
# After all the database dumps and archives are placed inside
# the folder, it'll make a single .tar package (archive) out of it
##
# [Encryption]
# Optionally encrypts the packaged file with one or more encryptors
##
# [Compression]
# Optionally compresses the packaged file with one or more compressors
##
# [Storages]
# Runs all (if any) storage objects to store the backups to remote locations
# and (if configured) it'll cycle the files on the remote location to limit the
# amount of backups stored on each individual location
##
# [Notifiers]
# Runs all (if any) notifier objects when a backup proces finished with or without
# any errors.
##
# [Cleaning]
# After the whole backup process finishes, it'll go ahead and remove any temporary
# file that it produced. If an exception(error) is raised during this process which
# breaks the process, it'll always ensure it removes the temporary files regardless
# to avoid mass consumption of storage space on the machine
def perform!
begin
databases.each { |d| d.perform! }
archives.each { |a| a.perform! }
package!
compressors.each { |c| c.perform! }
encryptors.each { |e| e.perform! }
storages.each { |s| s.perform! }
notifiers.each { |n| n.perform!(self) }
clean!
rescue Exception => exception
clean!
notifiers.each { |n| n.perform!(self, exception) }
show_exception!(exception)
end
end
private
##
# After all the databases and archives have been dumped and sorted,
# these files will be bundled in to a .tar archive (uncompressed) so it
# becomes a single (transferrable) packaged file.
def package!
Logger.message "Backup started packaging everything to a single archive file."
run("#{ utility(:tar) } -c -C '#{ TMP_PATH }' '#{ TRIGGER }' > '#{ File.join(TMP_PATH, "#{TIME}.#{TRIGGER}.tar") }'")
end
##
# Cleans up the temporary files that were created after the backup process finishes
def clean!
Logger.message "Backup started cleaning up the temporary files."
run("#{ utility(:rm) } -rf '#{ File.join(TMP_PATH, TRIGGER) }' '#{ File.join(TMP_PATH, "#{TIME}.#{TRIGGER}.#{Backup::Model.extension}") }'")
end
##
# Returns the string representation of the last value of a nested constant
# example:
# Backup::Model::MySQL
# becomes and returns:
# "MySQL"
def last_constant(constant)
constant.to_s.split("::").last
end
##
# Formats an exception
def show_exception!(exception)
puts ("=" * 75) + "\nException that got raised:\n#{exception}\n" + ("=" * 75) + "\n" + exception.backtrace.join("\n")
puts ("=" * 75) + "\n\nYou are running Backup version \"#{Backup::Version.current}\" and Ruby version \"#{ENV['RUBY_VERSION']}\".\n"
puts "If you've setup a \"Notification\" in your configuration file, the above error will have been sent."
end
end
end