/lib/backup/database/mongodb.rb
Ruby | 186 lines | 116 code | 32 blank | 38 comment | 14 complexity | 6700aa468a9a8395bf160603d2f90834 MD5 | raw file
- module Backup
- module Database
- class MongoDB < Base
- class Error < Backup::Error; end
- ##
- # Name of the database that needs to get dumped
- attr_accessor :name
- ##
- # Credentials for the specified database
- attr_accessor :username, :password, :authdb
- ##
- # Connectivity options
- attr_accessor :host, :port
- ##
- # IPv6 support (disabled by default)
- attr_accessor :ipv6
- ##
- # Collections to dump, collections that aren't specified won't get dumped
- attr_accessor :only_collections
- ##
- # Additional "mongodump" options
- attr_accessor :additional_options
- ##
- # Forces mongod to flush all pending write operations to the disk and
- # locks the entire mongod instance to prevent additional writes until the
- # dump is complete.
- #
- # Note that if Profiling is enabled, this will disable it and will not
- # re-enable it after the dump is complete.
- attr_accessor :lock
- ##
- # Creates a dump of the database that includes an oplog, to create a
- # point-in-time snapshot of the state of a mongod instance.
- #
- # If this option is used, you would not use the `lock` option.
- #
- # This will only work against nodes that maintain a oplog.
- # This includes all members of a replica set, as well as master nodes in
- # master/slave replication deployments.
- attr_accessor :oplog
- def initialize(model, database_id = nil, &block)
- super
- instance_eval(&block) if block_given?
- end
- def perform!
- super
- lock_database if @lock
- dump!
- package!
- ensure
- unlock_database if @lock
- end
- private
- ##
- # Performs all required mongodump commands, dumping the output files
- # into the +dump_packaging_path+ directory for packaging.
- def dump!
- FileUtils.mkdir_p dump_packaging_path
- collections = Array(only_collections)
- if collections.empty?
- run(mongodump)
- else
- collections.each do |collection|
- run("#{mongodump} --collection='#{collection}'")
- end
- end
- end
- ##
- # Creates a tar archive of the +dump_packaging_path+ directory
- # and stores it in the +dump_path+ using +dump_filename+.
- #
- # <trigger>/databases/MongoDB[-<database_id>].tar[.gz]
- #
- # If successful, +dump_packaging_path+ is removed.
- def package!
- pipeline = Pipeline.new
- dump_ext = "tar"
- pipeline << "#{utility(:tar)} -cf - " \
- "-C '#{dump_path}' '#{dump_filename}'"
- if model.compressor
- model.compressor.compress_with do |command, ext|
- pipeline << command
- dump_ext << ext
- end
- end
- pipeline << "#{utility(:cat)} > " \
- "'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
- pipeline.run
- if pipeline.success?
- FileUtils.rm_rf dump_packaging_path
- log!(:finished)
- else
- raise Error, "Dump Failed!\n#{pipeline.error_messages}"
- end
- end
- def dump_packaging_path
- File.join(dump_path, dump_filename)
- end
- def mongodump
- "#{utility(:mongodump)} #{name_option} #{credential_options} " \
- "#{connectivity_options} #{ipv6_option} #{oplog_option} " \
- "#{user_options} --out='#{dump_packaging_path}'"
- end
- def name_option
- return unless name
- "--db='#{name}'"
- end
- def credential_options
- opts = []
- opts << "--username='#{username}'" if username
- opts << "--password='#{password}'" if password
- opts << "--authenticationDatabase='#{authdb}'" if authdb
- opts.join(" ")
- end
- def connectivity_options
- opts = []
- opts << "--host='#{host}'" if host
- opts << "--port='#{port}'" if port
- opts.join(" ")
- end
- def ipv6_option
- "--ipv6" if ipv6
- end
- def oplog_option
- "--oplog" if oplog
- end
- def user_options
- Array(additional_options).join(" ")
- end
- def lock_database
- lock_command = <<-EOS.gsub(/^ +/, "")
- echo 'use admin
- db.setProfilingLevel(0)
- db.fsyncLock()' | #{mongo_shell}
- EOS
- run(lock_command)
- end
- def unlock_database
- unlock_command = <<-EOS.gsub(/^ +/, "")
- echo 'use admin
- db.fsyncUnlock()' | #{mongo_shell}
- EOS
- run(unlock_command)
- end
- def mongo_shell
- cmd = "#{utility(:mongo)} #{connectivity_options}".rstrip
- cmd << " #{credential_options}".rstrip
- cmd << " #{ipv6_option}".rstrip
- cmd << " '#{name}'" if name
- cmd
- end
- end
- end
- end