Skip to content

Instantly share code, notes, and snippets.

@mankind
Created June 6, 2011 18:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mankind/1010822 to your computer and use it in GitHub Desktop.
Save mankind/1010822 to your computer and use it in GitHub Desktop.
Sets up replication based on node[:mongodb][:repl_set]
# Install the MongoDB gem
%w{ mongo bson_ext }.each do |mongo_gem|
gem_install = gem_package mongo_gem do
action :nothing
end
gem_install.run_action(:install)
end
Gem.clear_paths
require 'mongo'
# Get a list of other nodes in the same replica set
REPL_SET = node[:mongodb][:repl_set]
srch_trm = "mongodb_repl_set:#{REPL_SET}"
REPLICA_PEERS = search(:node, srch_trm).to_ary.collect {|host| host[:fqdn] }
REPLICA_PEERS.delete node[:fqdn]
ruby_block "Configure mongoDB replication" do
block do
Chef::Log.info "Configuring mongoDB replication..."
require 'mongo'
##########################################################################
# HELPER FUNCTIONS - should be in a library, but the 'mongo' require fails
# Returns a BSON::OrderedHash from a hash (the keys are sorted)
def self.ordered_hash_from_hash(regular_hash)
ordered_hash = BSON::OrderedHash.new
regular_hash.keys.sort.each do |key|
ordered_hash[key.to_sym] = regular_hash[key]
end
ordered_hash
end
# returns the results of mongoDB's 'isMaster' shell command
def is_master(connection)
connection['admin'].command(ordered_hash_from_hash({:isMaster => true}))
end
##########################################################################
# STEP 1: wait for the server to become available, and get current status
local_db = master_status = nil
my_hostport = "#{node[:fqdn]}:#{node[:mongodb][:port]}"
Chef::Log.info "Connecting to mongodb://localhost:#{node[:mongodb][:port]}..."
while (local_db == nil) or (master_status == nil)
begin
local_db = Mongo::Connection.from_uri(
"mongodb://localhost:#{node[:mongodb][:port]}",
:logger => Chef::Log, :slave_ok => true)
master_status = is_master(local_db)
rescue Exception => e
Chef::Log.warn "Error getting local master status: #{e.message}"
localdb_files = Dir["#{node[:mongodb][:db_path]}/local*"].sort
Chef::Log.info "Waiting for replication log to initilize? "+
"(#{localdb_files.size} local.* files in #{node[:mongodb][:db_path]})"
sleep 20
end
end
# STEP 2: configure replication based on current status
if master_status['ismaster'] or master_status['secondary']
Chef::Log.info "Replication is already configured: #{master_status.inspect}"
else
Chef::Log.info "Replication is not configured (#{master_status['info']})"
while is_master(local_db)['info'] =~ /local.system.replset .*EMPTYUNREACHABLE/
Chef::Log.info "Replication is not configured: #{is_master(local_db)['info']}"
sleep 10
end
if REPLICA_PEERS.empty?
# Configure as the lone master
Chef::Log.info "No other servers in replica set #{REPL_SET}; becoming master"
Chef::Log.info "Setting master to: #{my_hostport}"
local_db['admin'].command(ordered_hash_from_hash(
:replSetInitiate => {
"_id" => node[:mongodb][:repl_set],
"members" => [{ "_id" => 0, "votes" => 2,
"host" => my_hostport }]
}
))
else
# Configure as a slave
Chef::Log.info "Found replica peers: #{REPLICA_PEERS.join ','}"
replication_stared = false
REPLICA_PEERS.each do |server|
if not replication_stared
Chef::Log.info "Retrieving replication settings from #{server}..."
peer_db = Mongo::Connection.from_uri(
"mongodb://#{server}:#{node[:mongodb][:port]}",
:logger => Chef::Log, :slave_ok => true)
repl_settings = peer_db['local']["system.replset"].find_one
Chef::Log.info "Replication settings for #{server}: "+
repl_settings.inspect
if is_master(peer_db)['ismaster']
Chef::Log.info "Starting replication from master: #{server}..."
active_peers = repl_settings['members'].collect {|m| m['host'] }
if active_peers.include? my_hostport
Chef::Log.error "Host #{my_hostport} already in replica set!"
else
repl_settings["version"] += 1 # increment config version
max_id = repl_settings['members'].inject(0) do |max, peer|
peer['_id'] > max ? peer['_id'] : max
end
repl_settings['members'].push(
{'host' => my_hostport, '_id' => max_id+1})
bson = ordered_hash_from_hash(:replSetReconfig => repl_settings)
Chef::Log.info "Pushing replication settings: #{repl_settings.inspect}"
peer_db['admin'].command(bson)
replication_stared = true
end
end
end
end
end
end
# STEP 3: wait for the local replication status to be OK
while not (is_master(local_db)['ismaster'] or is_master(local_db)['secondary'])
Chef::Log.info "Waiting for local replication (#{is_master(local_db)['info']})"
sleep 10
end
Chef::Log.info("Local replication status: #{is_master(local_db).inspect}")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment