#!/usr/bin/env ruby
#
# Wrapping script to launch both nginx and unicorn for Helix Web Services.
#
require 'hws_settings'
require 'socket'
require 'fileutils'
#=============================================================================
# Primary Helper Methods
#=============================================================================
# If the nginx server is not running, start it.
#
# Files created /var/run/perforce/helix-web-services/:
#
# - nginx.pid
# - nginx.conf
#
def start_nginx
create_nginx_config
spawn_nginx
end
# If the unicorn server is not running, start it.
#
# This should block until unicorn is ready to respond.
def start_unicorn
create_unicorn_config
spawn_unicorn
end
def stop_nginx
if process_running?(nginx_pid)
Process.kill('QUIT', nginx_pid)
end
end
def stop_unicorn
if process_running?(unicorn_pid)
Process.kill('QUIT', unicorn_pid)
end
end
# This should let you know what processes we're running nginx and unicorn at.
# Or if they're shut down.
def check_status
if process_running?(nginx_pid)
puts 'nginx running'
else
puts 'nginx not running'
end
if process_running?(unicorn_pid)
puts 'unicorn running'
else
puts 'unicorn not running'
end
end
# This will pause until the local nginx server is capable of returning
# responses from the web application.
def wait_for_response
attempts = 150 # wait up to 15 seconds
connected = false
while !(connected = connect_to_server(HWSSettings.system.NGINX_PORT))
sleep(0.1)
end
connected
end
def connect_to_server(port)
begin
s = TCPSocket.new 'localhost', port
s.close
return true
rescue Exception => e
return false
end
end
#=============================================================================
# Secondary Helper Methods
#=============================================================================
# These are methods used by the primary methods but aren't really the main API
def spawn_nginx
return if process_running?(nginx_pid)
# Note: this variation of Process.spawn() does *not* execute within a
# standard shell
nginx_pid = Process.spawn(HWSSettings.system.NGINX_COMMAND,
'-c',
HWSSettings.system.NGINX_CONFIG_PATH)
# Note: We set the pid file via config, so this PID may not be the master
# process ID
Process.detach(nginx_pid)
end
def spawn_unicorn
# Note: this launch PID is *not* the unicorn master pid, which is set in
# the config file process.
launch_pid = Process.spawn(HWSSettings.system.UNICORN_COMMAND,
'-D',
'-c',
HWSSettings.system.UNICORN_CONFIG_PATH,
HWSSettings.system.RACKUP_CONFIG)
Process.detach(launch_pid)
end
def nginx_pid
read_pid(nginx_pid_file)
end
def nginx_pid_file
File.join(HWSSettings.system.RUN_DIR, 'nginx.pid')
end
def unicorn_pid
read_pid(unicorn_pid_file)
end
def read_pid(path)
if File.exist?(path)
IO.read(path).strip.to_i
else
nil
end
end
def unicorn_pid_file
File.join(HWSSettings.system.RUN_DIR, 'unicorn.pid')
end
# Note: sending signal 0 basically doesn't do anything, but validates that the
# process exists (and we have access to it)
def process_running?(pid)
return false if pid.nil?
begin
Process.kill(0, pid)
true
rescue Errno::ESRCH
false
end
end
#=============================================================================
# Local Config File Generation
#=============================================================================
# Both Unicorn and Nginx configuration are tuned for HWS only usage. We do
# expose some parameters through the system configuration file.
def create_nginx_config
IO.write(HWSSettings.system.NGINX_CONFIG_PATH, <<-END.gsub(/^[ ]{4}/, '')
pid #{nginx_pid_file};
error_log #{HWSSettings.system.LOG_DIR}/nginx-error.log info;
worker_processes 10;
events {
worker_connections #{HWSSettings.system.NGINX_WORKER_CONNECTIONS};
}
http {
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log #{HWSSettings.system.LOG_DIR}/nginx-access.log;
upstream unicorn_server {
server #{HWSSettings.system.UNICORN_CONNECTION};
}
server {
listen #{HWSSettings.system.NGINX_PORT};
#{nginx_ssl_settings}
location / {
proxy_pass http://unicorn_server;
}
}
}
END
)
end
def nginx_ssl_settings
if HWSSettings.system.ENABLE_HTTPS
<<-END.gsub(/^[ ]{4}/, '')
ssl on;
ssl_certificate #{HWSSettings.system.SSL_CERTIFICATE_PATH};
ssl_certificate_key #{HWSSettings.system.SSL_CERTIFICATE_KEY_PATH};
END
else
''
end
end
def create_unicorn_config
IO.write(HWSSettings.system.UNICORN_CONFIG_PATH, <<-END.gsub(/^[ ]{4}/, '')
listen "#{HWSSettings.system.UNICORN_CONNECTION}"
pid "#{unicorn_pid_file}"
stdout_path "#{HWSSettings.system.LOG_DIR}/unicorn.log"
stderr_path "#{HWSSettings.system.LOG_DIR}/unicorn.log"
timeout #{HWSSettings.system.UNICORN_TIMEOUT}
user "#{HWSSettings.system.SYSTEM_USER}"
worker_processes #{HWSSettings.system.UNICORN_WORKER_PROCESSES}
END
)
FileUtils.chown(HWSSettings.system.SYSTEM_USER,
HWSSettings.system.SYSTEM_GROUP,
HWSSettings.system.UNICORN_CONFIG_PATH
)
end
#=============================================================================
# Parse the command line and start
#=============================================================================
require 'thor'
class CLI < Thor
desc 'hws_launch start', 'Launch both nginx and unicorn'
def start
start_unicorn
start_nginx
wait_for_response
end
desc 'hws_launch stop', 'Stop nginx and unicorn'
def stop
stop_unicorn
stop_nginx
end
desc 'hws_launch restart', 'Restart nginx and unicorn'
def restart
stop
start
end
desc 'hws_launch status', 'Report status of nginx and unicorn'
def status
check_status
end
end
CLI.start(ARGV)
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #2 | 16318 | Doug Scheirer | merge from main | ||
| #1 | 16289 | Doug Scheirer | merge from main | ||
| //guest/perforce_software/helix-web-services/main/source/helix_web_services/bin/hws_launch | |||||
| #1 | 16285 | tjuricek |
Deploy/install improvements - Include nginx in Omnibus distribution, do not conflict with system nginx install - Use old-school sysvinit scripts - Create 'hws_launch' wrapper to initiate nginx and unicorn, which also reads system config file for settings |
||