#!/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)