backup 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,8 +9,10 @@ namespace :backup do
9
9
  task :truncate => :environment do
10
10
  backup = Backup::Setup.new(ENV['trigger'], @backup_procedures)
11
11
  case backup.procedure.storage_name.to_sym
12
- when :s3 then Backup::Record::S3.destroy_all(:trigger => ENV['trigger'], :storage => 's3')
13
- when :scp then Backup::Record::SCP.destroy_all(:trigger => ENV['trigger'], :storage => 'scp')
12
+ when :s3 then Backup::Record::S3.destroy_all :trigger => ENV['trigger'], :storage => 's3'
13
+ when :scp then Backup::Record::SCP.destroy_all :trigger => ENV['trigger'], :storage => 'scp'
14
+ when :ftp then Backup::Record::FTP.destroy_all :trigger => ENV['trigger'], :storage => 'ftp'
15
+ when :sftp then Backup::Record::SFTP.destroy_all :trigger => ENV['trigger'], :storage => 'sftp'
14
16
  end
15
17
  end
16
18
 
@@ -18,23 +20,30 @@ namespace :backup do
18
20
  task :truncate_all => :environment do
19
21
  Backup::Record::S3.destroy_all
20
22
  Backup::Record::SCP.destroy_all
23
+ Backup::Record::FTP.destroy_all
24
+ Backup::Record::SFTP.destroy_all
21
25
  end
22
26
 
23
27
  desc "Destroys all records for the specified \"trigger\", including the physical files on s3 or the remote server."
24
28
  task :destroy => :environment do
25
29
  backup = Backup::Setup.new(ENV['trigger'], @backup_procedures)
26
30
  case backup.procedure.storage_name.to_sym
27
- when :s3 then Backup::Record::S3.destroy_all_backups(backup.procedure, ENV['trigger'])
28
- when :scp then Backup::Record::SCP.destroy_all_backups(backup.procedure, ENV['trigger'])
31
+ when :s3 then Backup::Record::S3.destroy_all_backups backup.procedure, ENV['trigger']
32
+ when :scp then Backup::Record::SCP.destroy_all_backups backup.procedure, ENV['trigger']
33
+ when :ftp then Backup::Record::FTP.destroy_all_backups backup.procedure, ENV['trigger']
34
+ when :sftp then Backup::Record::SFTP.destroy_all_backups backup.procedure, ENV['trigger']
29
35
  end
30
36
  end
31
37
 
32
38
  desc "Destroys all records for the specified \"trigger\", including the physical files on s3 or the remote server."
33
39
  task :destroy_all => :environment do
34
- @backup_procedures.each do |backup_procedure|
40
+ backup = Backup::Setup.new(false, @backup_procedures)
41
+ backup.procedures.each do |backup_procedure|
35
42
  case backup_procedure.storage_name.to_sym
36
- when :s3 then Backup::Record::S3.destroy_all_backups(backup_procedure, backup_procedure.trigger)
37
- when :scp then Backup::Record::SCP.destroy_all_backups(backup_procedure, backup_procedure.trigger)
43
+ when :s3 then Backup::Record::S3.destroy_all_backups backup_procedure, backup_procedure.trigger
44
+ when :scp then Backup::Record::SCP.destroy_all_backups backup_procedure, backup_procedure.trigger
45
+ when :ftp then Backup::Record::FTP.destroy_all_backups backup_procedure, backup_procedure.trigger
46
+ when :sftp then Backup::Record::SFTP.destroy_all_backups backup_procedure, backup_procedure.trigger
38
47
  end
39
48
  end
40
49
  end
data/lib/backup.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  # Load in Connectivity and Transfer Gems
2
2
  require 'net/ssh'
3
3
  require 'net/scp'
4
+ require 'net/ftp'
5
+ require 'net/sftp'
4
6
  require 'aws/s3'
5
7
 
6
8
  # Load in Adapters
7
9
  require 'backup/adapters/base'
8
10
  require 'backup/adapters/mysql'
11
+ require 'backup/adapters/postgresql'
9
12
  require 'backup/adapters/archive'
13
+ require 'backup/adapters/custom'
10
14
 
11
15
  # Load in Connectors
12
16
  require 'backup/connection/s3'
