backup 3.0.16 → 3.0.18
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/.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
|