dreamhost-personal-backup 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.coveralls.yml +1 -0
- data/.travis.yml +10 -0
- data/CHANGELOG +4 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +78 -0
- data/LICENSE +21 -0
- data/README.rdoc +105 -0
- data/bin/dreamhost_personal_backup +5 -0
- data/dreamhost-personal-backup.gemspec +73 -0
- data/lib/backup/backup.rb +55 -0
- data/lib/backup/configurator.rb +59 -0
- data/lib/backup/status_manager.rb +31 -0
- data/lib/dreamhost_personal_backup.rb +32 -0
- data/rakefile.rb +36 -0
- data/test/test_backup.rb +61 -0
- data/test/test_configurator.rb +59 -0
- data/test/test_dreamhost_personal_backup.rb +5 -0
- data/test/test_files/test_config_file_missing_optional_params.yml +7 -0
- data/test/test_files/test_invalid_parameters.yml +8 -0
- data/test/test_files/test_standard_config_file.yml +11 -0
- data/test/test_helper.rb +20 -0
- data/test/test_status_manager.rb +34 -0
- metadata +151 -0
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.travis.yml
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.3.5)
|
5
|
+
builder (3.2.2)
|
6
|
+
coveralls (0.7.0)
|
7
|
+
multi_json (~> 1.3)
|
8
|
+
rest-client
|
9
|
+
simplecov (>= 0.7)
|
10
|
+
term-ansicolor
|
11
|
+
thor
|
12
|
+
docile (1.1.1)
|
13
|
+
faraday (0.8.8)
|
14
|
+
multipart-post (~> 1.2.0)
|
15
|
+
git (1.2.6)
|
16
|
+
github_api (0.10.1)
|
17
|
+
addressable
|
18
|
+
faraday (~> 0.8.1)
|
19
|
+
hashie (>= 1.2)
|
20
|
+
multi_json (~> 1.4)
|
21
|
+
nokogiri (~> 1.5.2)
|
22
|
+
oauth2
|
23
|
+
hashie (2.0.5)
|
24
|
+
highline (1.6.20)
|
25
|
+
httpauth (0.2.0)
|
26
|
+
jeweler (1.8.8)
|
27
|
+
builder
|
28
|
+
bundler (~> 1.0)
|
29
|
+
git (>= 1.2.5)
|
30
|
+
github_api (= 0.10.1)
|
31
|
+
highline (>= 1.6.15)
|
32
|
+
nokogiri (= 1.5.10)
|
33
|
+
rake
|
34
|
+
rdoc
|
35
|
+
json (1.8.1)
|
36
|
+
jwt (0.1.8)
|
37
|
+
multi_json (>= 1.5)
|
38
|
+
metaclass (0.0.1)
|
39
|
+
mime-types (2.0)
|
40
|
+
mocha (0.14.0)
|
41
|
+
metaclass (~> 0.0.1)
|
42
|
+
multi_json (1.8.2)
|
43
|
+
multi_xml (0.5.5)
|
44
|
+
multipart-post (1.2.0)
|
45
|
+
nokogiri (1.5.10)
|
46
|
+
oauth2 (0.9.2)
|
47
|
+
faraday (~> 0.8)
|
48
|
+
httpauth (~> 0.2)
|
49
|
+
jwt (~> 0.1.4)
|
50
|
+
multi_json (~> 1.0)
|
51
|
+
multi_xml (~> 0.5)
|
52
|
+
rack (~> 1.2)
|
53
|
+
rack (1.5.2)
|
54
|
+
rake (10.1.1)
|
55
|
+
rdoc (4.1.0)
|
56
|
+
json (~> 1.4)
|
57
|
+
rest-client (1.6.7)
|
58
|
+
mime-types (>= 1.16)
|
59
|
+
rsync (1.0.8)
|
60
|
+
simplecov (0.8.2)
|
61
|
+
docile (~> 1.1.0)
|
62
|
+
multi_json
|
63
|
+
simplecov-html (~> 0.8.0)
|
64
|
+
simplecov-html (0.8.0)
|
65
|
+
term-ansicolor (1.2.2)
|
66
|
+
tins (~> 0.8)
|
67
|
+
thor (0.18.1)
|
68
|
+
tins (0.13.1)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
coveralls
|
75
|
+
jeweler
|
76
|
+
mocha
|
77
|
+
rsync
|
78
|
+
simplecov
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
==== Dreamhost Backup Gem License
|
2
|
+
|
3
|
+
Copyright (c) 2013 Phil Trimble
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
= Dreamhost Personal Backup Gem
|
2
|
+
|
3
|
+
THIS IS A WORK IN PROGRESS. FEATURES MAY NOT WORK AND DOCUMENTATION MAY NOT BE ACCURATE.
|
4
|
+
PLEASE DO NOT USE YET FOR BACKUPS.
|
5
|
+
|
6
|
+
{<img src="https://travis-ci.org/ptrimble/dreamhost-personal-backup.png" />}[https://travis-ci.org/ptrimble/dreamhost-personal-backup] {<img src="https://coveralls.io/repos/ptrimble/dreamhost-personal-backup/badge.png?branch=master" />}[https://coveralls.io/r/ptrimble/dreamhost-personal-backup]
|
7
|
+
|
8
|
+
Provides functionality to perform personal backups (on Linux or OSX) to a Dreamhost personal backup server.
|
9
|
+
|
10
|
+
Dreamhost provides a 'personal backup' that allows you to back up your personal files to
|
11
|
+
special Dreamhost backup servers. I use it as an offsite mirror of my personal files (documents,
|
12
|
+
music, photos, etc).
|
13
|
+
|
14
|
+
This backup is a mirror of what is on your home machine. It does not have versioning. If you want to restore from
|
15
|
+
backup then you simply copy what is on the Dreamhost server to your home machine.
|
16
|
+
|
17
|
+
Currently you must set up your own schedule to execute this gem.
|
18
|
+
|
19
|
+
This gem uses rsync to perform the backups.
|
20
|
+
|
21
|
+
Dreamhost Personal Backup Wiki: http://wiki.dreamhost.com/Personal_Backup
|
22
|
+
|
23
|
+
=== Usage
|
24
|
+
|
25
|
+
Install the usual way:
|
26
|
+
gem install dreamhost-personal-backup
|
27
|
+
|
28
|
+
You can then call it from the command line.
|
29
|
+
$ dreamhost_personal_backup
|
30
|
+
|
31
|
+
The gem accepts only one parameter (which is optional): a config file path.
|
32
|
+
$ dreamhost_personal_backup '/path/to/you/config/file'
|
33
|
+
|
34
|
+
Calling without a config file parameter will cause the gem to look for a config file at
|
35
|
+
'~/.dreamhost_personal_backup/default_config.yml'. If this file does not exist then an error will be returned.
|
36
|
+
|
37
|
+
==== Dreamhost Setup
|
38
|
+
|
39
|
+
This gem currently assumes that you have activated your Dreamhost Backup User via the Dreamhost Panel and that you
|
40
|
+
have set up SSH trust with your backup server.
|
41
|
+
|
42
|
+
Dreamhost Backup Servers use rssh, which is a restricted shell. As such setting up SSH via their instructions is the
|
43
|
+
only way to truly automate backups. See their wiki (http://wiki.dreamhost.com/Personal_Backup) for step-by-step
|
44
|
+
instructions on how to set up SSH trust between your host and the backup server.
|
45
|
+
|
46
|
+
=== Configuration
|
47
|
+
|
48
|
+
This gem can be called either by passing a configuration YAML filepath or by setting up a configuration file in a
|
49
|
+
default location. The default location that this gem will reference for a configuration file is
|
50
|
+
'~/.dreamhost_personal_backup/default_config.yml'. If this file does not exist and no config file is passed then an
|
51
|
+
error will be returned.
|
52
|
+
|
53
|
+
The following parameters are available:
|
54
|
+
|
55
|
+
* user (required)
|
56
|
+
* host (required)
|
57
|
+
* targets (required)
|
58
|
+
* logfile (optional, default: STDOUT)
|
59
|
+
* logrotationsizeinbytes (optional, default: 105000000)
|
60
|
+
* logkeepcount (optional, default: 3)
|
61
|
+
* notifyemail (optional, default: nil)
|
62
|
+
|
63
|
+
Here is an example config file:
|
64
|
+
|
65
|
+
user: <username>
|
66
|
+
host: <host>
|
67
|
+
|
68
|
+
logfile: ~/logs/backup.log
|
69
|
+
logrotationsizeinbytes: 500000000
|
70
|
+
logkeepcount: 5
|
71
|
+
|
72
|
+
notifyemail: target_address@email.com
|
73
|
+
|
74
|
+
targets:
|
75
|
+
music: /media/drive/Music
|
76
|
+
documents: ~/Documents
|
77
|
+
writing: ~/Writing
|
78
|
+
scripts: ~/scripts
|
79
|
+
gmail-archive: ~/gmail-archive
|
80
|
+
patches: ~/Code/patches
|
81
|
+
|
82
|
+
==== cron
|
83
|
+
|
84
|
+
Tasks with this gem can be scheduled to execute at regular intervals. Example:
|
85
|
+
|
86
|
+
# Run the backup script every 3 hours every day
|
87
|
+
0 0,3,6,9,12,15,18,22 * * * dreamhost_personal_backup
|
88
|
+
|
89
|
+
=== rsync details
|
90
|
+
|
91
|
+
The following parameters are used with rsync:
|
92
|
+
* Archive (-a) - ensures that all symbolic links, devices, attributes, permissions, ownerships, etc are preserved
|
93
|
+
* Verbose (-v) - Shows verbose logging
|
94
|
+
* Compress (-z) - Compresses files during transfer
|
95
|
+
* Partial/ Progress (-P) - Keeps partially tranferred files and shows progress in the logs
|
96
|
+
* Deletes (--delete) - Deletes files on the Dreamhost side that do not exist on the sending side
|
97
|
+
|
98
|
+
In practice what this means is that it keeps a mirror of what is on the sending side. Deleting a file on the home host
|
99
|
+
will remove it from the backup server.
|
100
|
+
|
101
|
+
=== Credits and code
|
102
|
+
|
103
|
+
* Source: https://github.com/ptrimble/dreamhost-personal-backup
|
104
|
+
* Build status: http://travis-ci.org/#!/ptrimble/dreamhost-personal-backup
|
105
|
+
* Code Coverage: https://coveralls.io/r/ptrimble/dreamhost-personal-backup
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile.rb, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "dreamhost-personal-backup"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Phil Trimble"]
|
12
|
+
s.date = "2014-01-15"
|
13
|
+
s.description = "Provides functionality to perform personal backups (on Linux or OSX) to a Dreamhost personal backup server."
|
14
|
+
s.email = "philtrimble@gmail.com"
|
15
|
+
s.executables = ["dreamhost_personal_backup"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE",
|
18
|
+
"README.rdoc"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".coveralls.yml",
|
22
|
+
".travis.yml",
|
23
|
+
"CHANGELOG",
|
24
|
+
"Gemfile",
|
25
|
+
"Gemfile.lock",
|
26
|
+
"LICENSE",
|
27
|
+
"README.rdoc",
|
28
|
+
"bin/dreamhost_personal_backup",
|
29
|
+
"dreamhost-personal-backup.gemspec",
|
30
|
+
"lib/backup/backup.rb",
|
31
|
+
"lib/backup/configurator.rb",
|
32
|
+
"lib/backup/status_manager.rb",
|
33
|
+
"lib/dreamhost_personal_backup.rb",
|
34
|
+
"rakefile.rb",
|
35
|
+
"test/test_backup.rb",
|
36
|
+
"test/test_configurator.rb",
|
37
|
+
"test/test_dreamhost_personal_backup.rb",
|
38
|
+
"test/test_files/test_config_file_missing_optional_params.yml",
|
39
|
+
"test/test_files/test_invalid_parameters.yml",
|
40
|
+
"test/test_files/test_standard_config_file.yml",
|
41
|
+
"test/test_helper.rb",
|
42
|
+
"test/test_status_manager.rb"
|
43
|
+
]
|
44
|
+
s.homepage = "https://github.com/ptrimble/dreamhost-personal-backup"
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = "1.8.23"
|
47
|
+
s.summary = "Provides functionality to perform personal backups (on Linux or OSX) to a Dreamhost personal backup server."
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<rsync>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<coveralls>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<rsync>, [">= 0"])
|
60
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
61
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
62
|
+
s.add_dependency(%q<coveralls>, [">= 0"])
|
63
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<rsync>, [">= 0"])
|
67
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
68
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
69
|
+
s.add_dependency(%q<coveralls>, [">= 0"])
|
70
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rsync'
|
2
|
+
|
3
|
+
module DreamhostPersonalBackup
|
4
|
+
|
5
|
+
class MissingConfigParameter < StandardError; end
|
6
|
+
|
7
|
+
module Backup
|
8
|
+
RSYNC_COMMAND_ARGS = ['-e ssh', '-avzP', '--delete']
|
9
|
+
|
10
|
+
def self.run_for_target_directory(target_dir, config_parameters)
|
11
|
+
check_for_required_parameters(config_parameters)
|
12
|
+
|
13
|
+
user = config_parameters[:user]
|
14
|
+
host = config_parameters[:host]
|
15
|
+
logger = config_parameters[:logger]
|
16
|
+
|
17
|
+
target_dir = File.expand_path(target_dir)
|
18
|
+
|
19
|
+
logger.info(" Running backup for target directory: #{target_dir}")
|
20
|
+
|
21
|
+
rsync_result = Rsync.run(target_dir, "#{user}@#{host}:~/", RSYNC_COMMAND_ARGS)
|
22
|
+
|
23
|
+
log_rsync_result(rsync_result, logger)
|
24
|
+
|
25
|
+
rsync_result.success?
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def self.check_for_required_parameters(config_parameters)
|
31
|
+
raise MissingConfigParameter if config_parameters[:user].nil?
|
32
|
+
raise MissingConfigParameter if config_parameters[:host].nil?
|
33
|
+
raise MissingConfigParameter if config_parameters[:logger].nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.log_rsync_result(rsync_result, logger)
|
37
|
+
if rsync_result.success?
|
38
|
+
if rsync_result.changes.count > 0
|
39
|
+
logger.info(" Results:")
|
40
|
+
|
41
|
+
rsync_result.changes.each do |change|
|
42
|
+
logger.info " #{change.summary} - #{change.filename}"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
logger.info(" No changes took place!")
|
46
|
+
end
|
47
|
+
|
48
|
+
logger.info(" Backup completed successfully")
|
49
|
+
else
|
50
|
+
logger.error(" Backup failed, error: #{rsync_result.error}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module DreamhostPersonalBackup
|
5
|
+
|
6
|
+
class ConfigFileNotFound < StandardError; end
|
7
|
+
class InvalidConfigParameter < StandardError; end
|
8
|
+
|
9
|
+
class Configurator
|
10
|
+
|
11
|
+
VALID_CONFIG_PARAMETERS = [:user, :host, :logfile, :targets, :notifyemail, :logrotationsizeinbytes, :logkeepcount]
|
12
|
+
|
13
|
+
DEFAULT_CONFIG_FILE = '~/.dreamhost_personal_backup/default_config.yml'
|
14
|
+
DEFAULT_LOG_SIZE = 105000000
|
15
|
+
DEFAULT_LOG_KEEP_COUNT = 3
|
16
|
+
|
17
|
+
def self.process_config_file(config_file_path = nil)
|
18
|
+
config_file_path ||= DEFAULT_CONFIG_FILE
|
19
|
+
config_file = File.expand_path(config_file_path)
|
20
|
+
|
21
|
+
raise DreamhostPersonalBackup::ConfigFileNotFound unless File.file?(config_file)
|
22
|
+
|
23
|
+
config_parameters = Hash.new
|
24
|
+
|
25
|
+
config_file = YAML.load_file(config_file)
|
26
|
+
config_file.each do |config_key, config_value|
|
27
|
+
config_parameter = config_key.downcase.to_sym
|
28
|
+
|
29
|
+
raise DreamhostPersonalBackup::InvalidConfigParameter unless VALID_CONFIG_PARAMETERS.include?(config_parameter)
|
30
|
+
|
31
|
+
config_parameters[config_parameter] = config_value
|
32
|
+
end
|
33
|
+
|
34
|
+
config_parameters = set_default_values_if_necessary(config_parameters)
|
35
|
+
|
36
|
+
config_parameters[:logger] = create_logger(config_parameters)
|
37
|
+
|
38
|
+
config_parameters
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def self.set_default_values_if_necessary(config_parameters)
|
44
|
+
config_parameters[:logrotationsizeinbytes] = DEFAULT_LOG_SIZE unless config_parameters.has_key?(:logrotationsizeinbytes)
|
45
|
+
config_parameters[:logkeepcount] = DEFAULT_LOG_KEEP_COUNT unless config_parameters.has_key?(:logkeepcount)
|
46
|
+
|
47
|
+
config_parameters
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.create_logger(config_parameters)
|
51
|
+
logfile = File.expand_path(config_parameters[:logfile]) unless config_parameters[:logfile].nil?
|
52
|
+
logfile = STDOUT if logfile.nil?
|
53
|
+
|
54
|
+
Logger.new(logfile,
|
55
|
+
shift_age = config_parameters[:logkeepcount],
|
56
|
+
shift_size = config_parameters[:logrotationsizeinbytes])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DreamhostPersonalBackup
|
2
|
+
|
3
|
+
module StatusManager
|
4
|
+
|
5
|
+
PID_FILE = '~/.dreamhost_personal_backup/running.pid'
|
6
|
+
|
7
|
+
def self.is_backup_running?
|
8
|
+
pid_file = File.expand_path(PID_FILE)
|
9
|
+
return false unless File.file?(pid_file)
|
10
|
+
|
11
|
+
pid = File.open(pid_file).read.to_i
|
12
|
+
|
13
|
+
begin
|
14
|
+
Process.getpgid(pid)
|
15
|
+
return true
|
16
|
+
rescue Errno::ESRCH
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create_pid_file
|
22
|
+
File.open(File.expand_path(PID_FILE), "w") { |f| f.write(Process.pid) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.remove_pid_file
|
26
|
+
pid_file = File.expand_path(PID_FILE)
|
27
|
+
File.delete(pid_file) if File.file?(pid_file)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'backup/configurator'
|
2
|
+
require 'backup/backup'
|
3
|
+
require 'backup/status_manager'
|
4
|
+
|
5
|
+
module DreamhostPersonalBackup
|
6
|
+
VERSION = '0.1.0'
|
7
|
+
|
8
|
+
def self.perform_backup(config_file)
|
9
|
+
return if DreamhostPersonalBackup::StatusManager.is_backup_running?
|
10
|
+
|
11
|
+
DreamhostPersonalBackup::StatusManager.create_pid_file
|
12
|
+
|
13
|
+
config_parameters = DreamhostPersonalBackup::Configurator.process_config_file(config_file)
|
14
|
+
|
15
|
+
logger = config_parameters[:logger]
|
16
|
+
|
17
|
+
# Add some newlines for readability
|
18
|
+
logger.info("")
|
19
|
+
logger.info("")
|
20
|
+
|
21
|
+
logger.info("Starting new backup run at #{DateTime.now}")
|
22
|
+
|
23
|
+
config_parameters[:targets].each_value do |target|
|
24
|
+
DreamhostPersonalBackup::Backup.run_for_target_directory(target, config_parameters)
|
25
|
+
end
|
26
|
+
|
27
|
+
DreamhostPersonalBackup::StatusManager.remove_pid_file
|
28
|
+
|
29
|
+
logger.info("Backup run completed at #{DateTime.now}")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/rakefile.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rdoc/task'
|
6
|
+
require 'dreamhost_personal_backup'
|
7
|
+
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Run all tests'
|
11
|
+
task :test => ["test:lib"]
|
12
|
+
|
13
|
+
namespace :test do
|
14
|
+
|
15
|
+
desc 'Run gem tests.'
|
16
|
+
Rake::TestTask.new(:lib) do |t|
|
17
|
+
t.libs << 'lib'
|
18
|
+
t.test_files = FileList['test/test*.rb'].exclude('test_helper.rb')
|
19
|
+
t.verbose = false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'jeweler'
|
25
|
+
Jeweler::Tasks.new do |gemspec|
|
26
|
+
gemspec.name = "dreamhost-personal-backup"
|
27
|
+
gemspec.summary = "Provides functionality to perform personal backups (on Linux or OSX) to a Dreamhost personal backup server."
|
28
|
+
gemspec.description = "Provides functionality to perform personal backups (on Linux or OSX) to a Dreamhost personal backup server."
|
29
|
+
gemspec.email = "philtrimble@gmail.com"
|
30
|
+
gemspec.homepage = "https://github.com/ptrimble/dreamhost-personal-backup"
|
31
|
+
gemspec.version = DreamhostPersonalBackup::VERSION
|
32
|
+
gemspec.authors = ["Phil Trimble"]
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
36
|
+
end
|
data/test/test_backup.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + '/test_helper'
|
2
|
+
|
3
|
+
require 'backup/backup'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
class BackupTests < Test::Unit::TestCase
|
7
|
+
|
8
|
+
CONFIG_PARAMETERS = {:user => "testuser", :host => "localhost", :logger => Logger.new(STDOUT)}
|
9
|
+
|
10
|
+
SOURCE_DIR_PARAMETER = "/path/to/source"
|
11
|
+
DEST_DIR_PARAMETER = "#{CONFIG_PARAMETERS[:user]}@#{CONFIG_PARAMETERS[:host]}:~/"
|
12
|
+
|
13
|
+
def test_backup_is_performed_without_errors_with_updated_file
|
14
|
+
set_expected_rsync_result_as(true)
|
15
|
+
assert DreamhostPersonalBackup::Backup.run_for_target_directory(SOURCE_DIR_PARAMETER, CONFIG_PARAMETERS)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_backup_returns_failure_if_rsync_reports_an_error
|
19
|
+
set_expected_rsync_result_as(false)
|
20
|
+
assert_equal false, DreamhostPersonalBackup::Backup.run_for_target_directory(SOURCE_DIR_PARAMETER, CONFIG_PARAMETERS)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_backup_fails_if_user_is_missing
|
24
|
+
parameters_with_host_only = {:host => "localhost"}
|
25
|
+
|
26
|
+
assert_raise(DreamhostPersonalBackup::MissingConfigParameter) {
|
27
|
+
DreamhostPersonalBackup::Backup.run_for_target_directory(SOURCE_DIR_PARAMETER, parameters_with_host_only)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_backup_fails_if_host_is_missing
|
32
|
+
parameters_with_user_only = {:user => "testuser"}
|
33
|
+
|
34
|
+
assert_raise(DreamhostPersonalBackup::MissingConfigParameter) {
|
35
|
+
DreamhostPersonalBackup::Backup.run_for_target_directory(SOURCE_DIR_PARAMETER, parameters_with_user_only)
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def set_expected_rsync_result_as(rsync_return_code)
|
42
|
+
rsync_change = stub(:summary => "timestamp", :filename => "test.dat")
|
43
|
+
|
44
|
+
rsync_change_array = Array.new
|
45
|
+
rsync_change_array.push(rsync_change)
|
46
|
+
|
47
|
+
rsync_return_code = stub(:success? => rsync_return_code, :changes => rsync_change_array, :error => "rsync example error")
|
48
|
+
|
49
|
+
suppress_logging_messages
|
50
|
+
|
51
|
+
Rsync.expects(:run).with(SOURCE_DIR_PARAMETER,
|
52
|
+
DEST_DIR_PARAMETER,
|
53
|
+
DreamhostPersonalBackup::Backup::RSYNC_COMMAND_ARGS).returns(rsync_return_code)
|
54
|
+
end
|
55
|
+
|
56
|
+
def suppress_logging_messages
|
57
|
+
Logger.any_instance.expects(:info).at_least(0).returns(nil)
|
58
|
+
Logger.any_instance.expects(:error).at_least(0).returns(nil)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + '/test_helper'
|
2
|
+
|
3
|
+
require 'backup/configurator'
|
4
|
+
|
5
|
+
class ConfiguratorTests < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_raise_error_if_config_file_does_not_exist
|
8
|
+
assert_raise(DreamhostPersonalBackup::ConfigFileNotFound) {
|
9
|
+
DreamhostPersonalBackup::Configurator.process_config_file("/file/does/not/exist")
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_config_file_is_processed_without_errors
|
14
|
+
config_parameters = DreamhostPersonalBackup::Configurator.process_config_file('test/test_files/test_standard_config_file.yml')
|
15
|
+
assert config_parameters.is_a?(Hash), "Config parameters should be a hash"
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_config_parameters_set_without_error_from_valid_file
|
19
|
+
config_parameters = DreamhostPersonalBackup::Configurator.process_config_file('test/test_files/test_standard_config_file.yml')
|
20
|
+
|
21
|
+
assert_equal "testuser", config_parameters[:user]
|
22
|
+
assert_equal "testhost.com", config_parameters[:host]
|
23
|
+
assert_equal "TestEmail@email.com", config_parameters[:notifyemail]
|
24
|
+
|
25
|
+
assert config_parameters[:targets].is_a?(Hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_raise_error_if_invalid_parameter_is_passed
|
29
|
+
assert_raise(DreamhostPersonalBackup::InvalidConfigParameter) {
|
30
|
+
DreamhostPersonalBackup::Configurator.process_config_file('test/test_files/test_invalid_parameters.yml')
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_all_targets_set_as_expected
|
35
|
+
config_parameters = DreamhostPersonalBackup::Configurator.process_config_file('test/test_files/test_standard_config_file.yml')
|
36
|
+
|
37
|
+
assert_equal "~/music", config_parameters[:targets]["Music"]
|
38
|
+
assert_equal "~/movies", config_parameters[:targets]["Movies"]
|
39
|
+
assert_equal "~/photos", config_parameters[:targets]["Photos"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_default_log_size_set_if_not_present_in_config_file
|
43
|
+
config_parameters = DreamhostPersonalBackup::Configurator.process_config_file('test/test_files/test_config_file_missing_optional_params.yml')
|
44
|
+
|
45
|
+
assert_equal DreamhostPersonalBackup::Configurator::DEFAULT_LOG_SIZE, config_parameters[:logrotationsizeinbytes]
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_default_log_file_keep_count_set_if_not_present_in_config_file
|
49
|
+
config_parameters = DreamhostPersonalBackup::Configurator.process_config_file('test/test_files/test_config_file_missing_optional_params.yml')
|
50
|
+
|
51
|
+
assert_equal DreamhostPersonalBackup::Configurator::DEFAULT_LOG_KEEP_COUNT, config_parameters[:logkeepcount]
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_logger_created_even_if_no_parameters_set
|
55
|
+
config_parameters = DreamhostPersonalBackup::Configurator.process_config_file('test/test_files/test_config_file_missing_optional_params.yml')
|
56
|
+
|
57
|
+
assert_not_nil config_parameters[:logger]
|
58
|
+
end
|
59
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
Coveralls::SimpleCov::Formatter,
|
6
|
+
SimpleCov::Formatter::HTMLFormatter
|
7
|
+
]
|
8
|
+
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter 'test'
|
11
|
+
end
|
12
|
+
|
13
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__) + '../../lib/'))
|
14
|
+
|
15
|
+
$KCODE = 'u' if RUBY_VERSION =~ /^1\.8/
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'test/unit'
|
19
|
+
|
20
|
+
require 'mocha/setup' # The mocha docs state that this MUST come after the require for test/unit
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + '/test_helper'
|
2
|
+
|
3
|
+
require 'backup/status_manager'
|
4
|
+
|
5
|
+
class StatusManagerTests < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_is_backup_running_returns_false_if_no_pid_file_is_found
|
8
|
+
File.expects(:file?).with(File.expand_path(DreamhostPersonalBackup::StatusManager::PID_FILE)).at_least(1).returns(false)
|
9
|
+
|
10
|
+
assert_equal false, DreamhostPersonalBackup::StatusManager.is_backup_running?
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_is_backup_running_returns_true_if_pid_file_is_found_and_pid_is_running
|
14
|
+
File.expects(:file?).with(File.expand_path(DreamhostPersonalBackup::StatusManager::PID_FILE)).at_least(1).returns(true)
|
15
|
+
|
16
|
+
file_handle = stub(read: "100")
|
17
|
+
File.expects(:open).at_least(1).returns(file_handle)
|
18
|
+
|
19
|
+
Process.expects(:getpgid).at_least(1)
|
20
|
+
|
21
|
+
assert DreamhostPersonalBackup::StatusManager.is_backup_running?
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_is_backup_running_returns_false_if_pid_file_is_found_and_pid_is_not_running
|
25
|
+
File.expects(:file?).with(File.expand_path(DreamhostPersonalBackup::StatusManager::PID_FILE)).at_least(1).returns(true)
|
26
|
+
|
27
|
+
file_handle = stub(read: "100")
|
28
|
+
File.expects(:open).at_least(1).returns(file_handle)
|
29
|
+
|
30
|
+
Process.expects(:getpgid).at_least(1).raises(Errno::ESRCH)
|
31
|
+
|
32
|
+
assert_equal false, DreamhostPersonalBackup::StatusManager.is_backup_running?
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dreamhost-personal-backup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Phil Trimble
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rsync
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mocha
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: simplecov
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: coveralls
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: jeweler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Provides functionality to perform personal backups (on Linux or OSX)
|
95
|
+
to a Dreamhost personal backup server.
|
96
|
+
email: philtrimble@gmail.com
|
97
|
+
executables:
|
98
|
+
- dreamhost_personal_backup
|
99
|
+
extensions: []
|
100
|
+
extra_rdoc_files:
|
101
|
+
- LICENSE
|
102
|
+
- README.rdoc
|
103
|
+
files:
|
104
|
+
- .coveralls.yml
|
105
|
+
- .travis.yml
|
106
|
+
- CHANGELOG
|
107
|
+
- Gemfile
|
108
|
+
- Gemfile.lock
|
109
|
+
- LICENSE
|
110
|
+
- README.rdoc
|
111
|
+
- bin/dreamhost_personal_backup
|
112
|
+
- dreamhost-personal-backup.gemspec
|
113
|
+
- lib/backup/backup.rb
|
114
|
+
- lib/backup/configurator.rb
|
115
|
+
- lib/backup/status_manager.rb
|
116
|
+
- lib/dreamhost_personal_backup.rb
|
117
|
+
- rakefile.rb
|
118
|
+
- test/test_backup.rb
|
119
|
+
- test/test_configurator.rb
|
120
|
+
- test/test_dreamhost_personal_backup.rb
|
121
|
+
- test/test_files/test_config_file_missing_optional_params.yml
|
122
|
+
- test/test_files/test_invalid_parameters.yml
|
123
|
+
- test/test_files/test_standard_config_file.yml
|
124
|
+
- test/test_helper.rb
|
125
|
+
- test/test_status_manager.rb
|
126
|
+
homepage: https://github.com/ptrimble/dreamhost-personal-backup
|
127
|
+
licenses: []
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ! '>='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 1.8.23
|
147
|
+
signing_key:
|
148
|
+
specification_version: 3
|
149
|
+
summary: Provides functionality to perform personal backups (on Linux or OSX) to a
|
150
|
+
Dreamhost personal backup server.
|
151
|
+
test_files: []
|