Class: Backup::Model

Inherits:
Object
  • Object
show all
Includes:
CLI::Helpers
Defined in:
lib/backup/model.rb

Constant Summary

Constants included from CLI::Helpers

CLI::Helpers::UTILITY

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(trigger, label, &block) ⇒ Model

Takes a trigger, label and the configuration block. After the instance has evaluated the configuration block to configure the model, it will be appended to Model.all



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/backup/model.rb', line 94

def initialize(trigger, label, &block)
  @trigger = trigger.to_s
  @label   = label.to_s

  # default noop hooks
  @hooks   = Hooks.new(self)

  procedure_instance_variables.each do |variable|
    instance_variable_set(variable, Array.new)
  end

  instance_eval(&block) if block_given?
  Model.all << self
end

Instance Attribute Details

#archivesObject (readonly)

The archives attr_accessor holds an array of archive objects



52
53
54
# File 'lib/backup/model.rb', line 52

def archives
  @archives
end

#compressorObject (readonly)

Holds the configured Compressor



68
69
70
# File 'lib/backup/model.rb', line 68

def compressor
  @compressor
end

#databasesObject (readonly)

The databases attribute holds an array of database objects



48
49
50
# File 'lib/backup/model.rb', line 48

def databases
  @databases
end

#encryptorObject (readonly)

Holds the configured Encryptor



72
73
74
# File 'lib/backup/model.rb', line 72

def encryptor
  @encryptor
end

#hooksObject (readonly)

Hooks which can be run before or after the backup process



88
89
90
# File 'lib/backup/model.rb', line 88

def hooks
  @hooks
end

#labelObject (readonly)

The label (stored as a String) is used for a more friendly user output



44
45
46
# File 'lib/backup/model.rb', line 44

def label
  @label
end

#notifiersObject (readonly)

The notifiers attr_accessor holds an array of notifier objects



56
57
58
# File 'lib/backup/model.rb', line 56

def notifiers
  @notifiers
end

#packageObject (readonly)

The final backup Package this model will create.



80
81
82
# File 'lib/backup/model.rb', line 80

def package
  @package
end

#splitterObject (readonly)

Holds the configured Splitter



76
77
78
# File 'lib/backup/model.rb', line 76

def splitter
  @splitter
end

#storagesObject (readonly)

The storages attribute holds an array of storage objects



60
61
62
# File 'lib/backup/model.rb', line 60

def storages
  @storages
end

#syncersObject (readonly)

The syncers attribute holds an array of syncer objects



64
65
66
# File 'lib/backup/model.rb', line 64

def syncers
  @syncers
end

#timeObject (readonly)

The time when the backup initiated (in format: 2011.02.20.03.29.59)



84
85
86
# File 'lib/backup/model.rb', line 84

def time
  @time
end

#triggerObject (readonly)

The trigger (stored as a String) is used as an identifier for initializing the backup process



40
41
42
# File 'lib/backup/model.rb', line 40

def trigger
  @trigger
end

Class Method Details

.allObject

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



12
13
14
# File 'lib/backup/model.rb', line 12

def all
  @all ||= []
end

.find(trigger) ⇒ Object

Return the first model matching trigger. Raises Errors::MissingTriggerError if no matches are found.

Raises:

  • (Errors::Model::MissingTriggerError)


19
20
21
22
23
24
25
26
# File 'lib/backup/model.rb', line 19

def find(trigger)
  trigger = trigger.to_s
  all.each do |model|
    return model if model.trigger == trigger
  end
  raise Errors::Model::MissingTriggerError,
      "Could not find trigger '#{trigger}'."
end

.find_matching(trigger) ⇒ Object

Find and return an Array of all models matching trigger Used to match triggers using a wildcard (*)



31
32
33
34
# File 'lib/backup/model.rb', line 31

def find_matching(trigger)
  regex = /^#{ trigger.to_s.gsub('*', '(.*)') }$/
  all.select {|model| regex =~ model.trigger }
end

Instance Method Details

#after(&block) ⇒ Object

Run a block of ruby code after the backup process



185
186
187
# File 'lib/backup/model.rb', line 185

def after(&block)
  @hooks.after &block
end

#archive(name, &block) ⇒ Object

Adds an archive to the array of archives to store during the backup process



112
113
114
# File 'lib/backup/model.rb', line 112

def archive(name, &block)
  @archives << Archive.new(self, name, &block)
end

#before(&block) ⇒ Object

Run a block of ruby code before the backup process



179
180
181
# File 'lib/backup/model.rb', line 179

def before(&block)
  @hooks.before &block
end

#compress_with(name, &block) ⇒ Object

Adds a compressor to use during the backup process



173
174
175
# File 'lib/backup/model.rb', line 173

def compress_with(name, &block)
  @compressor = get_class_from_scope(Compressor, name).new(&block)
end

#database(name, &block) ⇒ Object

Adds a database to the array of databases to dump during the backup process



119
120
121
# File 'lib/backup/model.rb', line 119

def database(name, &block)
  @databases << get_class_from_scope(Database, name).new(self, &block)
