dated_backup 0.1.0 → 0.2.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/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
|
-
|