Skip to content

Instantly share code, notes, and snippets.

@ta
Created September 9, 2011 18:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ta/1207003 to your computer and use it in GitHub Desktop.
Save ta/1207003 to your computer and use it in GitHub Desktop.
unicornctl manages your app's unicorn server in an easy manner
#!/usr/bin/env ruby
require "getoptlong"
module UnicornCTL
class Controller
def initialize(args, argv)
@args = args
@argv = argv
end
def run!
commands = ["--status", "--start", "--stop", "--reload", "--restart", "--add-worker", "--del-worker", "--help"]
# Make sure that command makes sense
usage if (commands & @args.keys).length < 1
raise UnicornCTL::Error, "Please specify exactly one command" if (commands & @args.keys).length != 1
# Extract command and execute it
case (commands & @args.keys)[0]
when "--status" then status get_pid(@args["--pid"])
when "--start" then start @args["--start"]
when "--stop" then stop @args["--stop"], get_pid(@args["--pid"])
when "--reload" then reload get_pid(@args["--pid"])
when "--restart" then restart get_pid(@args["--pid"])
when "--add-worker" then add_worker get_pid(@args["--pid"])
when "--del-worker" then del_worker get_pid(@args["--pid"])
when "--help" then usage
end
end
private
def status(pid)
sysexec "PGID=`ps -o pgid= #{pid}` && ps -o pid,start,state,%cpu,%mem,command -p `pgrep -g $PGID`"
end
def start(path = "")
cmd = "bundle exec unicorn #{@argv.join(" ")}"
if !path.empty?
raise UnicornCTL::Error, "Directory not found: #{path}" if !Dir.exists? path
pwd = `pwd`.chomp
cmd = "cd #{path} && #{cmd} && cd #{pwd}"
end
sysexec cmd
end
def stop(arg, pid)
raise UnicornCTL::Error, "Invalid argument for command --stop: #{arg}" unless !!["", "force"].index(arg)
sysexec arg == "force" ? "kill -TERM #{pid}" : "kill -QUIT #{pid}"
end
def reload(pid)
sysexec "kill -HUP #{pid}"
end
def restart(pid)
sysexec "kill -USR2 #{pid}"
end
def add_worker(pid)
sysexec "kill -TTIN #{pid}"
end
def del_worker(pid)
sysexec "kill -TTOU #{pid}"
end
def get_pid(arg)
if arg
# Argument is a pid
pid = arg if !!(arg =~ /^[1-9]([0-9]*)?$/)
# Argument is a pid file
if !pid
raise UnicornCTL::Error, "No such pid file: #{arg}" unless File.exists?(arg)
pid = File.read(arg, 5).chomp
end
else
raise UnicornCTL::Error, "No such pid file: unicorn.pid" unless File.exists?("unicorn.pid")
pid = File.read("unicorn.pid", 5).chomp
end
# Validate pid
raise UnicornCTL::Error, "Corrupt pid found: #{pid}" unless !!(pid =~ /^[1-9]([0-9]*)?$/)
%x[ps -p #{pid} > /dev/null 2>&1]
raise UnicornCTL::Error, "No process with id: #{pid}" unless $?.exitstatus == 0
pid
end
def dry?
!!@args["--dry"]
end
def sysexec(cmd)
if dry?
puts "Would have run command: #{cmd}"
else
exec cmd
end
end
def usage
puts <<-EOF
USAGE:
unicornctl COMMAND [OPTIONS]
COMMANDS:
--status
Show status and other information about the master process
--start [-- [unicorn options]]
Starts unicorn - add unicorn specific options after "--"
--stop [force]
Stops unicorn - add "force" argument to kill workers without allowing
them to finish their current request.
--reload
Reloads config file and gracefully restart all workers
--restart
Restarts unicorn by replacing the running binary with a new one without
loosing any incoming connections
--add-worker
Increase number of workers by one
--del-worker
Decrease number of workers by one
--help
Show this help message
OPTIONS:
--pid (PID|PIDFILE)
Unicorn pid of the master process or path to unicorn pid file - used with
--status, --stop, --reload, --restart, --add-worker and --del-worker.
If this option is omitted, unicornctl will look for a "unicorn.pid" file
in the current directory.
--dry
If set commands are just send to stdout - not executed
EOF
exit 1
end
end
class Error < StandardError; end
end
begin
opts = GetoptLong.new(
[ '--help', GetoptLong::NO_ARGUMENT ],
[ '--status', GetoptLong::NO_ARGUMENT ],
[ '--start', GetoptLong::OPTIONAL_ARGUMENT ],
[ '--stop', GetoptLong::OPTIONAL_ARGUMENT ],
[ '--reload', GetoptLong::NO_ARGUMENT ],
[ '--restart', GetoptLong::NO_ARGUMENT ],
[ '--add-worker', GetoptLong::NO_ARGUMENT ],
[ '--del-worker', GetoptLong::NO_ARGUMENT ],
[ '--pid', GetoptLong::REQUIRED_ARGUMENT ],
[ '--dry', GetoptLong::NO_ARGUMENT ]
)
opts.quiet = true
args = {}
opts.each { |opt,arg| args[opt] = arg }
UnicornCTL::Controller.new(args, ARGV).run!
rescue UnicornCTL::Error, GetoptLong::Error => e
puts "Error: " + e.message
rescue => e
puts e.message
puts e.backtrace
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment