Monday, February 11, 2008

Script for starting and controlling Rails Mongrel clusters automatically

This script is designed to be a simple way of launching all rails apps installed in a root path (default:/var/www) and starting/stopping them as required.



#!/usr/bin/ruby
#
# mongrels by Gary McGhee
#
# This is a startup script for use in /etc/init.d
# and for starting/stopping etc all packs of mongrels at once
#
# based on code from http://www.simplisticcomplexity.com/2006/9/26/start-and-stop-all-your-mongrel_cluster-processes/
#
# features :
# + finds and runs your rails apps assuming they are under
# APP_DIR and have a mongrel_cluster.yml file in their
# config folder
# + uses mongrel_cluster clean feature to clean up processes
# without pid files. This is important ! Otherwise processes
# can survive a deployment and then your old version will
# keep running despite the deployment.
# + status feature gives details on procs and pid files for all
# apps
# + by default, commands apply to all found apps
# + stop will do nothing and won't give an error if the app is
# not running
# + start and restart will do nothing and won't give an error if
# the app is already running
# + restart will just start if it is currently stopped
#
# Installation
# 1) put this in /etc/init.d/
# 2) chmod 755 /etc/init.d/mongrels
# 3) update-rc.d -f mongrels defaults
#
# Sample mongrel_cluster.yml :
#
# ---
# cwd: /var/www/defaultdomain/current/
# port: "8000"
# environment: production
# #address: 127.0.0.1
# pid_file: /var/run/mongrel_cluster/my_app.pid
# servers: 8
# #log_file: /var/www/defaultdomain/current/log/mongrel.log
#

require 'fileutils'
require 'yaml'

SCRIPT_NAME = 'mongrels'
APP_DIR = '/var/www'
SCRIPT_VERSION = '1.0'

DEFAULT_PID_FILE='/var/run/mongrel_cluster/mongrel.pid'
DEFAULT_USER='root'

def cluster_config_file(app)
File.join(APP_DIR, app, "current/config/mongrel_cluster.yml")
end

def load_cluster_config(aFile)
(YAML::load(File.open(aFile)) rescue nil)
end

def is_cluster?(app)
File.exists?(cluster_config_file(app))
end

# not currently used, but left for potential future use
def is_started?(app,aConfig=nil)
pid_file = (aConfig && aConfig['pid_file']) || DEFAULT_PID_FILE
pid_path = File.dirname(pid_file)
pid_pattern = File.basename(pid_file).sub(/\.([^.]*)$/,'.*.\1')
return !Dir[File.join(pid_path,pid_pattern)].empty?
end

def cluster_command(aApp,aCommand)
config_file = cluster_config_file(aApp)
config ||= load_cluster_config(config_file)
pid_file = (config && config['pid_file']) || DEFAULT_PID_FILE
user = (config && config['user']) || DEFAULT_USER
pid_path = File.dirname(pid_file)
pid_pattern = File.basename(pid_file).sub(/\.([^.]*)$/,'.*.\1')

if %w(start restart).include? aCommand
`mkdir -p #{pid_path}`
`chown #{user}:#{user} #{pid_path}`
end
options = (%w(start stop restart).include? aCommand) ? '--clean' : ''


`mongrel_rails cluster::#{aCommand} #{options} -C #{config_file}`
end

puts

cluster_apps = Dir.open(APP_DIR).to_a.delete_if { |aApp| !is_cluster?(aApp) }

VERBS = {
'start' => 'starting',
'stop' => 'stopping',
'restart' => 'restarting',
'status' => 'getting status for'
}

case command = ARGV.first
when 'start','stop','restart','status'
cluster_apps.each do |aApp|
puts VERBS[command]+' '+aApp
puts cluster_command(aApp,command)
end
when 'version'
puts "#{SCRIPT_NAME} version #{SCRIPT_VERSION}"
exit
else
puts "Usage: #{SCRIPT_NAME} {start|stop|restart|version|status}"
exit
end

No comments: