namxam-backup 2.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +131 -0
- data/LICENSE +20 -0
- data/README.md +122 -0
- data/bin/backup +108 -0
- data/generators/backup/backup_generator.rb +69 -0
- data/generators/backup/templates/backup.rake +56 -0
- data/generators/backup/templates/backup.rb +253 -0
- data/generators/backup/templates/create_backup_tables.rb +18 -0
- data/generators/backup_update/backup_update_generator.rb +50 -0
- data/generators/backup_update/templates/migrations/update_backup_tables.rb +27 -0
- data/lib/backup.rb +132 -0
- data/lib/backup/adapters/archive.rb +34 -0
- data/lib/backup/adapters/base.rb +167 -0
- data/lib/backup/adapters/custom.rb +41 -0
- data/lib/backup/adapters/mongo_db.rb +139 -0
- data/lib/backup/adapters/mysql.rb +60 -0
- data/lib/backup/adapters/postgresql.rb +56 -0
- data/lib/backup/adapters/sqlite.rb +25 -0
- data/lib/backup/command_helper.rb +14 -0
- data/lib/backup/configuration/adapter.rb +21 -0
- data/lib/backup/configuration/adapter_options.rb +8 -0
- data/lib/backup/configuration/attributes.rb +19 -0
- data/lib/backup/configuration/base.rb +75 -0
- data/lib/backup/configuration/helpers.rb +24 -0
- data/lib/backup/configuration/mail.rb +20 -0
- data/lib/backup/configuration/smtp.rb +8 -0
- data/lib/backup/configuration/storage.rb +8 -0
- data/lib/backup/connection/cloudfiles.rb +75 -0
- data/lib/backup/connection/dropbox.rb +62 -0
- data/lib/backup/connection/s3.rb +88 -0
- data/lib/backup/core_ext/object.rb +5 -0
- data/lib/backup/environment/base.rb +12 -0
- data/lib/backup/environment/rails_configuration.rb +15 -0
- data/lib/backup/environment/unix_configuration.rb +109 -0
- data/lib/backup/mail/base.rb +97 -0
- data/lib/backup/mail/mail.txt +7 -0
- data/lib/backup/record/base.rb +65 -0
- data/lib/backup/record/cloudfiles.rb +28 -0
- data/lib/backup/record/dropbox.rb +27 -0
- data/lib/backup/record/ftp.rb +39 -0
- data/lib/backup/record/local.rb +26 -0
- data/lib/backup/record/s3.rb +25 -0
- data/lib/backup/record/scp.rb +33 -0
- data/lib/backup/record/sftp.rb +38 -0
- data/lib/backup/storage/base.rb +10 -0
- data/lib/backup/storage/cloudfiles.rb +16 -0
- data/lib/backup/storage/dropbox.rb +12 -0
- data/lib/backup/storage/ftp.rb +38 -0
- data/lib/backup/storage/local.rb +22 -0
- data/lib/backup/storage/s3.rb +15 -0
- data/lib/backup/storage/scp.rb +30 -0
- data/lib/backup/storage/sftp.rb +31 -0
- data/lib/backup/version.rb +3 -0
- data/lib/generators/backup/USAGE +10 -0
- data/lib/generators/backup/backup_generator.rb +47 -0
- data/lib/generators/backup/templates/backup.rake +56 -0
- data/lib/generators/backup/templates/backup.rb +236 -0
- data/lib/generators/backup/templates/create_backup_tables.rb +18 -0
- data/setup/backup.rb +255 -0
- data/setup/backup.sqlite3 +0 -0
- metadata +278 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module Backup
|
2
|
+
module Environment
|
3
|
+
module RailsConfiguration
|
4
|
+
|
5
|
+
if defined?(Rails.root)
|
6
|
+
# Sets BACKUP_PATH equal to Rails.root
|
7
|
+
BACKUP_PATH = Rails.root.to_s
|
8
|
+
|
9
|
+
# Sets DB_CONNECTION_SETTINGS to false
|
10
|
+
DB_CONNECTION_SETTINGS = false
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Backup
|
2
|
+
module Environment
|
3
|
+
module UnixConfiguration
|
4
|
+
|
5
|
+
require 'active_record'
|
6
|
+
|
7
|
+
# Sets BACKUP_PATH
|
8
|
+
BACKUP_PATH = ENV['BACKUP_PATH'] || "/opt/backup"
|
9
|
+
|
10
|
+
# Sets DB_CONNECTION_SETTINGS
|
11
|
+
DB_CONNECTION_SETTINGS = {
|
12
|
+
:adapter => "sqlite3",
|
13
|
+
:database => "#{BACKUP_PATH}/backup.sqlite3",
|
14
|
+
:pool => 5,
|
15
|
+
:timeout => 5000
|
16
|
+
}
|
17
|
+
|
18
|
+
module Commands
|
19
|
+
|
20
|
+
def setup
|
21
|
+
unless File.directory?(BACKUP_PATH)
|
22
|
+
puts "Installing Backup in #{BACKUP_PATH}.."
|
23
|
+
%x{ #{sudo} mkdir -p #{File.join(BACKUP_PATH, 'config')} }
|
24
|
+
%x{ #{sudo} cp #{File.join(File.dirname(__FILE__), '..', '..', '..', 'setup', 'backup.sqlite3')} #{BACKUP_PATH} }
|
25
|
+
%x{ #{sudo} cp #{File.join(File.dirname(__FILE__), '..', '..', '..', 'setup', 'backup.rb')} #{File.join(BACKUP_PATH, 'config')} }
|
26
|
+
puts <<-MESSAGE
|
27
|
+
|
28
|
+
==============================================================
|
29
|
+
Backup has been set up!
|
30
|
+
==============================================================
|
31
|
+
|
32
|
+
1: Set up some "Backup Settings" inside the configuration file!
|
33
|
+
|
34
|
+
#{BACKUP_PATH}/config/backup.rb
|
35
|
+
|
36
|
+
|
37
|
+
2: Run the backups!
|
38
|
+
|
39
|
+
sudo backup --run [trigger]
|
40
|
+
|
41
|
+
|
42
|
+
For a list of Backup commands:
|
43
|
+
|
44
|
+
sudo backup --help
|
45
|
+
|
46
|
+
|
47
|
+
For More Information:
|
48
|
+
|
49
|
+
http://github.com/meskyanichi/backup
|
50
|
+
|
51
|
+
==============================================================
|
52
|
+
|
53
|
+
MESSAGE
|
54
|
+
else
|
55
|
+
puts "\nBackup is already installed in #{BACKUP_PATH}..\n"
|
56
|
+
puts "If you want to reset it, run:\n\nbackup --reset\n\n"
|
57
|
+
puts "This will reinstall it."
|
58
|
+
puts "Warning: All configuration will be lost!\n\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
if File.directory?(BACKUP_PATH)
|
64
|
+
remove
|
65
|
+
setup
|
66
|
+
else
|
67
|
+
puts "Backup is not installed.\n"
|
68
|
+
puts "Run the following command to install it:\n\nbackup --setup"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove
|
73
|
+
puts "Removing Backup..\n"
|
74
|
+
%x{ #{sudo} rm -rf #{BACKUP_PATH} }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module Helpers
|
79
|
+
|
80
|
+
def confirm_configuration_file_existence
|
81
|
+
unless File.exist?(File.join(BACKUP_PATH, 'config', 'backup.rb'))
|
82
|
+
puts "\nBackup could not find the Backup Configuration File."
|
83
|
+
puts "Did you set up Backup? Do so if you haven't yet:"
|
84
|
+
puts "\nbackup --setup\n "
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def sudo
|
90
|
+
if writable?(BACKUP_PATH)
|
91
|
+
""
|
92
|
+
else
|
93
|
+
"sudo"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def writable?(f)
|
99
|
+
unless File.exists?(f)
|
100
|
+
writable?(File.dirname(f))
|
101
|
+
else
|
102
|
+
File.writable?(f)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Backup
|
2
|
+
module Mail
|
3
|
+
class Base
|
4
|
+
|
5
|
+
# Sets up the Mail Configuration for the Backup::Mail::Base class.
|
6
|
+
# This must be set in order to send emails
|
7
|
+
# It will dynamically add class methods (configuration) for each email that will be sent
|
8
|
+
def self.setup(config)
|
9
|
+
if config
|
10
|
+
(class << self; self; end).instance_eval do
|
11
|
+
config.attributes.each do |method, value|
|
12
|
+
define_method method do
|
13
|
+
value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
config.get_smtp_configuration.attributes.each do |method, value|
|
17
|
+
define_method method do
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns true if the "to" and "from" attributes are set
|
26
|
+
def self.setup?
|
27
|
+
return true if defined?(from) and defined?(to)
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Delivers the backup details by email to the recipient
|
32
|
+
# Requires the Backup Object
|
33
|
+
def self.notify!(backup)
|
34
|
+
if self.setup? and backup.procedure.attributes['notify'].eql?(true)
|
35
|
+
require 'pony'
|
36
|
+
|
37
|
+
@backup = backup
|
38
|
+
self.parse_body
|
39
|
+
Pony.mail({
|
40
|
+
:subject => "Backup for \"#{@backup.trigger}\" was successfully created!",
|
41
|
+
:body => @content
|
42
|
+
}.merge(self.smtp_configuration))
|
43
|
+
puts "Sending notification to #{self.to}."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Retrieves SMTP configuration
|
48
|
+
def self.smtp_configuration
|
49
|
+
{ :to => self.to,
|
50
|
+
:from => self.from,
|
51
|
+
:via => :smtp,
|
52
|
+
:smtp => {
|
53
|
+
:host => self.host,
|
54
|
+
:port => self.port,
|
55
|
+
:user => self.username,
|
56
|
+
:password => self.password,
|
57
|
+
:auth => self.authentication,
|
58
|
+
:domain => self.domain,
|
59
|
+
:tls => self.tls
|
60
|
+
}}
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.parse_body
|
64
|
+
File.open(File.join(File.dirname(__FILE__), 'mail.txt'), 'r') do |file|
|
65
|
+
self.gsub_content(file.readlines)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.gsub_content(lines)
|
70
|
+
container = @backup.procedure.get_storage_configuration.attributes['container']
|
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', container || bucket || path)
|
83
|
+
line.gsub!(':backup', @backup.final_file)
|
84
|
+
case @backup.procedure.storage_name.to_sym
|
85
|
+
when :cloudfiles then line.gsub!(':remote', "on Rackspace Cloudfiles")
|
86
|
+
when :s3 then line.gsub!(':remote', "on Amazon S3")
|
87
|
+
when :local then line.gsub!(':remote', "on the local server")
|
88
|
+
when :scp, :sftp, :ftp then line.gsub!(':remote', "on the remote server (#{ip})")
|
89
|
+
end
|
90
|
+
@content ||= String.new
|
91
|
+
@content << line
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -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,28 @@
|
|
1
|
+
require 'backup/connection/cloudfiles'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Record
|
5
|
+
class CloudFiles < Backup::Record::Base
|
6
|
+
|
7
|
+
alias_attribute :container, :bucket
|
8
|
+
|
9
|
+
def load_specific_settings(adapter)
|
10
|
+
self.container = adapter.procedure.get_storage_configuration.attributes['container']
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def self.destroy_backups(procedure, backups)
|
16
|
+
cf = Backup::Connection::CloudFiles.new
|
17
|
+
cf.static_initialize(procedure)
|
18
|
+
cf.connect
|
19
|
+
backups.each do |backup|
|
20
|
+
puts "\nDestroying backup \"#{backup.filename}\" from container \"#{backup.container}\"."
|
21
|
+
cf.destroy(backup.filename, backup.container)
|
22
|
+
backup.destroy
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'backup/connection/dropbox'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Record
|
5
|
+
class Dropbox < Backup::Record::Base
|
6
|
+
def load_specific_settings(adapter)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def self.destroy_backups(procedure, backups)
|
12
|
+
dropbox = Backup::Connection::Dropbox.new
|
13
|
+
dropbox.static_initialize(procedure)
|
14
|
+
session = dropbox.session
|
15
|
+
backups.each do |backup|
|
16
|
+
puts "\nDestroying backup \"#{backup.filename}\"."
|
17
|
+
path_to_file = File.join(dropbox.path, backup.filename)
|
18
|
+
begin
|
19
|
+
session.delete(path_to_file, :mode => :dropbox)
|
20
|
+
rescue ::Dropbox::FileNotFoundError => e
|
21
|
+
puts "\n Backup with name '#{backup.filename}' was not found in '#{dropbox.path}'"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -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,25 @@
|
|
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
|
+
backups.each do |backup|
|
17
|
+
puts "\nDestroying backup \"#{backup.filename}\" from bucket \"#{backup.bucket}\"."
|
18
|
+
s3.destroy(backup.filename, backup.bucket)
|
19
|
+
backup.destroy
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|