| 1 |
require 'gem_plugin' |
|---|
| 2 |
require 'mongrel' |
|---|
| 3 |
require 'yaml' |
|---|
| 4 |
|
|---|
| 5 |
module Cluster |
|---|
| 6 |
module ExecBase |
|---|
| 7 |
include Mongrel::Command::Base |
|---|
| 8 |
|
|---|
| 9 |
STATUS_OK = 0 |
|---|
| 10 |
STATUS_ERROR = 2 |
|---|
| 11 |
|
|---|
| 12 |
def validate |
|---|
| 13 |
valid_exists?(@config_file, "Configuration file does not exist. Run mongrel_rails cluster::configure.") |
|---|
| 14 |
@valid |
|---|
| 15 |
end |
|---|
| 16 |
|
|---|
| 17 |
def read_options |
|---|
| 18 |
@options = { |
|---|
| 19 |
"environment" => ENV['RAILS_ENV'] || "development", |
|---|
| 20 |
"port" => 3000, |
|---|
| 21 |
"pid_file" => "tmp/pids/mongrel.pid", |
|---|
| 22 |
"log_file" => "log/mongrel.log", |
|---|
| 23 |
"servers" => 2 |
|---|
| 24 |
} |
|---|
| 25 |
conf = YAML.load_file(@config_file) |
|---|
| 26 |
@options.merge! conf if conf |
|---|
| 27 |
|
|---|
| 28 |
process_pid_file @options["pid_file"] |
|---|
| 29 |
process_log_file @options["log_file"] |
|---|
| 30 |
|
|---|
| 31 |
start_port = end_port = @only |
|---|
| 32 |
start_port ||= @options["port"].to_i |
|---|
| 33 |
end_port ||= start_port + @options["servers"] - 1 |
|---|
| 34 |
@ports = (start_port..end_port).to_a |
|---|
| 35 |
end |
|---|
| 36 |
|
|---|
| 37 |
def process_pid_file(pid_file) |
|---|
| 38 |
@pid_file_ext = File.extname(pid_file) |
|---|
| 39 |
@pid_file_base = File.basename(pid_file, @pid_file_ext) |
|---|
| 40 |
@pid_file_dir = File.dirname(pid_file) |
|---|
| 41 |
end |
|---|
| 42 |
|
|---|
| 43 |
def process_log_file(log_file) |
|---|
| 44 |
@log_file_ext = File.extname(log_file) |
|---|
| 45 |
@log_file_base = File.basename(log_file, @log_file_ext) |
|---|
| 46 |
@log_file_dir = File.dirname(log_file) |
|---|
| 47 |
end |
|---|
| 48 |
|
|---|
| 49 |
def port_pid_file(port) |
|---|
| 50 |
pid_file = [@pid_file_base, port].join(".") + @pid_file_ext |
|---|
| 51 |
File.join(@pid_file_dir, pid_file) |
|---|
| 52 |
end |
|---|
| 53 |
|
|---|
| 54 |
def port_log_file(port) |
|---|
| 55 |
log_file = [@log_file_base, port].join(".") + @log_file_ext |
|---|
| 56 |
File.join(@log_file_dir, log_file) |
|---|
| 57 |
end |
|---|
| 58 |
|
|---|
| 59 |
def start |
|---|
| 60 |
read_options |
|---|
| 61 |
|
|---|
| 62 |
argv = [ "mongrel_rails" ] |
|---|
| 63 |
argv << "start" |
|---|
| 64 |
argv << "-d" |
|---|
| 65 |
argv << "-e #{@options['environment']}" if @options['environment'] |
|---|
| 66 |
argv << "-a #{@options['address']}" if @options['address'] |
|---|
| 67 |
argv << "-c #{@options['cwd']}" if @options['cwd'] |
|---|
| 68 |
argv << "-o #{@options['timeout']}" if @options['timeout'] |
|---|
| 69 |
argv << "-t #{@options['throttle']}" if @options['throttle'] |
|---|
| 70 |
argv << "-m #{@options['mime_map']}" if @options['mime_map'] |
|---|
| 71 |
argv << "-r #{@options['docroot']}" if @options['docroot'] |
|---|
| 72 |
argv << "-n #{@options['num_procs']}" if @options['num_procs'] |
|---|
| 73 |
argv << "-B" if @options['debug'] |
|---|
| 74 |
argv << "-S #{@options['config_script']}" if @options['config_script'] |
|---|
| 75 |
argv << "--user #{@options['user']}" if @options['user'] |
|---|
| 76 |
argv << "--group #{@options['group']}" if @options['group'] |
|---|
| 77 |
argv << "--prefix #{@options['prefix']}" if @options['prefix'] |
|---|
| 78 |
cmd = argv.join " " |
|---|
| 79 |
|
|---|
| 80 |
@ports.each do |port| |
|---|
| 81 |
if @clean && pid_file_exists?(port) && !check_process(port) |
|---|
| 82 |
pid_file = port_pid_file(port) |
|---|
| 83 |
Mongrel.log("missing process: removing #{pid_file}") |
|---|
| 84 |
chdir_cwd do |
|---|
| 85 |
File.unlink(pid_file) |
|---|
| 86 |
end |
|---|
| 87 |
end |
|---|
| 88 |
|
|---|
| 89 |
if pid_file_exists?(port) && check_process(port) |
|---|
| 90 |
Mongrel.log("already started port #{port}") |
|---|
| 91 |
next |
|---|
| 92 |
end |
|---|
| 93 |
|
|---|
| 94 |
exec_cmd = cmd + " -p #{port} -P #{port_pid_file(port)}" |
|---|
| 95 |
exec_cmd += " -l #{port_log_file(port)}" |
|---|
| 96 |
Mongrel.log("starting port #{port}") |
|---|
| 97 |
log_verbose exec_cmd |
|---|
| 98 |
output = `#{exec_cmd}` |
|---|
| 99 |
Mongrel.log(:error, output) unless $?.success? |
|---|
| 100 |
end |
|---|
| 101 |
end |
|---|
| 102 |
|
|---|
| 103 |
def stop |
|---|
| 104 |
read_options |
|---|
| 105 |
|
|---|
| 106 |
argv = [ "mongrel_rails" ] |
|---|
| 107 |
argv << "stop" |
|---|
| 108 |
argv << "-c #{@options["cwd"]}" if @options["cwd"] |
|---|
| 109 |
argv << "-f" if @force |
|---|
| 110 |
cmd = argv.join " " |
|---|
| 111 |
|
|---|
| 112 |
@ports.each do |port| |
|---|
| 113 |
pid = check_process(port) |
|---|
| 114 |
if @clean && pid && !pid_file_exists?(port) |
|---|
| 115 |
Mongrel.log("missing pid_file: killing mongrel_rails port #{port}, pid #{pid}") |
|---|
| 116 |
Process.kill("KILL", pid.to_i) |
|---|
| 117 |
end |
|---|
| 118 |
|
|---|
| 119 |
if !check_process(port) |
|---|
| 120 |
Mongrel.log("already stopped port #{port}") |
|---|
| 121 |
next |
|---|
| 122 |
end |
|---|
| 123 |
|
|---|
| 124 |
exec_cmd = cmd + " -P #{port_pid_file(port)}" |
|---|
| 125 |
Mongrel.log("stopping port #{port}") |
|---|
| 126 |
log_verbose exec_cmd |
|---|
| 127 |
output = `#{exec_cmd}` |
|---|
| 128 |
Mongrel.log(:error, output) unless $?.success? |
|---|
| 129 |
|
|---|
| 130 |
end |
|---|
| 131 |
end |
|---|
| 132 |
|
|---|
| 133 |
def status |
|---|
| 134 |
read_options |
|---|
| 135 |
|
|---|
| 136 |
status = STATUS_OK |
|---|
| 137 |
|
|---|
| 138 |
@ports.each do |port| |
|---|
| 139 |
pid = check_process(port) |
|---|
| 140 |
unless pid_file_exists?(port) |
|---|
| 141 |
Mongrel.log(:error, "missing pid_file: #{port_pid_file(port)}") |
|---|
| 142 |
status = STATUS_ERROR |
|---|
| 143 |
else |
|---|
| 144 |
Mongrel.log("found pid_file: #{port_pid_file(port)}") |
|---|
| 145 |
end |
|---|
| 146 |
if pid |
|---|
| 147 |
Mongrel.log("found mongrel_rails: port #{port}, pid #{pid}") |
|---|
| 148 |
else |
|---|
| 149 |
Mongrel.log(:error, "missing mongrel_rails: port #{port}") |
|---|
| 150 |
status = STATUS_ERROR |
|---|
| 151 |
end |
|---|
| 152 |
Mongrel.log("") |
|---|
| 153 |
end |
|---|
| 154 |
|
|---|
| 155 |
status |
|---|
| 156 |
end |
|---|
| 157 |
|
|---|
| 158 |
def pid_file_exists?(port) |
|---|
| 159 |
pid_file = port_pid_file(port) |
|---|
| 160 |
exists = false |
|---|
| 161 |
chdir_cwd do |
|---|
| 162 |
exists = File.exists?(pid_file) |
|---|
| 163 |
end |
|---|
| 164 |
exists |
|---|
| 165 |
end |
|---|
| 166 |
|
|---|
| 167 |
def check_process(port) |
|---|
| 168 |
if pid_file_exists?(port) |
|---|
| 169 |
pid = read_pid(port) |
|---|
| 170 |
ps_output = `ps -o #{cmd_name}= -p #{pid}` |
|---|
| 171 |
pid = ps_output =~ /mongrel_rails/ ? pid : nil |
|---|
| 172 |
else |
|---|
| 173 |
pid = find_pid(port) |
|---|
| 174 |
end |
|---|
| 175 |
pid |
|---|
| 176 |
end |
|---|
| 177 |
|
|---|
| 178 |
def cmd_name |
|---|
| 179 |
RUBY_PLATFORM =~ /solaris|aix/i ? "args" : "command" |
|---|
| 180 |
end |
|---|
| 181 |
|
|---|
| 182 |
def cmd_flags |
|---|
| 183 |
RUBY_PLATFORM =~ /solaris|aix/i ? "-eo" : "-ewwo" |
|---|
| 184 |
end |
|---|
| 185 |
|
|---|
| 186 |
def chdir_cwd |
|---|
| 187 |
pwd = Dir.pwd |
|---|
| 188 |
Dir.chdir(@options["cwd"]) if @options["cwd"] |
|---|
| 189 |
yield |
|---|
| 190 |
Dir.chdir(pwd) if @options["cwd"] |
|---|
| 191 |
end |
|---|
| 192 |
|
|---|
| 193 |
def read_pid(port) |
|---|
| 194 |
pid_file = port_pid_file(port) |
|---|
| 195 |
pid = 0 |
|---|
| 196 |
chdir_cwd do |
|---|
| 197 |
pid = File.read(pid_file) |
|---|
| 198 |
end |
|---|
| 199 |
pid |
|---|
| 200 |
end |
|---|
| 201 |
|
|---|
| 202 |
def find_pid(port) |
|---|
| 203 |
ps_cmd = "ps #{cmd_flags} pid,#{cmd_name}" |
|---|
| 204 |
ps_output = `#{ps_cmd}` |
|---|
| 205 |
ps_output.each do |line| |
|---|
| 206 |
if line =~ /-P #{Regexp.escape(port_pid_file(port))} / |
|---|
| 207 |
pid = line.split[0] |
|---|
| 208 |
return pid |
|---|
| 209 |
end |
|---|
| 210 |
end |
|---|
| 211 |
nil |
|---|
| 212 |
end |
|---|
| 213 |
|
|---|
| 214 |
def log_verbose(message) |
|---|
| 215 |
Mongrel.log(message) if @verbose |
|---|
| 216 |
end |
|---|
| 217 |
|
|---|
| 218 |
end |
|---|
| 219 |
|
|---|
| 220 |
class Start < GemPlugin::Plugin "/commands" |
|---|
| 221 |
include ExecBase |
|---|
| 222 |
|
|---|
| 223 |
def configure |
|---|
| 224 |
options [ |
|---|
| 225 |
['-C', '--config PATH', "Path to cluster configuration file", :@config_file, "config/mongrel_cluster.yml"], |
|---|
| 226 |
['-v', '--verbose', "Print all called commands and output.", :@verbose, false], |
|---|
| 227 |
['', '--clean', "Remove pid_file if needed before starting", :@clean, false], |
|---|
| 228 |
['', '--only PORT', "Port number of cluster member", :@only, nil] |
|---|
| 229 |
] |
|---|
| 230 |
end |
|---|
| 231 |
|
|---|
| 232 |
def run |
|---|
| 233 |
start |
|---|
| 234 |
end |
|---|
| 235 |
end |
|---|
| 236 |
|
|---|
| 237 |
class Stop < GemPlugin::Plugin "/commands" |
|---|
| 238 |
include ExecBase |
|---|
| 239 |
|
|---|
| 240 |
def configure |
|---|
| 241 |
options [ |
|---|
| 242 |
['-C', '--config PATH', "Path to cluster configuration file", :@config_file, "config/mongrel_cluster.yml"], |
|---|
| 243 |
['-f', '--force', "Force the shutdown.", :@force, false], |
|---|
| 244 |
['-v', '--verbose', "Print all called commands and output.", :@verbose, false], |
|---|
| 245 |
['', '--clean', "Remove orphaned process if needed before stopping", :@clean, false], |
|---|
| 246 |
['', '--only PORT', "Port number of cluster member", :@only, nil] |
|---|
| 247 |
] |
|---|
| 248 |
end |
|---|
| 249 |
|
|---|
| 250 |
def run |
|---|
| 251 |
stop |
|---|
| 252 |
end |
|---|
| 253 |
end |
|---|
| 254 |
|
|---|
| 255 |
class Restart < GemPlugin::Plugin "/commands" |
|---|
| 256 |
include ExecBase |
|---|
| 257 |
|
|---|
| 258 |
def configure |
|---|
| 259 |
options [ |
|---|
| 260 |
['-C', '--config PATH', "Path to cluster configuration file", :@config_file, "config/mongrel_cluster.yml"], |
|---|
| 261 |
['-f', '--force', "Force the shutdown.", :@force, false], |
|---|
| 262 |
['-v', '--verbose', "Print all called commands and output.", :@verbose, false], |
|---|
| 263 |
['', '--clean', "Call stop and start with --clean", :@clean, false], |
|---|
| 264 |
['', '--only PORT', "Port number of cluster member", :@only, nil] |
|---|
| 265 |
] |
|---|
| 266 |
end |
|---|
| 267 |
|
|---|
| 268 |
def run |
|---|
| 269 |
stop |
|---|
| 270 |
start |
|---|
| 271 |
end |
|---|
| 272 |
|
|---|
| 273 |
end |
|---|
| 274 |
|
|---|
| 275 |
class Configure < GemPlugin::Plugin "/commands" |
|---|
| 276 |
include ExecBase |
|---|
| 277 |
|
|---|
| 278 |
def configure |
|---|
| 279 |
options [ |
|---|
| 280 |
["-e", "--environment ENV", "Rails environment to run as", :@environment, nil], |
|---|
| 281 |
['-p', '--port PORT', "Starting port to bind to", :@port, 3000], |
|---|
| 282 |
['-a', '--address ADDR', "Address to bind to", :@address, nil], |
|---|
| 283 |
['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"], |
|---|
| 284 |
['-P', '--pid FILE', "Where to write the PID", :@pid_file, "tmp/pids/mongrel.pid"], |
|---|
| 285 |
['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, nil], |
|---|
| 286 |
['-o', '--timeout TIME', "Time to wait (in seconds) before killing a stalled thread", :@timeout, nil], |
|---|
| 287 |
['-t', '--throttle TIME', "Time to pause (in hundredths of a second) between accepting clients", :@throttle, nil], |
|---|
| 288 |
['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil], |
|---|
| 289 |
['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, nil], |
|---|
| 290 |
['-n', '--num-procs INT', "Number of processor threads to use", :@num_procs, nil], |
|---|
| 291 |
['-B', '--debug', "Enable debugging mode", :@debug, nil], |
|---|
| 292 |
['-S', '--script PATH', "Load the given file as an extra config script.", :@config_script, nil], |
|---|
| 293 |
['-N', '--num-servers INT', "Number of Mongrel servers", :@servers, 2], |
|---|
| 294 |
['-C', '--config PATH', "Path to cluster configuration file", :@config_file, "config/mongrel_cluster.yml"], |
|---|
| 295 |
['', '--user USER', "User to run as", :@user, nil], |
|---|
| 296 |
['', '--group GROUP', "Group to run as", :@group, nil], |
|---|
| 297 |
['', '--prefix PREFIX', "Rails prefix to use", :@prefix, nil] |
|---|
| 298 |
] |
|---|
| 299 |
end |
|---|
| 300 |
|
|---|
| 301 |
def validate |
|---|
| 302 |
@servers = @servers.to_i |
|---|
| 303 |
|
|---|
| 304 |
valid?(@servers > 0, "Must give a valid number of servers") |
|---|
| 305 |
valid_dir? File.dirname(@config_file), "Path to config file not valid: #{@config_file}" |
|---|
| 306 |
|
|---|
| 307 |
@valid |
|---|
| 308 |
end |
|---|
| 309 |
|
|---|
| 310 |
def run |
|---|
| 311 |
@options = { |
|---|
| 312 |
"port" => @port, |
|---|
| 313 |
"servers" => @servers, |
|---|
| 314 |
"pid_file" => @pid_file |
|---|
| 315 |
} |
|---|
| 316 |
|
|---|
| 317 |
@options["log_file"] = @log_file if @log_file |
|---|
| 318 |
@options["debug"] = @debug if @debug |
|---|
| 319 |
@options["num_procs"] = @num_procs if @num_procs |
|---|
| 320 |
@options["docroot"] = @docroot if @docroot |
|---|
| 321 |
@options["address"] = @address if @address |
|---|
| 322 |
@options["timeout"] = @timeout if @timeout |
|---|
| 323 |
@options["throttle"] = @throttle if @throttle |
|---|
| 324 |
@options["environment"] = @environment if @environment |
|---|
| 325 |
@options["mime_map"] = @mime_map if @mime_map |
|---|
| 326 |
@options["config_script"] = @config_script if @config_script |
|---|
| 327 |
@options["cwd"] = @cwd if @cwd |
|---|
| 328 |
@options["user"] = @user if @user |
|---|
| 329 |
@options["group"] = @group if @group |
|---|
| 330 |
@options["prefix"] = @prefix if @prefix |
|---|
| 331 |
|
|---|
| 332 |
Mongrel.log("Writing configuration file to #{@config_file}.") |
|---|
| 333 |
File.open(@config_file,"w") {|f| f.write(@options.to_yaml)} |
|---|
| 334 |
end |
|---|
| 335 |
end |
|---|
| 336 |
|
|---|
| 337 |
class Status < GemPlugin::Plugin "/commands" |
|---|
| 338 |
include ExecBase |
|---|
| 339 |
|
|---|
| 340 |
def configure |
|---|
| 341 |
options [ |
|---|
| 342 |
['-C', '--config PATH', "Path to cluster configuration file", :@config_file, "config/mongrel_cluster.yml"], |
|---|
| 343 |
['-v', '--verbose', "Print all called commands and output.", :@verbose, false], |
|---|
| 344 |
['', '--only PORT', "Port number of cluster member", :@only, nil] |
|---|
| 345 |
] |
|---|
| 346 |
end |
|---|
| 347 |
|
|---|
| 348 |
def run |
|---|
| 349 |
status |
|---|
| 350 |
end |
|---|
| 351 |
|
|---|
| 352 |
end |
|---|
| 353 |
end |
|---|
| 354 |
|
|---|