paparazzi 0.2.0 → 0.2.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/README.md +6 -6
- data/lib/paparazzi/camera.rb +43 -37
- data/lib/paparazzi/version.rb +1 -1
- data/test/unit/camera_test.rb +15 -2
- metadata +3 -3
data/README.md
CHANGED
@@ -39,14 +39,14 @@ Create a ruby script that you'll run hourly from a cron.
|
|
39
39
|
Available Settings
|
40
40
|
------------------
|
41
41
|
|
42
|
-
* `:source`
|
42
|
+
* `:source` **required** The source folder to be backed up. Trailing '/' recommended. See rsync manpage
|
43
43
|
for explanation of trailing '/'
|
44
|
-
* `:destination`
|
44
|
+
* `:destination` **required** The destination folder for backups to be written to, preferably on a different
|
45
45
|
physical drive.
|
46
|
-
* `:
|
47
|
-
default: {:hourly => 24, :daily => 7, :weekly => 5, :monthly => 12, :yearly => 9999}
|
48
|
-
* `:rsync_flags`
|
49
|
-
whatever you add. The author suggests considering
|
46
|
+
* `:intervals` A hash of snapshot intervals and number of snapshots of each to keep before purging.
|
47
|
+
default: `{:hourly => 24, :daily => 7, :weekly => 5, :monthly => 12, :yearly => 9999}`
|
48
|
+
* `:rsync_flags` Additional flags to pass to rsync. Paparazzi uses `-aq`, `--delete`, & `--link_dest`, plus
|
49
|
+
whatever you add. The author suggests considering `-L` and `--exclude`.
|
50
50
|
|
51
51
|
|
52
52
|
Supported Operating Systems
|
data/lib/paparazzi/camera.rb
CHANGED
@@ -8,7 +8,7 @@ module Paparazzi
|
|
8
8
|
REQUIRED_SETTINGS = [:source,:destination]
|
9
9
|
|
10
10
|
class << self
|
11
|
-
attr_accessor :source, :destination, :rsync_flags, :
|
11
|
+
attr_accessor :source, :destination, :rsync_flags, :intervals
|
12
12
|
|
13
13
|
def trigger(settings = {})
|
14
14
|
validate_and_cache_settings(settings)
|
@@ -25,7 +25,7 @@ module Paparazzi
|
|
25
25
|
#######
|
26
26
|
|
27
27
|
def validate_and_cache_settings(settings)
|
28
|
-
[:source,:destination,:rsync_flags,:reserves].each do |setting_name|
|
28
|
+
[:source,:destination,:rsync_flags,:intervals,:reserves].each do |setting_name|
|
29
29
|
if REQUIRED_SETTINGS.include?(setting_name) and settings[setting_name].nil?
|
30
30
|
raise MissingSettingError, "#{setting_name} is required"
|
31
31
|
else
|
@@ -42,21 +42,21 @@ module Paparazzi
|
|
42
42
|
|
43
43
|
def setup
|
44
44
|
@previous_snapshot_name = {}
|
45
|
-
|
46
|
-
Dir.mkdir(destination(
|
45
|
+
interval_names.each do |interval_name|
|
46
|
+
Dir.mkdir(destination(interval_name)) unless File.exists?(destination(interval_name)) && File.directory?(destination(interval_name))
|
47
47
|
|
48
|
-
full_path = Dir[destination(
|
49
|
-
@previous_snapshot_name[
|
48
|
+
full_path = Dir[destination(interval_name)+'/*'].sort{|a,b| File.ctime(b) <=> File.ctime(a) }.first
|
49
|
+
@previous_snapshot_name[interval_name] = full_path ? File.basename(full_path) : ''
|
50
50
|
end
|
51
51
|
|
52
|
-
if @previous_snapshot_name[
|
53
|
-
File.rename(previous_snapshot(
|
54
|
-
@previous_snapshot_name[
|
52
|
+
if @previous_snapshot_name[interval_names.first] != last_successful_snapshot and !last_successful_snapshot.nil? and File.exists?(destination(interval_names.first) + '/' + last_successful_snapshot)
|
53
|
+
File.rename(previous_snapshot(interval_names.first), current_snapshot(interval_names.first))
|
54
|
+
@previous_snapshot_name[interval_names.first] = last_successful_snapshot
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def destination(
|
59
|
-
|
58
|
+
def destination(interval_name = nil)
|
59
|
+
interval_name.nil? ? @destination : "#{@destination}/#{interval_name}"
|
60
60
|
end
|
61
61
|
|
62
62
|
def last_successful_snapshot
|
@@ -70,35 +70,34 @@ module Paparazzi
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def purge_old_snapshots
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
full_path = Dir[destination(frequency)+'/*'].sort{|a,b| File.ctime(a) <=> File.ctime(b) }.first
|
73
|
+
interval_names.each do |interval_name|
|
74
|
+
while Dir[destination(interval_name)+'/*'].size > (intervals[interval_name]-1)
|
75
|
+
full_path = Dir[destination(interval_name)+'/*'].sort{|a,b| File.ctime(a) <=> File.ctime(b) }.first
|
77
76
|
FileUtils.rm_rf(full_path)
|
78
77
|
end
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
82
81
|
def make_snapshots
|
83
|
-
|
84
|
-
Dir.mkdir(current_snapshot(
|
85
|
-
if
|
86
|
-
system 'rsync', *(['-aq', '--delete'] + rsync_flags + [source, current_snapshot(
|
87
|
-
self.last_successful_snapshot = current_snapshot_name(
|
88
|
-
elsif previous_snapshot_name(
|
89
|
-
system 'rsync', *(['-aq', '--delete', "--link-dest=#{link_destination(
|
90
|
-
self.last_successful_snapshot = current_snapshot_name(
|
82
|
+
interval_names.each do |interval_name|
|
83
|
+
Dir.mkdir(current_snapshot(interval_name)) unless File.exists?(current_snapshot(interval_name))
|
84
|
+
if interval_name == interval_names.first and previous_snapshot_name(interval_name) == ''
|
85
|
+
system 'rsync', *(['-aq', '--delete'] + rsync_flags + [source, current_snapshot(interval_name)])
|
86
|
+
self.last_successful_snapshot = current_snapshot_name(interval_name)
|
87
|
+
elsif previous_snapshot_name(interval_name) != current_snapshot_name(interval_name)
|
88
|
+
system 'rsync', *(['-aq', '--delete', "--link-dest=#{link_destination(interval_name)}"] + rsync_flags + [source, current_snapshot(interval_name)])
|
89
|
+
self.last_successful_snapshot = current_snapshot_name(interval_names.first)
|
91
90
|
end
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
95
|
-
def current_snapshot(
|
96
|
-
destination(
|
94
|
+
def current_snapshot(interval_name)
|
95
|
+
destination(interval_name) + '/' + current_snapshot_name(interval_name)
|
97
96
|
end
|
98
97
|
|
99
|
-
def current_snapshot_name(
|
98
|
+
def current_snapshot_name(interval_name)
|
100
99
|
@start_time ||= Time.now #lock in time so that all results stay consistent over long runs
|
101
|
-
case
|
100
|
+
case interval_name
|
102
101
|
when :hourly then @start_time.strftime('%Y-%m-%d.%H')
|
103
102
|
when :daily then @start_time.strftime('%Y-%m-%d')
|
104
103
|
when :weekly then sprintf("%04d-%02d-week-%02d", @start_time.year, @start_time.month, (@start_time.day/7))
|
@@ -107,28 +106,35 @@ module Paparazzi
|
|
107
106
|
end
|
108
107
|
end
|
109
108
|
|
110
|
-
def previous_snapshot(
|
111
|
-
destination(
|
109
|
+
def previous_snapshot(interval_name)
|
110
|
+
destination(interval_name) + '/' + previous_snapshot_name(interval_name)
|
112
111
|
end
|
113
112
|
|
114
|
-
def previous_snapshot_name(
|
115
|
-
@previous_snapshot_name[
|
113
|
+
def previous_snapshot_name(interval_name)
|
114
|
+
@previous_snapshot_name[interval_name]
|
116
115
|
end
|
117
116
|
|
118
|
-
def link_destination(
|
119
|
-
|
117
|
+
def link_destination(interval_name)
|
118
|
+
interval_name == interval_names.first ? "../#{previous_snapshot_name(interval_names.first)}" : "../../#{interval_names.first}/#{current_snapshot_name(interval_names.first)}"
|
120
119
|
end
|
121
120
|
|
122
|
-
def
|
123
|
-
@
|
121
|
+
def intervals
|
122
|
+
@intervals ||= {:hourly => 24, :daily => 7, :weekly => 5, :monthly => 12, :yearly => 9999}
|
124
123
|
end
|
125
124
|
|
126
|
-
def
|
127
|
-
|
125
|
+
def interval_names
|
126
|
+
intervals.keys.select{ |key| intervals[key] > 0 }.sort{ |a,b|
|
128
127
|
[:hourly,:daily,:weekly,:monthly,:yearly].find_index(a) <=> [:hourly,:daily,:weekly,:monthly,:yearly].find_index(b)
|
129
128
|
}
|
130
129
|
end
|
131
130
|
|
131
|
+
# provide backwards compatability with silly setting name
|
132
|
+
def reserves=(x)
|
133
|
+
if x.is_a?(Hash)
|
134
|
+
$stderr.puts ':reserves is deprecated. Please use :intervals instead.'
|
135
|
+
self.intervals=x
|
136
|
+
end
|
137
|
+
end
|
132
138
|
end
|
133
139
|
end
|
134
140
|
|
data/lib/paparazzi/version.rb
CHANGED
data/test/unit/camera_test.rb
CHANGED
@@ -107,11 +107,24 @@ class CameraTest < Test::Unit::TestCase
|
|
107
107
|
|
108
108
|
def test_should_not_make_un_requested_frequency_snapshots
|
109
109
|
my_settings = default_test_settings
|
110
|
-
my_settings[:
|
110
|
+
my_settings[:intervals][:hourly] = 0
|
111
111
|
Paparazzi::Camera.trigger(my_settings)
|
112
112
|
assert(!File.exists?("#{destination}/hourly"))
|
113
113
|
end
|
114
114
|
|
115
|
+
def test_should_be_backwards_compatible_with_deprecated_config_option
|
116
|
+
original_stderr, $stderr = $stderr, StringIO.new
|
117
|
+
my_settings = default_test_settings
|
118
|
+
my_settings[:reserves] = my_settings[:intervals]
|
119
|
+
my_settings[:reserves][:hourly] = 1
|
120
|
+
my_settings.delete(:intervals)
|
121
|
+
Paparazzi::Camera.trigger(my_settings)
|
122
|
+
assert_equal(my_settings[:reserves],Paparazzi::Camera.instance_variable_get('@intervals'))
|
123
|
+
assert_equal(':reserves is deprecated. Please use :intervals instead.',$stderr.string.chomp)
|
124
|
+
ensure
|
125
|
+
$stderr = original_stderr
|
126
|
+
end
|
127
|
+
|
115
128
|
#######
|
116
129
|
private
|
117
130
|
#######
|
@@ -124,7 +137,7 @@ class CameraTest < Test::Unit::TestCase
|
|
124
137
|
{
|
125
138
|
:source => "#{File.expand_path('../../source', __FILE__)}/",
|
126
139
|
:destination => destination,
|
127
|
-
:
|
140
|
+
:intervals => {:hourly => 24, :daily => 7, :weekly => 5, :monthly => 12, :yearly => 9999},
|
128
141
|
:rsync_flags => '-L --exclude test.exclude'
|
129
142
|
|
130
143
|
}
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 1
|
9
|
+
version: 0.2.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jonathan S. Garvin
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-05-
|
17
|
+
date: 2011-05-28 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|