namxam-backup 2.4.5
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.
- 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
|