dreamhost-personal-backup 0.1.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/.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: []
|