backup 1.3.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/README.rdoc +121 -53
  2. data/Rakefile +3 -8
  3. data/VERSION +1 -1
  4. data/backup.gemspec +20 -34
  5. data/generators/backup/backup_generator.rb +31 -0
  6. data/generators/backup/templates/config/backup.rb +60 -0
  7. data/generators/backup/templates/migrations/create_backup_tables.rb +24 -0
  8. data/generators/backup/templates/tasks/backup.rake +42 -0
  9. data/lib/backup.rb +64 -12
  10. data/lib/backup/adapters/archive.rb +53 -0
  11. data/lib/backup/adapters/base.rb +65 -0
  12. data/lib/backup/adapters/mysql.rb +48 -0
  13. data/lib/backup/configuration/adapter.rb +17 -0
  14. data/lib/backup/configuration/base.rb +38 -0
  15. data/lib/backup/configuration/helpers.rb +12 -0
  16. data/lib/backup/configuration/storage.rb +17 -0
  17. data/lib/backup/connection/s3.rb +28 -13
  18. data/lib/backup/record/s3.rb +90 -0
  19. data/lib/backup/record/scp.rb +92 -0
  20. data/lib/backup/storage/s3.rb +14 -0
  21. data/lib/backup/storage/scp.rb +28 -0
  22. metadata +18 -35
  23. data/generators/backup_files/backup_files_generator.rb +0 -72
  24. data/generators/backup_files/templates/backup.sqlite3 +0 -0
  25. data/generators/backup_files/templates/config.rake +0 -20
  26. data/generators/backup_files/templates/db.rake +0 -62
  27. data/generators/backup_files/templates/s3.rake +0 -231
  28. data/generators/backup_files/templates/s3.yml +0 -120
  29. data/generators/backup_files/templates/setup.rake +0 -28
  30. data/generators/backup_files/templates/ssh.rake +0 -226
  31. data/generators/backup_files/templates/ssh.yml +0 -119
  32. data/lib/backup/adapter/assets.rb +0 -57
  33. data/lib/backup/adapter/custom.rb +0 -91
  34. data/lib/backup/adapter/mysql.rb +0 -65
  35. data/lib/backup/adapter/sqlite3.rb +0 -49
  36. data/lib/backup/backup_record/s3.rb +0 -90
  37. data/lib/backup/backup_record/ssh.rb +0 -90
  38. data/lib/backup/base.rb +0 -92
  39. data/lib/backup/connection/base.rb +0 -13
  40. data/lib/backup/connection/ssh.rb +0 -19
  41. data/lib/backup/encrypt.rb +0 -18
  42. data/lib/backup/transfer/base.rb +0 -13
  43. data/lib/backup/transfer/s3.rb +0 -36
  44. data/lib/backup/transfer/ssh.rb +0 -30
@@ -0,0 +1,24 @@
1
+ class CreateBackupTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :backup_s3 do |t|
4
+ t.string :trigger
5
+ t.string :adapter
6
+ t.string :filename
7
+ t.string :bucket
8
+ t.timestamps
9
+ end
10
+
11
+ create_table :backup_scp do |t|
12
+ t.string :trigger
13
+ t.string :adapter
14
+ t.string :filename
15
+ t.string :path
16
+ t.timestamps
17
+ end
18
+ end
19
+
20
+ def self.down
21
+ drop_table :backup_s3
22
+ drop_table :backup_scp
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ namespace :backup do
2
+
3
+ desc "Run Backup Procedure."
4
+ task :run => :environment do
5
+ Backup::Setup.new(ENV['trigger'], @backup_procedures).initialize_adapter
6
+ end
7
+
8
+ desc "Truncates all records for the specified \"trigger\", excluding the physical files on s3 or the remote server."
9
+ task :truncate => :environment do
10
+ backup = Backup::Setup.new(ENV['trigger'], @backup_procedures)
11
+ case backup.procedure.storage_name.to_sym
12
+ when :s3 then Backup::Record::S3.destroy_all(:trigger => ENV['trigger'])
13
+ when :scp then Backup::Record::SCP.destroy_all(:trigger => ENV['trigger'])
14
+ end
15
+ end
16
+
17
+ desc "Truncates everything."
18
+ task :truncate_all => :environment do
19
+ Backup::Record::S3.destroy_all
20
+ Backup::Record::SCP.destroy_all
21
+ end
22
+
23
+ desc "Destroys all records for the specified \"trigger\", including the physical files on s3 or the remote server."
24
+ task :destroy => :environment do
25
+ backup = Backup::Setup.new(ENV['trigger'], @backup_procedures)
26
+ 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'])
29
+ end
30
+ end
31
+
32
+ desc "Destroys all records for the specified \"trigger\", including the physical files on s3 or the remote server."
33
+ task :destroy_all => :environment do
34
+ @backup_procedures.each do |backup_procedure|
35
+ 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)
38
+ end
39
+ end
40
+ end
41
+
42
+ end
data/lib/backup.rb CHANGED
@@ -1,16 +1,68 @@
1
- require 'backup/base'
2
- require 'backup/encrypt'
3
- require 'backup/adapter/sqlite3'
4
- require 'backup/adapter/mysql'
5
- require 'backup/adapter/assets'
6
- require 'backup/adapter/custom'
7
- require 'backup/transfer/base'
8
- require 'backup/transfer/s3'
9
- require 'backup/transfer/ssh'
10
- require 'backup/connection/base'
1
+ # Connectivity and Record Gems
2
+ require 'net/ssh'
3
+ require 'net/scp'
4
+ require 'aws/s3'
5
+ require 'sqlite3'
6
+
7
+ # Load in Adapters
8
+ require 'backup/adapters/base'
9
+ require 'backup/adapters/mysql'
10
+ require 'backup/adapters/archive'
11
+
12
+ # Load in Connectors
11
13
  require 'backup/connection/s3'
