backup 3.0.16 → 3.0.18
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +10 -0
- data/Gemfile.lock +50 -47
- data/Guardfile +3 -3
- data/README.md +136 -81
- data/backup.gemspec +3 -2
- data/bin/backup +36 -15
- data/lib/backup.rb +30 -20
- data/lib/backup/cli.rb +30 -2
- data/lib/backup/compressor/lzma.rb +63 -0
- data/lib/backup/configuration/compressor/lzma.rb +23 -0
- data/lib/backup/configuration/helpers.rb +10 -4
- data/lib/backup/configuration/notifier/mail.rb +5 -0
- data/lib/backup/configuration/storage/dropbox.rb +19 -4
- data/lib/backup/configuration/storage/ftp.rb +4 -0
- data/lib/backup/configuration/storage/local.rb +17 -0
- data/lib/backup/configuration/storage/ninefold.rb +20 -0
- data/lib/backup/configuration/storage/rsync.rb +4 -0
- data/lib/backup/database/postgresql.rb +12 -3
- data/lib/backup/database/redis.rb +5 -1
- data/lib/backup/dependency.rb +11 -12
- data/lib/backup/encryptor/gpg.rb +2 -0
- data/lib/backup/exception/command_failed.rb +8 -0
- data/lib/backup/finder.rb +49 -9
- data/lib/backup/notifier/mail.rb +7 -1
- data/lib/backup/notifier/twitter.rb +1 -1
- data/lib/backup/storage/dropbox.rb +93 -16
- data/lib/backup/storage/ftp.rb +10 -3
- data/lib/backup/storage/local.rb +78 -0
- data/lib/backup/storage/ninefold.rb +96 -0
- data/lib/backup/storage/rsync.rb +37 -20
- data/lib/backup/storage/s3.rb +1 -1
- data/lib/backup/storage/scp.rb +1 -1
- data/lib/backup/syncer/rsync.rb +1 -1
- data/lib/backup/version.rb +1 -1
- data/lib/templates/compressor/lzma +7 -0
- data/lib/templates/storage/dropbox +2 -2
- data/lib/templates/storage/ftp +8 -7
- data/lib/templates/storage/local +7 -0
- data/lib/templates/storage/ninefold +9 -0
- data/lib/templates/storage/rsync +1 -0
- data/spec/archive_spec.rb +0 -1
- data/spec/compressor/bzip2_spec.rb +0 -1
- data/spec/compressor/gzip_spec.rb +0 -1
- data/spec/compressor/lzma_spec.rb +58 -0
- data/spec/configuration/compressor/bzip2_spec.rb +28 -0
- data/spec/configuration/compressor/lzma_spec.rb +28 -0
- data/spec/configuration/database/mongodb_spec.rb +16 -0
- data/spec/configuration/database/mysql_spec.rb +17 -0
- data/spec/configuration/database/postgresql_spec.rb +17 -0
- data/spec/configuration/database/redis_spec.rb +16 -0
- data/spec/configuration/notifier/campfire_spec.rb +11 -0
- data/spec/configuration/notifier/mail_spec.rb +20 -0
- data/spec/configuration/notifier/presently_spec.rb +34 -0
- data/spec/configuration/notifier/twitter_spec.rb +12 -0
- data/spec/configuration/storage/dropbox_spec.rb +0 -6
- data/spec/configuration/storage/ftp_spec.rb +15 -12
- data/spec/configuration/storage/local_spec.rb +28 -0
- data/spec/configuration/storage/ninefold_spec.rb +31 -0
- data/spec/configuration/storage/rsync_spec.rb +2 -0
- data/spec/database/mongodb_spec.rb +0 -1
- data/spec/database/mysql_spec.rb +0 -1
- data/spec/database/postgresql_spec.rb +31 -11
- data/spec/database/redis_spec.rb +9 -4
- data/spec/encryptor/gpg_spec.rb +1 -1
- data/spec/encryptor/open_ssl_spec.rb +0 -1
- data/spec/logger_spec.rb +32 -24
- data/spec/model_spec.rb +15 -15
- data/spec/spec_helper.rb +8 -4
- data/spec/storage/base_spec.rb +0 -4
- data/spec/storage/cloudfiles_spec.rb +0 -1
- data/spec/storage/dropbox_spec.rb +44 -14
- data/spec/storage/ftp_spec.rb +26 -15
- data/spec/storage/local_spec.rb +83 -0
- data/spec/storage/ninefold_spec.rb +142 -0
- data/spec/storage/object_spec.rb +1 -1
- data/spec/storage/rsync_spec.rb +17 -7
- data/spec/storage/s3_spec.rb +4 -3
- data/spec/storage/scp_spec.rb +0 -1
- data/spec/storage/sftp_spec.rb +0 -1
- data/spec/syncer/rsync_spec.rb +8 -8
- metadata +62 -36
data/lib/backup/storage/ftp.rb
CHANGED
@@ -20,6 +20,10 @@ module Backup
|
|
20
20
|
# Path to store backups to
|
21
21
|
attr_accessor :path
|
22
22
|
|
23
|
+
##
|
24
|
+
# use passive mode?
|
25
|
+
attr_accessor :passive_mode
|
26
|
+
|
23
27
|
##
|
24
28
|
# Creates a new instance of the FTP storage object
|
25
29
|
# First it sets the defaults (if any exist) and then evaluates
|
@@ -27,8 +31,9 @@ module Backup
|
|
27
31
|
def initialize(&block)
|
28
32
|
load_defaults!
|
29
33
|
|
30
|
-
@port
|
31
|
-
@path
|
34
|
+
@port ||= 21
|
35
|
+
@path ||= 'backups'
|
36
|
+
@passive_mode ||= false
|
32
37
|
|
33
38
|
instance_eval(&block) if block_given?
|
34
39
|
|
@@ -66,7 +71,9 @@ module Backup
|
|
66
71
|
Net::FTP.send(:remove_const, :FTP_PORT)
|
67
72
|
end; Net::FTP.send(:const_set, :FTP_PORT, port)
|
68
73
|
|
69
|
-
Net::FTP.new(ip, username, password)
|
74
|
+
ftp = Net::FTP.new(ip, username, password)
|
75
|
+
ftp.passive = true if passive_mode
|
76
|
+
ftp
|
70
77
|
end
|
71
78
|
|
72
79
|
##
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# Load the Ruby FileUtils library
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Backup
|
8
|
+
module Storage
|
9
|
+
class Local < Base
|
10
|
+
|
11
|
+
##
|
12
|
+
# Path to store backups to
|
13
|
+
attr_accessor :path
|
14
|
+
|
15
|
+
##
|
16
|
+
# Creates a new instance of the Local storage object
|
17
|
+
# First it sets the defaults (if any exist) and then evaluates
|
18
|
+
# the configuration block which may overwrite these defaults
|
19
|
+
def initialize(&block)
|
20
|
+
load_defaults!
|
21
|
+
|
22
|
+
@path ||= "#{ENV['HOME']}/backups"
|
23
|
+
|
24
|
+
instance_eval(&block) if block_given?
|
25
|
+
|
26
|
+
@time = TIME
|
27
|
+
fix_path!
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# This is the remote path to where the backup files will be stored.
|
32
|
+
# Eventhough it says "remote", it's actually the "local" path, but
|
33
|
+
# the naming is necessary for compatibility reasons
|
34
|
+
def remote_path
|
35
|
+
File.join(path, TRIGGER)
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Performs the backup transfer
|
40
|
+
def perform!
|
41
|
+
transfer!
|
42
|
+
cycle!
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
##
|
48
|
+
# Transfers the archived file to the specified local path
|
49
|
+
def transfer!
|
50
|
+
Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
|
51
|
+
create_local_directories!
|
52
|
+
FileUtils.cp(
|
53
|
+
File.join(local_path, local_file),
|
54
|
+
File.join(remote_path, remote_file)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Removes the transferred archive file from the local path
|
60
|
+
def remove!
|
61
|
+
FileUtils.rm(File.join(remote_path, remote_file))
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Creates the path to where the backups are stored if it doesn't exist yet
|
66
|
+
def create_local_directories!
|
67
|
+
FileUtils.mkdir_p(remote_path)
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Replaces ~/ with the full path to the users $HOME directory
|
72
|
+
def fix_path!
|
73
|
+
@path = path.sub(/^\~\//, "#{ENV['HOME']}/")
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# Only load the Fog gem when the Backup::Storage::Ninefold class is loaded
|
5
|
+
Backup::Dependency.load('fog')
|
6
|
+
|
7
|
+
module Backup
|
8
|
+
module Storage
|
9
|
+
class Ninefold < Base
|
10
|
+
|
11
|
+
##
|
12
|
+
# Ninefold Credentials
|
13
|
+
attr_accessor :storage_token, :storage_secret
|
14
|
+
|
15
|
+
##
|
16
|
+
# Ninefold directory path
|
17
|
+
attr_accessor :path
|
18
|
+
|
19
|
+
##
|
20
|
+
# Creates a new instance of the Ninefold storage object
|
21
|
+
# First it sets the defaults (if any exist) and then evaluates
|
22
|
+
# the configuration block which may overwrite these defaults
|
23
|
+
#
|
24
|
+
def initialize(&block)
|
25
|
+
load_defaults!
|
26
|
+
|
27
|
+
@path ||= 'backups'
|
28
|
+
|
29
|
+
instance_eval(&block) if block_given?
|
30
|
+
|
31
|
+
@time = TIME
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# This is the remote path to where the backup files will be stored
|
36
|
+
def remote_path
|
37
|
+
File.join(path, TRIGGER).sub(/^\//, '')
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# This is the provider that Fog uses for the Ninefold storage
|
42
|
+
def provider
|
43
|
+
'Ninefold'
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Performs the backup transfer
|
48
|
+
def perform!
|
49
|
+
transfer!
|
50
|
+
cycle!
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
##
|
56
|
+
# Establishes a connection to Amazon S3 and returns the Fog object.
|
57
|
+
# Not doing any instance variable caching because this object gets persisted in YAML
|
58
|
+
# format to a file and will issues. This, however has no impact on performance since it only
|
59
|
+
# gets invoked once per object for a #transfer! and once for a remove! Backups run in the
|
60
|
+
# background anyway so even if it were a bit slower it shouldn't matter.
|
61
|
+
def connection
|
62
|
+
Fog::Storage.new(
|
63
|
+
:provider => provider,
|
64
|
+
:ninefold_storage_token => storage_token,
|
65
|
+
:ninefold_storage_secret => storage_secret
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Transfers the archived file to the specified directory
|
71
|
+
def transfer!
|
72
|
+
begin
|
73
|
+
Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
|
74
|
+
directory = connection.directories.get remote_path
|
75
|
+
directory ||= connection.directories.create(:key => remote_path)
|
76
|
+
directory.files.create(
|
77
|
+
:key => remote_file,
|
78
|
+
:body => File.open(File.join(local_path, local_file))
|
79
|
+
)
|
80
|
+
rescue Excon::Errors::NotFound
|
81
|
+
raise "An error occurred while trying to transfer the file."
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Removes the transferred archive file from the Amazon S3 bucket
|
87
|
+
def remove!
|
88
|
+
begin
|
89
|
+
directory = connection.directories.get remote_path
|
90
|
+
directory.files.get(remote_file).destroy
|
91
|
+
rescue Excon::Errors::SocketError; end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/backup/storage/rsync.rb
CHANGED
@@ -25,6 +25,10 @@ module Backup
|
|
25
25
|
# Path to store backups to
|
26
26
|
attr_accessor :path
|
27
27
|
|
28
|
+
##
|
29
|
+
# Flag to use local backups
|
30
|
+
attr_accessor :local
|
31
|
+
|
28
32
|
##
|
29
33
|
# Creates a new instance of the RSync storage object
|
30
34
|
# First it sets the defaults (if any exist) and then evaluates
|
@@ -32,8 +36,9 @@ module Backup
|
|
32
36
|
def initialize(&block)
|
33
37
|
load_defaults!
|
34
38
|
|
35
|
-
@port
|
36
|
-
@path
|
39
|
+
@port ||= 22
|
40
|
+
@path ||= 'backups'
|
41
|
+
@local ||= false
|
37
42
|
|
38
43
|
instance_eval(&block) if block_given?
|
39
44
|
write_password_file!
|
@@ -55,6 +60,25 @@ module Backup
|
|
55
60
|
remove_password_file!
|
56
61
|
end
|
57
62
|
|
63
|
+
##
|
64
|
+
# Returns Rsync syntax for defining a port to connect to
|
65
|
+
def port
|
66
|
+
"-e 'ssh -p #{@port}'"
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Returns Rsync syntax for using a password file
|
71
|
+
def password
|
72
|
+
"--password-file='#{@password_file.path}'" unless @password.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# RSync options
|
77
|
+
# -z = Compresses the bytes that will be transferred to reduce bandwidth usage
|
78
|
+
def options
|
79
|
+
"-z"
|
80
|
+
end
|
81
|
+
|
58
82
|
private
|
59
83
|
|
60
84
|
##
|
@@ -64,7 +88,7 @@ module Backup
|
|
64
88
|
# gets invoked once per object for a #transfer! and once for a remove! Backups run in the
|
65
89
|
# background anyway so even if it were a bit slower it shouldn't matter.
|
66
90
|
def connection
|
67
|
-
Net::SSH.start(ip, username, :password => @password, :port => port)
|
91
|
+
Net::SSH.start(ip, username, :password => @password, :port => @port)
|
68
92
|
end
|
69
93
|
|
70
94
|
##
|
@@ -72,7 +96,11 @@ module Backup
|
|
72
96
|
def transfer!
|
73
97
|
Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
|
74
98
|
create_remote_directories!
|
75
|
-
|
99
|
+
if @local
|
100
|
+
run("#{ utility(:rsync) } '#{ File.join(local_path, local_file) }' '#{ File.join(remote_path, TIME+'.'+remote_file[20..-1]) }'")
|
101
|
+
else
|
102
|
+
run("#{ utility(:rsync) } #{ options } #{ port } #{ password } '#{ File.join(local_path, local_file) }' '#{ username }@#{ ip }:#{ File.join(remote_path, remote_file[20..-1]) }'")
|
103
|
+
end
|
76
104
|
end
|
77
105
|
|
78
106
|
##
|
@@ -88,22 +116,11 @@ module Backup
|
|
88
116
|
# Creates (if they don't exist yet) all the directories on the remote
|
89
117
|
# server in order to upload the backup file.
|
90
118
|
def create_remote_directories!
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
# -z = Compresses the bytes that will be transferred to reduce bandwidth usage
|
97
|
-
# --port = the port to connect to through SSH
|
98
|
-
# -Phv = debug options
|
99
|
-
def options
|
100
|
-
"-z --port='#{ port }'"
|
101
|
-
end
|
102
|
-
|
103
|
-
##
|
104
|
-
# Returns Rsync syntax for using a password file
|
105
|
-
def password
|
106
|
-
"--password-file='#{@password_file.path}'" unless @password.nil?
|
119
|
+
if @local
|
120
|
+
mkdir(remote_path)
|
121
|
+
else
|
122
|
+
connection.exec!("mkdir -p '#{ remote_path }'")
|
123
|
+
end
|
107
124
|
end
|
108
125
|
|
109
126
|
##
|
data/lib/backup/storage/s3.rb
CHANGED
@@ -77,7 +77,7 @@ module Backup
|
|
77
77
|
# Transfers the archived file to the specified Amazon S3 bucket
|
78
78
|
def transfer!
|
79
79
|
begin
|
80
|
-
Logger.message("#{ self.class } started transferring \"#{ remote_file }\"
|
80
|
+
Logger.message("#{ self.class } started transferring \"#{ remote_file }\" to bucket \"#{ bucket }\"")
|
81
81
|
connection.sync_clock
|
82
82
|
connection.put_object(
|
83
83
|
bucket,
|
data/lib/backup/storage/scp.rb
CHANGED
@@ -55,7 +55,7 @@ module Backup
|
|
55
55
|
private
|
56
56
|
|
57
57
|
##
|
58
|
-
# Establishes a connection to the remote server and returns the Net::
|
58
|
+
# Establishes a connection to the remote server and returns the Net::SSH object.
|
59
59
|
# Not doing any instance variable caching because this object gets persisted in YAML
|
60
60
|
# format to a file and will issues. This, however has no impact on performance since it only
|
61
61
|
# gets invoked once per object for a #transfer! and once for a remove! Backups run in the
|
data/lib/backup/syncer/rsync.rb
CHANGED
data/lib/backup/version.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
##
|
2
2
|
# Dropbox File Hosting Service [Storage]
|
3
|
+
# Note: Initial backup must be performed manually to authorize
|
4
|
+
# this machine with your Dropbox account.
|
3
5
|
#
|
4
6
|
store_with Dropbox do |db|
|
5
|
-
db.email = 'my@email.com'
|
6
|
-
db.password = 'my_password'
|
7
7
|
db.api_key = 'my_api_key'
|
8
8
|
db.api_secret = 'my_api_secret'
|
9
9
|
db.timeout = 300
|
data/lib/templates/storage/ftp
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
# FTP (File Transfer Protocol) [Storage]
|
3
3
|
#
|
4
4
|
store_with FTP do |server|
|
5
|
-
server.username
|
6
|
-
server.password
|
7
|
-
server.ip
|
8
|
-
server.port
|
9
|
-
server.path
|
10
|
-
server.keep
|
11
|
-
|
5
|
+
server.username = 'my_username'
|
6
|
+
server.password = 'my_password'
|
7
|
+
server.ip = '123.45.678.90'
|
8
|
+
server.port = 21
|
9
|
+
server.path = '~/backups/'
|
10
|
+
server.keep = 5
|
11
|
+
server.passive_mode = false
|
12
|
+
end
|
data/lib/templates/storage/rsync
CHANGED
data/spec/archive_spec.rb
CHANGED
@@ -58,7 +58,6 @@ describe Backup::Archive do
|
|
58
58
|
describe '#perform!' do
|
59
59
|
before do
|
60
60
|
[:mkdir, :run, :utility].each { |method| archive.stubs(method) }
|
61
|
-
Backup::Logger.stubs(:message)
|
62
61
|
end
|
63
62
|
|
64
63
|
context 'when both paths were added and paths that should be excluded were added' do
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
4
|
+
|
5
|
+
describe Backup::Compressor::Lzma do
|
6
|
+
let(:compressor) { Backup::Compressor::Lzma.new }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Backup::Model.extension = 'tar'
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'the options' do
|
13
|
+
it do
|
14
|
+
compressor.send(:best).should == []
|
15
|
+
end
|
16
|
+
|
17
|
+
it do
|
18
|
+
compressor.send(:fast).should == []
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#perform!' do
|
23
|
+
before do
|
24
|
+
[:run, :utility].each { |method| compressor.stubs(method) }
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should perform the compression' do
|
28
|
+
compressor.expects(:utility).with(:lzma).returns(:lzma)
|
29
|
+
compressor.expects(:run).with("lzma '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }'")
|
30
|
+
compressor.perform!
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should perform the compression with the --best and --fast options' do
|
34
|
+
compressor = Backup::Compressor::Lzma.new do |c|
|
35
|
+
c.best = true
|
36
|
+
c.fast = true
|
37
|
+
end
|
38
|
+
|
39
|
+
compressor.stubs(:utility).returns(:lzma)
|
40
|
+
compressor.expects(:run).with("lzma --best --fast '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }'")
|
41
|
+
compressor.perform!
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should set the class variable @extension (Backup::Model.extension) to .lzma' do
|
45
|
+
compressor.stubs(:utility).returns(:lzma)
|
46
|
+
compressor.expects(:run)
|
47
|
+
|
48
|
+
Backup::Model.extension.should == 'tar'
|
49
|
+
compressor.perform!
|
50
|
+
Backup::Model.extension.should == 'tar.lzma'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should log' do
|
54
|
+
Backup::Logger.expects(:message).with("Backup::Compressor::Lzma started compressing the archive.")
|
55
|
+
compressor.perform!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|