dreamback 0.0.1
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/.gitignore +17 -0
- data/.rbenv-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +34 -0
- data/Rakefile +2 -0
- data/bin/dreamback +3 -0
- data/dreamback.gemspec +24 -0
- data/lib/dreamback/backup.rb +99 -0
- data/lib/dreamback/base.rb +22 -0
- data/lib/dreamback/exclusions.txt +13 -0
- data/lib/dreamback/initializer.rb +184 -0
- data/lib/dreamback/version.rb +3 -0
- data/lib/dreamback.rb +18 -0
- data/test/dreamback_test.rb +58 -0
- data/test/mock/settings.json +19 -0
- metadata +139 -0
data/.gitignore
ADDED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.8.7-p352
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Paul R Alexander
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Dreamback
|
2
|
+
|
3
|
+
Dreamback is the easiest way to automate your backups on dreamhost. Dreamhost does not guarantee their backups of your users (though they've saved me with backups before), so it's best to run backups yourself.
|
4
|
+
|
5
|
+
Using Dreamback is easy:
|
6
|
+
|
7
|
+
1. Create a user on dreamhost to schedule your backups
|
8
|
+
2. Log in with your new user
|
9
|
+
3. `gem install dreamback`
|
10
|
+
4. `dreamback`
|
11
|
+
5. Answer the questions to setup your automated backup
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'dreamback'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install dreamback
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Run `dreamback` to configure automated backups.
|
30
|
+
Run `dreamback backup` to immediately run a backup.
|
31
|
+
|
32
|
+
## Contributing
|
33
|
+
|
34
|
+
I'm happy to take pull or feature requests. Use the Github issue tracker if you have suggestions or need help.
|
data/Rakefile
ADDED
data/bin/dreamback
ADDED
data/dreamback.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/dreamback/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Paul R Alexander"]
|
6
|
+
gem.email = ["palexander@stanford.edu"]
|
7
|
+
gem.description = %q{The easiest, cheapest way to back up your dreamhost accounts}
|
8
|
+
gem.summary = %q{Dreamback is the easiest way to automate your backups on dreamhost. Dreamhost does not guarantee their backups of your users (though they've saved me with backups before), so it's best to run backups yourself.}
|
9
|
+
gem.homepage = "https://github.com/palexander/dreamback"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "dreamback"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Dreamback::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency("json")
|
19
|
+
gem.add_dependency("net-sftp")
|
20
|
+
gem.add_dependency("cronedit")
|
21
|
+
gem.add_dependency("commander")
|
22
|
+
|
23
|
+
gem.executables = %w(dreamback)
|
24
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'net/sftp'
|
2
|
+
|
3
|
+
module Dreamback
|
4
|
+
class Backup
|
5
|
+
BACKUP_FOLDER = "./dreamback"
|
6
|
+
|
7
|
+
# Manage the backup process
|
8
|
+
def self.start
|
9
|
+
# rotate the backup folders
|
10
|
+
backup_to_link = rotate_backups
|
11
|
+
# rsync backup of dreamhost user accounts
|
12
|
+
rsync_backup(backup_to_link)
|
13
|
+
# mysql backup
|
14
|
+
# TODO: Add mysql backups
|
15
|
+
end
|
16
|
+
|
17
|
+
# Rotate out the backups that are past our limit
|
18
|
+
# @returns [String] name of the most recent folder to link against
|
19
|
+
def self.rotate_backups
|
20
|
+
backup_to_link = ""
|
21
|
+
Net::SFTP.start(Dreamback.settings[:backup_server], Dreamback.settings[:backup_server_user]) do |sftp|
|
22
|
+
# Create the backup folder if it doesn't exist
|
23
|
+
backup_folder_exists = false
|
24
|
+
begin
|
25
|
+
sftp.stat!(BACKUP_FOLDER) do |response|
|
26
|
+
backup_folder_exists = response.ok?
|
27
|
+
end
|
28
|
+
rescue Net::SFTP::StatusException => e
|
29
|
+
backup_folder_exists = false if e.code == 2
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get a list of all backup directories
|
33
|
+
backup_folders = []
|
34
|
+
if backup_folder_exists
|
35
|
+
sftp.dir.foreach(BACKUP_FOLDER) do |entry|
|
36
|
+
# Names should be like "dreamback.20120520", so we can sort on the folder name
|
37
|
+
backup_folders << [ entry.name, entry.name.split(".")[1].to_i ] if entry.name.include?("dreamback")
|
38
|
+
backup_folders.sort! {|a,b| b <=> a}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the newest folder for linking
|
43
|
+
backup_to_link = backup_folders.first[0]
|
44
|
+
|
45
|
+
# Delete any folders older than our limit
|
46
|
+
# Subtract one to account for the folder we're about to create
|
47
|
+
# Normally we remove a folder so that our count is one less than the "days to keep"
|
48
|
+
# However, if today's folder already exists then there's no need
|
49
|
+
offset = backup_folders.include?("dreamback.#{Time.now.strftime("%Y%m%d")}") ? 0 : 1
|
50
|
+
if backup_folders.length >= Dreamback.settings[:days_to_keep] - offset
|
51
|
+
folders_to_delete = backup_folders.slice(Dreamback.settings[:days_to_keep] - offset, backup_folders.length)
|
52
|
+
folders_to_delete.map! {|f| f[0]}
|
53
|
+
rsync_delete(folders_to_delete)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
backup_to_link
|
57
|
+
end
|
58
|
+
|
59
|
+
# This uses a hack where we sync an empty directory to remove files
|
60
|
+
# We do this because sftp has no recursive delete method
|
61
|
+
def self.rsync_delete(directories)
|
62
|
+
empty_dir_path = File.expand_path("../.dreamback_empty_dir", __FILE__)
|
63
|
+
empty_dir = Dir.mkdir(empty_dir_path) unless File.exists?(empty_dir_path)
|
64
|
+
begin
|
65
|
+
directories.each do |dir|
|
66
|
+
`rsync --delete -a #{empty_dir_path}/ #{Dreamback.settings[:backup_server_user]}@#{Dreamback.settings[:backup_server]}:#{BACKUP_FOLDER}/#{dir}`
|
67
|
+
Net::SFTP.start(Dreamback.settings[:backup_server], Dreamback.settings[:backup_server_user]) do |sftp|
|
68
|
+
sftp.rmdir!("#{BACKUP_FOLDER}/#{dir}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
ensure
|
72
|
+
Dir.delete(empty_dir_path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sync to the backup server using link-dest to save space
|
77
|
+
# @param [String] name of the most recent backup folder prior to starting this run to link against
|
78
|
+
def self.rsync_backup(link_dir)
|
79
|
+
backup_server_user = Dreamback.settings[:backup_server_user]
|
80
|
+
backup_server = Dreamback.settings[:backup_server]
|
81
|
+
today = Time.now.strftime("%Y%m%d")
|
82
|
+
Dreamback.settings[:dreamhost_users].each do |dreamhost|
|
83
|
+
# User that we're going to back up
|
84
|
+
user = dreamhost[:user]
|
85
|
+
server = dreamhost[:server]
|
86
|
+
# rsync won't do remote<->remote syncing, so we stage everything here first
|
87
|
+
tmp_dir = File.expand_path("~/.dreamback_tmp")
|
88
|
+
Dir.mkdir(tmp_dir) unless File.exists?(tmp_dir)
|
89
|
+
`rsync -e ssh -av --keep-dirlinks --copy-links #{user}@#{server}:~/ #{tmp_dir}/#{user}@#{server}`
|
90
|
+
# Now we can sync local to remote
|
91
|
+
# Only use link-dest if a previous folder to link to exists
|
92
|
+
link_dest = link_dir.nil? ? "" : "--link-dest=~#{BACKUP_FOLDER.gsub(".", "")}/#{link_dir}"
|
93
|
+
`rsync -e ssh -av --delete --copy-links --keep-dirlinks #{link_dest} #{tmp_dir}/ #{backup_server_user}@#{backup_server}:#{BACKUP_FOLDER}/dreamback.#{today}`
|
94
|
+
# Remove the staging directory
|
95
|
+
`rm -rf #{tmp_dir}`
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
program :version, Dreamback::VERSION
|
2
|
+
program :description, 'Automated backup for dreamhost accounts'
|
3
|
+
|
4
|
+
default_command :setup
|
5
|
+
|
6
|
+
command :setup do |c|
|
7
|
+
c.syntax = 'dreamback start [options]'
|
8
|
+
c.summary = 'This will guide you through setting up Dreamback'
|
9
|
+
c.action do |args, options|
|
10
|
+
Dreamback::Initializer.setup
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
command :backup do |c|
|
15
|
+
c.syntax = 'dreamback backup'
|
16
|
+
c.summary = 'Run a backup process immediately. This command can also be added to a cron job.'
|
17
|
+
c.action do |args, options|
|
18
|
+
Dreamback::Initializer.setup
|
19
|
+
Dreamback::Backup.start
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/sftp'
|
3
|
+
require 'cronedit'
|
4
|
+
|
5
|
+
module Dreamback
|
6
|
+
@settings
|
7
|
+
|
8
|
+
def self.settings
|
9
|
+
@settings
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.settings=(settings)
|
13
|
+
@settings=settings
|
14
|
+
end
|
15
|
+
|
16
|
+
class Initializer
|
17
|
+
SETTINGS_LOCATION = File.expand_path("~/.dreamback")
|
18
|
+
|
19
|
+
DEFAULT_SETTINGS = {
|
20
|
+
:backup_server => "",
|
21
|
+
:backup_server_user => "",
|
22
|
+
:dreamhost_users => [ {:user => "", :server => ""} ]
|
23
|
+
}
|
24
|
+
|
25
|
+
@settings = {}
|
26
|
+
|
27
|
+
# Walks a user through the initial setup process
|
28
|
+
def self.setup
|
29
|
+
# Check to see if settings exist
|
30
|
+
load_settings(SETTINGS_LOCATION)
|
31
|
+
|
32
|
+
# Ask user questions if no settings file
|
33
|
+
if @settings.nil? || @settings.empty?
|
34
|
+
create_new_settings
|
35
|
+
save_settings(SETTINGS_LOCATION)
|
36
|
+
else
|
37
|
+
say(bold("You have already setup Dreamback. Please run \"dreamback backup\" to start a backup."))
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create ssh keys if they don't exist
|
41
|
+
ssh_keys_exist = File.exists?(File.expand_path("~/.ssh/id_dsa"))
|
42
|
+
create_ssh_keys unless ssh_keys_exist
|
43
|
+
|
44
|
+
# Copy ssh keys to backup server
|
45
|
+
unless @settings[:copied_backup_server_ssh_keys]
|
46
|
+
say(bold("Copying the ssh key to your backup server, type in your password if prompted for #{@settings[:backup_server_user]}@#{@settings[:backup_server]}"))
|
47
|
+
overwrite_keys = agree(bold("WARNING: ") + "This will overwrite existing ssh keys on your backup user account. Proceed? [y/n]: ")
|
48
|
+
if overwrite_keys
|
49
|
+
sftp_password = ask("Password for #{@settings[:backup_server_user]}@#{@settings[:backup_server]}: ") { |q| q.echo = "*" }
|
50
|
+
sftp_ssh_key_upload(sftp_password)
|
51
|
+
else
|
52
|
+
say("You will need to add the ssh key yourself to automate backups")
|
53
|
+
end
|
54
|
+
@settings[:copied_backup_server_ssh_keys] = true
|
55
|
+
end
|
56
|
+
|
57
|
+
# Copy ssh keys to dreamhost servers
|
58
|
+
unless @settings[:copied_dreamhost_users_ssh_keys]
|
59
|
+
say(bold("Copying the ssh key to the dreamhost accounts you want to back up"))
|
60
|
+
@settings[:dreamhost_users].each do |dreamhost|
|
61
|
+
say(bold("Type in password if prompted for #{dreamhost[:user]}@#{dreamhost[:server]}"))
|
62
|
+
`ssh-copy-id -i #{dreamhost[:user]}@#{dreamhost[:server]}`
|
63
|
+
end
|
64
|
+
@settings[:copied_dreamhost_users_ssh_keys] = true
|
65
|
+
end
|
66
|
+
|
67
|
+
# Setup a cron job if the user would like to
|
68
|
+
unless @settings[:cron_setup_completed]
|
69
|
+
setup_cron = agree(bold("Would you like to add a cron job to automatically run the backup? [y/n]: "))
|
70
|
+
if setup_cron
|
71
|
+
crontab_email = ask("Dreamhost requires an email address to send crontab output to, please provide one: ") { |q| q.validate = /\b[A-Za-z0-9._%-\+]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}\b/ }
|
72
|
+
ct = File.open(File.expand_path("~/.dreamback_crontab"), "w+")
|
73
|
+
ct << "MAILTO=#{crontab_email}\n"
|
74
|
+
ct << "0 1 * * * dreamback backup"
|
75
|
+
ct.close
|
76
|
+
`crontab #{ct.path}`
|
77
|
+
File.delete(ct.path)
|
78
|
+
say("Cron job added. Backups will run at 1:00am Pacific every day.")
|
79
|
+
end
|
80
|
+
@settings[:cron_setup_completed] = true
|
81
|
+
end
|
82
|
+
|
83
|
+
save_settings(SETTINGS_LOCATION)
|
84
|
+
|
85
|
+
# Set global settings
|
86
|
+
Dreamback.settings = @settings
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Dreamhost doesn't allow ssh shell access, so use sftp to write to the authorized_key file
|
92
|
+
# @param [String] password for backup user
|
93
|
+
def self.sftp_ssh_key_upload(sftp_password)
|
94
|
+
Net::SFTP.start(@settings[:backup_server], @settings[:backup_server_user], :password => sftp_password) do |sftp|
|
95
|
+
# Create the .ssh folder if it doesn't exist
|
96
|
+
ssh_folder_exists = false
|
97
|
+
begin
|
98
|
+
sftp.stat!(".ssh") do |response|
|
99
|
+
ssh_folder_exists = response.ok?
|
100
|
+
end
|
101
|
+
rescue Net::SFTP::StatusException => e
|
102
|
+
ssh_folder_exists = false if e.code == 2
|
103
|
+
end
|
104
|
+
|
105
|
+
unless ssh_folder_exists
|
106
|
+
sftp.mkdir!(".ssh")
|
107
|
+
end
|
108
|
+
|
109
|
+
sftp.file.open(".ssh/authorized_keys", "w+") do |f|
|
110
|
+
f.write File.open(File.expand_path("~/.ssh/id_dsa.pub"), "r").read
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Ask the user for settings
|
116
|
+
def self.create_new_settings
|
117
|
+
settings = {}
|
118
|
+
say("#{bold("Server Where We Should Store Your Backup")}\nYour dreamhost backup-specific account will work best, but any POSIX server with rsync should work\n<%= color('Note:', BOLD)%> dreamhost does not allow you to store non-webhosted data except your BACKUP-SPECIFIC account")
|
119
|
+
settings[:backup_server] = ask("Server name: ")
|
120
|
+
settings[:backup_server_user] = ask(bold("Username for the backup server: "))
|
121
|
+
settings[:dreamhost_users] = []
|
122
|
+
another_user = true
|
123
|
+
while another_user
|
124
|
+
dreamhost = {}
|
125
|
+
dreamhost[:user] = ask(bold("Dreamhost user to back up: "))
|
126
|
+
dreamhost[:server] = ask(bold("Server where the dreamhost user is located: "))
|
127
|
+
settings[:dreamhost_users] << dreamhost
|
128
|
+
another_user = agree(bold("Add another dreamhost account? [y/n]"))
|
129
|
+
end
|
130
|
+
settings[:days_to_keep] = ask(bold("How many days of backups do you want to keep [1-30]? "), Integer) {|q| q.in = 1..30}
|
131
|
+
@settings = settings
|
132
|
+
end
|
133
|
+
|
134
|
+
# Create ssh keys for user when they don't exist
|
135
|
+
def self.create_ssh_keys
|
136
|
+
ssh_key_location = File.expand_path("~/.ssh/id_dsa")
|
137
|
+
say(bold("You are missing a DSA ssh key for this user at #{ssh_key_location}, we will create one now"))
|
138
|
+
say("More on creating ssh keys here: http://en.wikipedia.org/wiki/ssh-keygen")
|
139
|
+
`ssh-keygen -t dsa`
|
140
|
+
success = File.exists?(ssh_key_location)
|
141
|
+
if success
|
142
|
+
say(bold("Key created successfully"))
|
143
|
+
else
|
144
|
+
try_again = agree(bold("It looks like the ssh key creation failed, try again? [y/n]"))
|
145
|
+
if try_again
|
146
|
+
create_ssh_keys
|
147
|
+
else
|
148
|
+
say("Please try running the setup process again")
|
149
|
+
exit
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Load settings from a JSON file
|
155
|
+
# @param [String] path where settings are located
|
156
|
+
def self.load_settings(settings_path)
|
157
|
+
settings_file = File.open(settings_path, "r") if File.exists?(settings_path)
|
158
|
+
@settings = JSON.parse(settings_file.read, :symbolize_names => true) unless settings_file.nil?
|
159
|
+
settings_file.close unless settings_file.nil?
|
160
|
+
@settings
|
161
|
+
end
|
162
|
+
|
163
|
+
# Store settings from a JSON file
|
164
|
+
# @param [String] path where settings are located
|
165
|
+
def self.save_settings(settings_path)
|
166
|
+
settings = @settings ||= ""
|
167
|
+
settings_file = File.open(settings_path, "w+")
|
168
|
+
settings_file << JSON.pretty_generate(settings)
|
169
|
+
settings_file.close
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
# Setter used primarily in testing
|
174
|
+
def self.settings=(settings)
|
175
|
+
@settings = settings
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.bold(text)
|
179
|
+
"<%= color('\n#{text.gsub("'", "\\'")}', BOLD) %>"
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
data/lib/dreamback.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Use this file with a regular ruby setup (IE not from command line or bin/dreamback)
|
2
|
+
|
3
|
+
begin
|
4
|
+
# Try loading without rubygems
|
5
|
+
require 'commander/import'
|
6
|
+
require 'dreamback/version'
|
7
|
+
require 'dreamback/initializer'
|
8
|
+
require 'dreamback/backup'
|
9
|
+
require 'dreamback/base'
|
10
|
+
rescue LoadError
|
11
|
+
# If that fails, then load with it
|
12
|
+
require 'rubygems'
|
13
|
+
require 'commander/import'
|
14
|
+
require File.expand_path('../dreamback/version.rb', __FILE__)
|
15
|
+
require File.expand_path('../dreamback/initializer.rb', __FILE__)
|
16
|
+
require File.expand_path('../dreamback/backup.rb', __FILE__)
|
17
|
+
require File.expand_path('../dreamback/base.rb', __FILE__)
|
18
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "test/unit"
|
3
|
+
require File.expand_path('../../lib/dreamback/initializer', __FILE__)
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
# Used for testing private methods
|
7
|
+
class Class
|
8
|
+
def publicize_methods
|
9
|
+
saved_private_instance_methods = self.private_instance_methods
|
10
|
+
self.class_eval { public *saved_private_instance_methods }
|
11
|
+
yield
|
12
|
+
self.class_eval { private *saved_private_instance_methods }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class DreambackTest < Test::Unit::TestCase
|
17
|
+
|
18
|
+
def test_settings_save
|
19
|
+
settings = {:testing_settings_save => "worked"}
|
20
|
+
settings_file = File.open("./test_settings.json", "w+")
|
21
|
+
Dreamback::Initializer.publicize_methods do
|
22
|
+
Dreamback::Initializer.settings = settings
|
23
|
+
Dreamback::Initializer.save_settings(settings_file.path)
|
24
|
+
end
|
25
|
+
settings_new = JSON.parse(settings_file.read, :symbolize_names => true)
|
26
|
+
assert_equal settings, settings_new
|
27
|
+
settings_file.close
|
28
|
+
File.delete(settings_file.path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_settings_load
|
32
|
+
file = File.open(File.expand_path('../mock/settings.json', __FILE__), "r")
|
33
|
+
settings_file = Dreamback::Initializer.load_settings(file.path)
|
34
|
+
settings_test =<<-EOS
|
35
|
+
{
|
36
|
+
"backup_server_user": "blah",
|
37
|
+
"dreamhost_users": [
|
38
|
+
{
|
39
|
+
"user": "u",
|
40
|
+
"server": "d1.dev"
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"user": "u2",
|
44
|
+
"server": "d2.dev"
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"user": "u3",
|
48
|
+
"server": "d3.dev"
|
49
|
+
}
|
50
|
+
],
|
51
|
+
"days_to_keep": 7,
|
52
|
+
"backup_server": "backup.dev"
|
53
|
+
}
|
54
|
+
EOS
|
55
|
+
assert_equal settings_file, JSON.parse(settings_test, :symbolize_names => true)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"backup_server_user": "blah",
|
3
|
+
"dreamhost_users": [
|
4
|
+
{
|
5
|
+
"user": "u",
|
6
|
+
"server": "d1.dev"
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"user": "u2",
|
10
|
+
"server": "d2.dev"
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"user": "u3",
|
14
|
+
"server": "d3.dev"
|
15
|
+
}
|
16
|
+
],
|
17
|
+
"days_to_keep": 7,
|
18
|
+
"backup_server": "backup.dev"
|
19
|
+
}
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dreamback
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Paul R Alexander
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-05-22 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: json
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: net-sftp
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: cronedit
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: commander
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id004
|
77
|
+
description: The easiest, cheapest way to back up your dreamhost accounts
|
78
|
+
email:
|
79
|
+
- palexander@stanford.edu
|
80
|
+
executables:
|
81
|
+
- dreamback
|
82
|
+
extensions: []
|
83
|
+
|
84
|
+
extra_rdoc_files: []
|
85
|
+
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- .rbenv-version
|
89
|
+
- Gemfile
|
90
|
+
- LICENSE
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- bin/dreamback
|
94
|
+
- dreamback.gemspec
|
95
|
+
- lib/dreamback.rb
|
96
|
+
- lib/dreamback/backup.rb
|
97
|
+
- lib/dreamback/base.rb
|
98
|
+
- lib/dreamback/exclusions.txt
|
99
|
+
- lib/dreamback/initializer.rb
|
100
|
+
- lib/dreamback/version.rb
|
101
|
+
- test/dreamback_test.rb
|
102
|
+
- test/mock/settings.json
|
103
|
+
has_rdoc: true
|
104
|
+
homepage: https://github.com/palexander/dreamback
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 3
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
requirements: []
|
131
|
+
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 1.6.2
|
134
|
+
signing_key:
|
135
|
+
specification_version: 3
|
136
|
+
summary: Dreamback is the easiest way to automate your backups on dreamhost. Dreamhost does not guarantee their backups of your users (though they've saved me with backups before), so it's best to run backups yourself.
|
137
|
+
test_files:
|
138
|
+
- test/dreamback_test.rb
|
139
|
+
- test/mock/settings.json
|