backupgem 0.0.2
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/CHANGELOG +6 -0
- data/README +387 -0
- data/bin/backup +12 -0
- data/bin/commands.sh +2 -0
- data/doc/LICENSE-GPL +280 -0
- data/doc/index.html +716 -0
- data/doc/styles.css +157 -0
- data/examples/global.rb +28 -0
- data/examples/mediawiki.rb +24 -0
- data/lib/backup/actor.rb +196 -0
- data/lib/backup/cli.rb +105 -0
- data/lib/backup/configuration.rb +138 -0
- data/lib/backup/date_parser.rb +37 -0
- data/lib/backup/extensions.rb +17 -0
- data/lib/backup/recipes/standard.rb +89 -0
- data/lib/backup/rotator.rb +155 -0
- data/lib/backup/ssh_helpers.rb +138 -0
- data/lib/backup.rb +7 -0
- data/tests/actor_test.rb +70 -0
- data/tests/cleanup.sh +2 -0
- data/tests/rotation_test.rb +32 -0
- data/tests/ssh_test.rb +21 -0
- data/tests/tests_helper.rb +5 -0
- metadata +99 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
#------------------------------------------------------------------------------
|
2
|
+
# Backup Global Settings
|
3
|
+
# @author: Nate Murray <nate@natemurray.com>
|
4
|
+
# @date: Mon Aug 28 07:28:22 PDT 2006
|
5
|
+
#
|
6
|
+
# The settings contained in this file will be global for all tasks and can be
|
7
|
+
# overridden locally.
|
8
|
+
#------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
# Sepcify sever settings
|
11
|
+
set :servers, %w{ localhost }
|
12
|
+
set :action_order, %w{ content compress encrypt deliver rotate cleanup }
|
13
|
+
|
14
|
+
# Name of the SSH user
|
15
|
+
set :ssh_user, ENV['USER']
|
16
|
+
|
17
|
+
# Path to your SSH key
|
18
|
+
set :identity_key, ENV['HOME'] + "/.ssh/id_rsa"
|
19
|
+
|
20
|
+
# Set global actions
|
21
|
+
action :compress, :method => :tar_bz2
|
22
|
+
action :deliver, :method => :mv # action :deliver, :method => :scp
|
23
|
+
action :rotate, :method => :via_mv # action :rotate, :method => :via_ssh
|
24
|
+
# action :encrypt, :method => :gpg
|
25
|
+
|
26
|
+
# Specify a directory that backup can use as a temporary directory
|
27
|
+
set :tmp_dir, "/tmp"
|
28
|
+
|
29
|
+
# Options to be passed to gpg when encrypting
|
30
|
+
set :encrypt, false
|
31
|
+
set :gpg_encrypt_options, ""
|
32
|
+
|
33
|
+
# These settings specify the rotation variables
|
34
|
+
# Rotation method. Currently the only method is gfs, grandfather-father-son.
|
35
|
+
# Read more about that below
|
36
|
+
set :rotation_method, :gfs
|
37
|
+
|
38
|
+
# :mon-sun
|
39
|
+
# :last_day_of_the_month # whatever son_promoted on son was, but the last of the month
|
40
|
+
# everything else you can define with a Runt object
|
41
|
+
# set :son_created_on, :every_day - if you dont want a son created dont run the program
|
42
|
+
# a backup is created every time the program is run
|
43
|
+
|
44
|
+
set :son_promoted_on, :fri
|
45
|
+
set :father_promoted_on, :last_fri_of_the_month
|
46
|
+
|
47
|
+
# more complex
|
48
|
+
# mon_wed_fri = Runt::DIWeek.new(Runt::Mon) |
|
49
|
+
# Runt::DIWeek.new(Runt::Wed) |
|
50
|
+
# Runt::DIWeek.new(Runt::Fri)
|
51
|
+
# set :son_promoted_on, mon_wed_fri
|
52
|
+
|
53
|
+
set :sons_to_keep, 14
|
54
|
+
set :fathers_to_keep, 6
|
55
|
+
set :grandfathers_to_keep, 6 # 6 months, by default
|
56
|
+
|
57
|
+
|
58
|
+
# -------------------------
|
59
|
+
# Standard Actions
|
60
|
+
# -------------------------
|
61
|
+
action(:tar_bz2) do
|
62
|
+
name = c[:tmp_dir] + "/" + File.basename(last_result) + ".tar.bz2"
|
63
|
+
sh "tar -cvjf #{name} #{last_result}"
|
64
|
+
name
|
65
|
+
end
|
66
|
+
|
67
|
+
action(:scp) do
|
68
|
+
# what should the default scp task be?
|
69
|
+
# scp the local file to the foreign directory. same name.
|
70
|
+
c[:servers].each do |server|
|
71
|
+
host = server =~ /localhost/ ? "" : "#{server}:"
|
72
|
+
sh "scp #{last_result} #{host}#{c[:backup_path]}/"
|
73
|
+
end
|
74
|
+
c[:backup_path] + "/" + File.basename(last_result)
|
75
|
+
end
|
76
|
+
|
77
|
+
action(:mv) do
|
78
|
+
sh "mv #{last_result} #{c[:backup_path]}/"
|
79
|
+
c[:backup_path] + "/" + File.basename(last_result)
|
80
|
+
end
|
81
|
+
|
82
|
+
action(:encrypt) do
|
83
|
+
result = last_result
|
84
|
+
if c[:encrypt]
|
85
|
+
sh "gpg #{c[:gpg_encrypt_options]} --encrypt #{last_result}"
|
86
|
+
result = last_result + ".gpg" # ?
|
87
|
+
end
|
88
|
+
result
|
89
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require "rake"
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
class Rotator
|
5
|
+
include FileUtils
|
6
|
+
|
7
|
+
attr_reader :actor
|
8
|
+
attr_reader :configuration
|
9
|
+
alias_method :c, :configuration
|
10
|
+
|
11
|
+
def initialize(actor)
|
12
|
+
@actor = actor
|
13
|
+
@configuration = actor.configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
# Take the last result and rotate it via mv on the local machine
|
17
|
+
def rotate_via_mv(last_result)
|
18
|
+
# verify that each of the directories exist, grandfathers, fathers, sons
|
19
|
+
hierarchy.each { |m| verify_local_backup_directory_exists(m) }
|
20
|
+
|
21
|
+
# place todays backup into the specified directory with a timestamp.
|
22
|
+
newname = timestamped_prefix(last_result)
|
23
|
+
sh "mv #{last_result} #{place_in}/#{newname}"
|
24
|
+
|
25
|
+
cleanup_via_mv(place_in, how_many_to_keep_today)
|
26
|
+
end
|
27
|
+
|
28
|
+
def rotate_via_ssh(last_result)
|
29
|
+
ssh = Backup::SshActor.new(c)
|
30
|
+
ssh.connect
|
31
|
+
ssh.run "echo \"#{last_result}\""
|
32
|
+
|
33
|
+
hierarchy.each do |m|
|
34
|
+
dir = c[:backup_path] + "/" + m
|
35
|
+
ssh.verify_directory_exists(dir)
|
36
|
+
end
|
37
|
+
|
38
|
+
newname = timestamped_prefix(last_result)
|
39
|
+
ssh.run "mv #{last_result} #{place_in}/#{newname}"
|
40
|
+
|
41
|
+
ssh.cleanup_directory(place_in, how_many_to_keep_today)
|
42
|
+
ssh.close
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO
|
46
|
+
def rotate_via_ftp(last_result)
|
47
|
+
# ftp = Backup::FtpActor.new(c)
|
48
|
+
# ftp.connect
|
49
|
+
#
|
50
|
+
# hierarchy.each do |m|
|
51
|
+
# dir = c[:backup_path] + "/" + m
|
52
|
+
# ftp.verify_directory_exists(dir)
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# newname = timestamped_prefix(last_result)
|
56
|
+
# ftp.run "mv #{last_result} #{place_in}/#{newname}"
|
57
|
+
#
|
58
|
+
# ftp.cleanup_directory(place_in, how_many_to_keep_today)
|
59
|
+
# ftp.close
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_sons_today?; is_today_a? :son_created_on; end
|
63
|
+
def promote_sons_today?; is_today_a? :son_promoted_on; end
|
64
|
+
def promote_fathers_today?; is_today_a? :father_promoted_on; end
|
65
|
+
|
66
|
+
# old ( offset_days % c[:son_promoted_on] ) == 0 ? true : false
|
67
|
+
# old ( offset_days % (c[:son_promoted_on] * c[:father_promoted_on]) ) == 0 ? true : false
|
68
|
+
|
69
|
+
private
|
70
|
+
def is_today_a?(symbol)
|
71
|
+
t = $test_time || Date.today
|
72
|
+
day = DateParser.date_from( c[symbol] )
|
73
|
+
day.include?( t )
|
74
|
+
end
|
75
|
+
|
76
|
+
def verify_local_backup_directory_exists(dir)
|
77
|
+
path = c[:backup_path]
|
78
|
+
full = path + "/" + dir
|
79
|
+
unless File.exists?(full)
|
80
|
+
sh "mkdir -p #{full}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#def offset_days
|
85
|
+
# t = $test_time || Time.now # todo, write a test to use this
|
86
|
+
# num_from_day = Time.num_from_day( c[:promote_on] )
|
87
|
+
# offset = ( t.days_since_epoch + num_from_day + 0)
|
88
|
+
# offset
|
89
|
+
#end
|
90
|
+
|
91
|
+
def cleanup_via_mv(where, num_keep)
|
92
|
+
|
93
|
+
files = Dir[where + "/*"].sort
|
94
|
+
diff = files.size - num_keep
|
95
|
+
|
96
|
+
1.upto( diff ) do
|
97
|
+
extra = files.shift
|
98
|
+
sh "rm #{extra}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def hierarchy
|
103
|
+
%w{grandfathers fathers sons}
|
104
|
+
end
|
105
|
+
|
106
|
+
# figure out which generation today's backup is
|
107
|
+
public
|
108
|
+
def todays_generation
|
109
|
+
goes_in = promote_fathers_today? ? "grandfathers" : \
|
110
|
+
promote_sons_today? ? "fathers" : "sons"
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def place_in
|
115
|
+
goes_in = todays_generation
|
116
|
+
place_in = c[:backup_path] + "/" + goes_in
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
# Given +name+ returns a timestamped version of name.
|
121
|
+
def timestamped_prefix(name)
|
122
|
+
newname = Time.now.strftime("%Y-%m-%d-%H-%M-%S_") + File.basename(name)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the number of sons to keep. Looks for config values +:sons_to_keep+,
|
126
|
+
# +:son_promoted_on+. Default +14+.
|
127
|
+
def sons_to_keep
|
128
|
+
c[:sons_to_keep] || c[:son_promoted_on] || 14
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the number of fathers to keep. Looks for config values +:fathers_to_keep+,
|
132
|
+
# +:fathers_promoted_on+. Default +6+.
|
133
|
+
def fathers_to_keep
|
134
|
+
c[:fathers_to_keep] || c[:father_promoted_on] || 6
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns the number of grandfathers to keep. Looks for config values
|
138
|
+
# +:grandfathers_to_keep+. Default +6+.
|
139
|
+
def gfathers_to_keep
|
140
|
+
c[:grandfathers_to_keep] || 6
|
141
|
+
end
|
142
|
+
|
143
|
+
# This method returns the number of how many to keep in whatever today
|
144
|
+
# goes in. Example: if today is a day to create a +son+ then this
|
145
|
+
# function returns the value of +sons_to_keep+. If today is a +father+
|
146
|
+
# then +fathers_to_keep+ etc.
|
147
|
+
def how_many_to_keep_today
|
148
|
+
goes_in = todays_generation
|
149
|
+
keep = goes_in =~ /^sons$/ ? sons_to_keep :
|
150
|
+
goes_in =~ /^fathers$/ ? fathers_to_keep :
|
151
|
+
goes_in =~ /^grandfathers$/ ? gfathers_to_keep : 14
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# large portions borrowed from capistrano
|
2
|
+
require 'rubygems'
|
3
|
+
require 'net/ssh'
|
4
|
+
|
5
|
+
# TODO - add in a way to extend the belay script to work with this. thats a
|
6
|
+
# good idea for the belay script over all. write the kernal extensions, and the
|
7
|
+
# rake extensions. form there write an example third party extension.
|
8
|
+
module Backup
|
9
|
+
class Command
|
10
|
+
attr_reader :command, :options
|
11
|
+
attr_reader :actor
|
12
|
+
|
13
|
+
def initialize(server_name, command, callback, options, actor) #:nodoc:
|
14
|
+
@command = command.strip.gsub(/\r?\n/, "\\\n")
|
15
|
+
@callback = callback
|
16
|
+
@options = options
|
17
|
+
@actor = actor
|
18
|
+
@channels = open_channels
|
19
|
+
end
|
20
|
+
|
21
|
+
def process!
|
22
|
+
since = Time.now
|
23
|
+
loop do
|
24
|
+
active = 0
|
25
|
+
@channels.each do |ch|
|
26
|
+
next if ch[:closed]
|
27
|
+
active += 1
|
28
|
+
ch.connection.process(true)
|
29
|
+
end
|
30
|
+
|
31
|
+
break if active == 0
|
32
|
+
if Time.now - since >= 1
|
33
|
+
since = Time.now
|
34
|
+
@channels.each { |ch| ch.connection.ping! }
|
35
|
+
end
|
36
|
+
sleep 0.01 # a brief respite, to keep the CPU from going crazy
|
37
|
+
end
|
38
|
+
|
39
|
+
#logger.trace "command finished"
|
40
|
+
|
41
|
+
if failed = @channels.detect { |ch| ch[:status] != 0 }
|
42
|
+
raise "command #{@command.inspect} failed"
|
43
|
+
end
|
44
|
+
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def open_channels
|
49
|
+
channel = actor.session.open_channel do |channel|
|
50
|
+
channel.request_pty( :want_reply => true )
|
51
|
+
channel[:actor] = @actor
|
52
|
+
|
53
|
+
channel.on_success do |ch|
|
54
|
+
#logger.trace "executing command", ch[:host]
|
55
|
+
ch.exec command
|
56
|
+
ch.send_data options[:data] if options[:data]
|
57
|
+
end
|
58
|
+
|
59
|
+
channel.on_data do |ch, data|
|
60
|
+
puts data
|
61
|
+
@callback[ch, :out, data] if @callback
|
62
|
+
end
|
63
|
+
|
64
|
+
channel.on_failure do |ch|
|
65
|
+
#logger.important "could not open channel", ch[:host]
|
66
|
+
# puts "we got a faulure"
|
67
|
+
ch.close
|
68
|
+
end
|
69
|
+
|
70
|
+
channel.on_request do |ch, request, reply, data|
|
71
|
+
ch[:status] = data.read_long if request == "exit-status"
|
72
|
+
end
|
73
|
+
|
74
|
+
channel.on_close do |ch|
|
75
|
+
ch[:closed] = true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
[channel]
|
79
|
+
end
|
80
|
+
end # end Class Command
|
81
|
+
|
82
|
+
class SshActor
|
83
|
+
|
84
|
+
#def self.new_for_ssh(server_name)
|
85
|
+
# a = new(server_name)
|
86
|
+
#end
|
87
|
+
|
88
|
+
attr_reader :session
|
89
|
+
attr_reader :config
|
90
|
+
alias_method :c, :config
|
91
|
+
|
92
|
+
def initialize(config)
|
93
|
+
@config = config
|
94
|
+
end
|
95
|
+
|
96
|
+
def connect
|
97
|
+
c[:servers].each do |server| # todo, make this actually work
|
98
|
+
@session = Net::SSH.start(
|
99
|
+
server,
|
100
|
+
c[:ssh_user],
|
101
|
+
:host_key => "ssh-rsa",
|
102
|
+
:keys => [ c[:identity_key] ],
|
103
|
+
:auth_methods => %w{ publickey } )
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def run(cmd, options={}, &block)
|
108
|
+
#logger.debug "executing #{cmd.strip.inspect}"
|
109
|
+
puts "executing #{cmd.strip.inspect}"
|
110
|
+
command = Command.new(@server_name, cmd, block, options, self)
|
111
|
+
command.process! # raises an exception if command fails on any server
|
112
|
+
end
|
113
|
+
|
114
|
+
def on_remote(&block)
|
115
|
+
connect
|
116
|
+
self.instance_eval(&block)
|
117
|
+
close
|
118
|
+
end
|
119
|
+
|
120
|
+
def close
|
121
|
+
@session.close
|
122
|
+
end
|
123
|
+
|
124
|
+
def verify_directory_exists(dir)
|
125
|
+
run "if [ -d '#{dir}' ]; then true; else mkdir -p '#{dir}'; fi"
|
126
|
+
end
|
127
|
+
|
128
|
+
def cleanup_directory(dir, keep)
|
129
|
+
puts "Cleaning up"
|
130
|
+
cleanup = <<-END
|
131
|
+
LOOK_IN="#{dir}"; COUNT=`ls -1 $LOOK_IN | wc -l`; MAX=#{keep}; if (( $COUNT > $MAX )); then let "OFFSET=$COUNT-$MAX"; i=1; for f in `ls -1 $LOOK_IN | sort`; do if (( $i <= $OFFSET )); then CMD="rm $LOOK_IN/$f"; echo $CMD; $CMD; fi; let "i=$i + 1"; done; else true; fi
|
132
|
+
END
|
133
|
+
run cleanup # todo make it so that even this can be overridden
|
134
|
+
end
|
135
|
+
|
136
|
+
end # end class SshActor
|
137
|
+
|
138
|
+
end # end Module Backup
|
data/lib/backup.rb
ADDED
data/tests/actor_test.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require File.dirname(__FILE__) + "/tests_helper"
|
3
|
+
|
4
|
+
class ActorTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@config = Backup::Configuration.new
|
7
|
+
@config.load "standard"
|
8
|
+
@actor = @config.actor
|
9
|
+
setup_tmp_backup_dir
|
10
|
+
end
|
11
|
+
|
12
|
+
def dont_test_exists
|
13
|
+
assert @actor
|
14
|
+
end
|
15
|
+
|
16
|
+
def dont_test_is_file
|
17
|
+
dir = create_tmp_files
|
18
|
+
config = <<-END
|
19
|
+
action :content, :is_file => "#{dir}/1"
|
20
|
+
END
|
21
|
+
@config.load :string => config
|
22
|
+
assert result = @actor.content
|
23
|
+
assert File.exists?(result)
|
24
|
+
puts "content result is: #{result}"
|
25
|
+
@actor.start_process!
|
26
|
+
end
|
27
|
+
|
28
|
+
def dont_test_is_folder
|
29
|
+
dir = create_tmp_files
|
30
|
+
config = <<-END
|
31
|
+
action :content, :is_folder => "#{dir}"
|
32
|
+
END
|
33
|
+
@config.load :string => config
|
34
|
+
assert result = @actor.content
|
35
|
+
assert File.exists?(result)
|
36
|
+
assert File.directory?(result)
|
37
|
+
puts "content result is: #{result}"
|
38
|
+
@actor.start_process!
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_is_contents_of
|
42
|
+
dir = create_tmp_files
|
43
|
+
config = <<-END
|
44
|
+
action :content, :is_contents_of => "#{dir}", :copy => true
|
45
|
+
END
|
46
|
+
@config.load :string => config
|
47
|
+
#@actor.content
|
48
|
+
#@actor.cleanup
|
49
|
+
@actor.start_process!
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def setup_tmp_backup_dir
|
54
|
+
newtmp = @config[:tmp_dir] + "/backup_#{rand}_" + Time.now.strftime("%Y%m%d%H%M%S")
|
55
|
+
sh "mkdir #{newtmp}"
|
56
|
+
config = <<-END
|
57
|
+
set :backup_path, "#{newtmp}"
|
58
|
+
END
|
59
|
+
@config.load :string => config
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_tmp_files
|
63
|
+
newtmp = @config[:tmp_dir] + "/test_#{rand}_" + Time.now.strftime("%Y%m%d%H%M%S")
|
64
|
+
sh "mkdir #{newtmp}"
|
65
|
+
0.upto(5) { |i| sh "touch #{newtmp}/#{i}" }
|
66
|
+
newtmp
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
data/tests/cleanup.sh
ADDED
@@ -0,0 +1,2 @@
|
|
1
|
+
#!/usr/bin/bash
|
2
|
+
LOOK_IN="/var/local/backups/mediawiki/sons"; COUNT=`ls -1 $LOOK_IN | wc -l`; MAX=14; if (( $COUNT > $MAX )); then let "OFFSET=$COUNT-$MAX"; i=1; for f in `ls -1 $LOOK_IN | sort`; do if (( $i <= $OFFSET )); then CMD="rm $LOOK_IN/$f"; echo $CMD; $CMD; fi; echo "$i $f"; let "i=$i + 1"; done; else true; fi
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require File.dirname(__FILE__) + "/tests_helper"
|
3
|
+
require 'runt'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
class RotationTest < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
# setup our config to test the objects
|
9
|
+
@config = Backup::Configuration.new
|
10
|
+
@config.load "standard"
|
11
|
+
@rotator = Backup::Rotator.new(@config.actor)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_rotator
|
15
|
+
t = Date.today
|
16
|
+
r = @rotator
|
17
|
+
0.upto(14) do |i|
|
18
|
+
$test_time = t
|
19
|
+
print t.to_s + t.strftime(" #{i} %a ").to_s
|
20
|
+
puts r.todays_generation
|
21
|
+
t += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_date_parsing
|
26
|
+
dp = Backup::DateParser.new
|
27
|
+
assert fri = dp.date_from(:fri)
|
28
|
+
assert daily = dp.date_from(:daily)
|
29
|
+
assert last = dp.date_from(:last_mon_of_the_month)
|
30
|
+
assert_raise(RuntimeError) { dp.date_from(:asdfasdf) }
|
31
|
+
end
|
32
|
+
end
|
data/tests/ssh_test.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/tests_helper"
|
2
|
+
|
3
|
+
class SSHTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@config = Backup::Configuration.new
|
6
|
+
@config.load "standard"
|
7
|
+
@config.action :deliver, :method => :via_ssh
|
8
|
+
@actor = Backup::SshActor.new(@config)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_exists
|
12
|
+
assert @config
|
13
|
+
assert @actor
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_on_remote
|
17
|
+
@actor.on_remote do
|
18
|
+
run "echo \"hello $HOSTNAME\""
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: backupgem
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.2
|
7
|
+
date: 2006-10-06 00:00:00 -07:00
|
8
|
+
summary: Beginning-to-end solution for backups and rotation.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: nate@natemurray.com
|
12
|
+
homepage: http://tech.natemurray.com/backup
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: backupgem
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Nate Murray
|
31
|
+
files:
|
32
|
+
- bin/backup
|
33
|
+
- bin/commands.sh
|
34
|
+
- lib/backup
|
35
|
+
- lib/backup.rb
|
36
|
+
- lib/backup/actor.rb
|
37
|
+
- lib/backup/cli.rb
|
38
|
+
- lib/backup/configuration.rb
|
39
|
+
- lib/backup/date_parser.rb
|
40
|
+
- lib/backup/extensions.rb
|
41
|
+
- lib/backup/recipes
|
42
|
+
- lib/backup/rotator.rb
|
43
|
+
- lib/backup/ssh_helpers.rb
|
44
|
+
- lib/backup/recipes/standard.rb
|
45
|
+
- tests/actor_test.rb
|
46
|
+
- tests/cleanup.sh
|
47
|
+
- tests/rotation_test.rb
|
48
|
+
- tests/ssh_test.rb
|
49
|
+
- tests/tests_helper.rb
|
50
|
+
- examples/global.rb
|
51
|
+
- examples/mediawiki.rb
|
52
|
+
- doc/index.html
|
53
|
+
- doc/LICENSE-GPL
|
54
|
+
- doc/styles.css
|
55
|
+
- README
|
56
|
+
- CHANGELOG
|
57
|
+
test_files:
|
58
|
+
- tests/actor_test.rb
|
59
|
+
- tests/rotation_test.rb
|
60
|
+
- tests/ssh_test.rb
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
extra_rdoc_files:
|
64
|
+
- README
|
65
|
+
- CHANGELOG
|
66
|
+
executables: []
|
67
|
+
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
dependencies:
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rake
|
75
|
+
version_requirement:
|
76
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 0.7.1
|
81
|
+
version:
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: runt
|
84
|
+
version_requirement:
|
85
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.3.0
|
90
|
+
version:
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: net-ssh
|
93
|
+
version_requirement:
|
94
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: 1.0.9
|
99
|
+
version:
|