TeX and CSS tweaks.

Sync with latest Instiki Trunk
(Updates Rails to 1.2.2)
This commit is contained in:
Jacques Distler 2007-02-09 02:04:31 -06:00
parent 0ac586ee25
commit c358389f25
443 changed files with 24218 additions and 9823 deletions

View file

@ -4,89 +4,102 @@ require 'uri'
if RUBY_PLATFORM =~ /mswin32/ then abort("Reaper is only for Unix") end
# Instances of this class represent a single running process. Processes may
# be queried by "keyword" to find those that meet a specific set of criteria.
class ProgramProcess
class Killer
class << self
# Searches for all processes matching the given keywords, and then invokes
# a specific action on each of them. This is useful for (e.g.) reloading a
# set of processes:
#
# ProgramProcess.process_keywords(:reload, "basecamp")
def process_keywords(action, *keywords)
processes = keywords.collect { |keyword| find_by_keyword(keyword) }.flatten
if processes.empty?
puts "Couldn't find any process matching: #{keywords.join(" or ")}"
else
processes.each do |process|
puts "#{action.capitalize}ing #{process}"
process.send(action)
end
end
# Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid")
def process(action, pid_path, pattern, keyword)
new(pid_path, pattern, keyword).process(action)
end
# Searches for all processes matching the given keyword:
#
# ProgramProcess.find_by_keyword("basecamp")
def find_by_keyword(keyword)
process_lines_with_keyword(keyword).split("\n").collect { |line|
next if line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/
pid, *command = line.split
new(pid, command.join(" "))
}.compact
# Forces the (rails) application to reload by sending a +HUP+ signal to the
# process.
def reload(pid)
`kill -s HUP #{pid}`
end
private
def process_lines_with_keyword(keyword)
`ps axww -o 'pid command' | grep #{keyword}`
# Force the (rails) application to restart by sending a +USR2+ signal to the
# process.
def restart(pid)
`kill -s USR2 #{pid}`
end
# Forces the (rails) application to gracefully terminate by sending a
# +TERM+ signal to the process.
def graceful(pid)
`kill -s TERM #{pid}`
end
# Forces the (rails) application to terminate immediately by sending a -9
# signal to the process.
def kill(pid)
`kill -9 #{pid}`
end
# Send a +USR1+ signal to the process.
def usr1(pid)
`kill -s USR1 #{pid}`
end
end
def initialize(pid_path, pattern, keyword=nil)
@pid_path, @pattern, @keyword = pid_path, pattern, keyword
end
def process(action)
pids = find_processes
if pids.empty?
warn "Couldn't find any pid file in '#{@pid_path}' matching '#{@pattern}'"
warn "(also looked for processes matching #{@keyword.inspect})" if @keyword
else
pids.each do |pid|
puts "#{action.capitalize}ing #{pid}"
self.class.send(action, pid)
end
delete_pid_files if terminating?(action)
end
end
private
def terminating?(action)
[ "kill", "graceful" ].include?(action)
end
def find_processes
files = pid_files
if files.empty?
find_processes_via_grep
else
files.collect { |pid_file| File.read(pid_file).to_i }
end
end
# Create a new ProgramProcess instance that represents the process with the
# given pid, running the given command.
def initialize(pid, command)
@pid, @command = pid, command
end
# Forces the (rails) application to reload by sending a +HUP+ signal to the
# process.
def reload
`kill -s HUP #{@pid}`
end
# Forces the (rails) application to gracefully terminate by sending a
# +TERM+ signal to the process.
def graceful
`kill -s TERM #{@pid}`
end
# Forces the (rails) application to terminate immediately by sending a -9
# signal to the process.
def kill
`kill -9 #{@pid}`
end
# Send a +USR1+ signal to the process.
def usr1
`kill -s USR1 #{@pid}`
end
# Force the (rails) application to restart by sending a +USR2+ signal to the
# process.
def restart
`kill -s USR2 #{@pid}`
end
def to_s #:nodoc:
"[#{@pid}] #{@command}"
end
def find_processes_via_grep
lines = `ps axww -o 'pid command' | grep #{@keyword}`.split(/\n/).
reject { |line| line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/ }
lines.map { |line| line[/^\s*(\d+)/, 1].to_i }
end
def delete_pid_files
pid_files.each { |pid_file| File.delete(pid_file) }
end
def pid_files
Dir.glob(@pid_path + "/" + @pattern)
end
end
OPTIONS = {
:action => "restart",
:dispatcher => File.expand_path(RAILS_ROOT + '/public/dispatch.fcgi')
:action => "restart",
:pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'),
:pattern => "dispatch.[0-9]*.pid",
:dispatcher => File.expand_path("#{RAILS_ROOT}/public/dispatch.fcgi")
}
ARGV.options do |opts|
@ -96,9 +109,13 @@ ARGV.options do |opts|
opts.on <<-EOF
Description:
The reaper is used to restart, reload, gracefully exit, and forcefully exit FCGI processes
running a Rails Dispatcher. This is commonly done when a new version of the application
is available, so the existing processes can be updated to use the latest code.
The reaper is used to restart, reload, gracefully exit, and forcefully exit processes
running a Rails Dispatcher (or any other process responding to the same signals). This
is commonly done when a new version of the application is available, so the existing
processes can be updated to use the latest code.
It uses pid files to work on the processes and by default assume them to be located
in RAILS_ROOT/tmp/pids.
The reaper actions are:
@ -110,15 +127,17 @@ ARGV.options do |opts|
Restart is the most common and default action.
Examples:
reaper # restarts the default dispatcher
reaper -a reload
reaper -a exit -d /my/special/dispatcher.fcgi
reaper # restarts the default dispatchers
reaper -a reload # reload the default dispatchers
reaper -a kill -r *.pid # kill all processes that keep pids in tmp/pids
EOF
opts.on(" Options:")
opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |v| OPTIONS[:action] = v }
opts.on("-d", "--dispatcher=path", "default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v }
opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v }
opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v }
opts.on("-d", "--dispatcher=path", "DEPRECATED. default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v }
opts.separator ""
@ -127,4 +146,4 @@ ARGV.options do |opts|
opts.parse!
end
ProgramProcess.process_keywords(OPTIONS[:action], OPTIONS[:dispatcher])
Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher])

