Created
July 28, 2016 18:49
-
-
Save csfrancis/02da80579605d4c70c112bc7f2ccf281 to your computer and use it in GitHub Desktop.
Revisions
-
csfrancis created this gist
Jul 28, 2016 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,133 @@ #!/usr/bin/env ruby NGINX_PID_FILE = "/var/run/nginx.pid" NGINX_OLD_PID_FILE = "/var/run/nginx.pid.oldbin" NGINX_CONF_FILE = "/etc/nginx/nginx.conf" NGINX_BIN = "/usr/sbin/nginx" LD_OVERRIDES = "/etc/nginx/ld-overrides" DEFAULT_NGINX_WORKERS = 2 def with_term_color(color, eol: true) @has_colors ||= `tput colors`.to_i > 0 ? true : false $stdout.print "#{color}" if @has_colors yield $stdout.print "\e[0m" if @has_colors $stdout.print "\n" if eol end def error(msg, should_exit: true) with_term_color("\e[0;31m") { $stdout.print(msg) } exit 1 if should_exit end def warning(msg) with_term_color("\e[0;32m") { $stdout.print(msg) } end def with_timeout(seconds, timeout: 90, error_message: "error", warning_message: nil, exit_on_error: true) count = 0 timeout.times do |i| sleep 1 $stdout.print "." count += 1 break if yield i end if count == timeout if warning_message warning(warning_message) else error(error_message, should_exit: exit_on_error) end else $stdout.puts " done." end end def get_child_processes(pid) `pgrep -P #{pid}`.split.map{ |p| Integer(p) } end def get_process_cmdline(pids) pids = [pids] unless pids.class == Array pids.map do |pid| begin IO.read("/proc/#{pid}/cmdline").strip rescue Errno::ENOENT "" end end end def get_nginx_master_pid Integer(IO.read(NGINX_PID_FILE).chomp) end def get_nginx_config_value(key) match = File.read(NGINX_CONF_FILE).match(/#{key}\s+(\S+);/) match.captures.first if match end def get_num_workers(pid) get_process_cmdline(get_child_processes(pid)).count { |p| p.start_with? "nginx: worker process" } end def test_nginx_config ld_library_path = "" if Dir.exist? LD_OVERRIDES ld_library_path = "LD_LIBRARY_PATH=#{LD_OVERRIDES} " end $stdout.print("Testing nginx configuration .. ") unless system("#{ld_library_path}#{NGINX_BIN} -c #{NGINX_CONF_FILE} -t > /dev/null 2>&1") error "nginx configuration is invalid!" end $stdout.puts("done.") end def spawn_new_nginx_master(master_pid) $stdout.print "Spawning new nginx master .." Process.kill("USR2", master_pid) old_master_pid = master_pid with_timeout 20, error_message: "timed out waiting for new master to spawn!" do if File.exist?(NGINX_OLD_PID_FILE) && File.exist?(NGINX_PID_FILE) new_pid = get_nginx_master_pid error("error spawning new master - old master still running.") if new_pid == old_master_pid end # Check if the new master process has spawned with correct number of workers expected_workers = Integer(get_nginx_config_value('worker_processes')) || DEFAULT_NGINX_WORKERS get_num_workers(get_nginx_master_pid) == expected_workers end end def shutdown_nginx_workers(old_master_pid) $stdout.print "Shutting down workers on master .." Process.kill("WINCH", old_master_pid) with_timeout 90, warning_message: "not all workers shut down on time - killing stragglers." do get_process_cmdline(get_child_processes(old_master_pid)).all? { |p| p.start_with? "nginx: master process" } end %w(TERM KILL).each do |sig| sleep 0.1 workers = get_child_processes(old_master_pid).select { |p| get_process_cmdline(p)[0].start_with? "nginx: worker process" } workers.each do |pid| warning "Sending #{pid} #{sig}." Process.kill(sig, pid) rescue Errno::ESRCH end end end def shutdown_old_nginx_master(master_pid) $stdout.print "Waiting for old master to shut down .." Process.kill("QUIT", master_pid) with_timeout 10, error_message: "timed out waiting for old master to shut down!" do ! File.exist? NGINX_OLD_PID_FILE end end master_pid = get_nginx_master_pid test_nginx_config spawn_new_nginx_master(master_pid) shutdown_nginx_workers(master_pid) shutdown_old_nginx_master(master_pid)