ext_backup 5.0.0.beta.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +33 -0
- data/bin/backup +5 -0
- data/bin/docker_test +24 -0
- data/lib/backup.rb +140 -0
- data/lib/backup/archive.rb +169 -0
- data/lib/backup/binder.rb +18 -0
- data/lib/backup/cleaner.rb +112 -0
- data/lib/backup/cli.rb +370 -0
- data/lib/backup/cloud_io/base.rb +38 -0
- data/lib/backup/cloud_io/cloud_files.rb +296 -0
- data/lib/backup/cloud_io/s3.rb +253 -0
- data/lib/backup/compressor/base.rb +32 -0
- data/lib/backup/compressor/bzip2.rb +35 -0
- data/lib/backup/compressor/custom.rb +49 -0
- data/lib/backup/compressor/gzip.rb +73 -0
- data/lib/backup/config.rb +128 -0
- data/lib/backup/config/dsl.rb +102 -0
- data/lib/backup/config/helpers.rb +137 -0
- data/lib/backup/database/base.rb +86 -0
- data/lib/backup/database/mongodb.rb +186 -0
- data/lib/backup/database/mysql.rb +191 -0
- data/lib/backup/database/openldap.rb +93 -0
- data/lib/backup/database/postgresql.rb +132 -0
- data/lib/backup/database/redis.rb +176 -0
- data/lib/backup/database/riak.rb +79 -0
- data/lib/backup/database/sqlite.rb +55 -0
- data/lib/backup/encryptor/base.rb +27 -0
- data/lib/backup/encryptor/gpg.rb +737 -0
- data/lib/backup/encryptor/open_ssl.rb +74 -0
- data/lib/backup/errors.rb +53 -0
- data/lib/backup/logger.rb +197 -0
- data/lib/backup/logger/console.rb +48 -0
- data/lib/backup/logger/fog_adapter.rb +25 -0
- data/lib/backup/logger/logfile.rb +131 -0
- data/lib/backup/logger/syslog.rb +114 -0
- data/lib/backup/model.rb +472 -0
- data/lib/backup/notifier/base.rb +126 -0
- data/lib/backup/notifier/campfire.rb +61 -0
- data/lib/backup/notifier/command.rb +99 -0
- data/lib/backup/notifier/datadog.rb +104 -0
- data/lib/backup/notifier/flowdock.rb +99 -0
- data/lib/backup/notifier/hipchat.rb +116 -0
- data/lib/backup/notifier/http_post.rb +114 -0
- data/lib/backup/notifier/mail.rb +232 -0
- data/lib/backup/notifier/nagios.rb +65 -0
- data/lib/backup/notifier/pagerduty.rb +79 -0
- data/lib/backup/notifier/prowl.rb +68 -0
- data/lib/backup/notifier/pushover.rb +71 -0
- data/lib/backup/notifier/ses.rb +123 -0
- data/lib/backup/notifier/slack.rb +147 -0
- data/lib/backup/notifier/twitter.rb +55 -0
- data/lib/backup/notifier/zabbix.rb +60 -0
- data/lib/backup/package.rb +51 -0
- data/lib/backup/packager.rb +106 -0
- data/lib/backup/pipeline.rb +120 -0
- data/lib/backup/splitter.rb +73 -0
- data/lib/backup/storage/base.rb +66 -0
- data/lib/backup/storage/cloud_files.rb +156 -0
- data/lib/backup/storage/cycler.rb +70 -0
- data/lib/backup/storage/dropbox.rb +206 -0
- data/lib/backup/storage/ftp.rb +116 -0
- data/lib/backup/storage/local.rb +61 -0
- data/lib/backup/storage/qiniu.rb +65 -0
- data/lib/backup/storage/rsync.rb +246 -0
- data/lib/backup/storage/s3.rb +155 -0
- data/lib/backup/storage/scp.rb +65 -0
- data/lib/backup/storage/sftp.rb +80 -0
- data/lib/backup/syncer/base.rb +67 -0
- data/lib/backup/syncer/cloud/base.rb +176 -0
- data/lib/backup/syncer/cloud/cloud_files.rb +81 -0
- data/lib/backup/syncer/cloud/local_file.rb +97 -0
- data/lib/backup/syncer/cloud/s3.rb +109 -0
- data/lib/backup/syncer/rsync/base.rb +50 -0
- data/lib/backup/syncer/rsync/local.rb +27 -0
- data/lib/backup/syncer/rsync/pull.rb +47 -0
- data/lib/backup/syncer/rsync/push.rb +201 -0
- data/lib/backup/template.rb +41 -0
- data/lib/backup/utilities.rb +233 -0
- data/lib/backup/version.rb +3 -0
- data/lib/ext_backup.rb +5 -0
- data/lib/ext_backup/version.rb +5 -0
- data/templates/cli/archive +28 -0
- data/templates/cli/compressor/bzip2 +4 -0
- data/templates/cli/compressor/custom +7 -0
- data/templates/cli/compressor/gzip +4 -0
- data/templates/cli/config +123 -0
- data/templates/cli/databases/mongodb +15 -0
- data/templates/cli/databases/mysql +18 -0
- data/templates/cli/databases/openldap +24 -0
- data/templates/cli/databases/postgresql +16 -0
- data/templates/cli/databases/redis +16 -0
- data/templates/cli/databases/riak +17 -0
- data/templates/cli/databases/sqlite +11 -0
- data/templates/cli/encryptor/gpg +27 -0
- data/templates/cli/encryptor/openssl +9 -0
- data/templates/cli/model +26 -0
- data/templates/cli/notifier/zabbix +15 -0
- data/templates/cli/notifiers/campfire +12 -0
- data/templates/cli/notifiers/command +32 -0
- data/templates/cli/notifiers/datadog +57 -0
- data/templates/cli/notifiers/flowdock +16 -0
- data/templates/cli/notifiers/hipchat +16 -0
- data/templates/cli/notifiers/http_post +32 -0
- data/templates/cli/notifiers/mail +24 -0
- data/templates/cli/notifiers/nagios +13 -0
- data/templates/cli/notifiers/pagerduty +12 -0
- data/templates/cli/notifiers/prowl +11 -0
- data/templates/cli/notifiers/pushover +11 -0
- data/templates/cli/notifiers/ses +15 -0
- data/templates/cli/notifiers/slack +22 -0
- data/templates/cli/notifiers/twitter +13 -0
- data/templates/cli/splitter +7 -0
- data/templates/cli/storages/cloud_files +11 -0
- data/templates/cli/storages/dropbox +20 -0
- data/templates/cli/storages/ftp +13 -0
- data/templates/cli/storages/local +8 -0
- data/templates/cli/storages/qiniu +12 -0
- data/templates/cli/storages/rsync +17 -0
- data/templates/cli/storages/s3 +16 -0
- data/templates/cli/storages/scp +15 -0
- data/templates/cli/storages/sftp +15 -0
- data/templates/cli/syncers/cloud_files +22 -0
- data/templates/cli/syncers/rsync_local +20 -0
- data/templates/cli/syncers/rsync_pull +28 -0
- data/templates/cli/syncers/rsync_push +28 -0
- data/templates/cli/syncers/s3 +27 -0
- data/templates/general/links +3 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +16 -0
- data/templates/notifier/mail/success.erb +16 -0
- data/templates/notifier/mail/warning.erb +16 -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 +506 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
module Backup
|
|
2
|
+
module Config
|
|
3
|
+
# Context for loading user config.rb and model files.
|
|
4
|
+
class DSL
|
|
5
|
+
class Error < Backup::Error; end
|
|
6
|
+
Model = Backup::Model
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# List the available database, storage, syncer, compressor, encryptor
|
|
12
|
+
# and notifier constants. These are used to define constant names within
|
|
13
|
+
# Backup::Config::DSL so that users may use a constant instead of a string.
|
|
14
|
+
# Nested namespaces are represented using Hashs. Deep nesting supported.
|
|
15
|
+
#
|
|
16
|
+
# Example, instead of:
|
|
17
|
+
# database "MySQL" do |mysql|
|
|
18
|
+
# sync_with "RSync::Local" do |rsync|
|
|
19
|
+
#
|
|
20
|
+
# You can do:
|
|
21
|
+
# database MySQL do |mysql|
|
|
22
|
+
# sync_with RSync::Local do |rsync|
|
|
23
|
+
#
|
|
24
|
+
def add_dsl_constants
|
|
25
|
+
create_modules(
|
|
26
|
+
DSL,
|
|
27
|
+
[ # Databases
|
|
28
|
+
["MySQL", "PostgreSQL", "MongoDB", "Redis", "Riak", "OpenLDAP", "SQLite"],
|
|
29
|
+
# Storages
|
|
30
|
+
["S3", "CloudFiles", "Dropbox", "FTP",
|
|
31
|
+
"SFTP", "SCP", "RSync", "Local", "Qiniu"],
|
|
32
|
+
# Compressors
|
|
33
|
+
["Gzip", "Bzip2", "Custom"],
|
|
34
|
+
# Encryptors
|
|
35
|
+
["OpenSSL", "GPG"],
|
|
36
|
+
# Syncers
|
|
37
|
+
[
|
|
38
|
+
{ "Cloud" => ["CloudFiles", "S3"] },
|
|
39
|
+
{ "RSync" => ["Push", "Pull", "Local"] }
|
|
40
|
+
],
|
|
41
|
+
# Notifiers
|
|
42
|
+
["Mail", "Twitter", "Campfire", "Prowl",
|
|
43
|
+
"Hipchat", "PagerDuty", "Pushover", "HttpPost", "Nagios",
|
|
44
|
+
"Slack", "FlowDock", "Zabbix", "Ses", "DataDog", "Command"]
|
|
45
|
+
]
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def create_modules(scope, names)
|
|
50
|
+
names.flatten.each do |name|
|
|
51
|
+
if name.is_a?(Hash)
|
|
52
|
+
name.each do |key, val|
|
|
53
|
+
create_modules(get_or_create_empty_module(scope, key), [val])
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
get_or_create_empty_module(scope, name)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def get_or_create_empty_module(scope, const)
|
|
62
|
+
if scope.const_defined?(const)
|
|
63
|
+
scope.const_get(const)
|
|
64
|
+
else
|
|
65
|
+
scope.const_set(const, Module.new)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
add_dsl_constants # add constants on load
|
|
71
|
+
|
|
72
|
+
attr_reader :_config_options
|
|
73
|
+
|
|
74
|
+
def initialize
|
|
75
|
+
@_config_options = {}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Allow users to set command line path options in config.rb
|
|
79
|
+
[:root_path, :data_path, :tmp_path].each do |name|
|
|
80
|
+
define_method name do |path|
|
|
81
|
+
_config_options[name] = path
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Allows users to create preconfigured models.
|
|
86
|
+
def preconfigure(name, &block)
|
|
87
|
+
unless name.is_a?(String) && name =~ /^[A-Z]/
|
|
88
|
+
raise Error, "Preconfigured model names must be given as a string " \
|
|
89
|
+
"and start with a capital letter."
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if DSL.const_defined?(name)
|
|
93
|
+
raise Error, "'#{name}' is already in use " \
|
|
94
|
+
"and can not be used for a preconfigured model."
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
DSL.const_set(name, Class.new(Model))
|
|
98
|
+
DSL.const_get(name).preconfigure(&block)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require "ostruct"
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
module Config
|
|
5
|
+
module Helpers
|
|
6
|
+
def self.included(klass)
|
|
7
|
+
klass.extend ClassMethods
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def defaults
|
|
12
|
+
@defaults ||= Config::Defaults.new
|
|
13
|
+
|
|
14
|
+
if block_given?
|
|
15
|
+
yield @defaults
|
|
16
|
+
else
|
|
17
|
+
@defaults
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Used only within the specs
|
|
22
|
+
def clear_defaults!
|
|
23
|
+
defaults.reset!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def deprecations
|
|
27
|
+
@deprecations ||= {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def log_deprecation_warning(name, deprecation)
|
|
31
|
+
msg = "#{self}##{name} has been deprecated as of " \
|
|
32
|
+
"backup v.#{deprecation[:version]}"
|
|
33
|
+
msg << "\n#{deprecation[:message]}" if deprecation[:message]
|
|
34
|
+
Logger.warn Config::Error.new(<<-EOS)
|
|
35
|
+
[DEPRECATION WARNING]
|
|
36
|
+
#{msg}
|
|
37
|
+
EOS
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
protected
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Method to deprecate an attribute.
|
|
44
|
+
#
|
|
45
|
+
# :version
|
|
46
|
+
# Must be set to the backup version which will first
|
|
47
|
+
# introduce the deprecation.
|
|
48
|
+
#
|
|
49
|
+
# :action
|
|
50
|
+
# If set, this Proc will be called with a reference to the
|
|
51
|
+
# class instance and the value set on the deprecated accessor.
|
|
52
|
+
# e.g. deprecation[:action].call(klass, value)
|
|
53
|
+
# This should perform whatever action is neccessary, such as
|
|
54
|
+
# transferring the value to a new accessor.
|
|
55
|
+
#
|
|
56
|
+
# :message
|
|
57
|
+
# If set, this will be appended to #log_deprecation_warning
|
|
58
|
+
#
|
|
59
|
+
# Note that this replaces the `attr_accessor` method, or other
|
|
60
|
+
# method previously used to set the accessor being deprecated.
|
|
61
|
+
# #method_missing will handle any calls to `name=`.
|
|
62
|
+
#
|
|
63
|
+
def attr_deprecate(name, args = {})
|
|
64
|
+
deprecations[name] = {
|
|
65
|
+
version: nil,
|
|
66
|
+
message: nil,
|
|
67
|
+
action: nil
|
|
68
|
+
}.merge(args)
|
|
69
|
+
end
|
|
70
|
+
end # ClassMethods
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# Sets any pre-configured default values.
|
|
76
|
+
# If a default value was set for an invalid accessor,
|
|
77
|
+
# this will raise a NameError.
|
|
78
|
+
def load_defaults!
|
|
79
|
+
self.class.defaults._attributes.each do |name|
|
|
80
|
+
val = self.class.defaults.send(name)
|
|
81
|
+
val = val.dup rescue val
|
|
82
|
+
send(:"#{ name }=", val)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# Check missing methods for deprecated attribute accessors.
|
|
88
|
+
#
|
|
89
|
+
# If a value is set on an accessor that has been deprecated
|
|
90
|
+
# using #attr_deprecate, a warning will be issued and any
|
|
91
|
+
# :action (Proc) specified will be called with a reference to
|
|
92
|
+
# the class instance and the value set on the deprecated accessor.
|
|
93
|
+
# See #attr_deprecate and #log_deprecation_warning
|
|
94
|
+
#
|
|
95
|
+
# Note that OpenStruct (used for setting defaults) does not allow
|
|
96
|
+
# multiple arguments when assigning values for members.
|
|
97
|
+
# So, we won't allow it here either, even though an attr_accessor
|
|
98
|
+
# will accept and convert them into an Array. Therefore, setting
|
|
99
|
+
# an option value using multiple values, whether as a default or
|
|
100
|
+
# directly on the class' accessor, should not be supported.
|
|
101
|
+
# i.e. if an option will accept being set as an Array, then it
|
|
102
|
+
# should be explicitly set as such. e.g. option = [val1, val2]
|
|
103
|
+
#
|
|
104
|
+
def method_missing(name, *args)
|
|
105
|
+
deprecation = nil
|
|
106
|
+
if method = name.to_s.chomp!("=")
|
|
107
|
+
if (len = args.count) != 1
|
|
108
|
+
raise ArgumentError,
|
|
109
|
+
"wrong number of arguments (#{len} for 1)", caller(1)
|
|
110
|
+
end
|
|
111
|
+
deprecation = self.class.deprecations[method.to_sym]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if deprecation
|
|
115
|
+
self.class.log_deprecation_warning(method, deprecation)
|
|
116
|
+
deprecation[:action].call(self, args[0]) if deprecation[:action]
|
|
117
|
+
else
|
|
118
|
+
super
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end # Helpers
|
|
122
|
+
|
|
123
|
+
# Store for pre-configured defaults.
|
|
124
|
+
class Defaults < OpenStruct
|
|
125
|
+
# Returns an Array of all attribute method names
|
|
126
|
+
# that default values were set for.
|
|
127
|
+
def _attributes
|
|
128
|
+
@table.keys
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Used only within the specs
|
|
132
|
+
def reset!
|
|
133
|
+
@table.clear
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Backup
|
|
2
|
+
module Database
|
|
3
|
+
class Error < Backup::Error; end
|
|
4
|
+
|
|
5
|
+
class Base
|
|
6
|
+
include Utilities::Helpers
|
|
7
|
+
include Config::Helpers
|
|
8
|
+
|
|
9
|
+
attr_reader :model, :database_id, :dump_path
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# If given, +database_id+ will be appended to the #dump_filename.
|
|
13
|
+
# This is required if multiple Databases of the same class are added to
|
|
14
|
+
# the model.
|
|
15
|
+
def initialize(model, database_id = nil)
|
|
16
|
+
@model = model
|
|
17
|
+
@database_id = database_id.to_s.gsub(/\W/, "_") if database_id
|
|
18
|
+
@dump_path = File.join(Config.tmp_path, model.trigger, "databases")
|
|
19
|
+
load_defaults!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def perform!
|
|
23
|
+
log!(:started)
|
|
24
|
+
prepare!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def prepare!
|
|
30
|
+
FileUtils.mkdir_p(dump_path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# Sets the base filename for the final dump file to be saved in +dump_path+,
|
|
35
|
+
# based on the class name. e.g. databases/MySQL.sql
|
|
36
|
+
#
|
|
37
|
+
# +database_id+ will be appended if it is defined.
|
|
38
|
+
# e.g. databases/MySQL-database_id.sql
|
|
39
|
+
#
|
|
40
|
+
# If multiple Databases of the same class are defined and no +database_id+
|
|
41
|
+
# is defined, the user will be warned and one will be auto-generated.
|
|
42
|
+
#
|
|
43
|
+
# Model#initialize calls this method *after* all defined databases have
|
|
44
|
+
# been initialized so `backup check` can report these warnings.
|
|
45
|
+
def dump_filename
|
|
46
|
+
@dump_filename ||=
|
|
47
|
+
begin
|
|
48
|
+
unless database_id
|
|
49
|
+
if model.databases.select { |d| d.class == self.class }.count > 1
|
|
50
|
+
sleep 1
|
|
51
|
+
@database_id = Time.now.to_i.to_s[-5, 5]
|
|
52
|
+
Logger.warn Error.new(<<-EOS)
|
|
53
|
+
Database Identifier Missing
|
|
54
|
+
When multiple Databases are configured in a single Backup Model
|
|
55
|
+
that have the same class (MySQL, PostgreSQL, etc.), the optional
|
|
56
|
+
+database_id+ must be specified to uniquely identify each instance.
|
|
57
|
+
e.g. database MySQL, :database_id do |db|
|
|
58
|
+
This will result in an output file in your final backup package like:
|
|
59
|
+
databases/MySQL-database_id.sql
|
|
60
|
+
|
|
61
|
+
Backup has auto-generated an identifier (#{database_id}) for this
|
|
62
|
+
database dump and will now continue.
|
|
63
|
+
EOS
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
self.class.name.split("::").last + (database_id ? "-#{database_id}" : "")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def database_name
|
|
72
|
+
@database_name ||= self.class.to_s.sub("Backup::", "") +
|
|
73
|
+
(database_id ? " (#{database_id})" : "")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def log!(action)
|
|
77
|
+
msg =
|
|
78
|
+
case action
|
|
79
|
+
when :started then "Started..."
|
|
80
|
+
when :finished then "Finished!"
|
|
81
|
+
end
|
|
82
|
+
Logger.info "#{database_name} #{msg}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
module Backup
|
|
2
|
+
module Database
|
|
3
|
+
class MongoDB < Base
|
|
4
|
+
class Error < Backup::Error; end
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# Name of the database that needs to get dumped
|
|
8
|
+
attr_accessor :name
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Credentials for the specified database
|
|
12
|
+
attr_accessor :username, :password, :authdb
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Connectivity options
|
|
16
|
+
attr_accessor :host, :port
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# IPv6 support (disabled by default)
|
|
20
|
+
attr_accessor :ipv6
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Collections to dump, collections that aren't specified won't get dumped
|
|
24
|
+
attr_accessor :only_collections
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Additional "mongodump" options
|
|
28
|
+
attr_accessor :additional_options
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# Forces mongod to flush all pending write operations to the disk and
|
|
32
|
+
# locks the entire mongod instance to prevent additional writes until the
|
|
33
|
+
# dump is complete.
|
|
34
|
+
#
|
|
35
|
+
# Note that if Profiling is enabled, this will disable it and will not
|
|
36
|
+
# re-enable it after the dump is complete.
|
|
37
|
+
attr_accessor :lock
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Creates a dump of the database that includes an oplog, to create a
|
|
41
|
+
# point-in-time snapshot of the state of a mongod instance.
|
|
42
|
+
#
|
|
43
|
+
# If this option is used, you would not use the `lock` option.
|
|
44
|
+
#
|
|
45
|
+
# This will only work against nodes that maintain a oplog.
|
|
46
|
+
# This includes all members of a replica set, as well as master nodes in
|
|
47
|
+
# master/slave replication deployments.
|
|
48
|
+
attr_accessor :oplog
|
|
49
|
+
|
|
50
|
+
def initialize(model, database_id = nil, &block)
|
|
51
|
+
super
|
|
52
|
+
instance_eval(&block) if block_given?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def perform!
|
|
56
|
+
super
|
|
57
|
+
|
|
58
|
+
lock_database if @lock
|
|
59
|
+
dump!
|
|
60
|
+
package!
|
|
61
|
+
ensure
|
|
62
|
+
unlock_database if @lock
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# Performs all required mongodump commands, dumping the output files
|
|
69
|
+
# into the +dump_packaging_path+ directory for packaging.
|
|
70
|
+
def dump!
|
|
71
|
+
FileUtils.mkdir_p dump_packaging_path
|
|
72
|
+
|
|
73
|
+
collections = Array(only_collections)
|
|
74
|
+
if collections.empty?
|
|
75
|
+
run(mongodump)
|
|
76
|
+
else
|
|
77
|
+
collections.each do |collection|
|
|
78
|
+
run("#{mongodump} --collection='#{collection}'")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
##
|
|
84
|
+
# Creates a tar archive of the +dump_packaging_path+ directory
|
|
85
|
+
# and stores it in the +dump_path+ using +dump_filename+.
|
|
86
|
+
#
|
|
87
|
+
# <trigger>/databases/MongoDB[-<database_id>].tar[.gz]
|
|
88
|
+
#
|
|
89
|
+
# If successful, +dump_packaging_path+ is removed.
|
|
90
|
+
def package!
|
|
91
|
+
pipeline = Pipeline.new
|
|
92
|
+
dump_ext = "tar"
|
|
93
|
+
|
|
94
|
+
pipeline << "#{utility(:tar)} -cf - " \
|
|
95
|
+
"-C '#{dump_path}' '#{dump_filename}'"
|
|
96
|
+
|
|
97
|
+
if model.compressor
|
|
98
|
+
model.compressor.compress_with do |command, ext|
|
|
99
|
+
pipeline << command
|
|
100
|
+
dump_ext << ext
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
pipeline << "#{utility(:cat)} > " \
|
|
105
|
+
"'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
|
|
106
|
+
|
|
107
|
+
pipeline.run
|
|
108
|
+
if pipeline.success?
|
|
109
|
+
FileUtils.rm_rf dump_packaging_path
|
|
110
|
+
log!(:finished)
|
|
111
|
+
else
|
|
112
|
+
raise Error, "Dump Failed!\n#{pipeline.error_messages}"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def dump_packaging_path
|
|
117
|
+
File.join(dump_path, dump_filename)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def mongodump
|
|
121
|
+
"#{utility(:mongodump)} #{name_option} #{credential_options} " \
|
|
122
|
+
"#{connectivity_options} #{ipv6_option} #{oplog_option} " \
|
|
123
|
+
"#{user_options} --out='#{dump_packaging_path}'"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def name_option
|
|
127
|
+
return unless name
|
|
128
|
+
"--db='#{name}'"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def credential_options
|
|
132
|
+
opts = []
|
|
133
|
+
opts << "--username='#{username}'" if username
|
|
134
|
+
opts << "--password='#{password}'" if password
|
|
135
|
+
opts << "--authenticationDatabase='#{authdb}'" if authdb
|
|
136
|
+
opts.join(" ")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def connectivity_options
|
|
140
|
+
opts = []
|
|
141
|
+
opts << "--host='#{host}'" if host
|
|
142
|
+
opts << "--port='#{port}'" if port
|
|
143
|
+
opts.join(" ")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def ipv6_option
|
|
147
|
+
"--ipv6" if ipv6
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def oplog_option
|
|
151
|
+
"--oplog" if oplog
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def user_options
|
|
155
|
+
Array(additional_options).join(" ")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def lock_database
|
|
159
|
+
lock_command = <<-EOS.gsub(/^ +/, "")
|
|
160
|
+
echo 'use admin
|
|
161
|
+
db.setProfilingLevel(0)
|
|
162
|
+
db.fsyncLock()' | #{mongo_shell}
|
|
163
|
+
EOS
|
|
164
|
+
|
|
165
|
+
run(lock_command)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def unlock_database
|
|
169
|
+
unlock_command = <<-EOS.gsub(/^ +/, "")
|
|
170
|
+
echo 'use admin
|
|
171
|
+
db.fsyncUnlock()' | #{mongo_shell}
|
|
172
|
+
EOS
|
|
173
|
+
|
|
174
|
+
run(unlock_command)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def mongo_shell
|
|
178
|
+
cmd = "#{utility(:mongo)} #{connectivity_options}".rstrip
|
|
179
|
+
cmd << " #{credential_options}".rstrip
|
|
180
|
+
cmd << " #{ipv6_option}".rstrip
|
|
181
|
+
cmd << " '#{name}'" if name
|
|
182
|
+
cmd
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|