View file

@ -1,5 +1,7 @@
require 'active_support'
require 'optparse'
require 'socket'
require 'fileutils'
def daemonize #:nodoc:
exit if fork # Parent exits, child continues.
@ -12,63 +14,175 @@ def daemonize #:nodoc:
STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
end
def spawn(port)
print "Checking if something is already running on port #{port}..."
begin
srv = TCPServer.new('0.0.0.0', port)
srv.close
srv = nil
print "NO\n "
print "Starting FCGI on port: #{port}\n "
system("#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port}")
rescue
print "YES\n"
class Spawner
def self.record_pid(name = "#{OPTIONS[:process]}.spawner", id = Process.pid)
FileUtils.mkdir_p(OPTIONS[:pids])
File.open(File.expand_path(OPTIONS[:pids] + "/#{name}.pid"), "w+") { |f| f.write(id) }
end
def self.spawn_all
OPTIONS[:instances].times do |i|
port = OPTIONS[:port] + i
print "Checking if something is already running on #{OPTIONS[:address]}:#{port}..."
begin
srv = TCPServer.new(OPTIONS[:address], port)
srv.close
srv = nil
puts "NO"
puts "Starting dispatcher on port: #{OPTIONS[:address]}:#{port}"
FileUtils.mkdir_p(OPTIONS[:pids])
spawn(port)
rescue
puts "YES"
end
end
end
end
def spawn_all
OPTIONS[:instances].times { |i| spawn(OPTIONS[:port] + i) }
class FcgiSpawner < Spawner
def self.spawn(port)
cmd = "#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port} -P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid"
cmd << " -a #{OPTIONS[:address]}" if can_bind_to_custom_address?
system(cmd)
end
def self.can_bind_to_custom_address?
@@can_bind_to_custom_address ||= /^\s-a\s/.match `#{OPTIONS[:spawner]} -h`
end
end
class MongrelSpawner < Spawner
def self.spawn(port)
cmd =
"mongrel_rails start -d " +
"-a #{OPTIONS[:address]} " +
"-p #{port} " +
"-P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid " +
"-e #{OPTIONS[:environment]} " +
"-c #{OPTIONS[:rails_root]} " +
"-l #{OPTIONS[:rails_root]}/log/mongrel.log"
system(cmd)
end
def self.can_bind_to_custom_address?
true
end
end
begin
require_library_or_gem 'fcgi'
rescue Exception
# FCGI not available
end
begin
require_library_or_gem 'mongrel'
rescue Exception
# Mongrel not available
end
server = case ARGV.first
when "fcgi", "mongrel"
ARGV.shift
else
if defined?(Mongrel)
"mongrel"
elsif RUBY_PLATFORM !~ /mswin/ && !silence_stderr { `spawn-fcgi -version` }.blank? && defined?(FCGI)
"fcgi"
end
end
case server
when "fcgi"
puts "=> Starting FCGI dispatchers"
spawner_class = FcgiSpawner
when "mongrel"
puts "=> Starting mongrel dispatchers"
spawner_class = MongrelSpawner
else
puts "Neither FCGI (spawn-fcgi) nor Mongrel was installed and available!"
exit(0)
end
OPTIONS = {
:environment => "production",
:spawner => '/usr/bin/env spawn-fcgi',
:dispatcher => File.expand_path(RAILS_ROOT + '/public/dispatch.fcgi'),
:pids => File.expand_path(RAILS_ROOT + "/tmp/pids"),
:rails_root => File.expand_path(RAILS_ROOT),
:process => "dispatch",
:port => 8000,
:address => '0.0.0.0',
:instances => 3,
:repeat => nil
}
ARGV.options do |opts|
opts.banner = "Usage: spawner [options]"
opts.banner = "Usage: spawner [platform] [options]"
opts.separator ""
opts.on <<-EOF
Description:
The spawner is a wrapper for spawn-fcgi that makes it easier to start multiple FCGI
processes running the Rails dispatcher. The spawn-fcgi command is included with the lighttpd
web server, but can be used with both Apache and lighttpd (and any other web server supporting
externally managed FCGI processes).
The spawner is a wrapper for spawn-fcgi and mongrel that makes it
easier to start multiple processes running the Rails dispatcher. The
spawn-fcgi command is included with the lighttpd web server, but can
be used with both Apache and lighttpd (and any other web server
supporting externally managed FCGI processes). Mongrel automatically
ships with with mongrel_rails for starting dispatchers.
You decide a starting port (default is 8000) and the number of FCGI process instances you'd
like to run. So if you pick 9100 and 3 instances, you'll start processes on 9100, 9101, and 9102.
The first choice you need to make is whether to spawn the Rails
dispatchers as FCGI or Mongrel. By default, this spawner will prefer
Mongrel, so if that's installed, and no platform choice is made,
Mongrel is used.
By setting the repeat option, you get a protection loop, which will attempt to restart any FCGI processes
that might have been exited or outright crashed.
Then decide a starting port (default is 8000) and the number of FCGI
process instances you'd like to run. So if you pick 9100 and 3
instances, you'll start processes on 9100, 9101, and 9102.
Examples:
spawner # starts instances on 8000, 8001, and 8002
spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109
spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds
By setting the repeat option, you get a protection loop, which will
attempt to restart any FCGI processes that might have been exited or
outright crashed.
You can select bind address for started processes. By default these
listen on every interface. For single machine installations you would
probably want to use 127.0.0.1, hiding them form the outside world.
Examples:
spawner # starts instances on 8000, 8001, and 8002
# using Mongrel if available.
spawner fcgi # starts instances on 8000, 8001, and 8002
# using FCGI.
spawner mongrel -i 5 # starts instances on 8000, 8001, 8002,
# 8003, and 8004 using Mongrel.
spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to
# 9109 using Mongrel if available.
spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to
# 9102 and attempts start them every 5
# seconds.
spawner -a 127.0.0.1 # starts 3 instances binding to localhost
EOF
opts.on(" Options:")
opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |OPTIONS[:port]| }
if spawner_class.can_bind_to_custom_address?
opts.on("-a", "--address=ip", String, "Bind to IP address (default: #{OPTIONS[:address]})") { |OPTIONS[:address]| }
end
opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v }
opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |v| OPTIONS[:instances] = v }
opts.on("-r", "--repeat=seconds", Integer, "Repeat spawn attempts every n seconds (default: off)") { |v| OPTIONS[:repeat] = v }
opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |v| OPTIONS[:environment] = v }
opts.on("-n", "--process=name", String, "default: #{OPTIONS[:process]}") { |v| OPTIONS[:process] = v }
opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |v| OPTIONS[:spawner] = v }
opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) }
@ -84,11 +198,12 @@ ENV["RAILS_ENV"] = OPTIONS[:environment]
if OPTIONS[:repeat]
daemonize
trap("TERM") { exit }
spawner_class.record_pid
loop do
spawn_all
spawner_class.spawn_all
sleep(OPTIONS[:repeat])
end
else
spawn_all
end
spawner_class.spawn_all
end