backupii 0.1.0.pre.alpha.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.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +37 -0
  4. data/bin/backupii +5 -0
  5. data/bin/docker_test +24 -0
  6. data/lib/backup/archive.rb +171 -0
  7. data/lib/backup/binder.rb +23 -0
  8. data/lib/backup/cleaner.rb +114 -0
  9. data/lib/backup/cli.rb +376 -0
  10. data/lib/backup/cloud_io/base.rb +40 -0
  11. data/lib/backup/cloud_io/cloud_files.rb +301 -0
  12. data/lib/backup/cloud_io/s3.rb +256 -0
  13. data/lib/backup/compressor/base.rb +34 -0
  14. data/lib/backup/compressor/bzip2.rb +37 -0
  15. data/lib/backup/compressor/custom.rb +51 -0
  16. data/lib/backup/compressor/gzip.rb +76 -0
  17. data/lib/backup/config/dsl.rb +103 -0
  18. data/lib/backup/config/helpers.rb +139 -0
  19. data/lib/backup/config.rb +122 -0
  20. data/lib/backup/database/base.rb +89 -0
  21. data/lib/backup/database/mongodb.rb +189 -0
  22. data/lib/backup/database/mysql.rb +194 -0
  23. data/lib/backup/database/openldap.rb +97 -0
  24. data/lib/backup/database/postgresql.rb +134 -0
  25. data/lib/backup/database/redis.rb +179 -0
  26. data/lib/backup/database/riak.rb +82 -0
  27. data/lib/backup/database/sqlite.rb +57 -0
  28. data/lib/backup/encryptor/base.rb +29 -0
  29. data/lib/backup/encryptor/gpg.rb +745 -0
  30. data/lib/backup/encryptor/open_ssl.rb +76 -0
  31. data/lib/backup/errors.rb +55 -0
  32. data/lib/backup/logger/console.rb +50 -0
  33. data/lib/backup/logger/fog_adapter.rb +27 -0
  34. data/lib/backup/logger/logfile.rb +134 -0
  35. data/lib/backup/logger/syslog.rb +116 -0
  36. data/lib/backup/logger.rb +199 -0
  37. data/lib/backup/model.rb +478 -0
  38. data/lib/backup/notifier/base.rb +128 -0
  39. data/lib/backup/notifier/campfire.rb +63 -0
  40. data/lib/backup/notifier/command.rb +101 -0
  41. data/lib/backup/notifier/datadog.rb +107 -0
  42. data/lib/backup/notifier/flowdock.rb +101 -0
  43. data/lib/backup/notifier/hipchat.rb +118 -0
  44. data/lib/backup/notifier/http_post.rb +116 -0
  45. data/lib/backup/notifier/mail.rb +235 -0
  46. data/lib/backup/notifier/nagios.rb +67 -0
  47. data/lib/backup/notifier/pagerduty.rb +82 -0
  48. data/lib/backup/notifier/prowl.rb +70 -0
  49. data/lib/backup/notifier/pushover.rb +73 -0
  50. data/lib/backup/notifier/ses.rb +126 -0
  51. data/lib/backup/notifier/slack.rb +149 -0
  52. data/lib/backup/notifier/twitter.rb +57 -0
  53. data/lib/backup/notifier/zabbix.rb +62 -0
  54. data/lib/backup/package.rb +53 -0
  55. data/lib/backup/packager.rb +108 -0
  56. data/lib/backup/pipeline.rb +122 -0
  57. data/lib/backup/splitter.rb +75 -0
  58. data/lib/backup/storage/base.rb +72 -0
  59. data/lib/backup/storage/cloud_files.rb +158 -0
  60. data/lib/backup/storage/cycler.rb +73 -0
  61. data/lib/backup/storage/dropbox.rb +208 -0
  62. data/lib/backup/storage/ftp.rb +118 -0
  63. data/lib/backup/storage/local.rb +63 -0
  64. data/lib/backup/storage/qiniu.rb +68 -0
  65. data/lib/backup/storage/rsync.rb +251 -0
  66. data/lib/backup/storage/s3.rb +157 -0
  67. data/lib/backup/storage/scp.rb +67 -0
  68. data/lib/backup/storage/sftp.rb +82 -0
  69. data/lib/backup/syncer/base.rb +70 -0
  70. data/lib/backup/syncer/cloud/base.rb +180 -0
  71. data/lib/backup/syncer/cloud/cloud_files.rb +83 -0
  72. data/lib/backup/syncer/cloud/local_file.rb +99 -0
  73. data/lib/backup/syncer/cloud/s3.rb +118 -0
  74. data/lib/backup/syncer/rsync/base.rb +55 -0
  75. data/lib/backup/syncer/rsync/local.rb +29 -0
  76. data/lib/backup/syncer/rsync/pull.rb +49 -0
  77. data/lib/backup/syncer/rsync/push.rb +206 -0
  78. data/lib/backup/template.rb +45 -0
  79. data/lib/backup/utilities.rb +235 -0
  80. data/lib/backup/version.rb +5 -0
  81. data/lib/backup.rb +141 -0
  82. data/templates/cli/archive +28 -0
  83. data/templates/cli/compressor/bzip2 +4 -0
  84. data/templates/cli/compressor/custom +7 -0
  85. data/templates/cli/compressor/gzip +4 -0
  86. data/templates/cli/config +123 -0
  87. data/templates/cli/databases/mongodb +15 -0
  88. data/templates/cli/databases/mysql +18 -0
  89. data/templates/cli/databases/openldap +24 -0
  90. data/templates/cli/databases/postgresql +16 -0
  91. data/templates/cli/databases/redis +16 -0
  92. data/templates/cli/databases/riak +17 -0
  93. data/templates/cli/databases/sqlite +11 -0
  94. data/templates/cli/encryptor/gpg +27 -0
  95. data/templates/cli/encryptor/openssl +9 -0
  96. data/templates/cli/model +26 -0
  97. data/templates/cli/notifier/zabbix +15 -0
  98. data/templates/cli/notifiers/campfire +12 -0
  99. data/templates/cli/notifiers/command +32 -0
  100. data/templates/cli/notifiers/datadog +57 -0
  101. data/templates/cli/notifiers/flowdock +16 -0
  102. data/templates/cli/notifiers/hipchat +16 -0
  103. data/templates/cli/notifiers/http_post +32 -0
  104. data/templates/cli/notifiers/mail +24 -0
  105. data/templates/cli/notifiers/nagios +13 -0
  106. data/templates/cli/notifiers/pagerduty +12 -0
  107. data/templates/cli/notifiers/prowl +11 -0
  108. data/templates/cli/notifiers/pushover +11 -0
  109. data/templates/cli/notifiers/ses +15 -0
  110. data/templates/cli/notifiers/slack +22 -0
  111. data/templates/cli/notifiers/twitter +13 -0
  112. data/templates/cli/splitter +7 -0
  113. data/templates/cli/storages/cloud_files +11 -0
  114. data/templates/cli/storages/dropbox +20 -0
  115. data/templates/cli/storages/ftp +13 -0
  116. data/templates/cli/storages/local +8 -0
  117. data/templates/cli/storages/qiniu +12 -0
  118. data/templates/cli/storages/rsync +17 -0
  119. data/templates/cli/storages/s3 +16 -0
  120. data/templates/cli/storages/scp +15 -0
  121. data/templates/cli/storages/sftp +15 -0
  122. data/templates/cli/syncers/cloud_files +22 -0
  123. data/templates/cli/syncers/rsync_local +20 -0
  124. data/templates/cli/syncers/rsync_pull +28 -0
  125. data/templates/cli/syncers/rsync_push +28 -0
  126. data/templates/cli/syncers/s3 +27 -0
  127. data/templates/general/links +3 -0
  128. data/templates/general/version.erb +2 -0
  129. data/templates/notifier/mail/failure.erb +16 -0
  130. data/templates/notifier/mail/success.erb +16 -0
  131. data/templates/notifier/mail/warning.erb +16 -0
  132. data/templates/storage/dropbox/authorization_url.erb +6 -0
  133. data/templates/storage/dropbox/authorized.erb +4 -0
  134. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  135. metadata +507 -0
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Backup
4
+ module Compressor
5
+ class Gzip < Base
6
+ class Error < Backup::Error; end
7
+ extend Utilities::Helpers
8
+
9
+ ##
10
+ # Specify the level of compression to use.
11
+ #
12
+ # Values should be a single digit from 1 to 9.
13
+ # Note that setting the level to either extreme may or may not
14
+ # give the desired result. Be sure to check the documentation
15
+ # for the compressor being used.
16
+ #
17
+ # The default `level` is 6.
18
+ attr_accessor :level
19
+
20
+ ##
21
+ # Use the `--rsyncable` option with `gzip`.
22
+ #
23
+ # This option directs `gzip` to compress data using an algorithm that
24
+ # allows `rsync` to efficiently detect changes. This is especially useful
25
+ # when used to compress `Archive` or `Database` backups that will be
26
+ # stored using Backup's `RSync` Storage option.
27
+ #
28
+ # The `--rsyncable` option is only available on patched versions of
29
+ # `gzip`. While most distributions apply this patch, this option may not
30
+ # be available on your system. If it's not available, Backup will log a
31
+ # warning and continue to use the compressor without this option.
32
+ attr_accessor :rsyncable
33
+
34
+ ##
35
+ # Determine if +--rsyncable+ is supported and cache the result.
36
+ def self.has_rsyncable?
37
+ return @has_rsyncable unless @has_rsyncable.nil?
38
+
39
+ cmd = "#{utility(:gzip)} --rsyncable --version >/dev/null 2>&1; echo $?"
40
+ @has_rsyncable = `#{cmd}`.chomp == "0"
41
+ end
42
+
43
+ ##
44
+ # Creates a new instance of Backup::Compressor::Gzip
45
+ def initialize(&block)
46
+ load_defaults!
47
+
48
+ @level ||= false
49
+ @rsyncable ||= false
50
+
51
+ instance_eval(&block) if block_given?
52
+
53
+ @cmd = "#{utility(:gzip)}#{options}"
54
+ @ext = ".gz"
55
+ end
56
+
57
+ private
58
+
59
+ def options
60
+ opts = String.new
61
+ opts << " -#{@level}" if @level
62
+ if @rsyncable
63
+ if self.class.has_rsyncable?
64
+ opts << " --rsyncable"
65
+ else
66
+ Logger.warn Error.new(<<-EOS)
67
+ 'rsyncable' option ignored.
68
+ Your system's 'gzip' does not support the `--rsyncable` option.
69
+ EOS
70
+ end
71
+ end
72
+ opts
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Backup
4
+ module Config
5
+ # Context for loading user config.rb and model files.
6
+ class DSL
7
+ class Error < Backup::Error; end
8
+ Model = Backup::Model
9
+
10
+ class << self
11
+ private
12
+
13
+ # List the available database, storage, syncer, compressor, encryptor
14
+ # and notifier constants. These are used to define constant names within
15
+ # Backup::Config::DSL so that users may use a constant instead of a
16
+ # string. Nested namespaces are represented using Hashs. Deep nesting
17
+ # supported.
18
+ #
19
+ # Example, instead of:
20
+ # database "MySQL" do |mysql|
21
+ # sync_with "RSync::Local" do |rsync|
22
+ #
23
+ # You can do:
24
+ # database MySQL do |mysql|
25
+ # sync_with RSync::Local do |rsync|
26
+ #
27
+ def add_dsl_constants
28
+ create_modules(
29
+ DSL,
30
+ [ # Databases
31
+ %w[MySQL PostgreSQL MongoDB Redis Riak OpenLDAP SQLite],
32
+ # Storages
33
+ %w[S3 CloudFiles Dropbox FTP SFTP SCP RSync Local Qiniu],
34
+ # Compressors
35
+ %w[Gzip Bzip2 Custom],
36
+ # Encryptors
37
+ %w[OpenSSL GPG],
38
+ # Syncers
39
+ [
40
+ { "Cloud" => %w[CloudFiles S3] },
41
+ { "RSync" => %w[Push Pull Local] }
42
+ ],
43
+ # Notifiers
44
+ %w[Mail Twitter Campfire Prowl Hipchat PagerDuty Pushover
45
+ HttpPost Nagios Slack FlowDock Zabbix Ses DataDog Command]
46
+ ]
47
+ )
48
+ end
49
+
50
+ def create_modules(scope, names)
51
+ names.flatten.each do |name|
52
+ if name.is_a?(Hash)
53
+ name.each do |key, val|
54
+ create_modules(get_or_create_empty_module(scope, key), [val])
55
+ end
56
+ else
57
+ get_or_create_empty_module(scope, name)
58
+ end
59
+ end
60
+ end
61
+
62
+ def get_or_create_empty_module(scope, const)
63
+ if scope.const_defined?(const)
64
+ scope.const_get(const)
65
+ else
66
+ scope.const_set(const, Module.new)
67
+ end
68
+ end
69
+ end
70
+
71
+ add_dsl_constants # add constants on load
72
+
73
+ attr_reader :_config_options
74
+
75
+ def initialize
76
+ @_config_options = {}
77
+ end
78
+
79
+ # Allow users to set command line path options in config.rb
80
+ [:root_path, :data_path, :tmp_path].each do |name|
81
+ define_method name do |path|
82
+ _config_options[name] = path
83
+ end
84
+ end
85
+
86
+ # Allows users to create preconfigured models.
87
+ def preconfigure(name, &block)
88
+ unless name.is_a?(String) && name =~ %r{^[A-Z]}
89
+ raise Error, "Preconfigured model names must be given as a string " \
90
+ "and start with a capital letter."
91
+ end
92
+
93
+ if DSL.const_defined?(name)
94
+ raise Error, "'#{name}' is already in use " \
95
+ "and can not be used for a preconfigured model."
96
+ end
97
+
98
+ DSL.const_set(name, Class.new(Model))
99
+ DSL.const_get(name).preconfigure(&block)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
4
+
5
+ module Backup
6
+ module Config
7
+ module Helpers
8
+ def self.included(klass)
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def defaults
14
+ @defaults ||= Config::Defaults.new
15
+
16
+ if block_given?
17
+ yield @defaults
18
+ else
19
+ @defaults
20
+ end
21
+ end
22
+
23
+ # Used only within the specs
24
+ def clear_defaults!
25
+ defaults.reset!
26
+ end
27
+
28
+ def deprecations
29
+ @deprecations ||= {}
30
+ end
31
+
32
+ def log_deprecation_warning(name, deprecation)
33
+ msg = "#{self}##{name} has been deprecated as of " \
34
+ "backup v.#{deprecation[:version]}".dup
35
+ msg << "\n#{deprecation[:message]}" if deprecation[:message]
36
+ Logger.warn Config::Error.new(<<-EOS)
37
+ [DEPRECATION WARNING]
38
+ #{msg}
39
+ EOS
40
+ end
41
+
42
+ protected
43
+
44
+ ##
45
+ # Method to deprecate an attribute.
46
+ #
47
+ # :version
48
+ # Must be set to the backup version which will first
49
+ # introduce the deprecation.
50
+ #
51
+ # :action
52
+ # If set, this Proc will be called with a reference to the
53
+ # class instance and the value set on the deprecated accessor.
54
+ # e.g. deprecation[:action].call(klass, value)
55
+ # This should perform whatever action is neccessary, such as
56
+ # transferring the value to a new accessor.
57
+ #
58
+ # :message
59
+ # If set, this will be appended to #log_deprecation_warning
60
+ #
61
+ # Note that this replaces the `attr_accessor` method, or other
62
+ # method previously used to set the accessor being deprecated.
63
+ # #method_missing will handle any calls to `name=`.
64
+ #
65
+ def attr_deprecate(name, args = {})
66
+ deprecations[name] = {
67
+ version: nil,
68
+ message: nil,
69
+ action: nil
70
+ }.merge(args)
71
+ end
72
+ end # ClassMethods
73
+
74
+ private
75
+
76
+ ##
77
+ # Sets any pre-configured default values.
78
+ # If a default value was set for an invalid accessor,
79
+ # this will raise a NameError.
80
+ def load_defaults!
81
+ self.class.defaults._attributes.each do |name|
82
+ val = self.class.defaults.send(name)
83
+ val = val.dup rescue val
84
+ send(:"#{name}=", val)
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Check missing methods for deprecated attribute accessors.
90
+ #
91
+ # If a value is set on an accessor that has been deprecated
92
+ # using #attr_deprecate, a warning will be issued and any
93
+ # :action (Proc) specified will be called with a reference to
94
+ # the class instance and the value set on the deprecated accessor.
95
+ # See #attr_deprecate and #log_deprecation_warning
96
+ #
97
+ # Note that OpenStruct (used for setting defaults) does not allow
98
+ # multiple arguments when assigning values for members.
99
+ # So, we won't allow it here either, even though an attr_accessor
100
+ # will accept and convert them into an Array. Therefore, setting
101
+ # an option value using multiple values, whether as a default or
102
+ # directly on the class' accessor, should not be supported.
103
+ # i.e. if an option will accept being set as an Array, then it
104
+ # should be explicitly set as such. e.g. option = [val1, val2]
105
+ #
106
+ def method_missing(name, *args)
107
+ deprecation = nil
108
+ if (method = name.to_s.chomp!("="))
109
+ if (len = args.count) != 1
110
+ raise ArgumentError,
111
+ "wrong number of arguments (#{len} for 1)", caller(1)
112
+ end
113
+ deprecation = self.class.deprecations[method.to_sym]
114
+ end
115
+
116
+ if deprecation
117
+ self.class.log_deprecation_warning(method, deprecation)
118
+ deprecation[:action].call(self, args[0]) if deprecation[:action]
119
+ else
120
+ super
121
+ end
122
+ end
123
+ end # Helpers
124
+
125
+ # Store for pre-configured defaults.
126
+ class Defaults < OpenStruct
127
+ # Returns an Array of all attribute method names
128
+ # that default values were set for.
129
+ def _attributes
130
+ @table.keys
131
+ end
132
+
133
+ # Used only within the specs
134
+ def reset!
135
+ @table.clear
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "backup/config/dsl"
4
+ require "backup/config/helpers"
5
+
6
+ module Backup
7
+ module Config
8
+ class Error < Backup::Error; end
9
+
10
+ DEFAULTS = {
11
+ config_file: "config.rb",
12
+ data_path: ".data",
13
+ tmp_path: ".tmp"
14
+ }.freeze
15
+
16
+ class << self
17
+ include Utilities::Helpers
18
+
19
+ attr_reader :user, :root_path, :config_file, :data_path, :tmp_path
20
+
21
+ # Loads the user's +config.rb+ and all model files.
22
+ def load(options = {})
23
+ update(options) # from the command line
24
+
25
+ unless File.exist?(config_file)
26
+ raise Error, "Could not find configuration file: '#{config_file}'."
27
+ end
28
+
29
+ config = File.read(config_file)
30
+ version = Backup::VERSION.split(".").first
31
+ unless config =~ %r{^# Backup v#{version}\.x Configuration$}
32
+ raise Error, <<-EOS
33
+ Invalid Configuration File
34
+ The configuration file at '#{config_file}'
35
+ does not appear to be a Backup v#{version}.x configuration file.
36
+ If you have upgraded to v#{version}.x from a previous version,
37
+ you need to upgrade your configuration file.
38
+ Please see the instructions for upgrading in the Backup documentation.
39
+ EOS
40
+ end
41
+
42
+ dsl = DSL.new
43
+ dsl.instance_eval(config, config_file)
44
+
45
+ update(dsl._config_options) # from config.rb
46
+ update(options) # command line takes precedence
47
+
48
+ dirs = Dir[File.join(File.dirname(config_file), "models", "*.rb")]
49
+ dirs.each do |model|
50
+ dsl.instance_eval(File.read(model), model)
51
+ end
52
+ end
53
+
54
+ def hostname
55
+ @hostname ||= run(utility(:hostname))
56
+ end
57
+
58
+ private
59
+
60
+ # If :root_path is set in the options, all paths will be updated.
61
+ # Otherwise, only the paths given will be updated.
62
+ def update(options = {})
63
+ root_path = options[:root_path].to_s.strip
64
+ new_root = root_path.empty? ? false : set_root_path(root_path)
65
+
66
+ DEFAULTS.each do |name, ending|
67
+ set_path_variable(name, options[name], ending, new_root)
68
+ end
69
+ end
70
+
71
+ # Sets the @root_path to the given +path+ and returns it.
72
+ # Raises an error if the given +path+ does not exist.
73
+ def set_root_path(path)
74
+ # allows #reset! to set the default @root_path,
75
+ # then use #update to set all other paths,
76
+ # without requiring that @root_path exist.
77
+ return @root_path if path == @root_path
78
+
79
+ path = File.expand_path(path)
80
+ unless File.directory?(path)
81
+ raise Error, <<-EOS
82
+ Root Path Not Found
83
+ When specifying a --root-path, the path must exist.
84
+ Path was: #{path}
85
+ EOS
86
+ end
87
+
88
+ @root_path = path
89
+ end
90
+
91
+ def set_path_variable(name, path, ending, root_path)
92
+ # strip any trailing '/' in case the user supplied this as part of
93
+ # an absolute path, so we can match it against File.expand_path()
94
+ path = path.to_s.sub(%r{/\s*$}, "").lstrip
95
+ new_path = false
96
+ # If no path is given, the variable will not be set/updated
97
+ # unless a root_path was given. In which case the value will
98
+ # be updated with our default ending.
99
+ if path.empty?
100
+ new_path = File.join(root_path, ending) if root_path
101
+ else
102
+ # When a path is given, the variable will be set/updated. If the path
103
+ # is relative, it will be joined with root_path (if given), or
104
+ # expanded relative to PWD.
105
+ new_path = File.expand_path(path)
106
+ unless path == new_path
107
+ new_path = File.join(root_path, path) if root_path
108
+ end
109
+ end
110
+ instance_variable_set(:"@#{name}", new_path) if new_path
111
+ end
112
+
113
+ def reset!
114
+ @user = ENV["USER"] || Etc.getpwuid.name
115
+ @root_path = File.join(File.expand_path(ENV["HOME"] || ""), "Backup")
116
+ update(root_path: @root_path)
117
+ end
118
+ end
119
+
120
+ reset! # set defaults on load
121
+ end
122
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Backup
4
+ module Database
5
+ class Error < Backup::Error; end
6
+
7
+ class Base
8
+ include Utilities::Helpers
9
+ include Config::Helpers
10
+
11
+ attr_reader :model, :database_id, :dump_path
12
+
13
+ ##
14
+ # If given, +database_id+ will be appended to the #dump_filename.
15
+ # This is required if multiple Databases of the same class are added to
16
+ # the model.
17
+ def initialize(model, database_id = nil)
18
+ @model = model
19
+ @database_id = database_id.to_s.gsub(%r{\W}, "_") if database_id
20
+ @dump_path = File.join(Config.tmp_path, model.trigger, "databases")
21
+ load_defaults!
22
+ end
23
+
24
+ def perform!
25
+ log!(:started)
26
+ prepare!
27
+ end
28
+
29
+ private
30
+
31
+ def prepare!
32
+ FileUtils.mkdir_p(dump_path)
33
+ end
34
+
35
+ ##
36
+ # Sets the base filename for the final dump file to be saved in
37
+ # +dump_path+, based on the class name. e.g. databases/MySQL.sql
38
+ #
39
+ # +database_id+ will be appended if it is defined.
40
+ # e.g. databases/MySQL-database_id.sql
41
+ #
42
+ # If multiple Databases of the same class are defined and no +database_id+
43
+ # is defined, the user will be warned and one will be auto-generated.
44
+ #
45
+ # Model#initialize calls this method *after* all defined databases have
46
+ # been initialized so `backup check` can report these warnings.
47
+ def dump_filename
48
+ @dump_filename ||=
49
+ begin
50
+ unless database_id
51
+ if model.databases.select { |d| d.class == self.class }.count > 1
52
+ sleep 1
53
+ @database_id = Time.now.to_i.to_s[-5, 5]
54
+ Logger.warn Error.new(<<-EOS)
55
+ Database Identifier Missing
56
+ When multiple Databases are configured in a single Backup Model
57
+ that have the same class (MySQL, PostgreSQL, etc.), the optional
58
+ +database_id+ must be specified to uniquely identify each instance.
59
+ e.g. database MySQL, :database_id do |db|
60
+ This will result in an output file in your final backup package like:
61
+ databases/MySQL-database_id.sql
62
+
63
+ Backup has auto-generated an identifier (#{database_id}) for this
64
+ database dump and will now continue.
65
+ EOS
66
+ end
67
+ end
68
+
69
+ self.class.name.split("::").last +
70
+ (database_id ? "-#{database_id}" : "")
71
+ end
72
+ end
73
+
74
+ def database_name
75
+ @database_name ||= self.class.to_s.sub("Backup::", "") +
76
+ (database_id ? " (#{database_id})" : "")
77
+ end
78
+
79
+ def log!(action)
80
+ msg =
81
+ case action
82
+ when :started then "Started..."
83
+ when :finished then "Finished!"
84
+ end
85
+ Logger.info "#{database_name} #{msg}"
86
+ end
87
+ end
88
+ end
89
+ end