12
- require 'backup/connection/ssh'
13
- require 'backup/backup_record/s3'
14
14
 
15
+ # Load in Storage
16
+ require 'backup/storage/s3'
17
+ require 'backup/storage/scp'
18
+
19
+ # Load in Backup Recorders
20
+ require 'backup/record/s3'
21
+ require 'backup/record/scp'
22
+
23
+ # Load in Configuration
24
+ require 'backup/configuration/base'
25
+ require 'backup/configuration/adapter'
26
+ require 'backup/configuration/storage'
27
+ require 'backup/configuration/helpers'
28
+
29
+ # Load Backup Configuration Helpers
30
+ include Backup::Configuration::Helpers
31
+
32
+ # Load in User Configured Backup Procedures if the file exists
33
+ if File.exist?(File.join(RAILS_ROOT, 'config', 'backup.rb'))
34
+ require File.join(RAILS_ROOT, 'config', 'backup.rb')
35
+ end
36
+
37
+ # Backup Module
15
38
  module Backup
39
+ class Setup
40
+
41
+ attr_accessor :trigger, :procedures, :procedure
42
+
43
+ def initialize(trigger, procedures)
44
+ self.trigger = trigger
45
+ self.procedures = procedures
46
+ self.procedure = find_triggered_procedure
47
+ end
48
+
49
+ def initialize_adapter
50
+ case procedure.adapter_name.to_sym
51
+ when :mysql then Backup::Adapters::MySQL.new(trigger, procedure)
52
+ when :archive then Backup::Adapters::Archive.new(trigger, procedure)
53
+ else raise "Unknown Adapter: \"#{procedure.adapter_name}\""
54
+ end
55
+ end
56
+
57
+ def find_triggered_procedure
58
+ procedures.each do |procedure|
59
+ if procedure.trigger.eql?(trigger)
60
+ return procedure
61
+ end
62
+ end
63
+ available_triggers = procedures.each.map {|procedure| "- #{procedure.trigger}\n" }
64
+ raise "Could not find a backup procedure with the trigger \"#{trigger}\". \nHere's a list of available triggers:\n#{available_triggers}"
65
+ end
66
+
67
+ end
16
68
  end