end

#encrypt_with(name, &block) ⇒ Object

Adds an encryptor to use during the backup process



167
168
169
# File 'lib/backup/model.rb', line 167

def encrypt_with(name, &block)
  @encryptor = get_class_from_scope(Encryptor, name).new(&block)
end

#notify_by(name, &block) ⇒ Object

Adds a notifier to the array of notifiers to use during the backup process



161
162
163
# File 'lib/backup/model.rb', line 161

def notify_by(name, &block)
  @notifiers << get_class_from_scope(Notifier, name).new(self, &block)
end

#perform!Object

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

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 the configured encryptor

Compression

Optionally compresses the each Archive and Database dump with the configured compressor

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

Syncers

Runs all (if any) sync objects to store the backups to remote locations. A Syncer does not go through the process of packaging, compressing, encrypting backups. A Syncer directly transfers data from the filesystem to the remote location

Notifiers

Runs all (if any) notifier objects when a backup proces finished with or without any errors.

Cleaning

Once the final Packaging is complete, the temporary folder used will be removed. Then, once all Storages have run, the final packaged files will be removed. If any errors occur during the backup process, all temporary files will be left in place. If the error occurs before Packaging, then the temporary folder (tmp_path/trigger) will remain and may contain all or some of the configured Archives and/or Database dumps. If the error occurs after Packaging, but before the Storages complete, then the final packaged files (located in the root of tmp_path) will remain. *** Important *** If an error occurs and any of the above mentioned temporary files remain, those files *** will be removed *** before the next scheduled backup for the same trigger.



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/backup/model.rb', line 265

def perform!
  @started_at = Time.now
  @time = @started_at.strftime("%Y.%m.%d.%H.%M.%S")
  log!(:started)

  @hooks.perform!(:before)

  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(&:perform!)
  notifiers.each(&:perform!)

  @hooks.perform!(:after)

  log!(:finished)

rescue Exception => err
  fatal = !err.is_a?(StandardError)

  err = Errors::ModelError.wrap(err, <<-EOS)
    Backup for #{label} (#{trigger}) Failed!
    An Error occured which has caused this Backup to abort before completion.
  EOS
  Logger.error err
  Logger.error "\nBacktrace:\n\s\s" + err.backtrace.join("\n\s\s") + "\n\n"

  Cleaner.warnings(self)

  if fatal
    Logger.error 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 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!(true)
    rescue Exception; end
  end

  exit(1) if fatal
end

#prepare!Object

Ensure DATA_PATH and DATA_PATH/TRIGGER are created if they do not yet exist

Clean any temporary files and/or package files left over from the last time this model/trigger was performed. Logs warnings if files exist and are cleaned.



212
213
214
215
# File 'lib/backup/model.rb', line 212

def prepare!
  FileUtils.mkdir_p(File.join(Config.data_path, trigger))
  Cleaner.prepare(self)
end

#split_into_chunks_of(chunk_size) ⇒ Object

Adds a method that allows the user to configure this backup model to use a Splitter, with the given chunk_size The chunk_size (in megabytes) will later determine in how many chunks the backup needs to be split into



194
195
196
197
198
199
200
201
202
203
# File 'lib/backup/model.rb', line 194

def split_into_chunks_of(chunk_size)
  if chunk_size.is_a?(Integer)
    @splitter = Splitter.new(self, chunk_size)
  else
    raise Errors::Model::ConfigurationError, <<-EOS
      Invalid Chunk Size for Splitter
      Argument to #split_into_chunks_of() must be an Integer
    EOS
  end
end

#store_with(name, storage_id = nil, &block) ⇒ Object

Adds a storage method to the array of storage methods to use during the backup process



126
127
128
# File 'lib/backup/model.rb', line 126

def store_with(name, storage_id = nil, &block)
  @storages << get_class_from_scope(Storage, name).new(self, storage_id, &block)
end

#sync_with(name, &block) ⇒ Object

Adds a syncer method to the array of syncer methods to use during the backup process



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/backup/model.rb', line 133

def sync_with(name, &block)
  ##
  # Warn user of DSL changes
  case name.to_s
  when 'Backup::Config::RSync'
    Logger.warn Errors::ConfigError.new(<<-EOS)
      Configuration Update Needed for Syncer::RSync
      The RSync Syncer has been split into three separate modules:
      RSync::Local, RSync::Push and RSync::Pull
      Please update your configuration.
      i.e. 'sync_with RSync' is now 'sync_with RSync::Push'
    EOS
    name = 'RSync::Push'
  when /(Backup::Config::S3|Backup::Config::CloudFiles)/
    syncer = $1.split('::')[2]
    Logger.warn Errors::ConfigError.new(<<-EOS)
      Configuration Update Needed for '#{ syncer }' Syncer.
      This Syncer is now referenced as Cloud::#{ syncer }
      i.e. 'sync_with #{ syncer }' is now 'sync_with Cloud::#{ syncer }'
    EOS
    name = "Cloud::#{ syncer }"
  end
  @syncers << get_class_from_scope(Syncer, name).new(&block)
end