backup-wakiki 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/CHANGELOG +77 -0
  4. data/LICENSE +9 -0
  5. data/README.textile +175 -0
  6. data/Rakefile +65 -0
  7. data/VERSION +1 -0
  8. data/backup-2.3.1.gem +0 -0
  9. data/backup-wakiki.gemspec +113 -0
  10. data/backup.gemspec +111 -0
  11. data/bin/backup +124 -0
  12. data/generators/backup/backup_generator.rb +72 -0
  13. data/generators/backup/templates/config/backup.rb +202 -0
  14. data/generators/backup/templates/migrations/create_backup_tables.rb +18 -0
  15. data/generators/backup/templates/tasks/backup.rake +71 -0
  16. data/generators/backup_update/backup_update_generator.rb +50 -0
  17. data/generators/backup_update/templates/migrations/update_backup_tables.rb +27 -0
  18. data/lib/backup.rb +112 -0
  19. data/lib/backup/adapters/archive.rb +34 -0
  20. data/lib/backup/adapters/base.rb +113 -0
  21. data/lib/backup/adapters/custom.rb +41 -0
  22. data/lib/backup/adapters/mysql.rb +60 -0
  23. data/lib/backup/adapters/postgresql.rb +56 -0
  24. data/lib/backup/adapters/sqlite.rb +25 -0
  25. data/lib/backup/command_helper.rb +11 -0
  26. data/lib/backup/configuration/adapter.rb +21 -0
  27. data/lib/backup/configuration/adapter_options.rb +8 -0
  28. data/lib/backup/configuration/attributes.rb +19 -0
  29. data/lib/backup/configuration/base.rb +55 -0
  30. data/lib/backup/configuration/helpers.rb +24 -0
  31. data/lib/backup/configuration/mail.rb +20 -0
  32. data/lib/backup/configuration/smtp.rb +8 -0
  33. data/lib/backup/configuration/storage.rb +8 -0
  34. data/lib/backup/connection/s3.rb +87 -0
  35. data/lib/backup/environment/base.rb +12 -0
  36. data/lib/backup/environment/rails.rb +17 -0
  37. data/lib/backup/environment/unix.rb +94 -0
  38. data/lib/backup/mail/base.rb +96 -0
  39. data/lib/backup/mail/mail.txt +7 -0
  40. data/lib/backup/record/base.rb +65 -0
  41. data/lib/backup/record/ftp.rb +39 -0
  42. data/lib/backup/record/local.rb +26 -0
  43. data/lib/backup/record/s3.rb +26 -0
  44. data/lib/backup/record/scp.rb +33 -0
  45. data/lib/backup/record/sftp.rb +38 -0
  46. data/lib/backup/storage/ftp.rb +38 -0
  47. data/lib/backup/storage/local.rb +24 -0
  48. data/lib/backup/storage/s3.rb +16 -0
  49. data/lib/backup/storage/scp.rb +30 -0
  50. data/lib/backup/storage/sftp.rb +31 -0
  51. data/setup/backup.rb +202 -0
  52. data/setup/backup.sqlite3 +0 -0
  53. data/spec/configuration/attributes_spec.rb +35 -0
  54. metadata +183 -0
