diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 6d822a2f88..3c56f9cecb 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *2.3.0 [Edge]* +* Extracted the process scripts (inspector, reaper, spawner) into the plugin irs_process_scripts [DHH] + * Changed Rails.root to return a Pathname object (allows for Rails.root.join('app', 'controllers') => "#{RAILS_ROOT}/app/controllers") #1482 [Damian Janowski/?] * Added view path support for engines [DHH] diff --git a/railties/Rakefile b/railties/Rakefile index bf70219aa8..f812b42f1d 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -53,7 +53,6 @@ BASE_DIRS = %w( public script script/performance - script/process test vendor vendor/plugins @@ -71,7 +70,7 @@ LOG_FILES = %w( server.log development.log test.log production.log ) HTML_FILES = %w( 422.html 404.html 500.html index.html robots.txt favicon.ico images/rails.png javascripts/prototype.js javascripts/application.js javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js ) -BIN_FILES = %w( about console destroy generate performance/benchmarker performance/profiler process/reaper process/spawner process/inspector runner server plugin ) +BIN_FILES = %w( about console destroy generate performance/benchmarker performance/profiler runner server plugin ) VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport activeresource railties ) @@ -174,9 +173,6 @@ task :copy_dispatches do copy_with_rewritten_ruby_path("dispatches/dispatch.fcgi", "#{PKG_DESTINATION}/public/dispatch.fcgi") chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi" - - # copy_with_rewritten_ruby_path("dispatches/gateway.cgi", "#{PKG_DESTINATION}/public/gateway.cgi") - # chmod 0755, "#{PKG_DESTINATION}/public/gateway.cgi" end task :copy_html_files do diff --git a/railties/bin/process/inspector b/railties/bin/process/inspector deleted file mode 100755 index bf25ad86d1..0000000000 --- a/railties/bin/process/inspector +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/inspector' diff --git a/railties/bin/process/reaper b/railties/bin/process/reaper deleted file mode 100755 index c77f04535f..0000000000 --- a/railties/bin/process/reaper +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/reaper' diff --git a/railties/bin/process/spawner b/railties/bin/process/spawner deleted file mode 100755 index 7118f3983c..0000000000 --- a/railties/bin/process/spawner +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/spawner' diff --git a/railties/lib/commands/process/inspector.rb b/railties/lib/commands/process/inspector.rb deleted file mode 100644 index 8a6437e715..0000000000 --- a/railties/lib/commands/process/inspector.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'optparse' - -if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Inspector is only for Unix") end - -OPTIONS = { - :pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'), - :pattern => "dispatch.*.pid", - :ps => "ps -o pid,state,user,start,time,pcpu,vsz,majflt,command -p %s" -} - -class Inspector - def self.inspect(pid_path, pattern) - new(pid_path, pattern).inspect - end - - def initialize(pid_path, pattern) - @pid_path, @pattern = pid_path, pattern - end - - def inspect - header = `#{OPTIONS[:ps] % 1}`.split("\n")[0] + "\n" - lines = pids.collect { |pid| `#{OPTIONS[:ps] % pid}`.split("\n")[1] } - - puts(header + lines.join("\n")) - end - - private - def pids - pid_files.collect do |pid_file| - File.read(pid_file).to_i - end - end - - def pid_files - Dir.glob(@pid_path + "/" + @pattern) - end -end - - -ARGV.options do |opts| - opts.banner = "Usage: inspector [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - Displays system information about Rails dispatchers (or other processes that use pid files) through - the ps command. - - Examples: - inspector # default ps on all tmp/pids/dispatch.*.pid files - inspector -s 'ps -o user,start,majflt,pcpu,vsz -p %s' # custom ps, %s is where the pid is interleaved - EOF - - opts.on(" Options:") - - opts.on("-s", "--ps=command", "default: #{OPTIONS[:ps]}", String) { |v| OPTIONS[:ps] = 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.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -Inspector.inspect(OPTIONS[:pid_path], OPTIONS[:pattern]) diff --git a/railties/lib/commands/process/reaper.rb b/railties/lib/commands/process/reaper.rb deleted file mode 100644 index 95175d41e0..0000000000 --- a/railties/lib/commands/process/reaper.rb +++ /dev/null @@ -1,149 +0,0 @@ -require 'optparse' -require 'net/http' -require 'uri' - -if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Reaper is only for Unix") end - -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: - # - # Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid") - def process(action, pid_path, pattern, keyword) - new(pid_path, pattern, keyword).process(action) - end - - # Forces the (rails) application to reload by sending a +HUP+ signal to the - # process. - def reload(pid) - `kill -s HUP #{pid}` - end - - # 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 - - 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", - :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| - opts.banner = "Usage: reaper [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - 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: - - * restart : Restarts the application by reloading both application and framework code - * reload : Only reloads the application, but not the framework (like the development environment) - * graceful: Marks all of the processes for exit after the next request - * kill : Forcefully exists all processes regardless of whether they're currently serving a request - - Restart is the most common and default action. - - Examples: - 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("-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 "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher]) diff --git a/railties/lib/commands/process/spawner.rb b/railties/lib/commands/process/spawner.rb deleted file mode 100644 index 8bf47abb75..0000000000 --- a/railties/lib/commands/process/spawner.rb +++ /dev/null @@ -1,219 +0,0 @@ -require 'active_support' -require 'optparse' -require 'socket' -require 'fileutils' - -def daemonize #:nodoc: - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. - Dir.chdir "/" # Release old working directory. - File.umask 0000 # Ensure sensible umask. Adjust as needed. - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile. -end - -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 - -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" - - # Add prefix functionality to spawner's call to mongrel_rails - # Digging through mongrel's project subversion server, the earliest - # Tag that has prefix implemented in the bin/mongrel_rails file - # is 0.3.15 which also happens to be the earliest tag listed. - # References: http://mongrel.rubyforge.org/svn/tags - if Mongrel::Const::MONGREL_VERSION.to_f >=0.3 && !OPTIONS[:prefix].nil? - cmd = cmd + " --prefix #{OPTIONS[:prefix]}" - end - 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|mingw)/ && !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(RELATIVE_RAILS_ROOT + '/public/dispatch.fcgi'), - :pids => File.expand_path(RELATIVE_RAILS_ROOT + "/tmp/pids"), - :rails_root => File.expand_path(RELATIVE_RAILS_ROOT), - :process => "dispatch", - :port => 8000, - :address => '0.0.0.0', - :instances => 3, - :repeat => nil, - :prefix => nil -} - -ARGV.options do |opts| - opts.banner = "Usage: spawner [platform] [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - 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. - - 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. - - 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. - - 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]})") { |v| OPTIONS[:port] = v } - - if spawner_class.can_bind_to_custom_address? - opts.on("-a", "--address=ip", String, "Bind to IP address (default: #{OPTIONS[:address]})") { |v| OPTIONS[:address] = v } - 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("-P", "--prefix=path", String, "URL prefix for Rails app. [Used only with Mongrel > v0.3.15]: (default: #{OPTIONS[:prefix]})") { |v| OPTIONS[:prefix] = 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) } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -ENV["RAILS_ENV"] = OPTIONS[:environment] - -if OPTIONS[:repeat] - daemonize - trap("TERM") { exit } - spawner_class.record_pid - - loop do - spawner_class.spawn_all - sleep(OPTIONS[:repeat]) - end -else - spawner_class.spawn_all -end diff --git a/railties/lib/commands/process/spinner.rb b/railties/lib/commands/process/spinner.rb deleted file mode 100644 index c0b2f09a94..0000000000 --- a/railties/lib/commands/process/spinner.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'optparse' - -def daemonize #:nodoc: - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. - Dir.chdir "/" # Release old working directory. - File.umask 0000 # Ensure sensible umask. Adjust as needed. - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile. -end - -OPTIONS = { - :interval => 5.0, - :command => File.expand_path(RAILS_ROOT + '/script/process/spawner'), - :daemon => false -} - -ARGV.options do |opts| - opts.banner = "Usage: spinner [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - The spinner is a protection loop for the spawner, which will attempt to restart any FCGI processes - that might have been exited or outright crashed. It's a brute-force attempt that'll just try - to run the spawner every X number of seconds, so it does pose a light load on the server. - - Examples: - spinner # attempts to run the spawner with default settings every second with output on the terminal - spinner -i 3 -d # only run the spawner every 3 seconds and detach from the terminal to become a daemon - spinner -c '/path/to/app/script/process/spawner -p 9000 -i 10' -d # using custom spawner - EOF - - opts.on(" Options:") - - opts.on("-c", "--command=path", String) { |v| OPTIONS[:command] = v } - opts.on("-i", "--interval=seconds", Float) { |v| OPTIONS[:interval] = v } - opts.on("-d", "--daemon") { |v| OPTIONS[:daemon] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -daemonize if OPTIONS[:daemon] - -trap(OPTIONS[:daemon] ? "TERM" : "INT") { exit } - -loop do - system(OPTIONS[:command]) - sleep(OPTIONS[:interval]) -end \ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb index 5435b2aeb3..fb3a407d2e 100644 --- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb +++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -85,7 +85,6 @@ class AppGenerator < Rails::Generator::Base public/javascripts public/stylesheets script/performance - script/process test/fixtures test/functional test/integration @@ -139,8 +138,8 @@ class AppGenerator < Rails::Generator::Base def create_script_files(m) %w( - about console dbconsole destroy generate performance/benchmarker performance/profiler - performance/request process/reaper process/spawner process/inspector runner server plugin + about console dbconsole destroy generate runner server plugin + performance/benchmarker performance/profiler performance/request ).each do |file| m.file "bin/#{file}", "script/#{file}", { :chmod => 0755,