@@ -14,14 +18,19 @@ require 'backup/connection/s3'
14
18
  # Load in Storage
15
19
  require 'backup/storage/s3'
16
20
  require 'backup/storage/scp'
21
+ require 'backup/storage/ftp'
22
+ require 'backup/storage/sftp'
17
23
 
18
24
  # Load in Backup Recorders
19
25
  require 'backup/record/s3'
20
26
  require 'backup/record/scp'
27
+ require 'backup/record/ftp'
28
+ require 'backup/record/sftp'
21
29
 
22
30
  # Load in Configuration
23
31
  require 'backup/configuration/base'
24
32
  require 'backup/configuration/adapter'
33
+ require 'backup/configuration/adapter_options'
25
34
  require 'backup/configuration/storage'
26
35
  require 'backup/configuration/helpers'
27
36
 
@@ -38,21 +47,30 @@ module Backup
38
47
  class Setup
39
48
 
40
49
  attr_accessor :trigger, :procedures, :procedure
41
-
50
+
51
+ # Sets the Trigger and All Available Procedures.
52
+ # Will not find a specific procedure if the "trigger" argument is set to false.
42
53
  def initialize(trigger, procedures)
43
54
  self.trigger = trigger
44
55
  self.procedures = procedures
45
- self.procedure = find_triggered_procedure
56
+ self.procedure = find_triggered_procedure unless trigger.eql?(false)
46
57
  end
47
58
 
59
+ # Initializes one of the few adapters and start the backup process
48
60
  def initialize_adapter
49
61
  case procedure.adapter_name.to_sym
50
- when :mysql then Backup::Adapters::MySQL.new(trigger, procedure)
51
- when :archive then Backup::Adapters::Archive.new(trigger, procedure)
52
- else raise "Unknown Adapter: \"#{procedure.adapter_name}\""
62
+ when :mysql then Backup::Adapters::MySQL.new trigger, procedure
63
+ when :postgresql then Backup::Adapters::PostgreSQL.new trigger, procedure
64
+ when :archive then Backup::Adapters::Archive.new trigger, procedure
65
+ when :custom then Backup::Adapters::Custom.new trigger, procedure
66
+ else raise "Unknown Adapter: \"#{procedure.adapter_name}\"."
53
67
  end
54
68
  end
55
-
69
+
70
+ # Scans through all the backup settings and returns the backup setting
71
+ # that was specified in the "trigger" argument.
72
+ # If an non-existing trigger is specified, it will raise an error and display
73
+ # all the available triggers.
56
74
  def find_triggered_procedure
57
75
  procedures.each do |procedure|
58
76
  if procedure.trigger.eql?(trigger)
@@ -2,18 +2,32 @@ module Backup
2
2
  module Adapters
3
3
  class Archive < Backup::Adapters::Base
4
4
 
5
- attr_accessor :archived_file, :compressed_file, :encrypted_file, :user, :password, :database
5
+ attr_accessor :archived_file, :compressed_file, :encrypted_file
6
6
 
7
7
  # Initializes the Backup Process
8
+ #
9
+ # This will first load in any prefixed settings from the Backup::Adapters::Base
10
+ # Then it will add it's own settings.
11
+ #
12
+ # First it will archive and compress every folder/file
13
+ # Then it will optionally encrypt the backed up file
14
+ # Then it will store it to the specified storage location
15
+ # Then it will record the data to the database
16
+ # Once this is all done, all the temporary files will be removed
17
+ #
18
+ # Wrapped inside of begin/ensure/end block to ensure the deletion of any files in the tmp directory
8
19
  def initialize(trigger, procedure)
9
20
  super
10
21
  load_settings
11
22
 
12
- targz
13
- encrypt
14
- store
15
- record
16
- remove_tmp_files
23
+ begin
24
+ targz
25
+ encrypt
26
+ store
27
+ record
28
+ ensure
29
+ remove_tmp_files
30
+ end
17
31
  end
18
32
 
19
33
  private
@@ -38,11 +52,9 @@ module Backup
38
52
 
39
53
  # Loads the initial settings
40
54
  def load_settings
41
- self.user = procedure.get_adapter_configuration.attributes['user']
42
- self.password = procedure.get_adapter_configuration.attributes['password']
43
- self.database = procedure.get_adapter_configuration.attributes['database']
55
+ self.trigger = procedure.trigger
44
56
 