@@ -0,0 +1,18 @@
1
+ class CreateBackupTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :backup do |t|
4
+ t.string :trigger
5
+ t.string :adapter
6
+ t.string :filename
7
+ t.string :md5sum
8
+ t.string :path
9
+ t.string :bucket
10
+ t.string :type
11
+ t.timestamps
12
+ end
13
+ end
14
+
15
+ def self.down
16
+ drop_table :backup
17
+ end
18
+ end
@@ -0,0 +1,71 @@
1
+ namespace :backup do
2
+
3
+ desc "Run Backup Procedure."
4
+ task :run => :environment do
5
+ puts "Running: #{ENV['trigger']}."
6
+ Backup::Setup.new(ENV['trigger'], @backup_procedures).initialize_adapter
7
+ end
8
+
9
+ desc "Finds backup records by trigger"
10
+ task :find => :environment do
11
+ puts "Finding backup records with trigger: #{ENV['trigger']}."
12
+ backup = Backup::Setup.new(ENV['trigger'], @backup_procedures)
13
+ records = Array.new
14
+ case backup.procedure.storage_name.to_sym
15
+ when :s3 then records = Backup::Record::S3.all :conditions => {:trigger => ENV['trigger']}
16
+ when :scp then records = Backup::Record::SCP.all :conditions => {:trigger => ENV['trigger']}
17
+ when :ftp then records = Backup::Record::FTP.all :conditions => {:trigger => ENV['trigger']}
18
+ when :sftp then records = Backup::Record::SFTP.all :conditions => {:trigger => ENV['trigger']}
19
+ when :local then records = Backup::Record::Local.all :conditions => {:trigger => ENV['trigger']}
20
+ end
21
+
22
+ if ENV['table'].eql?("true")
23
+ puts Hirb::Helpers::AutoTable.render(records)
24
+ else
25
+ records.each do |record|
26
+ puts record.to_yaml
27
+ end
28
+ end
29
+ end
30
+
31
+ desc "Truncates all records for the specified \"trigger\", excluding the physical files on s3 or the remote server."
32
+ task :truncate => :environment do
33
+ puts "Truncating backup records with trigger: #{ENV['trigger']}."
34
+ Backup::Record::Base.destroy_all :trigger => ENV['trigger']
35
+ end
36
+
37
+ desc "Truncates everything."
38
+ task :truncate_all => :environment do
39
+ puts "Truncating all backup records."
40
+ Backup::Record::Base.destroy_all
41
+ end
42
+
43
+ desc "Destroys all records for the specified \"trigger\", including the physical files on s3 or the remote server."
44
+ task :destroy => :environment do
45
+ puts "Destroying backup records with trigger: #{ENV['trigger']}."
46
+ backup = Backup::Setup.new(ENV['trigger'], @backup_procedures)
47
+ case backup.procedure.storage_name.to_sym
48
+ when :s3 then Backup::Record::S3.destroy_all_backups backup.procedure, ENV['trigger']
49
+ when :scp then Backup::Record::SCP.destroy_all_backups backup.procedure, ENV['trigger']
50
+ when :ftp then Backup::Record::FTP.destroy_all_backups backup.procedure, ENV['trigger']
51
+ when :sftp then Backup::Record::SFTP.destroy_all_backups backup.procedure, ENV['trigger']
52
+ when :local then Backup::Record::Local.destroy_all_backups backup.procedure, ENV['trigger']
53
+ end
54
+ end
55
+
56
+ desc "Destroys all records for the specified \"trigger\", including the physical files on s3 or the remote server."
57
+ task :destroy_all => :environment do
58
+ puts "Destroying all backup records."
59
+ backup = Backup::Setup.new(false, @backup_procedures)
60
+ backup.procedures.each do |backup_procedure|
61
+ case backup_procedure.storage_name.to_sym
62
+ when :s3 then Backup::Record::S3.destroy_all_backups backup_procedure, backup_procedure.trigger
63
+ when :scp then Backup::Record::SCP.destroy_all_backups backup_procedure, backup_procedure.trigger
64
+ when :ftp then Backup::Record::FTP.destroy_all_backups backup_procedure, backup_procedure.trigger
65
+ when :sftp then Backup::Record::SFTP.destroy_all_backups backup_procedure, backup_procedure.trigger
66
+ when :local then Backup::Record::Local.destroy_all_backups backup_procedure, backup_procedure.trigger
67
+ end
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,50 @@
1
+ class BackupUpdateGenerator < Rails::Generator::Base
2
+
3
+ # This method gets initialized when the generator gets run.
4
+ # It will receive an array of arguments inside @args
5
+ def initialize(runtime_args, runtime_options = {})
6
+ super
7
+ end
8
+
9
+ # Processes the file generation/templating
10
+ # This will automatically be run after the initialize method
11
+ def manifest
12
+ record do |m|
13
+
14
+ # Generates the database update migration file
15
+ m.migration_template "migrations/update_backup_tables.rb",
16
+ "db/migrate",
17
+ :migration_file_name => "update_backup_tables"
18
+
19
+ # Outputs the generators message to the terminal
20
+ puts message
21
+ end
22
+ end
23
+
24
+ def message
25
+ <<-MESSAGE
26
+
27
+
28
+
29
+ ==============================================================
30
+ Backup's update files have been generated!
31
+ ==============================================================
32
+
33
+ Please follow these instructions Backup:
34
+
35
+ 1: Please migrate the database to finish the update!
36
+
37
+ rake db:migrate
38
+
39
+
40
+ For More Information:
41
+ http://github.com/meskyanichi/backup
42
+
43
+ ==============================================================
44
+
45
+
46
+
47
+ MESSAGE
48
+ end
49
+
50
+ end
@@ -0,0 +1,27 @@
1
+ class UpdateBackupTables < ActiveRecord::Migration
2
+ def self.up
3
+ change_table :backup do |t|
4
+ t.rename :storage, :type # will use STI from now
5
+ t.string :md5sum
6
+ end
7
+
8
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='Backup::Record::FTP' WHERE type='ftp'"
9
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='Backup::Record::Local' WHERE type='local'"
10
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='Backup::Record::S3' WHERE type='s3'"
11
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='Backup::Record::SCP' WHERE type='scp'"
12
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='Backup::Record::SFTP' WHERE type='sftp'"
13
+ end
14
+
15
+ def self.down
16
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='ftp' WHERE type='Backup::Record::FTP'"
17
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='local' WHERE type='Backup::Record::Local'"
18
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='s3' WHERE type='Backup::Record::S3'"
19
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='scp' WHERE type='Backup::Record::SCP'"
20
+ ActiveRecord::Base.connection.execute "UPDATE backup SET type='sftp' WHERE type='Backup::Record::SFTP'"
21
+
22
+ change_table :backup do |t|
23
+ t.rename :type, :storage
24
+ t.remove :md5sum
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,112 @@
1
+ # Load Gems
2
+ require 'hirb'
3
+
4
+ # Load Environments
5
+ require 'backup/environment/base'
6
+ require 'backup/environment/unix'
7
+ require 'backup/environment/rails'
8
+
9
+ # Load Configuration
10
+ require 'backup/configuration/attributes'
11
+ require 'backup/configuration/base'
12
+ require 'backup/configuration/adapter'
13
+ require 'backup/configuration/adapter_options'
14
+ require 'backup/configuration/storage'
15
+ require 'backup/configuration/mail'
16
+ require 'backup/configuration/smtp'
17
+ require 'backup/configuration/helpers'
18
+
19
+ require 'backup/command_helper'
20
+
21
+ # Include the Configuration and Environment Helpers
22
+ include Backup::Configuration::Helpers
23
+ include Backup::Environment::Base
24
+
25
+ # Load either UNIX or RAILS environment configuration
26
+ case current_environment
27
+ when :unix then include Backup::Environment::Unix
28
+ when :rails then include Backup::Environment::Rails
29
+ end
30
+
31
+ # Load configuration
32
+ if File.exist?(File.join(BACKUP_PATH, 'config', 'backup.rb'))
33
+ require File.join(BACKUP_PATH, 'config', 'backup.rb')
34
+ end
35
+
36
+ # Load Mail Notifier
37
+ require 'backup/mail/base'
38
+
39
+ # Set Mail Configuration (extracted from the backup.rb configuration file) inside the Mail Class
40
+ Backup::Mail::Base.setup(@mail_configuration)
41
+
42
+ # Backup Module
43
+ module Backup
44
+ module Adapters
45
+ autoload :Base, 'backup/adapters/base'
46
+ autoload :MySQL, 'backup/adapters/mysql'
47
+ autoload :SQLite, 'backup/adapters/sqlite'
48
+ autoload :PostgreSQL, 'backup/adapters/postgresql'
49
+ autoload :Archive, 'backup/adapters/archive'
50
+ autoload :Custom, 'backup/adapters/custom'
51
+ end
52
+
53
+ module Storage
54
+ autoload :S3, 'backup/storage/s3'
55
+ autoload :SCP, 'backup/storage/scp'
56
+ autoload :FTP, 'backup/storage/ftp'
57
+ autoload :SFTP, 'backup/storage/sftp'
58
+ autoload :Local, 'backup/storage/local'
59
+ end
60
+
61
+ module Record
62
+ autoload :Base, 'backup/record/base'
63
+ autoload :S3, 'backup/record/s3'
64
+ autoload :SCP, 'backup/record/scp'
65
+ autoload :FTP, 'backup/record/ftp'
66
+ autoload :SFTP, 'backup/record/sftp'
67
+ autoload :Local, 'backup/record/local'
68
+ end
69
+
70
+ class Setup
71
+
72
+ attr_accessor :trigger, :procedures, :procedure
73
+
74
+ # Sets the Trigger and All Available Procedures.
75
+ # Will not find a specific procedure if the "trigger" argument is set to false.
76
+ def initialize(trigger, procedures)
77
+ self.trigger = trigger
78
+ self.procedures = procedures
79
+ self.procedure = find_triggered_procedure unless trigger.eql?(false)
80
+ end
81
+
82
+ # Initializes one of the few adapters and start the backup process
83
+ def initialize_adapter
84
+ case procedure.adapter_name.to_sym
85
+ when :mysql then Backup::Adapters::MySQL.new trigger, procedure
86
+ when :sqlite then Backup::Adapters::SQLite.new trigger, procedure
87
+ when :postgresql then Backup::Adapters::PostgreSQL.new trigger, procedure
88
+ when :archive then Backup::Adapters::Archive.new trigger, procedure
89
+ when :custom then Backup::Adapters::Custom.new trigger, procedure
90
+ else
91
+ puts "Unknown Adapter: \"#{procedure.adapter_name}\"."
92
+ exit
93
+ end
94
+ end
95
+
96
+ # Scans through all the backup settings and returns the backup setting
97
+ # that was specified in the "trigger" argument.
98
+ # If an non-existing trigger is specified, it will raise an error and display
99
+ # all the available triggers.
100
+ def find_triggered_procedure
101
+ procedures.each do |procedure|
102
+ if procedure.trigger.eql?(trigger)
103
+ return procedure
104
+ end
105
+ end
106
+ available_triggers = procedures.map {|procedure| "- #{procedure.trigger}\n" }
107
+ puts "Could not find a backup procedure with the trigger \"#{trigger}\". \nHere's a list of available triggers:\n#{available_triggers}"
108
+ exit
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,34 @@
1
+ module Backup
2
+ module Adapters
3
+ class Archive < Backup::Adapters::Base
4
+
5
+ attr_accessor :files, :exclude
6
+
7
+ private
8
+
9
+ # Archives and Compresses all files
10
+ def perform
11
+ log system_messages[:archiving]; log system_messages[:compressing]
12
+ run "tar -czf #{File.join(tmp_path, compressed_file)} #{exclude_files} #{tar_files}"
13
+ end
14
+
15
+ def load_settings
16
+ self.files = procedure.get_adapter_configuration.attributes['files']
17
+ self.exclude = procedure.get_adapter_configuration.attributes['exclude']
18
+ end
19
+
20
+ def performed_file_extension
21
+ ".tar"
22
+ end
23
+
24
+ def tar_files
25
+ [*files].map{|f| f.gsub(' ', '\ ')}.join(' ')
26
+ end
27
+
28
+ def exclude_files
29
+ [*exclude].compact.map{|x| "--exclude=#{x}"}.join(' ')
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,113 @@
1
+ module Backup
2
+ module Adapters
3
+ class Base
4
+
5
+ include Backup::CommandHelper
6
+
7
+ attr_accessor :procedure, :timestamp, :options, :tmp_path, :encrypt_with_password, :keep_backups, :trigger
8
+
9
+ # IMPORTANT
10
+ # final_file must have the value of the final filename result
11
+ # so if a file gets compressed, then the file could look like this:
12
+ # myfile.gz
13
+ #
14
+ # and if a file afterwards gets encrypted, the file will look like:
15
+ # myfile.gz.enc
16
+ #
17
+ # It is important that, whatever the final filename of the file will be, that :final_file will contain it.
18
+ attr_accessor :performed_file, :compressed_file, :encrypted_file, :final_file
19
+
20
+ # Initializes the Backup Process
21
+ #
22
+ # This will first load in any prefixed settings from the Backup::Adapters::Base
23
+ # Then it will add it's own settings.
24
+ #
25
+ # First it will call the 'perform' method. This method is concerned with the backup, and must
26
+ # be implemented by derived classes!
27
+ # Then it will optionally encrypt the backed up file
28
+ # Then it will store it to the specified storage location
29
+ # Then it will record the data to the database
30
+ # Once this is all done, all the temporary files will be removed
31
+ #
32
+ # Wrapped inside of begin/ensure/end block to ensure the deletion of any files in the tmp directory
33
+ def initialize(trigger, procedure)
34
+ self.trigger = trigger
35
+ self.procedure = procedure
36
+ self.timestamp = Time.now.strftime("%Y%m%d%H%M%S")
37
+ self.tmp_path = File.join(BACKUP_PATH.gsub(' ', '\ '), 'tmp', 'backup', trigger)
38
+ self.encrypt_with_password = procedure.attributes['encrypt_with_password']
39
+ self.keep_backups = procedure.attributes['keep_backups']
40
+
41
+ self.performed_file = "#{timestamp}.#{trigger.gsub(' ', '-')}#{performed_file_extension}"
42
+ self.compressed_file = "#{performed_file}.gz"
43
+ self.encrypted_file = "#{compressed_file}.enc"
44
+ self.final_file = compressed_file
45
+
46
+ begin
47
+ create_tmp_folder
48
+ load_settings # if respond_to?(:load_settings)
49
+ perform
50
+ encrypt
51
+ store
52
+ record
53
+ notify
54
+ ensure
55
+ remove_tmp_files
56
+ end
57
+ end
58
+
59
+ # Creates the temporary folder for the specified adapter
60
+ def create_tmp_folder
61
+ run "mkdir -p #{tmp_path}"
62
+ end
63
+
64
+ # TODO make methods in derived classes public? respond_to cannot identify private methods
65
+ def load_settings
66
+ end
67
+
68
+ # Removes the files inside the temporary folder
69
+ def remove_tmp_files
70
+ run "rm #{File.join(tmp_path, '*')}"
71
+ end
72
+
73
+ # Encrypts the archive file
74
+ def encrypt
75
+ if encrypt_with_password.is_a?(String)
76
+ log system_messages[:encrypting]
77
+ run "openssl enc -des-cbc -in #{File.join(tmp_path, compressed_file)} -out #{File.join(tmp_path, encrypted_file)} -k #{encrypt_with_password}"
78
+ self.final_file = encrypted_file
79
+ end
80
+ end
81
+
82
+ # Initializes the storing process
83
+ def store
84
+ procedure.initialize_storage(self)
85
+ end
86
+
87
+ # Records data on every individual file to the database
88
+ def record
89
+ record = procedure.initialize_record
90
+ record.load_adapter(self)
91
+ record.save
92
+ end
93
+
94
+ # Delivers a notification by email regarding the successfully stored backup
95
+ def notify
96
+ if Backup::Mail::Base.setup?
97
+ Backup::Mail::Base.notify!(self)
98
+ end
99
+ end
100
+
101
+ def system_messages
102
+ { :compressing => "Compressing backup..",
103
+ :archiving => "Archiving backup..",
104
+ :encrypting => "Encrypting backup..",
105
+ :mysqldump => "Creating MySQL dump..",
106
+ :pgdump => "Creating PostgreSQL dump..",
107
+ :sqlite => "Copying and compressing SQLite database..",
108
+ :commands => "Executing commands.." }
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,41 @@
1
+ module Backup
2
+ module Adapters
3
+ class Custom < Backup::Adapters::Base
4
+
5
+ attr_accessor :commands
6
+
7
+ private
8
+
9
+ # Execute any given commands, then archive and compress every folder/file
10
+ def perform
11
+ execute_commands
12
+ targz
13
+ end
14
+
15
+ # Executes the commands
16
+ def execute_commands
17
+ return unless commands
18
+ log system_messages[:commands]
19
+ [*commands].each do |command|
20
+ run "#{command.gsub(':tmp_path', tmp_path)}"
21
+ end
22
+ end
23
+
24
+ # Archives and Compresses
25
+ def targz
26
+ log system_messages[:archiving]; log system_messages[:compressing]
27
+ run "tar -czf #{File.join(tmp_path, compressed_file)} #{File.join(tmp_path, '*')}"
28
+ end
29
+
30
+ def performed_file_extension
31
+ ".tar"
32
+ end
33
+
34
+ # Loads the initial settings
35
+ def load_settings
36
+ self.commands = procedure.get_adapter_configuration.attributes['commands']
37
+ end
38
+
39
+ end
40
+ end
41
+ end