2017-04-20 12:51:38 +00:00
|
|
|
require 'fileutils'
|
|
|
|
|
|
|
|
require 'excon'
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
describe 'Unicorn' do
|
|
|
|
before(:all) do
|
|
|
|
config_lines = File.read('config/unicorn.rb.example').split("\n")
|
|
|
|
|
|
|
|
# Remove these because they make setup harder.
|
|
|
|
config_lines = config_lines.reject do |line|
|
|
|
|
%w[
|
2017-04-28 11:51:19 +00:00
|
|
|
working_directory
|
|
|
|
worker_processes
|
|
|
|
listen
|
2017-04-20 12:51:38 +00:00
|
|
|
pid
|
|
|
|
stderr_path
|
|
|
|
stdout_path
|
|
|
|
].any? { |prefix| line.start_with?(prefix) }
|
|
|
|
end
|
|
|
|
|
|
|
|
config_lines << "working_directory '#{Rails.root}'"
|
|
|
|
|
|
|
|
# We want to have exactly 1 worker process because that makes it
|
|
|
|
# predictable which process will handle our requests.
|
|
|
|
config_lines << 'worker_processes 1'
|
|
|
|
|
|
|
|
@socket_path = File.join(Dir.pwd, 'tmp/tests/unicorn.socket')
|
|
|
|
config_lines << "listen '#{@socket_path}'"
|
|
|
|
|
|
|
|
ready_file = 'tmp/tests/unicorn-worker-ready'
|
|
|
|
FileUtils.rm_f(ready_file)
|
|
|
|
after_fork_index = config_lines.index { |l| l.start_with?('after_fork') }
|
|
|
|
config_lines.insert(after_fork_index + 1, "File.write('#{ready_file}', Process.pid)")
|
|
|
|
|
|
|
|
config_path = 'tmp/tests/unicorn.rb'
|
|
|
|
File.write(config_path, config_lines.join("\n") + "\n")
|
|
|
|
|
|
|
|
cmd = %W[unicorn -E test -c #{config_path} #{Rails.root.join('config.ru')}]
|
|
|
|
@unicorn_master_pid = spawn(*cmd)
|
2017-04-28 10:27:48 +00:00
|
|
|
wait_unicorn_boot!(@unicorn_master_pid, ready_file)
|
2017-04-20 12:51:38 +00:00
|
|
|
WebMock.allow_net_connect!
|
|
|
|
end
|
|
|
|
|
|
|
|
%w[SIGQUIT SIGTERM SIGKILL].each do |signal|
|
|
|
|
it "has a worker that self-terminates on signal #{signal}" do
|
|
|
|
response = Excon.get('unix:///unicorn_test/pid', socket: @socket_path)
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
worker_pid = response.body.to_i
|
2017-04-28 10:29:15 +00:00
|
|
|
expect(worker_pid).to be > 0
|
2017-04-20 12:51:38 +00:00
|
|
|
|
|
|
|
begin
|
|
|
|
Excon.post('unix:///unicorn_test/kill', socket: @socket_path, body: "signal=#{signal}")
|
|
|
|
rescue Excon::Error::Socket
|
|
|
|
# The connection may be closed abruptly
|
|
|
|
end
|
|
|
|
|
|
|
|
expect(pid_gone?(worker_pid)).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
after(:all) do
|
|
|
|
WebMock.disable_net_connect!(allow_localhost: true)
|
|
|
|
Process.kill('TERM', @unicorn_master_pid)
|
|
|
|
end
|
|
|
|
|
2017-04-28 10:27:48 +00:00
|
|
|
def wait_unicorn_boot!(master_pid, ready_file)
|
|
|
|
# Unicorn should boot in under 60 seconds so 120 seconds seems like a good timeout.
|
|
|
|
timeout = 120
|
|
|
|
timeout.times do
|
|
|
|
return if File.exist?(ready_file)
|
|
|
|
pid = Process.waitpid(master_pid, Process::WNOHANG)
|
|
|
|
raise "unicorn failed to boot: #{$?}" unless pid.nil?
|
|
|
|
|
|
|
|
sleep 1
|
|
|
|
end
|
|
|
|
|
|
|
|
raise "unicorn boot timed out after #{timeout} seconds"
|
|
|
|
end
|
|
|
|
|
2017-04-20 12:51:38 +00:00
|
|
|
def pid_gone?(pid)
|
2017-04-28 10:27:48 +00:00
|
|
|
# Worker termination should take less than a second. That makes 10
|
|
|
|
# seconds a generous timeout.
|
2017-04-20 12:51:38 +00:00
|
|
|
10.times do
|
|
|
|
begin
|
|
|
|
Process.kill(0, pid)
|
|
|
|
rescue Errno::ESRCH
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
sleep 1
|
|
|
|
end
|
|
|
|
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|