lib/backup/model.rb in backup-3.0.19 vs lib/backup/model.rb in backup-3.0.20
- old
+ new
@@ -1,10 +1,10 @@
# encoding: utf-8
module Backup
class Model
- include Backup::CLI
+ include Backup::CLI::Helpers
##
# The trigger is used as an identifier for
# initializing the backup process
attr_accessor :trigger
@@ -40,10 +40,14 @@
##
# The syncers attribute holds an array of syncer objects
attr_accessor :syncers
##
+ # The chunk_size attribute holds the size of the chunks in megabytes
+ attr_accessor :chunk_size
+
+ ##
# The time when the backup initiated (in format: 2011.02.20.03.29.59)
attr_accessor :time
class << self
##
@@ -62,17 +66,27 @@
# 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
##
+ # Contains an array of chunk suffixes for a given file
+ attr_accessor :chunk_suffixes
+
+ ##
# 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 @chunk_suffixes variable, sets it to an emtpy array if nil
+ def chunk_suffixes
+ @chunk_suffixes ||= Array.new
+ 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
@@ -97,17 +111,13 @@
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
- @syncers = Array.new
+ procedure_instance_variables.each do |variable|
+ instance_variable_set(variable, Array.new)
+ end
instance_eval(&block)
Backup::Model.all << self
end
@@ -155,14 +165,14 @@
end
##
# Adds a storage method to the array of storage
# methods to use during the backup process
- def store_with(storage, &block)
+ def store_with(storage, storage_id = nil, &block)
@storages << Backup::Storage.const_get(
last_constant(storage)
- ).new(&block)
+ ).new(storage_id, &block)
end
##
# Adds a syncer method to the array of syncer
# methods to use during the backup process
@@ -171,29 +181,46 @@
last_constant(syncer)
).new(&block)
end
##
+ # Adds a method that allows the user to set the @chunk_size.
+ # The chunk_size (in megabytes) will later determine in how many chunks the
+ # backup needs to be split
+ def split_into_chunks_of(chunk_size = nil)
+ @chunk_size = chunk_size
+ end
+
+ ##
+ # Returns the path to the current file (including proper extension)
+ def file
+ Backup::Model.file
+ 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]
+ # [Packaging]
# 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
##
+ # [Splitting]
+ # Optionally splits the backup file in to multiple smaller chunks before transferring them
+ ##
# [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
##
@@ -210,65 +237,97 @@
# 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
- if databases.any? or archives.any?
- 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! }
- clean!
+ if databases.any? or archives.any?
+ procedures.each do |procedure|
+ (procedure.call; next) if procedure.is_a?(Proc)
+ procedure.each(&:perform!)
end
+ end
- syncers.each { |s| s.perform! }
- notifiers.each { |n| n.perform!(self) }
- rescue => exception
- clean!
- notifiers.each { |n| n.perform!(self, exception) }
- show_exception!(exception)
+ syncers.each(&:perform!)
+ notifiers.each { |n| n.perform!(self) }
+
+ rescue Exception => err
+ fatal = !err.is_a?(StandardError)
+
+ Logger.error Backup::Errors::ModelError.wrap(err, <<-EOS)
+ Backup for #{label} (#{trigger}) Failed!
+ An Error occured which has caused this Backup to abort before completion.
+ Please review the Log for this Backup to determine if steps need to be taken
+ to clean up, based on the point at which the failure occured.
+ EOS
+ Logger.error "\nBacktrace:\n" + err.backtrace.join("\n\s\s") + "\n\n"
+
+ if fatal
+ Logger.error Backup::Errors::ModelError.new(<<-EOS)
+ This Error was Fatal and Backup will now exit.
+ If you have other Backup jobs (triggers) configured to run,
+ they will not be processed.
+ EOS
+ else
+ Logger.message Backup::Errors::ModelError.new(<<-EOS)
+ If you have other Backup jobs (triggers) configured to run,
+ Backup will now attempt to continue...
+ EOS
end
+
+ notifiers.each do |n|
+ begin
+ n.perform!(self, err)
+ rescue Exception; end
+ end
+
+ exit(1) if fatal
+ ensure
+ clean!
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 -f '#{ File.join(TMP_PATH, "#{TIME}.#{TRIGGER}.tar") }' -C '#{ TMP_PATH }' '#{ TRIGGER }'|)
+ Backup::Packager.new(self).package!
end
##
+ # Create a new instance of Backup::Splitter,
+ # passing it the current model instance and runs it.
+ def split!
+ Backup::Splitter.new(self).split!
+ 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}") }'")
+ Backup::Cleaner.new(self).clean!
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
+ # Returns an array of procedures
+ def procedures
+ Array.new([
+ databases, archives, lambda { package! }, compressors,
+ encryptors, lambda { split! }, storages
+ ])
end
##
- # Formats an exception
- def show_exception!(exception)
- Logger.normal "=" * 75 + "\nException that got raised:\n#{exception.class} - #{exception} \n" + "=" * 75 + "\n" + exception.backtrace.join("\n")
- Logger.normal "=" * 75 + "\n\nYou are running Backup version \"#{Backup::Version.current}\" and Ruby version \"#{RUBY_VERSION} (patchlevel #{RUBY_PATCHLEVEL})\" on platform \"#{RUBY_PLATFORM}\".\n"
- Logger.normal "If you've setup a \"Notification\" in your configuration file, the above error will have been sent."
- #Notifies the shell an exception occured.
- exit 1
+ # Returns an Array of the names (String) of the procedure instance variables
+ def procedure_instance_variables
+ [:@databases, :@archives, :@encryptors, :@compressors, :@storages, :@notifiers, :@syncers]
+ end
+
+ ##
+ # Returns the string representation of the last value of a nested constant
+ # example: last_constant(Backup::Model::MySQL) becomes and returns "MySQL"
+ def last_constant(constant)
+ constant.to_s.split("::").last
end
end
end