Created
June 6, 2011 18:52
-
-
Save mankind/1010822 to your computer and use it in GitHub Desktop.
Sets up replication based on node[:mongodb][:repl_set]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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