dmitryv-backup 2.4.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.
- data/CHANGELOG +125 -0
- data/LICENSE +20 -0
- data/README.md +180 -0
- data/VERSION +1 -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 +229 -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 +131 -0
- data/lib/backup/adapters/archive.rb +34 -0
- data/lib/backup/adapters/base.rb +138 -0
- data/lib/backup/adapters/custom.rb +41 -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 +11 -0
- data/lib/backup/compressors/base.rb +7 -0
- data/lib/backup/compressors/gzip.rb +9 -0
- data/lib/backup/compressors/seven_zip.rb +9 -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 +77 -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/s3.rb +85 -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/ftp.rb +39 -0
- data/lib/backup/record/local.rb +26 -0
- data/lib/backup/record/s3.rb +26 -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/ftp.rb +38 -0
- data/lib/backup/storage/local.rb +22 -0
- data/lib/backup/storage/s3.rb +17 -0
- data/lib/backup/storage/scp.rb +30 -0
- data/lib/backup/storage/sftp.rb +31 -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 +229 -0
- data/lib/generators/backup/templates/create_backup_tables.rb +18 -0
- data/setup/backup.rb +231 -0
- data/setup/backup.sqlite3 +0 -0
- metadata +271 -0
@@ -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,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,16 @@
|
|
1
|
+
require 'backup/connection/cloudfiles'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class CloudFiles < Base
|
6
|
+
|
7
|
+
# Stores the backup file on the remote server using Rackspace Cloud Files
|
8
|
+
def initialize(adapter)
|
9
|
+
cf = Backup::Connection::CloudFiles.new(adapter)
|
10
|
+
cf.connect
|
11
|
+
cf.store
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class FTP < Backup::Storage::Base
|
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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Backup
|
2
|
+
module Storage
|
3
|
+
class Local < Base
|
4
|
+
|
5
|
+
# Store on same machine, preferentially in a different hard drive or in
|
6
|
+
# a mounted network path (NFS, Samba, etc)
|
7
|
+
attr_accessor :path, :tmp_path, :final_file
|
8
|
+
|
9
|
+
# Stores the backup file on local machine
|
10
|
+
def initialize(adapter)
|
11
|
+
self.path = adapter.procedure.get_storage_configuration.attributes['path']
|
12
|
+
self.tmp_path = adapter.tmp_path
|
13
|
+
self.final_file = adapter.final_file
|
14
|
+
|
15
|
+
run "mkdir -p #{path}"
|
16
|
+
run "cp #{File.join(tmp_path, final_file).gsub('\ ', ' ')} #{File.join(path, final_file)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'backup/connection/s3'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class S3 < Base
|
6
|
+
|
7
|
+
# Stores the backup file on the remote server using S3
|
8
|
+
def initialize(adapter)
|
9
|
+
s3 = Backup::Connection::S3.new(adapter)
|
10
|
+
s3.connect
|
11
|
+
log("Saving '#{s3.final_file}' to s3 bucket '#{s3.s3_bucket}'")
|
12
|
+
s3.store
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'net/scp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class SCP < Base
|
6
|
+
|
7
|
+
attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
|
8
|
+
|
9
|
+
# Stores the backup file on the remote server using SCP
|
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::SSH.start(ip, user, :password => password) do |ssh|
|
19
|
+
ssh.exec "mkdir -p #{path}"
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
|
23
|
+
Net::SCP.start(ip, user, :password => password) do |scp|
|
24
|
+
scp.upload! File.join(tmp_path, final_file).gsub('\ ', ' '), path
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'net/sftp'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Storage
|
5
|
+
class SFTP < Base
|
6
|
+
|
7
|
+
attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
|
8
|
+
|
9
|
+
# Stores the backup file on the remote server using SFTP
|
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::SFTP.start(ip, user, :password => password) do |sftp|
|
19
|
+
begin
|
20
|
+
puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
|
21
|
+
sftp.upload!(File.join(tmp_path, final_file).gsub('\ ', ' '), File.join(path, final_file))
|
22
|
+
rescue
|
23
|
+
puts "Could not find \"#{path}\" on \"#{ip}\", please ensure this directory exists."
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|