45
- self.archived_file = "#{timestamp}.archive.#{trigger.gsub(' ', '-')}.tar"
57
+ self.archived_file = "#{timestamp}.#{trigger.gsub(' ', '-')}.tar"
46
58
  self.compressed_file = "#{archived_file}.gz"
47
59
  self.encrypted_file = "#{compressed_file}.enc"
48
60
  self.final_file = compressed_file
@@ -19,7 +19,7 @@ module Backup
19
19
  self.trigger = trigger
20
20
  self.procedure = procedure
21
21
  self.timestamp = Time.now.strftime("%Y%m%d%H%M%S")
22
- self.tmp_path = "#{RAILS_ROOT.gsub(' ', '\ ')}/tmp/backup/#{procedure.adapter_name}"
22
+ self.tmp_path = File.join(RAILS_ROOT.gsub(' ', '\ '), 'tmp', 'backup', trigger)
23
23
  self.encrypt_with_password = procedure.attributes['encrypt_with_password']
24
24
  self.keep_backups = procedure.attributes['keep_backups']
25
25
  create_tmp_folder
@@ -32,21 +32,25 @@ module Backup
32
32
 
33
33
  # Removes the files inside the temporary folder
34
34
  def remove_tmp_files
35
- %x{ rm #{tmp_path}/* }
35
+ %x{ rm #{File.join(tmp_path, '*')} }
36
36
  end
37
37
 
38
38
  # Initializes the storing process depending on the store settings
39
39
  # Options:
40
40
  # Amazon (S3)
41
41
  # Remote Server (SCP)
42
+ # Remote Server (FTP)
43
+ # Remote Server (SFTP)
42
44
  def store
43
45
  case procedure.storage_name.to_sym
44
46
  when :s3 then Backup::Storage::S3.new(self)
45
47
  when :scp then Backup::Storage::SCP.new(self)
48
+ when :ftp then Backup::Storage::FTP.new(self)
49
+ when :sftp then Backup::Storage::SFTP.new(self)
46
50
  end
47
51
  end
48
52
 
49
- # Records data on every individual file to the backup.sqlite3 local database
53
+ # Records data on every individual file to the database
50
54
  def record
51
55
  case procedure.storage_name.to_sym
52
56
  when :s3
@@ -57,6 +61,14 @@ module Backup
57
61
  record = Backup::Record::SCP.new
58
62
  record.load_adapter(self)
59
63
  record.save
64
+ when :ftp
65
+ record = Backup::Record::FTP.new
66
+ record.load_adapter(self)
67
+ record.save
68
+ when :sftp
69
+ record = Backup::Record::SFTP.new
70
+ record.load_adapter(self)
71
+ record.save
60
72
  end
61
73
  end
62
74
 
@@ -0,0 +1,74 @@
1
+ module Backup
2
+ module Adapters
3
+ class Custom < Backup::Adapters::Base
4
+
5
+ attr_accessor :archived_file, :compressed_file, :encrypted_file, :commands
6
+
7
+ # Initializes the Backup Process
8
+ #
9
+ # This will first load in any prefixed settings from the Backup::Adapters::Base
10
+ # Then it will add it's own settings.
11
+ #
12
+ # First it will execute any given commands
13
+ # Then it will archive and compress every folder/file
14
+ # Then it will optionally encrypt the backed up file
15
+ # Then it will store it to the specified storage location
16
+ # Then it will record the data to the database
17
+ # Once this is all done, all the temporary files will be removed
18
+ #
19
+ # Wrapped inside of begin/ensure/end block to ensure the deletion of any files in the tmp directory
20
+ def initialize(trigger, procedure)
21
+ super
22
+ load_settings
23
+
24
+ begin
25
+ execute_commands
26
+ targz
27
+ encrypt
28
+ store
29
+ record
30
+ ensure
31
+ remove_tmp_files
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Executes the commands
38
+ def execute_commands
39
+ if commands.is_a?(Array)
40
+ commands.each do |command|
41
+ %x{ #{command.gsub(':tmp_path', tmp_path)} }
42
+ end
43
+ elsif commands.is_a?(String)
44
+ %x{ #{commands.gsub(':tmp_path', tmp_path)} }
45
+ end
46
+ end
47
+
48
+ # Archives and Compresses
49
+ def targz
50
+ %x{ tar -czf #{File.join(tmp_path, compressed_file)} #{File.join(tmp_path, '*')} }
51
+ end
52
+
53
+ # Encrypts the archive file
54
+ def encrypt
55
+ if encrypt_with_password.is_a?(String)
56
+ %x{ openssl enc -des-cbc -in #{File.join(tmp_path, compressed_file)} -out #{File.join(tmp_path, encrypted_file)} -k #{encrypt_with_password} }
57
+ self.final_file = encrypted_file
58
+ end
59
+ end
60
+
61
+ # Loads the initial settings
62
+ def load_settings
63
+ self.trigger = procedure.trigger
64
+ self.commands = procedure.get_adapter_configuration.attributes['commands']
65
+
66
+ self.archived_file = "#{timestamp}.#{trigger.gsub(' ', '-')}.tar"
67
+ self.compressed_file = "#{archived_file}.gz"
68
+ self.encrypted_file = "#{compressed_file}.enc"
69
+ self.final_file = compressed_file
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -2,25 +2,39 @@ module Backup
2
2
  module Adapters
3
3
  class MySQL < Backup::Adapters::Base
4
4
 
5
- attr_accessor :dumped_file, :compressed_file, :encrypted_file, :user, :password, :database
5
+ attr_accessor :dumped_file, :compressed_file, :encrypted_file, :user, :password, :database, :skip_tables, :host, :port, :socket
6
6
 
7
7
  # Initializes the Backup Process
8
+ #
9
+ # This will first load in any prefixed settings from the Backup::Adapters::Base
10
+ # Then it will add it's own settings.
11
+ #
12
+ # First it will create a compressed MySQL dump
13
+ # Then it will optionally encrypt the backed up file
14
+ # Then it will store it to the specified storage location
15
+ # Then it will record the data to the database
16
+ # Once this is all done, all the temporary files will be removed
17
+ #
18
+ # Wrapped inside of begin/ensure/end block to ensure the deletion of any files in the tmp directory
8
19
  def initialize(trigger, procedure)
9
20
  super
10
21
  load_settings
11
22
 
12
- mysqldump
13
- encrypt
14
- store
15
- record
16
- remove_tmp_files
23
+ begin
24
+ mysqldump
25
+ encrypt
26
+ store
27
+ record
28
+ ensure
29
+ remove_tmp_files
30
+ end
17
31
  end
18
32
 
19
33
  private
20
34
 
21
35
  # Dumps and Compresses the MySQL file
22
36
  def mysqldump
23
- %x{ mysqldump -u #{user} --password='#{password}' #{database} | gzip -f --best > #{File.join(tmp_path, compressed_file)} }
37
+ %x{ mysqldump -u #{user} --password='#{password}' #{options} #{database} #{tables_to_skip} | gzip -f --best > #{File.join(tmp_path, compressed_file)} }
24
38
  end
25
39
 
26
40
  # Encrypts the MySQL file
@@ -33,16 +47,42 @@ module Backup
33
47
 
34
48
  # Loads the initial settings
35
49
  def load_settings
36
- self.user = procedure.get_adapter_configuration.attributes['user']
37
- self.password = procedure.get_adapter_configuration.attributes['password']
38
- self.database = procedure.get_adapter_configuration.attributes['database']
50
+ self.trigger = procedure.trigger
51
+
52
+ %w(user password database skip_tables).each do |attribute|
53
+ send(:"#{attribute}=", procedure.get_adapter_configuration.attributes[attribute])
54
+ end
55
+
56
+ %w(host port socket).each do |attribute|
57
+ send(:"#{attribute}=", procedure.get_adapter_configuration.get_options.attributes[attribute])
58
+ end
39
59
 
40
- self.dumped_file = "#{timestamp}.#{database}.sql"
60
+ self.dumped_file = "#{timestamp}.#{trigger.gsub(' ', '-')}.sql"
41
61
  self.compressed_file = "#{dumped_file}.gz"
42
62
  self.encrypted_file = "#{compressed_file}.enc"
43
63
  self.final_file = compressed_file
44
64
  end
45
65
 
66
+ # Returns a list of options in MySQL syntax
67
+ def options
68
+ options = String.new
69
+ options += " --host='#{host}' " unless host.blank?
70
+ options += " --port='#{port}' " unless port.blank?
71
+ options += " --socket='#{socket}' " unless socket.blank?
72
+ options
73
+ end
74
+
75
+ # Returns a list of tables to skip in MySQL syntax
76
+ def tables_to_skip
77
+ if skip_tables.is_a?(Array)
78
+ skip_tables.map {|table| " --ignore-table='#{database}.#{table}' "}
79
+ elsif skip_tables.is_a?(String)
80
+ " --ignore-table='#{database}.#{skip_tables}' "
81
+ else
82
+ ""
83
+ end
84
+ end
85
+
46
86
  end
47
87
  end
48
88
  end
@@ -0,0 +1,88 @@
1
+ module Backup
2
+ module Adapters
3
+ class PostgreSQL < Backup::Adapters::Base
4
+
5
+ attr_accessor :dumped_file, :compressed_file, :encrypted_file, :user, :password, :database, :skip_tables, :host, :port, :socket
6
+
7
+ # Initializes the Backup Process
8
+ #
9
+ # This will first load in any prefixed settings from the Backup::Adapters::Base
10
+ # Then it will add it's own settings.
11
+ #
12
+ # First it will create a compressed PostgreSQL dump
13
+ # Then it will optionally encrypt the backed up file
14
+ # Then it will store it to the specified storage location
15
+ # Then it will record the data to the database
16
+ # Once this is all done, all the temporary files will be removed
17
+ #
18
+ # Wrapped inside of begin/ensure/end block to ensure the deletion of any files in the tmp directory
19
+ def initialize(trigger, procedure)
20
+ super
21
+ load_settings
22
+
23
+ begin
24
+ pg_dump
25
+ encrypt
26
+ store
27
+ record
28
+ ensure
29
+ remove_tmp_files
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # Dumps and Compresses the PostgreSQL file
36
+ def pg_dump
37
+ %x{ pg_dump -U #{user} #{options} #{tables_to_skip} #{database} | gzip -f --best > #{File.join(tmp_path, compressed_file)} }
38
+ end
39
+
40
+ # Encrypts the PostgreSQL file
41
+ def encrypt
42
+ if encrypt_with_password.is_a?(String)
43
+ %x{ openssl enc -des-cbc -in #{File.join(tmp_path, compressed_file)} -out #{File.join(tmp_path, encrypted_file)} -k #{encrypt_with_password} }
44
+ self.final_file = encrypted_file
45
+ end
46
+ end
47
+
48
+ # Loads the initial settings
49
+ def load_settings
50
+ self.trigger = procedure.trigger
51
+
52
+ %w(user password database skip_tables).each do |attribute|
53
+ send(:"#{attribute}=", procedure.get_adapter_configuration.attributes[attribute])
54
+ end
55
+
56
+ %w(host port socket).each do |attribute|
57
+ send(:"#{attribute}=", procedure.get_adapter_configuration.get_options.attributes[attribute])
58
+ end
59
+
60
+ self.dumped_file = "#{timestamp}.#{trigger.gsub(' ', '-')}.sql"
61
+ self.compressed_file = "#{dumped_file}.gz"
62
+ self.encrypted_file = "#{compressed_file}.enc"
63
+ self.final_file = compressed_file
64
+ end
65
+
66
+ # Returns a list of options in PostgreSQL syntax
67
+ def options
68
+ options = String.new
69
+ options += " --port='#{port}' " unless port.blank?
70
+ options += " --host='#{host}' " unless host.blank?
71
+ options += " --host='#{socket}' " unless socket.blank? unless options.include?('--host=')
72
+ options
73
+ end
74
+
75
+ # Returns a list of tables to skip in PostgreSQL syntax
76
+ def tables_to_skip
77
+ if skip_tables.is_a?(Array)
78
+ skip_tables.map {|table| " -T \"#{table}\" "}
79
+ elsif skip_tables.is_a?(String)
80
+ " -T \"#{skip_tables}\" "
81
+ else
82
+ ""
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+ end