backup 3.0.19 → 3.0.20
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/.gitignore +4 -0
- data/Gemfile +9 -8
- data/Gemfile.lock +19 -1
- data/Guardfile +13 -9
- data/README.md +93 -31
- data/backup.gemspec +3 -3
- data/bin/backup +6 -283
- data/lib/backup.rb +101 -72
- data/lib/backup/archive.rb +21 -9
- data/lib/backup/binder.rb +22 -0
- data/lib/backup/cleaner.rb +36 -0
- data/lib/backup/cli/helpers.rb +103 -0
- data/lib/backup/cli/utility.rb +308 -0
- data/lib/backup/compressor/base.rb +2 -2
- data/lib/backup/compressor/pbzip2.rb +76 -0
- data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
- data/lib/backup/configuration/database/riak.rb +25 -0
- data/lib/backup/configuration/encryptor/open_ssl.rb +6 -0
- data/lib/backup/configuration/helpers.rb +5 -18
- data/lib/backup/configuration/notifier/base.rb +13 -0
- data/lib/backup/configuration/notifier/hipchat.rb +41 -0
- data/lib/backup/configuration/notifier/mail.rb +38 -0
- data/lib/backup/configuration/notifier/prowl.rb +23 -0
- data/lib/backup/configuration/storage/cloudfiles.rb +4 -0
- data/lib/backup/configuration/storage/dropbox.rb +8 -4
- data/lib/backup/database/base.rb +10 -2
- data/lib/backup/database/mongodb.rb +16 -19
- data/lib/backup/database/mysql.rb +2 -2
- data/lib/backup/database/postgresql.rb +2 -2
- data/lib/backup/database/redis.rb +15 -7
- data/lib/backup/database/riak.rb +45 -0
- data/lib/backup/dependency.rb +21 -7
- data/lib/backup/encryptor/base.rb +1 -1
- data/lib/backup/encryptor/open_ssl.rb +20 -5
- data/lib/backup/errors.rb +124 -0
- data/lib/backup/finder.rb +11 -3
- data/lib/backup/logger.rb +121 -82
- data/lib/backup/model.rb +103 -44
- data/lib/backup/notifier/base.rb +50 -0
- data/lib/backup/notifier/campfire.rb +32 -52
- data/lib/backup/notifier/hipchat.rb +99 -0
- data/lib/backup/notifier/mail.rb +100 -61
- data/lib/backup/notifier/presently.rb +31 -40
- data/lib/backup/notifier/prowl.rb +73 -0
- data/lib/backup/notifier/twitter.rb +29 -39
- data/lib/backup/packager.rb +25 -0
- data/lib/backup/splitter.rb +62 -0
- data/lib/backup/storage/base.rb +178 -18
- data/lib/backup/storage/cloudfiles.rb +34 -28
- data/lib/backup/storage/dropbox.rb +64 -67
- data/lib/backup/storage/ftp.rb +48 -40
- data/lib/backup/storage/local.rb +33 -28
- data/lib/backup/storage/ninefold.rb +40 -26
- data/lib/backup/storage/object.rb +8 -6
- data/lib/backup/storage/rsync.rb +61 -51
- data/lib/backup/storage/s3.rb +29 -27
- data/lib/backup/storage/scp.rb +56 -36
- data/lib/backup/storage/sftp.rb +49 -33
- data/lib/backup/syncer/base.rb +1 -1
- data/lib/backup/syncer/rsync.rb +1 -1
- data/lib/backup/template.rb +46 -0
- data/lib/backup/version.rb +1 -1
- data/spec/archive_spec.rb +34 -9
- data/spec/backup_spec.rb +1 -1
- data/spec/cli/helpers_spec.rb +35 -0
- data/spec/cli/utility_spec.rb +38 -0
- data/spec/compressor/bzip2_spec.rb +1 -1
- data/spec/compressor/gzip_spec.rb +1 -1
- data/spec/compressor/lzma_spec.rb +1 -1
- data/spec/compressor/pbzip2_spec.rb +63 -0
- data/spec/configuration/base_spec.rb +1 -1
- data/spec/configuration/compressor/bzip2_spec.rb +1 -1
- data/spec/configuration/compressor/gzip_spec.rb +1 -1
- data/spec/configuration/compressor/lzma_spec.rb +1 -1
- data/spec/configuration/database/base_spec.rb +1 -1
- data/spec/configuration/database/mongodb_spec.rb +1 -1
- data/spec/configuration/database/mysql_spec.rb +1 -1
- data/spec/configuration/database/postgresql_spec.rb +1 -1
- data/spec/configuration/database/redis_spec.rb +1 -1
- data/spec/configuration/database/riak_spec.rb +31 -0
- data/spec/configuration/encryptor/gpg_spec.rb +1 -1
- data/spec/configuration/encryptor/open_ssl_spec.rb +4 -1
- data/spec/configuration/notifier/campfire_spec.rb +1 -1
- data/spec/configuration/notifier/hipchat_spec.rb +43 -0
- data/spec/configuration/notifier/mail_spec.rb +34 -22
- data/spec/configuration/notifier/presently_spec.rb +1 -1
- data/spec/configuration/notifier/prowl_spec.rb +28 -0
- data/spec/configuration/notifier/twitter_spec.rb +1 -1
- data/spec/configuration/storage/cloudfiles_spec.rb +19 -16
- data/spec/configuration/storage/dropbox_spec.rb +1 -1
- data/spec/configuration/storage/ftp_spec.rb +1 -1
- data/spec/configuration/storage/local_spec.rb +1 -1
- data/spec/configuration/storage/ninefold_spec.rb +1 -1
- data/spec/configuration/storage/rsync_spec.rb +1 -1
- data/spec/configuration/storage/s3_spec.rb +1 -1
- data/spec/configuration/storage/scp_spec.rb +1 -1
- data/spec/configuration/storage/sftp_spec.rb +1 -1
- data/spec/configuration/syncer/rsync_spec.rb +1 -1
- data/spec/configuration/syncer/s3_spec.rb +1 -1
- data/spec/database/base_spec.rb +10 -1
- data/spec/database/mongodb_spec.rb +34 -7
- data/spec/database/mysql_spec.rb +8 -7
- data/spec/database/postgresql_spec.rb +8 -7
- data/spec/database/redis_spec.rb +39 -9
- data/spec/database/riak_spec.rb +50 -0
- data/spec/encryptor/gpg_spec.rb +1 -1
- data/spec/encryptor/open_ssl_spec.rb +77 -20
- data/spec/errors_spec.rb +306 -0
- data/spec/finder_spec.rb +91 -0
- data/spec/logger_spec.rb +254 -33
- data/spec/model_spec.rb +120 -15
- data/spec/notifier/campfire_spec.rb +127 -52
- data/spec/notifier/hipchat_spec.rb +193 -0
- data/spec/notifier/mail_spec.rb +290 -74
- data/spec/notifier/presently_spec.rb +290 -73
- data/spec/notifier/prowl_spec.rb +149 -0
- data/spec/notifier/twitter_spec.rb +106 -41
- data/spec/spec_helper.rb +8 -2
- data/spec/splitter_spec.rb +71 -0
- data/spec/storage/base_spec.rb +280 -19
- data/spec/storage/cloudfiles_spec.rb +38 -22
- data/spec/storage/dropbox_spec.rb +17 -13
- data/spec/storage/ftp_spec.rb +145 -55
- data/spec/storage/local_spec.rb +6 -6
- data/spec/storage/ninefold_spec.rb +70 -29
- data/spec/storage/object_spec.rb +44 -44
- data/spec/storage/rsync_spec.rb +186 -63
- data/spec/storage/s3_spec.rb +23 -24
- data/spec/storage/scp_spec.rb +116 -41
- data/spec/storage/sftp_spec.rb +124 -46
- data/spec/syncer/rsync_spec.rb +3 -3
- data/spec/syncer/s3_spec.rb +1 -1
- data/spec/version_spec.rb +1 -1
- data/templates/cli/utility/archive +13 -0
- data/{lib/templates → templates/cli/utility}/compressor/bzip2 +1 -1
- data/{lib/templates → templates/cli/utility}/compressor/gzip +1 -1
- data/{lib/templates → templates/cli/utility}/compressor/lzma +0 -0
- data/templates/cli/utility/compressor/pbzip2 +7 -0
- data/templates/cli/utility/config +31 -0
- data/{lib/templates → templates/cli/utility}/database/mongodb +1 -1
- data/{lib/templates → templates/cli/utility}/database/mysql +1 -1
- data/{lib/templates → templates/cli/utility}/database/postgresql +1 -1
- data/{lib/templates → templates/cli/utility}/database/redis +1 -1
- data/templates/cli/utility/database/riak +8 -0
- data/{lib/templates → templates/cli/utility}/encryptor/gpg +1 -1
- data/templates/cli/utility/encryptor/openssl +9 -0
- data/templates/cli/utility/model.erb +23 -0
- data/{lib/templates → templates/cli/utility}/notifier/campfire +2 -1
- data/templates/cli/utility/notifier/hipchat +15 -0
- data/{lib/templates → templates/cli/utility}/notifier/mail +6 -1
- data/{lib/templates → templates/cli/utility}/notifier/presently +1 -0
- data/templates/cli/utility/notifier/prowl +11 -0
- data/{lib/templates → templates/cli/utility}/notifier/twitter +2 -1
- data/templates/cli/utility/splitter +7 -0
- data/templates/cli/utility/storage/cloudfiles +12 -0
- data/{lib/templates → templates/cli/utility}/storage/dropbox +1 -1
- data/{lib/templates → templates/cli/utility}/storage/ftp +0 -0
- data/templates/cli/utility/storage/local +7 -0
- data/{lib/templates → templates/cli/utility}/storage/ninefold +1 -1
- data/templates/cli/utility/storage/rsync +11 -0
- data/{lib/templates → templates/cli/utility}/storage/s3 +0 -2
- data/templates/cli/utility/storage/scp +11 -0
- data/templates/cli/utility/storage/sftp +11 -0
- data/{lib/templates → templates/cli/utility}/syncer/rsync +1 -1
- data/{lib/templates → templates/cli/utility}/syncer/s3 +1 -1
- data/templates/general/links +11 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +9 -0
- data/templates/notifier/mail/success.erb +7 -0
- data/templates/notifier/mail/warning.erb +9 -0
- data/templates/storage/dropbox/authorization_url.erb +6 -0
- data/templates/storage/dropbox/authorized.erb +4 -0
- data/templates/storage/dropbox/cache_file_written.erb +10 -0
- metadata +81 -45
- data/lib/backup/cli.rb +0 -110
- data/lib/backup/exception/command_failed.rb +0 -8
- data/lib/backup/exception/command_not_found.rb +0 -8
- data/lib/backup/notifier/binder.rb +0 -32
- data/lib/backup/notifier/templates/notify_failure.erb +0 -33
- data/lib/backup/notifier/templates/notify_success.erb +0 -16
- data/lib/templates/archive +0 -7
- data/lib/templates/encryptor/openssl +0 -8
- data/lib/templates/readme +0 -15
- data/lib/templates/storage/cloudfiles +0 -11
- data/lib/templates/storage/local +0 -7
- data/lib/templates/storage/rsync +0 -11
- data/lib/templates/storage/scp +0 -11
- data/lib/templates/storage/sftp +0 -11
data/lib/backup/archive.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Backup
|
|
4
4
|
class Archive
|
|
5
|
-
include Backup::CLI
|
|
5
|
+
include Backup::CLI::Helpers
|
|
6
6
|
|
|
7
7
|
##
|
|
8
8
|
# Stores the name of the archive
|
|
@@ -23,10 +23,10 @@ module Backup
|
|
|
23
23
|
##
|
|
24
24
|
# Takes the name of the archive and the configuration block
|
|
25
25
|
def initialize(name, &block)
|
|
26
|
-
@name
|
|
27
|
-
@paths
|
|
28
|
-
@excludes
|
|
29
|
-
@
|
|
26
|
+
@name = name.to_sym
|
|
27
|
+
@paths = Array.new
|
|
28
|
+
@excludes = Array.new
|
|
29
|
+
@tar_options = ''
|
|
30
30
|
|
|
31
31
|
instance_eval(&block)
|
|
32
32
|
end
|
|
@@ -34,22 +34,34 @@ module Backup
|
|
|
34
34
|
##
|
|
35
35
|
# Adds new paths to the @paths instance variable array
|
|
36
36
|
def add(path)
|
|
37
|
-
@paths << path
|
|
37
|
+
@paths << File.expand_path(path)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
##
|
|
41
41
|
# Adds new paths to the @excludes instance variable array
|
|
42
42
|
def exclude(path)
|
|
43
|
-
@excludes << path
|
|
43
|
+
@excludes << File.expand_path(path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Adds the given String of +options+ to the `tar` command.
|
|
48
|
+
# e.g. '-h --xattrs'
|
|
49
|
+
def tar_options(options)
|
|
50
|
+
@tar_options = options
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
##
|
|
47
54
|
# Archives all the provided paths in to a single .tar file
|
|
48
55
|
# and places that .tar file in the folder which later will be packaged
|
|
49
56
|
def perform!
|
|
57
|
+
@archive_path = File.join(TMP_PATH, TRIGGER, 'archive')
|
|
50
58
|
mkdir(archive_path)
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
|
|
60
|
+
Logger.message "#{ self.class } started packaging and archiving:\n" +
|
|
61
|
+
paths.map {|path| " #{path}" }.join("\n")
|
|
62
|
+
|
|
63
|
+
run("#{ utility(:tar) } #{ @tar_options } -cf '#{ File.join(archive_path, "#{name}.tar") }' " +
|
|
64
|
+
"#{ paths_to_exclude } #{ paths_to_package }", :ignore_exit_codes => [1])
|
|
53
65
|
end
|
|
54
66
|
|
|
55
67
|
private
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
class Binder
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# Creates a new Backup::Notifier::Binder instance. Loops through the provided
|
|
8
|
+
# Hash to set instance variables
|
|
9
|
+
def initialize(key_and_values)
|
|
10
|
+
key_and_values.each do |key, value|
|
|
11
|
+
instance_variable_set("@#{ key }", value)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Returns the binding (needs a wrapper method because #binding is a private method)
|
|
17
|
+
def get_binding
|
|
18
|
+
binding
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
class Cleaner
|
|
5
|
+
include Backup::CLI::Helpers
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# Holds an instance of the current Backup model
|
|
9
|
+
attr_accessor :model
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Creates a new instance of Backup::Cleaner
|
|
13
|
+
def initialize(model)
|
|
14
|
+
@model = model
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Runs the cleaner object which removes all the tmp files generated by Backup
|
|
19
|
+
def clean!
|
|
20
|
+
Logger.message "#{self.class} started cleaning up the temporary files."
|
|
21
|
+
run("#{ utility(:rm) } -rf #{ paths.map { |path| "'#{ path }'" }.join(" ") }")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Returns an Array of paths to temporary files generated by Backup that need to be removed
|
|
28
|
+
def paths
|
|
29
|
+
Array.new([
|
|
30
|
+
File.join(TMP_PATH, TRIGGER),
|
|
31
|
+
Backup::Model.file,
|
|
32
|
+
Backup::Model.chunk_suffixes.map { |chunk_suffix| "#{Backup::Model.file}-#{chunk_suffix}" }
|
|
33
|
+
]).flatten
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
module CLI
|
|
5
|
+
module Helpers
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# Runs a given command in an isolated (sub) process using POpen4.
|
|
9
|
+
# The STDOUT, STDERR and the returned exit code of the utility will be stored in the process_data Hash.
|
|
10
|
+
#
|
|
11
|
+
# If a command returns an exit code other than 0, an exception will raise and the backup process will abort.
|
|
12
|
+
# Some utilities return exit codes other than 0 which aren't an issue in Backup's context. If this is the case,
|
|
13
|
+
# you can pass in an array of exit codes to ignore (whitelist), for example:
|
|
14
|
+
#
|
|
15
|
+
# run("tar -cf /output.tar /some/folder", :ignore_exit_codes => [1])
|
|
16
|
+
#
|
|
17
|
+
# So if the `tar` utility returns in this case 1, Backup will consider it an acceptable return code.
|
|
18
|
+
#
|
|
19
|
+
# Note: Exit code 0 is always automatically added to the :ignore_exit_codes array, regardless of whether you specify an
|
|
20
|
+
# array to ignore or not.
|
|
21
|
+
def run(command, options = {})
|
|
22
|
+
command.gsub!(/^\s+/, "")
|
|
23
|
+
|
|
24
|
+
process_data = Hash.new
|
|
25
|
+
pid, stdin, stdout, stderr = Open4::popen4(command)
|
|
26
|
+
ignored, process_data[:status] = Process::waitpid2(pid)
|
|
27
|
+
process_data[:stdout] = stdout.read
|
|
28
|
+
process_data[:stderr] = stderr.read
|
|
29
|
+
process_data[:ignore_exit_codes] = ((options[:ignore_exit_codes] || Array.new) << 0).uniq
|
|
30
|
+
|
|
31
|
+
raise_if_command_failed!(command_name(command), process_data)
|
|
32
|
+
process_data[:stdout]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Wrapper method for FileUtils.mkdir_p to create directories
|
|
37
|
+
# through a ruby method. This helps with test coverage and
|
|
38
|
+
# improves readability
|
|
39
|
+
def mkdir(path)
|
|
40
|
+
FileUtils.mkdir_p(path)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Wrapper for the FileUtils.rm_rf to remove files and folders
|
|
45
|
+
# through a ruby method. This helps with test coverage and
|
|
46
|
+
# improves readability
|
|
47
|
+
def rm(path)
|
|
48
|
+
FileUtils.rm_rf(path)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Tries to find the full path of the specified utility. If the full
|
|
53
|
+
# path is found, it'll return that. Otherwise it'll just return the
|
|
54
|
+
# name of the utility. If the 'utility_path' is defined, it'll check
|
|
55
|
+
# to see if it isn't an empty string, and if it isn't, it'll go ahead and
|
|
56
|
+
# always use that path rather than auto-detecting it
|
|
57
|
+
def utility(name)
|
|
58
|
+
if respond_to?(:utility_path)
|
|
59
|
+
if utility_path.is_a?(String) and not utility_path.empty?
|
|
60
|
+
return utility_path
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if path = %x[which #{name} 2>/dev/null].chomp and not path.empty?
|
|
65
|
+
return path
|
|
66
|
+
end
|
|
67
|
+
name
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
# Returns the name of the command
|
|
72
|
+
def command_name(command)
|
|
73
|
+
command.slice(0, command.index(/\s/)).split('/')[-1]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# Inspects the exit code returned from the POpen4 child process. If the exit code isn't listed
|
|
78
|
+
# in the process_data[:ignore_exit_codes] array, an exception will be raised, aborting the backup process.
|
|
79
|
+
#
|
|
80
|
+
# Information regarding the error ( EXIT CODE and STDERR ) will be returned to the shell so the user can
|
|
81
|
+
# investigate the issue.
|
|
82
|
+
#
|
|
83
|
+
# raises Backup::Errors::CLI::SystemCallError
|
|
84
|
+
def raise_if_command_failed!(utility, process_data)
|
|
85
|
+
unless process_data[:ignore_exit_codes].include?(process_data[:status].to_i)
|
|
86
|
+
|
|
87
|
+
stderr = process_data[:stderr].empty? ?
|
|
88
|
+
nil : "STDERR:\n#{process_data[:stderr]}\n"
|
|
89
|
+
stdout = process_data[:stdout].empty? ?
|
|
90
|
+
nil : "STDOUT:\n#{process_data[:stdout]}\n"
|
|
91
|
+
|
|
92
|
+
raise Errors::CLI::SystemCallError, <<-EOS
|
|
93
|
+
Failed to run #{utility} on #{RUBY_PLATFORM}
|
|
94
|
+
The following information should help to determine the problem:
|
|
95
|
+
Exit Code: #{process_data[:status]}
|
|
96
|
+
#{stderr}#{stdout}
|
|
97
|
+
EOS
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Build the Backup Command Line Interface using Thor
|
|
5
|
+
module Backup
|
|
6
|
+
module CLI
|
|
7
|
+
class Utility < Thor
|
|
8
|
+
include Thor::Actions
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# [Perform]
|
|
12
|
+
# Performs the backup process. The only required option is the --trigger [-t].
|
|
13
|
+
# If the other options (--config-file, --data-path, --cache--path, --tmp-path) aren't specified
|
|
14
|
+
# they will fallback to the (good) defaults
|
|
15
|
+
#
|
|
16
|
+
# If --root-path is given, it will be used as the base (Backup::PATH) for our defaults,
|
|
17
|
+
# as well as the base path for any option specified as a relative path.
|
|
18
|
+
# Any option given as an absolute path will be used "as-is".
|
|
19
|
+
method_option :trigger, :type => :string, :required => true, :aliases => ['-t', '--triggers']
|
|
20
|
+
method_option :config_file, :type => :string, :default => '', :aliases => '-c'
|
|
21
|
+
method_option :root_path, :type => :string, :default => '', :aliases => '-r'
|
|
22
|
+
method_option :data_path, :type => :string, :default => '', :aliases => '-d'
|
|
23
|
+
method_option :log_path, :type => :string, :default => '', :aliases => '-l'
|
|
24
|
+
method_option :cache_path, :type => :string, :default => ''
|
|
25
|
+
method_option :tmp_path, :type => :string, :default => ''
|
|
26
|
+
method_option :quiet, :type => :boolean, :default => false, :aliases => '-q'
|
|
27
|
+
desc 'perform', "Performs the backup for the specified trigger.\n" +
|
|
28
|
+
"You may perform multiple backups by providing multiple triggers, separated by commas.\n\n" +
|
|
29
|
+
"Example:\n\s\s$ backup perform --triggers backup1,backup2,backup3,backup4\n\n" +
|
|
30
|
+
"This will invoke 4 backups, and they will run in the order specified (not asynchronous)."
|
|
31
|
+
def perform
|
|
32
|
+
##
|
|
33
|
+
# Setup required paths based on the given options
|
|
34
|
+
setup_paths(options)
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Silence Backup::Logger from printing to STDOUT, if --quiet was specified
|
|
38
|
+
Logger.send(:const_set, :QUIET, options[:quiet])
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# Prepare all trigger names by splitting them by ','
|
|
42
|
+
# and finding trigger names matching wildcard
|
|
43
|
+
triggers = options[:trigger].split(",")
|
|
44
|
+
triggers.map!(&:strip).map!{ |t|
|
|
45
|
+
t.include?(Backup::Finder::WILDCARD) ?
|
|
46
|
+
Backup::Finder.new(t).matching : t
|
|
47
|
+
}.flatten!
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Process each trigger
|
|
51
|
+
triggers.each do |trigger|
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Defines the TRIGGER constant
|
|
55
|
+
Backup.send(:const_set, :TRIGGER, trigger)
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# Define the TIME constants
|
|
59
|
+
Backup.send(:const_set, :TIME, Time.now.strftime("%Y.%m.%d.%H.%M.%S"))
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Ensure DATA_PATH and DATA_PATH/TRIGGER are created if they do not yet exist
|
|
63
|
+
FileUtils.mkdir_p(File.join(Backup::DATA_PATH, Backup::TRIGGER))
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# Parses the backup configuration file and returns the model instance by trigger
|
|
67
|
+
model = Backup::Finder.new(trigger).find
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# Runs the returned model
|
|
71
|
+
Logger.message "Performing backup for #{model.label}!"
|
|
72
|
+
model.perform!
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# Removes the TRIGGER constant
|
|
76
|
+
Backup.send(:remove_const, :TRIGGER) if defined? Backup::TRIGGER
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# Removes the TIME constant
|
|
80
|
+
Backup.send(:remove_const, :TIME) if defined? Backup::TIME
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# Reset the Backup::Model.current to nil for the next potential run
|
|
84
|
+
Backup::Model.current = nil
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# Clear the Log Messages for the next potential run
|
|
88
|
+
Logger.clear!
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# Reset the Backup::Model.extension to 'tar' so it's at its
|
|
92
|
+
# initial state when the next Backup::Model initializes
|
|
93
|
+
Backup::Model.extension = 'tar'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
rescue => err
|
|
97
|
+
Logger.error Backup::Errors::CLIError.wrap(err)
|
|
98
|
+
exit(1)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# [Generate:Model]
|
|
103
|
+
# Generates a model configuration file based on the arguments passed in.
|
|
104
|
+
# For example:
|
|
105
|
+
# $ backup generate:model --trigger my_backup --databases='mongodb'
|
|
106
|
+
# will generate a pre-populated model with a base MongoDB setup
|
|
107
|
+
method_option :trigger, :type => :string, :required => true
|
|
108
|
+
method_option :config_path, :type => :string,
|
|
109
|
+
:desc => 'Path to your Backup configuration directory'
|
|
110
|
+
desc 'generate:model', "Generates a Backup model file\n\n" +
|
|
111
|
+
"Note:\n" +
|
|
112
|
+
"\s\s'--config-path' is the path to the directory where 'config.rb' is located.\n" +
|
|
113
|
+
"\s\sThe model file will be created as '<config_path>/models/<trigger>.rb'\n" +
|
|
114
|
+
"\s\sDefault: #{Backup::PATH}\n"
|
|
115
|
+
|
|
116
|
+
# options with their available values
|
|
117
|
+
%w{ databases storages syncers
|
|
118
|
+
encryptors compressors notifiers }.map(&:to_sym).each do |name|
|
|
119
|
+
path = File.join(Backup::TEMPLATE_PATH, 'cli', 'utility', name.to_s[0..-2])
|
|
120
|
+
method_option name, :type => :string, :desc =>
|
|
121
|
+
"(#{Dir[path + '/*'].sort.map {|p| File.basename(p) }.join(', ')})"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
method_option :archives, :type => :boolean
|
|
125
|
+
method_option :splitter, :type => :boolean, :default => true,
|
|
126
|
+
:desc => "use `--no-splitter` to disable"
|
|
127
|
+
|
|
128
|
+
define_method "generate:model" do
|
|
129
|
+
opts = options.merge(
|
|
130
|
+
:trigger => options[:trigger].gsub(/[\W\s]/, '_'),
|
|
131
|
+
:config_path => options[:config_path] ? File.expand_path(options[:config_path]) : nil
|
|
132
|
+
)
|
|
133
|
+
config_path = opts[:config_path] || Backup::PATH
|
|
134
|
+
models_path = File.join(config_path, "models")
|
|
135
|
+
config = File.join(config_path, "config.rb")
|
|
136
|
+
model = File.join(models_path, "#{opts[:trigger]}.rb")
|
|
137
|
+
|
|
138
|
+
FileUtils.mkdir_p(models_path)
|
|
139
|
+
if overwrite?(model)
|
|
140
|
+
File.open(model, 'w') do |file|
|
|
141
|
+
file.write(Backup::Template.new({:options => opts}).
|
|
142
|
+
result("cli/utility/model.erb"))
|
|
143
|
+
end
|
|
144
|
+
puts "Generated model file in '#{ model }'."
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
if not File.exist?(config)
|
|
148
|
+
File.open(config, "w") do |file|
|
|
149
|
+
file.write(Backup::Template.new.result("cli/utility/config"))
|
|
150
|
+
end
|
|
151
|
+
puts "Generated configuration file in '#{ config }'."
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
##
|
|
156
|
+
# [Generate:Config]
|
|
157
|
+
# Generates the main configuration file
|
|
158
|
+
desc 'generate:config', 'Generates the main Backup bootstrap/configuration file'
|
|
159
|
+
method_option :path, :type => :string
|
|
160
|
+
define_method 'generate:config' do
|
|
161
|
+
path = options[:path] ? File.expand_path(options[:path]) : nil
|
|
162
|
+
config_path = path || Backup::PATH
|
|
163
|
+
config = File.join(config_path, "config.rb")
|
|
164
|
+
|
|
165
|
+
FileUtils.mkdir_p(config_path)
|
|
166
|
+
if overwrite?(config)
|
|
167
|
+
File.open(config, "w") do |file|
|
|
168
|
+
file.write(Backup::Template.new.result("cli/utility/config"))
|
|
169
|
+
end
|
|
170
|
+
puts "Generated configuration file in '#{ config }'"
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
##
|
|
175
|
+
# [Decrypt]
|
|
176
|
+
# Shorthand for decrypting encrypted files
|
|
177
|
+
desc 'decrypt', 'Decrypts encrypted files'
|
|
178
|
+
method_option :encryptor, :type => :string, :required => true
|
|
179
|
+
method_option :in, :type => :string, :required => true
|
|
180
|
+
method_option :out, :type => :string, :required => true
|
|
181
|
+
method_option :base64, :type => :boolean, :default => false
|
|
182
|
+
method_option :password_file, :type => :string, :default => ''
|
|
183
|
+
method_option :salt, :type => :boolean, :default => false
|
|
184
|
+
def decrypt
|
|
185
|
+
case options[:encryptor].downcase
|
|
186
|
+
when 'openssl'
|
|
187
|
+
base64 = options[:base64] ? '-base64' : ''
|
|
188
|
+
password = options[:password_file] ? "-pass file:#{options[:password_file]}" : ''
|
|
189
|
+
salt = options[:salt] ? '-salt' : ''
|
|
190
|
+
%x[openssl aes-256-cbc -d #{base64} #{password} #{salt} -in '#{options[:in]}' -out '#{options[:out]}']
|
|
191
|
+
when 'gpg'
|
|
192
|
+
%x[gpg -o '#{options[:out]}' -d '#{options[:in]}']
|
|
193
|
+
else
|
|
194
|
+
puts "Unknown encryptor: #{options[:encryptor]}"
|
|
195
|
+
puts "Use either 'openssl' or 'gpg'"
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
##
|
|
200
|
+
# [Dependencies]
|
|
201
|
+
# Returns a list of Backup's dependencies
|
|
202
|
+
desc 'dependencies', 'Display the list of dependencies for Backup, or install them through Backup.'
|
|
203
|
+
method_option :install, :type => :string
|
|
204
|
+
method_option :list, :type => :boolean
|
|
205
|
+
def dependencies
|
|
206
|
+
unless options.any?
|
|
207
|
+
puts
|
|
208
|
+
puts "To display a list of available dependencies, run:\n\n"
|
|
209
|
+
puts " backup dependencies --list"
|
|
210
|
+
puts
|
|
211
|
+
puts "To install one of these dependencies (with the correct version), run:\n\n"
|
|
212
|
+
puts " backup dependencies --install <name>"
|
|
213
|
+
exit
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
if options[:list]
|
|
217
|
+
Backup::Dependency.all.each do |name, gemspec|
|
|
218
|
+
puts
|
|
219
|
+
puts name
|
|
220
|
+
puts "--------------------------------------------------"
|
|
221
|
+
puts "version: #{gemspec[:version]}"
|
|
222
|
+
puts "lib required: #{gemspec[:require]}"
|
|
223
|
+
puts "used for: #{gemspec[:for]}"
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
if options[:install]
|
|
228
|
+
puts
|
|
229
|
+
puts "Installing \"#{options[:install]}\" version \"#{Backup::Dependency.all[options[:install]][:version]}\".."
|
|
230
|
+
puts "If this doesn't work, please issue the following command yourself:\n\n"
|
|
231
|
+
puts " gem install #{options[:install]} -v '#{Backup::Dependency.all[options[:install]][:version]}'\n\n"
|
|
232
|
+
puts "Please wait..\n\n"
|
|
233
|
+
puts %x[gem install #{options[:install]} -v '#{Backup::Dependency.all[options[:install]][:version]}']
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
##
|
|
238
|
+
# [Version]
|
|
239
|
+
# Returns the current version of the Backup gem
|
|
240
|
+
map '-v' => :version
|
|
241
|
+
desc 'version', 'Display installed Backup version'
|
|
242
|
+
def version
|
|
243
|
+
puts "Backup #{Backup::Version.current}"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
private
|
|
247
|
+
|
|
248
|
+
##
|
|
249
|
+
# Setup required paths based on the given options
|
|
250
|
+
#
|
|
251
|
+
def setup_paths(options)
|
|
252
|
+
##
|
|
253
|
+
# Set PATH if --root-path is given and the directory exists
|
|
254
|
+
root_path = false
|
|
255
|
+
root_given = options[:root_path].strip
|
|
256
|
+
if !root_given.empty? && File.directory?(root_given)
|
|
257
|
+
root_path = File.expand_path(root_given)
|
|
258
|
+
Backup.send(:remove_const, :PATH)
|
|
259
|
+
Backup.send(:const_set, :PATH, root_path)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
##
|
|
263
|
+
# Update all defaults and given paths to use root_path (if given).
|
|
264
|
+
# Paths given as an absolute path will be used 'as-is'
|
|
265
|
+
{ :config_file => 'config.rb',
|
|
266
|
+
:data_path => 'data',
|
|
267
|
+
:log_path => 'log',
|
|
268
|
+
:cache_path => '.cache',
|
|
269
|
+
:tmp_path => '.tmp' }.each do |key, name|
|
|
270
|
+
# strip any trailing '/' in case the user supplied this as part of
|
|
271
|
+
# an absolute path, so we can match it against File.expand_path()
|
|
272
|
+
given = options[key].sub(/\/\s*$/, '').lstrip
|
|
273
|
+
path = false
|
|
274
|
+
if given.empty?
|
|
275
|
+
path = File.join(root_path, name) if root_path
|
|
276
|
+
else
|
|
277
|
+
path = File.expand_path(given)
|
|
278
|
+
unless given == path
|
|
279
|
+
path = File.join(root_path, given) if root_path
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
const = key.to_s.upcase
|
|
284
|
+
if path
|
|
285
|
+
Backup.send(:remove_const, const)
|
|
286
|
+
Backup.send(:const_set, const, path)
|
|
287
|
+
else
|
|
288
|
+
path = Backup.const_get(const)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
##
|
|
292
|
+
# Ensure the LOG_PATH, CACHE_PATH and TMP_PATH are created if they do not yet exist
|
|
293
|
+
FileUtils.mkdir_p(path) if [:log_path, :cache_path, :tmp_path].include?(key)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
##
|
|
298
|
+
# Helper method for asking the user if he/she wants to overwrite the file
|
|
299
|
+
def overwrite?(path)
|
|
300
|
+
if File.exist?(path)
|
|
301
|
+
return yes? "A file already exists at '#{ path }'. Do you want to overwrite? [y/n]"
|
|
302
|
+
end
|
|
303
|
+
true
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|