backup-wakiki 2.4.1

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 (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,94 @@
1
+ module Backup
2
+ module Environment
3
+ module Unix
4
+
5
+ require 'active_record'
6
+ require 'optparse'
7
+
8
+ # Sets BACKUP_PATH equal to /opt/backup
9
+ BACKUP_PATH = "/opt/backup"
10
+
11
+ # Sets DB_CONNECTION_SETTINGS
12
+ DB_CONNECTION_SETTINGS = {
13
+ :adapter => "sqlite3",
14
+ :database => "/opt/backup/backup.sqlite3",
15
+ :pool => 5,
16
+ :timeout => 5000
17
+ }
18
+
19
+ module Commands
20
+
21
+ def setup
22
+ unless File.directory?(BACKUP_PATH)
23
+ puts "Installing Backup in #{BACKUP_PATH}.."
24
+ %x{ sudo mkdir -p #{File.join(BACKUP_PATH, 'config')} }
25
+ %x{ sudo cp #{File.join(File.dirname(__FILE__), '..', '..', '..', 'setup', 'backup.sqlite3')} #{BACKUP_PATH} }
26
+ %x{ sudo cp #{File.join(File.dirname(__FILE__), '..', '..', '..', 'setup', 'backup.rb')} #{File.join(BACKUP_PATH, 'config')} }
27
+ puts <<-MESSAGE
28
+
29
+ ==============================================================
30
+ Backup has been set up!
31
+ ==============================================================
32
+
33
+ 1: Set up some "Backup Settings" inside the configuration file!
34
+
35
+ /opt/backup/config/backup.rb
36
+
37
+
38
+ 2: Run the backups!
39
+
40
+ sudo backup --run [trigger]
41
+
42
+
43
+ For a list of Backup commands:
44
+
45
+ sudo backup --help
46
+
47
+
48
+ For More Information:
49
+
50
+ http://github.com/meskyanichi/backup
51
+
52
+ ==============================================================
53
+
54
+ MESSAGE
55
+ else
56
+ puts "\nBackup is already installed in #{BACKUP_PATH}..\n"
57
+ puts "If you want to reset it, run:\n\nbackup --reset\n\n"
58
+ puts "This will reinstall it."
59
+ puts "Warning: All configuration will be lost!\n\n"
60
+ end
61
+ end
62
+
63
+ def reset
64
+ if File.directory?(BACKUP_PATH)
65
+ remove
66
+ setup
67
+ else
68
+ puts "Backup is not installed.\n"
69
+ puts "Run the following command to install it:\n\nbackup --setup"
70
+ end
71
+ end
72
+
73
+ def remove
74
+ puts "Removing Backup..\n"
75
+ %x{ sudo rm -rf #{BACKUP_PATH} }
76
+ end
77
+ end
78
+
79
+ module Helpers
80
+
81
+ def confirm_configuration_file_existence
82
+ unless File.exist?(File.join(BACKUP_PATH, 'config', 'backup.rb'))
83
+ puts "\nBackup could not find the Backup Configuration File."
84
+ puts "Did you set up Backup? Do so if you haven't yet:"
85
+ puts "\nbackup --setup\n "
86
+ exit
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,96 @@
1
+ require 'pony'
2
+
3
+ module Backup
4
+ module Mail
5
+ class Base
6
+
7
+ # Sets up the Mail Configuration for the Backup::Mail::Base class.
8
+ # This must be set in order to send emails
9
+ # It will dynamically add class methods (configuration) for each email that will be sent
10
+ def self.setup(config)
11
+ if config
12
+ (class << self; self; end).instance_eval do
13
+ config.attributes.each do |method, value|
14
+ define_method method do
15
+ value
16
+ end
17
+ end
18
+ config.get_smtp_configuration.attributes.each do |method, value|
19
+ define_method method do
20
+ value
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ # Returns true if the "to" and "from" attributes are set
28
+ def self.setup?
29
+ return true if defined?(from) and defined?(to)
30
+ false
31
+ end
32
+
33
+ # Delivers the backup details by email to the recipient
34
+ # Requires the Backup Object
35
+ def self.notify!(backup)
36
+ if self.setup? and backup.procedure.attributes['notify'].eql?(true)
37
+ @backup = backup
38
+ self.parse_body
39
+ PersonMailer.deliver_backup_notification(@backup.trigger, @content, :stream => :support)
40
+ # Pony.mail({
41
+ # :subject => "Backup for \"#{@backup.trigger}\" was successfully created!",
42
+ # :body => @content
43
+ # }.merge(self.smtp_configuration))
44
+ puts "Sending notification to #{self.to}."
45
+ end
46
+ end
47
+
48
+ # Retrieves SMTP configuration
49
+ def self.smtp_configuration
50
+ { :to => self.to,
51
+ :from => self.from,
52
+ :via => :smtp,
53
+ :smtp => {
54
+ :host => self.host,
55
+ :port => self.port,
56
+ :user => self.username,
57
+ :password => self.password,
58
+ :auth => self.authentication,
59
+ :domain => self.domain,
60
+ :tls => self.tls
61
+ }}
62
+ end
63
+
64
+ def self.parse_body
65
+ File.open(File.join(File.dirname(__FILE__), 'mail.txt'), 'r') do |file|
66
+ self.gsub_content(file.readlines)
67
+ end
68
+ end
69
+
70
+ def self.gsub_content(lines)
71
+ bucket = @backup.procedure.get_storage_configuration.attributes['bucket']
72
+ path = @backup.procedure.get_storage_configuration.attributes['path']
73
+ ip = @backup.procedure.get_storage_configuration.attributes['ip']
74
+
75
+ lines.each do |line|
76
+ line.gsub!(':trigger', @backup.trigger)
77
+ line.gsub!(':day', Time.now.strftime("%A (%d)"))
78
+ line.gsub!(':month', Time.now.strftime("%B"))
79
+ line.gsub!(':year', Time.now.strftime("%Y"))
80
+ line.gsub!(':time', Time.now.strftime("%r"))
81
+ line.gsub!(':adapter', @backup.procedure.adapter_name.to_s)
82
+ line.gsub!(':location', bucket || path)
83
+ line.gsub!(':backup', @backup.final_file)
84
+ case @backup.procedure.storage_name.to_sym
85
+ when :s3 then line.gsub!(':remote', "on Amazon S3")
86
+ when :local then line.gsub!(':remote', "on the local server")
87
+ when :scp, :sftp, :ftp then line.gsub!(':remote', "on the remote server (#{ip})")
88
+ end
89
+ @content ||= String.new
90
+ @content << line
91
+ end
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,7 @@
1
+ A backup was successfully created for the trigger: ":trigger"!
2
+
3
+ The backup was created on ":day of :month :year at :time", using the ":adapter" adapter.
4
+
5
+ The Backup was stored in ":location" :remote.
6
+
7
+ The following backup was stored: ":backup"
@@ -0,0 +1,65 @@
1
+ module Backup
2
+ module Record
3
+ class Base < ActiveRecord::Base
4
+
5
+ if DB_CONNECTION_SETTINGS
6
+ establish_connection(DB_CONNECTION_SETTINGS)
7
+ end
8
+
9
+ set_table_name 'backup'
10
+
11
+ default_scope :order => 'created_at desc'
12
+
13
+ # Callbacks
14
+ after_save :clean_backups
15
+
16
+ # Attributes
17
+ attr_accessor :adapter_config, :keep_backups
18
+
19
+ # Receives the options hash and stores it
20
+ def load_adapter(adapter)
21
+ self.adapter_config = adapter
22
+ self.trigger = adapter.procedure.trigger
23
+ self.adapter = adapter.procedure.adapter_name.to_s
24
+ self.filename = adapter.final_file
25
+ self.keep_backups = adapter.procedure.attributes['keep_backups']
26
+
27
+ # TODO calculate md5sum of file
28
+ load_specific_settings(adapter) if respond_to?(:load_specific_settings)
29
+ end
30
+
31
+ # Destroys all backups for the specified trigger from Remote Server (FTP)
32
+ def self.destroy_all_backups(procedure, trigger)
33
+ backups = self.all(:conditions => {:trigger => trigger})
34
+ unless backups.empty?
35
+ # Derived classes must implement this method!
36
+ self.destroy_backups(procedure, backups)
37
+
38
+ puts "\nAll \"#{procedure.trigger}\" backups destroyed.\n\n"
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # Maintains the backup file amount on the remote server
45
+ # This is invoked after a successful record save
46
+ # This deletes the oldest files when the backup limit has been exceeded
47
+ def clean_backups
48
+ if keep_backups.is_a?(Integer)
49
+ backups = self.class.all(:conditions => {:trigger => trigger})
50
+ backups_to_destroy = backups[keep_backups, backups.size] || []
51
+
52
+ unless backups_to_destroy.empty?
53
+ # Derived classes must implement this method!
54
+ self.class.destroy_backups(adapter_config.procedure, backups_to_destroy)
55
+
56
+ puts "\nBackup storage for \"#{trigger}\" is limited to #{keep_backups} backups."
57
+ puts "\nThe #{keep_backups} most recent backups are now stored on the remote server.\n\n"
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,39 @@
1
+ require 'net/ftp'
2
+
3
+ module Backup
4
+ module Record
5
+ class FTP < Backup::Record::Base
6
+
7
+ attr_accessor :ip, :user, :password
8
+
9
+ def load_specific_settings(adapter)
10
+ %w(ip user password path).each do |method|
11
+ send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def self.destroy_backups(procedure, backups)
18
+ ip = procedure.get_storage_configuration.attributes['ip']
19
+ user = procedure.get_storage_configuration.attributes['user']
20
+ password = procedure.get_storage_configuration.attributes['password']
21
+
22
+ Net::FTP.open(ip, user, password) do |ftp|
23
+ backups.each do |backup|
24
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
25
+ begin
26
+ ftp.chdir(backup.path)
27
+ ftp.delete(backup.filename)
28
+ backup.destroy
29
+ rescue
30
+ puts "Could not find backup #{backup.path}/#{backup.filename}."
31
+ backup.destroy
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ module Backup
2
+ module Record
3
+ class Local < Backup::Record::Base
4
+
5
+ def load_specific_settings(adapter)
6
+ self.path = adapter.procedure.get_storage_configuration.attributes['path']
7
+ end
8
+
9
+ private
10
+
11
+ class << self
12
+ include Backup::CommandHelper
13
+
14
+ def destroy_backups(procedure, backups)
15
+ backups.each do |backup|
16
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
17
+ run "rm #{File.join(backup.path, backup.filename)}"
18
+ backup.destroy
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,26 @@
1
+ require 'backup/connection/s3'
2
+
3
+ module Backup
4
+ module Record
5
+ class S3 < Backup::Record::Base
6
+
7
+ def load_specific_settings(adapter)
8
+ self.bucket = adapter.procedure.get_storage_configuration.attributes['bucket']
9
+ end
10
+
11
+ private
12
+
13
+ def self.destroy_backups(procedure, backups)
14
+ s3 = Backup::Connection::S3.new
15
+ s3.static_initialize(procedure)
16
+ s3.connect
17
+ backups.each do |backup|
18
+ puts "\nDestroying backup \"#{backup.filename}\" from bucket \"#{backup.bucket}\"."
19
+ s3.destroy(backup.filename, backup.bucket)
20
+ backup.destroy
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ require 'net/scp'
2
+
3
+ module Backup
4
+ module Record
5
+ class SCP < Backup::Record::Base
6
+
7
+ attr_accessor :ip, :user, :password
8
+
9
+ def load_specific_settings(adapter)
10
+ %w(ip user password path).each do |method|
11
+ send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def self.destroy_backups(procedure, backups)
18
+ ip = procedure.get_storage_configuration.attributes['ip']
19
+ user = procedure.get_storage_configuration.attributes['user']
20
+ password = procedure.get_storage_configuration.attributes['password']
21
+
22
+ Net::SSH.start(ip, user, :password => password) do |ssh|
23
+ backups.each do |backup|
24
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
25
+ ssh.exec("rm #{File.join(backup.path, backup.filename)}")
26
+ backup.destroy
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ require 'net/sftp'
2
+
3
+ module Backup
4
+ module Record
5
+ class SFTP < Backup::Record::Base
6
+
7
+ attr_accessor :ip, :user, :password
8
+
9
+ def load_specific_settings(adapter)
10
+ %w(ip user password path).each do |method|
11
+ send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def self.destroy_backups(procedure, backups)
18
+ ip = procedure.get_storage_configuration.attributes['ip']
19
+ user = procedure.get_storage_configuration.attributes['user']
20
+ password = procedure.get_storage_configuration.attributes['password']
21
+
22
+ Net::SFTP.start(ip, user, :password => password) do |sftp|
23
+ backups.each do |backup|
24
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
25
+ begin
26
+ sftp.remove!(File.join(backup.path, backup.filename))
27
+ backup.destroy
28
+ rescue
29
+ puts "Could not find backup #{backup.path}/#{backup.filename}.."
30
+ backup.destroy
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ require 'net/ftp'
2
+
3
+ module Backup
4
+ module Storage
5
+ class FTP
6
+
7
+ attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
8
+
9
+ # Stores the backup file on the remote server using FTP
10
+ def initialize(adapter)
11
+ %w(ip user password path).each do |method|
12
+ send("#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
13
+ end
14
+
15
+ final_file = adapter.final_file
16
+ tmp_path = adapter.tmp_path
17
+
18
+ Net::FTP.open(ip, user, password) do |ftp|
19
+ begin
20
+ ftp.chdir(path)
21
+ rescue
22
+ puts "Could not find or access \"#{path}\" on \"#{ip}\", please ensure this directory exists and is accessible by the user \"#{user}\"."
23
+ exit
24
+ end
25
+
26
+ begin
27
+ puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
28
+ ftp.putbinaryfile(File.join(tmp_path, final_file).gsub('\ ', ' '), File.join(path, final_file))
29
+ rescue
30
+ puts "Could not save file to backup server. Is the \"#{path}\" directory writable?"
31
+ exit
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end