dated_backup 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +40 -0
- data/COPYRIGHT +621 -0
- data/README +186 -0
- data/RELEASES +21 -0
- data/bin/dbackup +22 -0
- data/{example_scripts/example.com.rb → example_configs/example.com} +4 -10
- data/example_configs/local_etc_backup +10 -0
- data/{example_scripts/samba_shares.rb → example_configs/samba_shares} +14 -18
- data/lib/dated_backup/core/backup_remover.rb +43 -0
- data/lib/dated_backup/core/backup_set.rb +118 -0
- data/lib/dated_backup/core/command_line.rb +11 -0
- data/lib/dated_backup/core/dated_backup.rb +71 -0
- data/lib/dated_backup/core/tasks.rb +44 -0
- data/lib/dated_backup/core/warnings.rb +15 -0
- data/lib/dated_backup/core.rb +6 -0
- data/lib/dated_backup/dsl/execution_context.rb +56 -0
- data/lib/dated_backup/dsl/main.rb +41 -0
- data/lib/dated_backup/dsl/time_extensions.rb +122 -0
- data/lib/dated_backup/dsl.rb +4 -0
- data/lib/dated_backup/extensions/array.rb +27 -0
- data/lib/dated_backup/extensions/error.rb +10 -0
- data/lib/dated_backup/extensions/string.rb +8 -0
- data/lib/dated_backup/extensions/time.rb +50 -0
- data/lib/dated_backup/extensions/time_symbol.rb +54 -0
- data/lib/dated_backup/extensions.rb +6 -0
- data/lib/dated_backup.rb +21 -1
- metadata +52 -15
- data/example_scripts/local_etc_backup.rb +0 -10
- data/lib/dated_backup/command_line.rb +0 -8
- data/lib/dated_backup/dated_backup.rb +0 -80
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module DatedBackup
|
3
|
+
class DSL
|
4
|
+
module Main
|
5
|
+
|
6
|
+
attr_reader :hash, :procs
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@hash = {}
|
10
|
+
@procs = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def before &blk
|
14
|
+
raise_without_block &blk
|
15
|
+
@procs[:before] = blk
|
16
|
+
end
|
17
|
+
|
18
|
+
def after &blk
|
19
|
+
raise_without_block &blk
|
20
|
+
@procs[:after] = blk
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing sym, *args
|
24
|
+
if RSYNC_OPTIONS.include?(sym)
|
25
|
+
@hash[sym] = args
|
26
|
+
else
|
27
|
+
raise InvalidKeyError, "The key '#{sym}' is not a recognized expression"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def raise_without_block &blk
|
34
|
+
raise NoBlockGiven, "A block (do...end) must be given" if !block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
RSYNC_OPTIONS = [:source, :sources, :destination, :options, :user_domain]
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
|
2
|
+
module DatedBackup
|
3
|
+
class DSL
|
4
|
+
|
5
|
+
# After a run through the DSL,
|
6
|
+
# the TimeExtensions#kept method should contain an array
|
7
|
+
# of all of the items to keep. Each item will be a hash
|
8
|
+
# with two keys: a :constraint key, which contains a
|
9
|
+
# range of Times (from oldest to newest), and a :scope
|
10
|
+
# key, which will indicate whether the backup to look
|
11
|
+
# at is a weekly, monthly, etc. If no :scope key is given,
|
12
|
+
# then all of the backups in the time range given by
|
13
|
+
# the :constraint key will be assumed to be kept.
|
14
|
+
module TimeExtensions
|
15
|
+
|
16
|
+
attr_reader :last_time, :time_range
|
17
|
+
attr_reader :kept
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@kept = []
|
21
|
+
@time_range = {}
|
22
|
+
@last_time = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def backup *args; return self; end
|
26
|
+
def from *args; return self; end
|
27
|
+
|
28
|
+
alias :backups :backup
|
29
|
+
|
30
|
+
TimeSymbol.valid_symbols.each do |sym|
|
31
|
+
time_sym = TimeSymbol.new sym
|
32
|
+
|
33
|
+
define_method time_sym.singular do |*args|
|
34
|
+
self.send :time_component, time_sym.singular
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method time_sym.plural, time_sym.singular
|
38
|
+
|
39
|
+
define_method time_sym.adverb do
|
40
|
+
@time_range[:scope] = time_sym.adverb
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def this arg, now=Time.now
|
46
|
+
set_time_range :this, now
|
47
|
+
end
|
48
|
+
|
49
|
+
def last arg, now=Time.now
|
50
|
+
set_time_range :last, now
|
51
|
+
end
|
52
|
+
|
53
|
+
def keep arg, now=Time.now
|
54
|
+
all(self, now) unless time_range[:constraint]
|
55
|
+
|
56
|
+
@kept << time_range
|
57
|
+
reset_times
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def all arg=self, now=Time.now
|
62
|
+
@time_range[:constraint] = Time.epoch...now
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
|
69
|
+
def set_time_range sym, now
|
70
|
+
if sym == :last
|
71
|
+
year, month, week, day = now.last_year, now.last_month, now.last_week, now.yesterday
|
72
|
+
elsif sym == :this
|
73
|
+
add_end_of_star_singletons(now)
|
74
|
+
|
75
|
+
# This is another little trick. Normally we would just call the methods
|
76
|
+
# below something like: Time.now.beginning_of_year...Time.now.end_of_year
|
77
|
+
year, month, week, day = now, now, now, now
|
78
|
+
end
|
79
|
+
|
80
|
+
@time_range[:constraint] =
|
81
|
+
case last_time
|
82
|
+
when :year
|
83
|
+
year.beginning_of_year...year.end_of_year
|
84
|
+
when :month
|
85
|
+
month.beginning_of_month...month.end_of_month
|
86
|
+
when :week
|
87
|
+
week.beginning_of_week...week.end_of_week
|
88
|
+
when :day
|
89
|
+
day.beginning_of_day...day.end_of_day
|
90
|
+
end
|
91
|
+
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# This method adds the end_of_* methods to the object passed in. They are used in the default case
|
96
|
+
# of the modifyer being "this" and not "last". In the 'this' case, the time
|
97
|
+
# *is* now, so end_of_* (end_of_year, end_of_day), etc. should *NOT* need to be called
|
98
|
+
# (and in fact, we do not want them to be called, because that would represent
|
99
|
+
# some time period in the future). Those methods are overrided to return self
|
100
|
+
def add_end_of_star_singletons(now)
|
101
|
+
class << now
|
102
|
+
[:end_of_year, :end_of_month, :end_of_week, :end_of_day].each do |end_of|
|
103
|
+
define_method end_of do
|
104
|
+
return self
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def time_component(type)
|
111
|
+
@last_time = type
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
def reset_times
|
116
|
+
@time_range = {}
|
117
|
+
@last_time = nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
def car
|
4
|
+
first
|
5
|
+
end
|
6
|
+
|
7
|
+
def cdr
|
8
|
+
self.[](1..self.length)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_backup_set
|
12
|
+
DatedBackup::Core::BackupSet.new(self)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# A subclass of Array, but it calls uniq!, sort!, and reverse!
|
17
|
+
# (in that order) after the instance is created
|
18
|
+
class ReverseSortedUniqueArray < Array
|
19
|
+
|
20
|
+
def initialize *args, &blk
|
21
|
+
super *args, &blk
|
22
|
+
uniq!
|
23
|
+
sort!
|
24
|
+
reverse!
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
class TimeSymbolError < RuntimeError; end
|
3
|
+
|
4
|
+
class DatedBackupError < RuntimeError; end
|
5
|
+
|
6
|
+
class DirectoryError < DatedBackupError; end
|
7
|
+
class InvalidDirectoryError < DirectoryError; end
|
8
|
+
class NoBlockGiven < DatedBackupError; end
|
9
|
+
class InvalidKeyError < DatedBackupError; end
|
10
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'active_support/core_ext/time/calculations'
|
2
|
+
require 'active_support/core_ext/numeric/time'
|
3
|
+
|
4
|
+
class Time
|
5
|
+
class << self
|
6
|
+
include ActiveSupport::CoreExtensions::Time::Calculations::ClassMethods
|
7
|
+
|
8
|
+
def epoch
|
9
|
+
Time.at(0)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
include ActiveSupport::CoreExtensions::Time::Calculations
|
15
|
+
|
16
|
+
def end_of_day
|
17
|
+
tomorrow.beginning_of_day - 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def end_of_month
|
21
|
+
next_month.beginning_of_month - 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def end_of_year
|
25
|
+
next_year.beginning_of_year - 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def end_of_week
|
29
|
+
next_week.beginning_of_week - 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def last_week
|
33
|
+
1.week.ago.beginning_of_week
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :at_end_of_week :end_of_week
|
37
|
+
alias :at_end_of_month :end_of_month
|
38
|
+
alias :at_end_of_day :end_of_day
|
39
|
+
alias :at_end_of_year :end_of_year
|
40
|
+
|
41
|
+
def week
|
42
|
+
beginning_of_week...end_of_week
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class Fixnum
|
48
|
+
include ActiveSupport::CoreExtensions::Numeric::Time
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
# Used to change the the TimeSymbols (:year, :month, :day, and :week) into the
|
3
|
+
# various natural language forms (the symbols singular, plural, and adverb).
|
4
|
+
# Initialize with the singular symbol name, or call TimeSymbol.valid_symbols
|
5
|
+
# to give an array of valid symbols.
|
6
|
+
class TimeSymbol
|
7
|
+
VALID_TIME_COMPONENTS = [:year, :month, :week, :day]
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def all
|
11
|
+
VALID_TIME_COMPONENTS.dup
|
12
|
+
end
|
13
|
+
|
14
|
+
alias :valid_symbols :all
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(sym)
|
18
|
+
if VALID_TIME_COMPONENTS.include? sym
|
19
|
+
@sym = sym
|
20
|
+
else
|
21
|
+
raise TimeSymbolError, "The symbol given must be a valid TimeSymbol (:year, :month, :week, or :day)"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def singular
|
26
|
+
@sym
|
27
|
+
end
|
28
|
+
|
29
|
+
def plural
|
30
|
+
"#{@sym}s".to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
def adverb
|
34
|
+
if @sym == :day
|
35
|
+
:daily
|
36
|
+
else
|
37
|
+
"#{@sym}ly".to_sym
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_sym
|
42
|
+
@sym.to_sym
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
@sym.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
@sym.inspect
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
TimeSymbols = TimeSymbol.all
|
@@ -0,0 +1,6 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + "/extensions/array"
|
3
|
+
require File.dirname(__FILE__) + "/extensions/time"
|
4
|
+
require File.dirname(__FILE__) + "/extensions/error"
|
5
|
+
require File.dirname(__FILE__) + "/extensions/time_symbol"
|
6
|
+
require File.dirname(__FILE__) + "/extensions/string"
|
data/lib/dated_backup.rb
CHANGED
@@ -1,2 +1,22 @@
|
|
1
|
+
# DatedBackup, A snapshot backup utility
|
2
|
+
# Copyright (C) Scott Taylor (scott@railsnewbie.com), 2007
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
1
17
|
|
2
|
-
|
18
|
+
|
19
|
+
require 'rubygems'
|
20
|
+
require File.dirname(__FILE__) + "/dated_backup/extensions"
|
21
|
+
require File.dirname(__FILE__) + "/dated_backup/dsl"
|
22
|
+
require File.dirname(__FILE__) + "/dated_backup/core"
|
metadata
CHANGED
@@ -3,9 +3,9 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: dated_backup
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
8
|
-
summary: Incremental Backups Using Rsync
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-08-02 00:00:00 -04:00
|
8
|
+
summary: Incremental Dated Backups Using Rsync
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: scott@railsnewbie.com
|
@@ -15,7 +15,7 @@ description:
|
|
15
15
|
autorequire:
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
18
|
-
has_rdoc:
|
18
|
+
has_rdoc: true
|
19
19
|
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
20
|
requirements:
|
21
21
|
- - ">"
|
@@ -29,24 +29,61 @@ post_install_message:
|
|
29
29
|
authors:
|
30
30
|
- Scott Taylor
|
31
31
|
files:
|
32
|
+
- bin/dbackup
|
32
33
|
- lib/dated_backup
|
33
|
-
- lib/dated_backup/
|
34
|
-
- lib/dated_backup/
|
34
|
+
- lib/dated_backup/core
|
35
|
+
- lib/dated_backup/core/backup_remover.rb
|
36
|
+
- lib/dated_backup/core/backup_set.rb
|
37
|
+
- lib/dated_backup/core/command_line.rb
|
38
|
+
- lib/dated_backup/core/dated_backup.rb
|
39
|
+
- lib/dated_backup/core/tasks.rb
|
40
|
+
- lib/dated_backup/core/warnings.rb
|
41
|
+
- lib/dated_backup/core.rb
|
42
|
+
- lib/dated_backup/dsl
|
43
|
+
- lib/dated_backup/dsl/execution_context.rb
|
44
|
+
- lib/dated_backup/dsl/main.rb
|
45
|
+
- lib/dated_backup/dsl/time_extensions.rb
|
46
|
+
- lib/dated_backup/dsl.rb
|
47
|
+
- lib/dated_backup/extensions
|
48
|
+
- lib/dated_backup/extensions/array.rb
|
49
|
+
- lib/dated_backup/extensions/error.rb
|
50
|
+
- lib/dated_backup/extensions/string.rb
|
51
|
+
- lib/dated_backup/extensions/time.rb
|
52
|
+
- lib/dated_backup/extensions/time_symbol.rb
|
53
|
+
- lib/dated_backup/extensions.rb
|
35
54
|
- lib/dated_backup.rb
|
36
|
-
-
|
37
|
-
-
|
38
|
-
-
|
55
|
+
- example_configs/example.com
|
56
|
+
- example_configs/local_etc_backup
|
57
|
+
- example_configs/samba_shares
|
58
|
+
- README
|
59
|
+
- COPYRIGHT
|
60
|
+
- RELEASES
|
61
|
+
- CHANGELOG
|
39
62
|
test_files: []
|
40
63
|
|
41
64
|
rdoc_options: []
|
42
65
|
|
43
|
-
extra_rdoc_files:
|
44
|
-
|
45
|
-
|
46
|
-
|
66
|
+
extra_rdoc_files:
|
67
|
+
- example_configs/example.com
|
68
|
+
- example_configs/local_etc_backup
|
69
|
+
- example_configs/samba_shares
|
70
|
+
- README
|
71
|
+
- COPYRIGHT
|
72
|
+
- RELEASES
|
73
|
+
- CHANGELOG
|
74
|
+
executables:
|
75
|
+
- dbackup
|
47
76
|
extensions: []
|
48
77
|
|
49
78
|
requirements: []
|
50
79
|
|
51
|
-
dependencies:
|
52
|
-
|
80
|
+
dependencies:
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: activesupport
|
83
|
+
version_requirement:
|
84
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 1.4.2
|
89
|
+
version:
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + "/command_line"
|
2
|
-
|
3
|
-
class DatedBackup
|
4
|
-
|
5
|
-
include DatedBackup::CommandLine
|
6
|
-
|
7
|
-
attr_accessor :source, :destination, :opts, :backup_root, :user_domain
|
8
|
-
|
9
|
-
def initialize(h={})
|
10
|
-
@backup_root = h[:destination]
|
11
|
-
@opts = h[:options] || ""
|
12
|
-
@user_domain = h[:user_domain]
|
13
|
-
@destination = generate_backup_filename
|
14
|
-
|
15
|
-
@sources = h[:sources] || [h[:source]]
|
16
|
-
|
17
|
-
if @user_domain
|
18
|
-
@sources.map! { |src| "#{@user_domain}:#{src}" }
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
# create the first backup, if non-existent
|
24
|
-
# otherwise cp -al (or # replace cp -al a b with cd a && find . -print | cpio -dpl ../b )
|
25
|
-
# and then create the backup of the dirs using rsync -a --delete
|
26
|
-
# the files, in the end, should be read only and undeletable
|
27
|
-
def run
|
28
|
-
create_main_backup_directory
|
29
|
-
cp_last_backup_to_new_backup unless Dir.glob("#{@backup_root}/*").empty?
|
30
|
-
create_backup
|
31
|
-
#set_permissions(dest)
|
32
|
-
end
|
33
|
-
|
34
|
-
def create_backup
|
35
|
-
@sources.each do |source|
|
36
|
-
cmd = "rsync -a --delete #{opts} #{source} #{@destination}"
|
37
|
-
execute(cmd)
|
38
|
-
puts "\n\n"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def cp_last_backup_to_new_backup
|
43
|
-
last_backup_dir = find_last_backup_filename
|
44
|
-
|
45
|
-
cmd = "cp -al #{last_backup_dir} #{@destination}"
|
46
|
-
execute(cmd)
|
47
|
-
end
|
48
|
-
|
49
|
-
def create_main_backup_directory
|
50
|
-
begin
|
51
|
-
unless File.exists? backup_root
|
52
|
-
puts "* Creating main backup directory #{backup_root}"
|
53
|
-
Dir.mkdir backup_root
|
54
|
-
end
|
55
|
-
rescue Errno::EACCES => e
|
56
|
-
puts "-- Exiting script because main directory could not be created. \n Error Message: #{e}"
|
57
|
-
exit
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
#def set_permissions(file, permission="400", user="root", group="root")
|
62
|
-
# cmd = "chmod -R #{permission} #{file}"
|
63
|
-
# execute(cmd)
|
64
|
-
# cmd = "chown -R #{user}:#{group} #{file}"
|
65
|
-
# execute(cmd)
|
66
|
-
#end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def generate_backup_filename
|
71
|
-
timestamp = Time.now.strftime "%Y-%m-%d-%Hh-%Mm-%Ss"
|
72
|
-
"#{@backup_root}/#{timestamp}"
|
73
|
-
end
|
74
|
-
|
75
|
-
def find_last_backup_filename
|
76
|
-
Dir.glob("#{@backup_root}/*").sort.last
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
|