@@ -0,0 +1,53 @@
1
+ module Backup
2
+ module Adapters
3
+ class Archive < Backup::Adapters::Base
4
+
5
+ attr_accessor :archived_file, :compressed_file, :encrypted_file, :user, :password, :database
6
+
7
+ # Initializes the Backup Process
8
+ def initialize(trigger, procedure)
9
+ super
10
+ load_settings
11
+
12
+ targz
13
+ encrypt
14
+ store
15
+ record
16
+ remove_tmp_files
17
+ end
18
+
19
+ private
20
+
21
+ # Archives and Compresses all files
22
+ def targz
23
+ files = procedure.get_adapter_configuration.attributes['files']
24
+ if files.is_a?(Array)
25
+ %x{ tar -czf #{File.join(tmp_path, compressed_file)} #{files.map{|f| f.gsub(' ', '\ ')}.join(' ')} }
26
+ elsif files.is_a?(String)
27
+ %x{ tar -czf #{File.join(tmp_path, compressed_file)} #{files.gsub(' ', '\ ')} }
28
+ end
29
+ end
30
+
31
+ # Encrypts the Archive
32
+ def encrypt
33
+ if encrypt_with_password.is_a?(String)
34
+ %x{ openssl enc -des-cbc -in #{File.join(tmp_path, compressed_file)} -out #{File.join(tmp_path, encrypted_file)} -k #{encrypt_with_password} }
35
+ self.final_file = encrypted_file
36
+ end
37
+ end
38
+
39
+ # Loads the initial settings
40
+ 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']
44
+
45
+ self.archived_file = "#{timestamp}.archive.#{trigger.gsub(' ', '-')}.tar"
46
+ self.compressed_file = "#{archived_file}.gz"
47
+ self.encrypted_file = "#{compressed_file}.enc"
48
+ self.final_file = compressed_file
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,65 @@
1
+ module Backup
2
+ module Adapters
3
+ class Base
4
+
5
+ attr_accessor :procedure, :timestamp, :options, :tmp_path, :encrypt_with_password, :keep_backups, :trigger
6
+
7
+ # IMPORTANT
8
+ # final_file must have the value of the final filename result
9
+ # so if a file gets compressed, then the file could look like this:
10
+ # myfile.gz
11
+ #
12
+ # and if a file afterwards gets encrypted, the file will look like:
13
+ # myfile.gz.enc
14
+ #
15
+ # It is important that, whatever the final filename of the file will be, that :final_file will contain it.
16
+ attr_accessor :final_file
17
+
18
+ def initialize(trigger, procedure)
19
+ self.trigger = trigger
20
+ self.procedure = procedure
21
+ self.timestamp = Time.now.strftime("%Y%m%d%H%M%S")
22
+ self.tmp_path = "#{RAILS_ROOT.gsub(' ', '\ ')}/tmp/backup/#{procedure.adapter_name}"
23
+ self.encrypt_with_password = procedure.attributes['encrypt_with_password']
24
+ self.keep_backups = procedure.attributes['keep_backups']
25
+ create_tmp_folder
26
+ end
27
+
28
+ # Creates the temporary folder for the specified adapter
29
+ def create_tmp_folder
30
+ %x{ mkdir -p #{tmp_path} }
31
+ end
32
+
33
+ # Removes the files inside the temporary folder
34
+ def remove_tmp_files
35
+ %x{ rm #{tmp_path}/* }
36
+ end
37
+
38
+ # Initializes the storing process depending on the store settings
39
+ # Options:
40
+ # Amazon (S3)
41
+ # Remote Server (SCP)
42
+ def store
43
+ case procedure.storage_name.to_sym
44
+ when :s3 then Backup::Storage::S3.new(self)
45
+ when :scp then Backup::Storage::SCP.new(self)
46
+ end
47
+ end
48
+
49
+ # Records data on every individual file to the backup.sqlite3 local database
50
+ def record
51
+ case procedure.storage_name.to_sym
52
+ when :s3
53
+ record = Backup::Record::S3.new
54
+ record.load_adapter(self)
55
+ record.save
56
+ when :scp
57
+ record = Backup::Record::SCP.new
58
+ record.load_adapter(self)
59
+ record.save
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,48 @@
1
+ module Backup
2
+ module Adapters
3
+ class MySQL < Backup::Adapters::Base
4
+
5
+ attr_accessor :dumped_file, :compressed_file, :encrypted_file, :user, :password, :database
6
+
7
+ # Initializes the Backup Process
8
+ def initialize(trigger, procedure)
9
+ super
10
+ load_settings
11
+
12
+ mysqldump
13
+ encrypt
14
+ store
15
+ record
16
+ remove_tmp_files
17
+ end
18
+
19
+ private
20
+
21
+ # Dumps and Compresses the MySQL file
22
+ def mysqldump
23
+ %x{ mysqldump -u #{user} --password='#{password}' #{database} | gzip -f --best > #{File.join(tmp_path, compressed_file)} }
24
+ end
25
+
26
+ # Encrypts the MySQL file
27
+ def encrypt
28
+ if encrypt_with_password.is_a?(String)
29
+ %x{ openssl enc -des-cbc -in #{File.join(tmp_path, compressed_file)} -out #{File.join(tmp_path, encrypted_file)} -k #{encrypt_with_password} }
30
+ self.final_file = encrypted_file
31
+ end
32
+ end
33
+
34
+ # Loads the initial settings
35
+ 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']
39
+
40
+ self.dumped_file = "#{timestamp}.#{database}.sql"
41
+ self.compressed_file = "#{dumped_file}.gz"
42
+ self.encrypted_file = "#{compressed_file}.enc"
43
+ self.final_file = compressed_file
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ module Backup
2
+ module Configuration
3
+ class Adapter
4
+ attr_accessor :attributes
5
+
6
+ %w(files user password database).each do |method|
7
+ define_method method do |value|
8
+ attributes[method] = value
9
+ end
10
+ end
11
+
12
+ def initialize
13
+ @attributes = {}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ module Backup
2
+ module Configuration
3
+ class Base
4
+ attr_accessor :attributes, :trigger, :storage_name, :adapter_name
5
+
6
+ %w(encrypt_with_password keep_backups).each do |method|
7
+ define_method method do |value|
8
+ attributes[method] = value
9
+ end
10
+ end
11
+
12
+ def initialize(trigger)
13
+ @attributes = {}
14
+ @trigger = trigger
15
+ @adapter_configuration = Backup::Configuration::Adapter.new
16
+ @storage_configuration = Backup::Configuration::Storage.new
17
+ end
18
+
19
+ def adapter(adapter, &block)
20
+ @adapter_name = adapter
21
+ @adapter_configuration.instance_eval &block
22
+ end
23
+
24
+ def storage(storage, &block)
25
+ @storage_name = storage
26
+ @storage_configuration.instance_eval &block
27
+ end
28
+
29
+ def get_adapter_configuration
30
+ @adapter_configuration
31
+ end
32
+
33
+ def get_storage_configuration
34
+ @storage_configuration
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ module Backup
2
+ module Configuration
3
+ module Helpers
4
+ def backup(trigger, &block)
5
+ backup = Backup::Configuration::Base.new(trigger)
6
+ backup.instance_eval &block
7
+ @backup_procedures ||= Array.new
8
+ @backup_procedures << backup
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ module Backup
2
+ module Configuration
3
+ class Storage
4
+ attr_accessor :attributes
5
+
6
+ %w(ip user password path access_key_id secret_access_key bucket).each do |method|
7
+ define_method method do |value|
8
+ attributes[method] = value
9
+ end
10
+ end
11
+
12
+ def initialize
13
+ @attributes = {}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,18 +1,32 @@
1
1
  module Backup
2
2
  module Connection
3
- class S3 < Backup::Connection::Base
3
+ class S3
4
4
 
5
- def initialize(options = {})
6
- options.symbolize_keys!
7
- options[:s3].symbolize_keys!
8
- super(options)
5
+ attr_accessor :adapter, :access_key_id, :secret_access_key, :s3_bucket, :final_file, :tmp_path
6
+
7
+ # Initializes the S3 connection, setting the values using the S3 adapter
8
+ def initialize(adapter = false)
9
+ if adapter
10
+ self.final_file = adapter.final_file
11
+ self.tmp_path = adapter.tmp_path.gsub('\ ', ' ')
12
+ self.access_key_id = adapter.procedure.get_storage_configuration.attributes['access_key_id']
13
+ self.secret_access_key = adapter.procedure.get_storage_configuration.attributes['secret_access_key']
14
+ self.s3_bucket = adapter.procedure.get_storage_configuration.attributes['bucket']
15
+ end
16
+ end
17
+
18
+ # Sets values from a procedure, rather than from the adapter object
19
+ def static_initialize(procedure)
20
+ self.access_key_id = procedure.get_storage_configuration.attributes['access_key_id']
21
+ self.secret_access_key = procedure.get_storage_configuration.attributes['secret_access_key']
22
+ self.s3_bucket = procedure.get_storage_configuration.attributes['bucket']
9
23
  end
10
24
 
11
25
  # Establishes a connection with Amazon S3 using the credentials provided by the user
12
26
  def connect
13
27
  AWS::S3::Base.establish_connection!(
14
- :access_key_id => options[:s3][:access_key_id],
15
- :secret_access_key => options[:s3][:secret_access_key]
28
+ :access_key_id => access_key_id,
29
+ :secret_access_key => secret_access_key
16
30
  )
17
31
  end
18
32
 
@@ -33,17 +47,18 @@ module Backup
33
47
 
34
48
  # Initializes the file transfer to Amazon S3
35
49
  # This can only run after a connection has been made using the #connect method
36
- def transfer
50
+ def store
51
+ puts "Storing \"#{final_file}\" to bucket \"#{s3_bucket}\" on Amazon S3."
37
52
  object.store(
38
- options[:backup_file],
39
- open(File.join(options[:backup_path], options[:backup_file])),
40
- options[:s3][:bucket] )
53
+ final_file,
54
+ open(File.join(tmp_path, final_file)),
55
+ s3_bucket )
41
56
  end
42
57
 
43
58
  # Destroys file from a bucket on Amazon S3
44
- def destroy(backup_file, bucket)
59
+ def destroy(file, bucket)
45
60
  object.delete(
46
- backup_file,
61
+ file,
47
62
  bucket )
48
63
  end
49
64