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.
Files changed (188) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +9 -8
  3. data/Gemfile.lock +19 -1
  4. data/Guardfile +13 -9
  5. data/README.md +93 -31
  6. data/backup.gemspec +3 -3
  7. data/bin/backup +6 -283
  8. data/lib/backup.rb +101 -72
  9. data/lib/backup/archive.rb +21 -9
  10. data/lib/backup/binder.rb +22 -0
  11. data/lib/backup/cleaner.rb +36 -0
  12. data/lib/backup/cli/helpers.rb +103 -0
  13. data/lib/backup/cli/utility.rb +308 -0
  14. data/lib/backup/compressor/base.rb +2 -2
  15. data/lib/backup/compressor/pbzip2.rb +76 -0
  16. data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
  17. data/lib/backup/configuration/database/riak.rb +25 -0
  18. data/lib/backup/configuration/encryptor/open_ssl.rb +6 -0
  19. data/lib/backup/configuration/helpers.rb +5 -18
  20. data/lib/backup/configuration/notifier/base.rb +13 -0
  21. data/lib/backup/configuration/notifier/hipchat.rb +41 -0
  22. data/lib/backup/configuration/notifier/mail.rb +38 -0
  23. data/lib/backup/configuration/notifier/prowl.rb +23 -0
  24. data/lib/backup/configuration/storage/cloudfiles.rb +4 -0
  25. data/lib/backup/configuration/storage/dropbox.rb +8 -4
  26. data/lib/backup/database/base.rb +10 -2
  27. data/lib/backup/database/mongodb.rb +16 -19
  28. data/lib/backup/database/mysql.rb +2 -2
  29. data/lib/backup/database/postgresql.rb +2 -2
  30. data/lib/backup/database/redis.rb +15 -7
  31. data/lib/backup/database/riak.rb +45 -0
  32. data/lib/backup/dependency.rb +21 -7
  33. data/lib/backup/encryptor/base.rb +1 -1
  34. data/lib/backup/encryptor/open_ssl.rb +20 -5
  35. data/lib/backup/errors.rb +124 -0
  36. data/lib/backup/finder.rb +11 -3
  37. data/lib/backup/logger.rb +121 -82
  38. data/lib/backup/model.rb +103 -44
  39. data/lib/backup/notifier/base.rb +50 -0
  40. data/lib/backup/notifier/campfire.rb +32 -52
  41. data/lib/backup/notifier/hipchat.rb +99 -0
  42. data/lib/backup/notifier/mail.rb +100 -61
  43. data/lib/backup/notifier/presently.rb +31 -40
  44. data/lib/backup/notifier/prowl.rb +73 -0
  45. data/lib/backup/notifier/twitter.rb +29 -39
  46. data/lib/backup/packager.rb +25 -0
  47. data/lib/backup/splitter.rb +62 -0
  48. data/lib/backup/storage/base.rb +178 -18
  49. data/lib/backup/storage/cloudfiles.rb +34 -28
  50. data/lib/backup/storage/dropbox.rb +64 -67
  51. data/lib/backup/storage/ftp.rb +48 -40
  52. data/lib/backup/storage/local.rb +33 -28
  53. data/lib/backup/storage/ninefold.rb +40 -26
  54. data/lib/backup/storage/object.rb +8 -6
  55. data/lib/backup/storage/rsync.rb +61 -51
  56. data/lib/backup/storage/s3.rb +29 -27
  57. data/lib/backup/storage/scp.rb +56 -36
  58. data/lib/backup/storage/sftp.rb +49 -33
  59. data/lib/backup/syncer/base.rb +1 -1
  60. data/lib/backup/syncer/rsync.rb +1 -1
  61. data/lib/backup/template.rb +46 -0
  62. data/lib/backup/version.rb +1 -1
  63. data/spec/archive_spec.rb +34 -9
  64. data/spec/backup_spec.rb +1 -1
  65. data/spec/cli/helpers_spec.rb +35 -0
  66. data/spec/cli/utility_spec.rb +38 -0
  67. data/spec/compressor/bzip2_spec.rb +1 -1
  68. data/spec/compressor/gzip_spec.rb +1 -1
  69. data/spec/compressor/lzma_spec.rb +1 -1
  70. data/spec/compressor/pbzip2_spec.rb +63 -0
  71. data/spec/configuration/base_spec.rb +1 -1
  72. data/spec/configuration/compressor/bzip2_spec.rb +1 -1
  73. data/spec/configuration/compressor/gzip_spec.rb +1 -1
  74. data/spec/configuration/compressor/lzma_spec.rb +1 -1
  75. data/spec/configuration/database/base_spec.rb +1 -1
  76. data/spec/configuration/database/mongodb_spec.rb +1 -1
  77. data/spec/configuration/database/mysql_spec.rb +1 -1
  78. data/spec/configuration/database/postgresql_spec.rb +1 -1
  79. data/spec/configuration/database/redis_spec.rb +1 -1
  80. data/spec/configuration/database/riak_spec.rb +31 -0
  81. data/spec/configuration/encryptor/gpg_spec.rb +1 -1
  82. data/spec/configuration/encryptor/open_ssl_spec.rb +4 -1
  83. data/spec/configuration/notifier/campfire_spec.rb +1 -1
  84. data/spec/configuration/notifier/hipchat_spec.rb +43 -0
  85. data/spec/configuration/notifier/mail_spec.rb +34 -22
  86. data/spec/configuration/notifier/presently_spec.rb +1 -1
  87. data/spec/configuration/notifier/prowl_spec.rb +28 -0
  88. data/spec/configuration/notifier/twitter_spec.rb +1 -1
  89. data/spec/configuration/storage/cloudfiles_spec.rb +19 -16
  90. data/spec/configuration/storage/dropbox_spec.rb +1 -1
  91. data/spec/configuration/storage/ftp_spec.rb +1 -1
  92. data/spec/configuration/storage/local_spec.rb +1 -1
  93. data/spec/configuration/storage/ninefold_spec.rb +1 -1
  94. data/spec/configuration/storage/rsync_spec.rb +1 -1
  95. data/spec/configuration/storage/s3_spec.rb +1 -1
  96. data/spec/configuration/storage/scp_spec.rb +1 -1
  97. data/spec/configuration/storage/sftp_spec.rb +1 -1
  98. data/spec/configuration/syncer/rsync_spec.rb +1 -1
  99. data/spec/configuration/syncer/s3_spec.rb +1 -1
  100. data/spec/database/base_spec.rb +10 -1
  101. data/spec/database/mongodb_spec.rb +34 -7
  102. data/spec/database/mysql_spec.rb +8 -7
  103. data/spec/database/postgresql_spec.rb +8 -7
  104. data/spec/database/redis_spec.rb +39 -9
  105. data/spec/database/riak_spec.rb +50 -0
  106. data/spec/encryptor/gpg_spec.rb +1 -1
  107. data/spec/encryptor/open_ssl_spec.rb +77 -20
  108. data/spec/errors_spec.rb +306 -0
  109. data/spec/finder_spec.rb +91 -0
  110. data/spec/logger_spec.rb +254 -33
  111. data/spec/model_spec.rb +120 -15
  112. data/spec/notifier/campfire_spec.rb +127 -52
  113. data/spec/notifier/hipchat_spec.rb +193 -0
  114. data/spec/notifier/mail_spec.rb +290 -74
  115. data/spec/notifier/presently_spec.rb +290 -73
  116. data/spec/notifier/prowl_spec.rb +149 -0
  117. data/spec/notifier/twitter_spec.rb +106 -41
  118. data/spec/spec_helper.rb +8 -2
  119. data/spec/splitter_spec.rb +71 -0
  120. data/spec/storage/base_spec.rb +280 -19
  121. data/spec/storage/cloudfiles_spec.rb +38 -22
  122. data/spec/storage/dropbox_spec.rb +17 -13
  123. data/spec/storage/ftp_spec.rb +145 -55
  124. data/spec/storage/local_spec.rb +6 -6
  125. data/spec/storage/ninefold_spec.rb +70 -29
  126. data/spec/storage/object_spec.rb +44 -44
  127. data/spec/storage/rsync_spec.rb +186 -63
  128. data/spec/storage/s3_spec.rb +23 -24
  129. data/spec/storage/scp_spec.rb +116 -41
  130. data/spec/storage/sftp_spec.rb +124 -46
  131. data/spec/syncer/rsync_spec.rb +3 -3
  132. data/spec/syncer/s3_spec.rb +1 -1
  133. data/spec/version_spec.rb +1 -1
  134. data/templates/cli/utility/archive +13 -0
  135. data/{lib/templates → templates/cli/utility}/compressor/bzip2 +1 -1
  136. data/{lib/templates → templates/cli/utility}/compressor/gzip +1 -1
  137. data/{lib/templates → templates/cli/utility}/compressor/lzma +0 -0
  138. data/templates/cli/utility/compressor/pbzip2 +7 -0
  139. data/templates/cli/utility/config +31 -0
  140. data/{lib/templates → templates/cli/utility}/database/mongodb +1 -1
  141. data/{lib/templates → templates/cli/utility}/database/mysql +1 -1
  142. data/{lib/templates → templates/cli/utility}/database/postgresql +1 -1
  143. data/{lib/templates → templates/cli/utility}/database/redis +1 -1
  144. data/templates/cli/utility/database/riak +8 -0
  145. data/{lib/templates → templates/cli/utility}/encryptor/gpg +1 -1
  146. data/templates/cli/utility/encryptor/openssl +9 -0
  147. data/templates/cli/utility/model.erb +23 -0
  148. data/{lib/templates → templates/cli/utility}/notifier/campfire +2 -1
  149. data/templates/cli/utility/notifier/hipchat +15 -0
  150. data/{lib/templates → templates/cli/utility}/notifier/mail +6 -1
  151. data/{lib/templates → templates/cli/utility}/notifier/presently +1 -0
  152. data/templates/cli/utility/notifier/prowl +11 -0
  153. data/{lib/templates → templates/cli/utility}/notifier/twitter +2 -1
  154. data/templates/cli/utility/splitter +7 -0
  155. data/templates/cli/utility/storage/cloudfiles +12 -0
  156. data/{lib/templates → templates/cli/utility}/storage/dropbox +1 -1
  157. data/{lib/templates → templates/cli/utility}/storage/ftp +0 -0
  158. data/templates/cli/utility/storage/local +7 -0
  159. data/{lib/templates → templates/cli/utility}/storage/ninefold +1 -1
  160. data/templates/cli/utility/storage/rsync +11 -0
  161. data/{lib/templates → templates/cli/utility}/storage/s3 +0 -2
  162. data/templates/cli/utility/storage/scp +11 -0
  163. data/templates/cli/utility/storage/sftp +11 -0
  164. data/{lib/templates → templates/cli/utility}/syncer/rsync +1 -1
  165. data/{lib/templates → templates/cli/utility}/syncer/s3 +1 -1
  166. data/templates/general/links +11 -0
  167. data/templates/general/version.erb +2 -0
  168. data/templates/notifier/mail/failure.erb +9 -0
  169. data/templates/notifier/mail/success.erb +7 -0
  170. data/templates/notifier/mail/warning.erb +9 -0
  171. data/templates/storage/dropbox/authorization_url.erb +6 -0
  172. data/templates/storage/dropbox/authorized.erb +4 -0
  173. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  174. metadata +81 -45
  175. data/lib/backup/cli.rb +0 -110
  176. data/lib/backup/exception/command_failed.rb +0 -8
  177. data/lib/backup/exception/command_not_found.rb +0 -8
  178. data/lib/backup/notifier/binder.rb +0 -32
  179. data/lib/backup/notifier/templates/notify_failure.erb +0 -33
  180. data/lib/backup/notifier/templates/notify_success.erb +0 -16
  181. data/lib/templates/archive +0 -7
  182. data/lib/templates/encryptor/openssl +0 -8
  183. data/lib/templates/readme +0 -15
  184. data/lib/templates/storage/cloudfiles +0 -11
  185. data/lib/templates/storage/local +0 -7
  186. data/lib/templates/storage/rsync +0 -11
  187. data/lib/templates/storage/scp +0 -11
  188. data/lib/templates/storage/sftp +0 -11
@@ -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 = name.to_sym
27
- @paths = Array.new
28
- @excludes = Array.new
29
- @archive_path = File.join(TMP_PATH, TRIGGER, 'archive')
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
- Logger.message("#{ self.class } started packaging and archiving #{ paths.map { |path| "\"#{path}\""}.join(", ") }.")
52
- run("#{ utility(:tar) } -c -f '#{ File.join(archive_path, "#{name}.tar") }' #{ paths_to_exclude } #{ paths_to_package } 2> /dev/null")
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