phusion-backup 1.0.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/LICENSE.TXT ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Phusion
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: binary
3
+ require 'etc'
4
+ require 'optparse'
5
+
6
+ module PhusionBackup
7
+
8
+ module TUI
9
+ protected
10
+ DEFAULT_TERMINAL_COLORS = "\e[0m\e[37m\e[40m"
11
+
12
+ def stdout
13
+ @stdout || STDOUT
14
+ end
15
+
16
+ def print(text)
17
+ stdout.write(substitute_color_tags(text))
18
+ stdout.flush
19
+ end
20
+
21
+ def puts(text = nil)
22
+ if text
23
+ stdout.puts(substitute_color_tags(text))
24
+ else
25
+ stdout.puts
26
+ end
27
+ stdout.flush
28
+ end
29
+
30
+ def puts_error(text)
31
+ STDERR.puts(substitute_color_tags("<red>#{text}</red>"))
32
+ STDERR.flush
33
+ end
34
+
35
+ def substitute_color_tags(data)
36
+ data = data.to_s
37
+ data = data.gsub(%r{<b>(.*?)</b>}m, "\e[1m\\1#{DEFAULT_TERMINAL_COLORS}")
38
+ data.gsub!(%r{<red>(.*?)</red>}m, "\e[1m\e[31m\\1#{DEFAULT_TERMINAL_COLORS}")
39
+ data.gsub!(%r{<green>(.*?)</green>}m, "\e[1m\e[32m\\1#{DEFAULT_TERMINAL_COLORS}")
40
+ data.gsub!(%r{<yellow>(.*?)</yellow>}m, "\e[1m\e[33m\\1#{DEFAULT_TERMINAL_COLORS}")
41
+ data.gsub!(%r{<banner>(.*?)</banner>}m, "\e[33m\e[44m\e[1m\\1#{DEFAULT_TERMINAL_COLORS}")
42
+ return data
43
+ end
44
+ end
45
+
46
+ class Server < Struct.new(:hostname, :dir)
47
+ end
48
+
49
+ class Core
50
+ ROOT = File.expand_path(File.dirname(__FILE__) + "/..")
51
+
52
+ include TUI
53
+
54
+ def initialize(backupdirs)
55
+ @backupdirs = backupdirs
56
+ end
57
+
58
+ def servers
59
+ results = []
60
+ @backupdirs.each do |dir|
61
+ next if !File.exist?(dir)
62
+ Dir.foreach(dir) do |hostname|
63
+ next if hostname =~ /^\./
64
+ fullname = "#{dir}/#{hostname}"
65
+ if File.directory?(fullname)
66
+ results << Server.new(hostname, fullname)
67
+ end
68
+ end
69
+ end
70
+ return results
71
+ end
72
+
73
+ def find_server(hostname_or_dir)
74
+ return servers.find { |s| s.hostname == hostname_or_dir || s.dir == hostname_or_dir }
75
+ end
76
+
77
+ def generate(dir)
78
+ copy_file_no_overwrite("#{ROOT}/resources/default-files.txt", "#{dir}/files.txt")
79
+ copy_file_no_overwrite("#{ROOT}/resources/default-install-script.txt", "#{dir}/install-script.txt")
80
+ end
81
+
82
+ def run_backup(server)
83
+ if !File.exist?("#{server.dir}/files.txt")
84
+ puts "<red>No `files.txt` found in this backup directory.</red>"
85
+ puts
86
+ puts "Each backup directory must have such a file, in which you " +
87
+ "specify which files must be backed up. You can create " +
88
+ "a default one with:"
89
+ puts
90
+ puts " <b>phusion-backup --generate #{server.dir}</b>"
91
+ exit 1
92
+ end
93
+
94
+ filename = create_globbing_filelist(server)
95
+ begin
96
+ sh!('rdiff-backup',
97
+ '-v6',
98
+ '--exclude-sockets',
99
+ '--include-globbing-filelist', filename,
100
+ "root@#{server.hostname}::/",
101
+ "#{server.dir}/data")
102
+ ensure
103
+ File.unlink(filename)
104
+ end
105
+ end
106
+
107
+ private
108
+ class CommandError < StandardError
109
+ end
110
+
111
+ def sh(*args)
112
+ puts "# #{args.join(' ')}"
113
+ result = system(*args)
114
+ if result
115
+ return true
116
+ elsif $?.signaled? && $?.termsig == Signal.list["INT"]
117
+ raise Interrupt
118
+ else
119
+ return false
120
+ end
121
+ end
122
+
123
+ def sh!(*args)
124
+ if !sh(*args)
125
+ puts_error "*** Command failed: #{args.join(' ')}"
126
+ raise CommandError
127
+ end
128
+ end
129
+
130
+ def copy_file_no_overwrite(source, target)
131
+ if File.exist?(target)
132
+ puts " [CREATE] #{target}"
133
+ puts " <yellow>File already exists. Not overwritten.</yellow>"
134
+ else
135
+ dir = File.dirname(target)
136
+ if !File.exist?(dir)
137
+ puts " [MKDIR] #{dir}"
138
+ Dir.mkdir(dir)
139
+ end
140
+ puts " [CREATE] #{target}"
141
+ File.open(target, "w") do |f|
142
+ f.write(File.read(source))
143
+ end
144
+ end
145
+ end
146
+
147
+ def create_globbing_filelist(server)
148
+ filename = "/tmp/phusion-backup-#{Process.pid}.txt"
149
+ File.open(filename, 'a') do |target|
150
+ File.open("#{server.dir}/files.txt", 'r') do |source|
151
+ while !source.eof?
152
+ line = source.readline.sub(/^#.*/, '')
153
+ target.puts(line)
154
+ end
155
+ end
156
+ target.puts "- **"
157
+ end
158
+ return filename
159
+ end
160
+ end
161
+
162
+ class App
163
+ include TUI
164
+
165
+ def initialize(argv)
166
+ @argv = argv.dup
167
+ @stdout = STDOUT
168
+ end
169
+
170
+ def run
171
+ options = {}
172
+ parser = OptionParser.new do |opts|
173
+ nl = "\n" << (" " * 37)
174
+ opts.banner = "General usage: phusion-backup <options...>"
175
+ opts.separator ""
176
+
177
+ opts.separator "Backup one or more servers:"
178
+ opts.separator " phusion-backup hostname1 [hostname2 ...]"
179
+ opts.separator ""
180
+ opts.separator " `hostnameX' is either a host name or directory name."
181
+ opts.separator ""
182
+
183
+ opts.separator "Display all servers that phusion-backup knows about:"
184
+ opts.separator " phusion-backup --list"
185
+ opts.separator ""
186
+
187
+ opts.separator "Please read the README for tutorial!"
188
+ opts.separator ""
189
+ opts.separator "Available options:"
190
+ opts.on("--list",
191
+ "Show all servers that phusion-backup knows#{nl}" +
192
+ "about.") do
193
+ options[:list] = true
194
+ end
195
+ opts.on("--generate DIR", String,
196
+ "Generate default files (like files.txt)#{nl}" +
197
+ "in the given directory.") do |dir|
198
+ options[:generate] = dir
199
+ end
200
+ opts.on("-h", "--help", "Show this help message.") do
201
+ options[:help] = true
202
+ end
203
+ end
204
+ begin
205
+ parser.parse!(@argv)
206
+ rescue OptionParser::ParseError => e
207
+ puts e
208
+ puts
209
+ puts "Please see '--help' for valid options."
210
+ exit 1
211
+ end
212
+
213
+ if options[:help]
214
+ puts parser
215
+ return 0
216
+ end
217
+
218
+ core = Core.new(["#{Core::ROOT}/backups", "#{home_dir}/Backups"])
219
+
220
+ if options[:list]
221
+ core.servers.each do |server|
222
+ printf "%-35s %s\n", server.hostname, server.dir
223
+ end
224
+ return 0
225
+ elsif options[:generate]
226
+ core.generate(options[:generate])
227
+ return 0
228
+ end
229
+
230
+ if @argv.empty?
231
+ if core.servers.empty?
232
+ puts_error "You must create a backup specification first. Please read README for tutorial."
233
+ else
234
+ puts_error "Please specify the server you want to backup:"
235
+ puts
236
+ core.servers.each do |server|
237
+ puts " phusion-backup #{server.hostname}"
238
+ end
239
+ puts
240
+ puts "For full usage please see `phusion-backup --help` and the README."
241
+ end
242
+ return 1
243
+ end
244
+
245
+ servers = []
246
+ @argv.each do |name|
247
+ server = core.find_server(name)
248
+ if !server
249
+ puts_error "This program doesn't know anything about the server '#{name}'."
250
+ puts "If you've misspelled the name, please correct it."
251
+ puts "If you want to see a list of available servers that this programs knows about, run <b>phusion-backup --list</b>."
252
+ puts "For general, please refer to the README."
253
+ exit 1
254
+ end
255
+ servers << server
256
+ end
257
+
258
+ servers.each do |server|
259
+ puts "<banner>Backing up #{server.hostname} - #{server.dir}</banner>"
260
+ core.run_backup(server)
261
+ puts
262
+ end
263
+
264
+ return 0
265
+ rescue Core::CommandError
266
+ return 1
267
+ end
268
+
269
+ private
270
+ def home_dir
271
+ return Etc.getpwuid(Process.uid).dir
272
+ end
273
+ end
274
+
275
+ end
276
+
277
+ exit(PhusionBackup::App.new(ARGV).run)
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "phusion-backup"
3
+ s.version = "1.0.0"
4
+ s.authors = ["Hongli Lai"]
5
+ s.date = "2011-03-30"
6
+ s.description = "Simple backup tool utilizing rdiff-backup."
7
+ s.summary = "Simple backup tool utilizing rdiff-backup."
8
+ s.email = "hongli@phusion.nl"
9
+ s.files = Dir[
10
+ "LICENSE.TXT",
11
+ "phusion-backup.gemspec",
12
+ "bin/*",
13
+ "resources/*"
14
+ ]
15
+ s.homepage = "https://github.com/FooBarWidget/crash-watch"
16
+ s.rdoc_options = ["--charset=UTF-8"]
17
+ s.executables = ["phusion-backup"]
18
+ s.require_paths = ["lib"]
19
+ end
20
+
@@ -0,0 +1,33 @@
1
+ # Standard system stuff.
2
+ /etc/skel
3
+ /etc/profile
4
+ /etc/bash.bashrc
5
+ /etc/hostname
6
+ /etc/nanorc
7
+ /etc/screenrc
8
+ /etc/mailname
9
+ /etc/rc.local
10
+ /var/spool/cron
11
+
12
+ # Daemon tools.
13
+ - /etc/service/*/supervise
14
+ /etc/service
15
+
16
+ # Backup all home directories, excluding non-critical things like caches.
17
+ /root
18
+ - /home/*/.gem
19
+ - /home/*/.bundle
20
+ - /home/*/.*/bundle
21
+ /home
22
+
23
+ # Backup all Capistrano-deployed web applications, excluding non-critical things like caches.
24
+ - /u/*/*/shared/cached-copy
25
+ - /u/*/*/shared/bundle
26
+ - /u/*/*/releases/*/.git
27
+ - /u/*/*/releases/*/vendor/bundle
28
+ - /u/*/**/*.log
29
+ /u
30
+
31
+ # Phusion stuff.
32
+ /etc/init.d/firewall
33
+ /opt/production/nginx/conf
@@ -0,0 +1,5 @@
1
+ # Standard, essential tools
2
+ apt-get install bash-completion screen
3
+
4
+ # Compiler toolchain
5
+ apt-get install build-essential gdb
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: phusion-backup
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Hongli Lai
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-30 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Simple backup tool utilizing rdiff-backup.
23
+ email: hongli@phusion.nl
24
+ executables:
25
+ - phusion-backup
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - LICENSE.TXT
32
+ - phusion-backup.gemspec
33
+ - bin/phusion-backup
34
+ - resources/default-files.txt
35
+ - resources/default-install-script.txt
36
+ has_rdoc: true
37
+ homepage: https://github.com/FooBarWidget/crash-watch
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.5.2
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Simple backup tool utilizing rdiff-backup.
70
+ test_files: []
71
+