Merge branch 'master' into just-dash-w

Conflicts:
	lib/puma/cli.rb
This commit is contained in:
Evan Phoenix 2012-09-08 23:05:36 -07:00
commit 111cc1da74
15 changed files with 638 additions and 15 deletions

View File

@ -1,3 +1,20 @@
=== 1.6.3 / 2012-09-04
* 1 bug fix:
* Close sockets waiting in the reactor when a hot restart is performed
so that browsers reconnect on the next request
=== 1.6.2 / 2012-08-27
* 1 bug fix:
* Rescue StandardError instead of IOError to handle SystemCallErrors
as well as other application exceptions inside the reactor.
=== 1.6.1 / 2012-07-23
* 1 packaging bug fixed:
* Include missing files
=== 1.6.0 / 2012-07-23
* 1 major bug fix:

View File

@ -1,6 +1,6 @@
.travis.yml
COPYING
Gemfile
Gemfile.lock
History.txt
LICENSE
Manifest.txt
@ -38,6 +38,7 @@ lib/puma/compat.rb
lib/puma/configuration.rb
lib/puma/const.rb
lib/puma/control_cli.rb
lib/puma/detect.rb
lib/puma/events.rb
lib/puma/jruby_restart.rb
lib/puma/null_io.rb

View File

@ -122,6 +122,10 @@ If you start puma with `-S some/path` then you can pass that same path to the `p
will cause the server to perform a restart. `pumactl` is a simple CLI frontend to the control/status app described above.
## Managing multiple Pumas / init.d script
If you want an easy way to manage multiple scripts at once check [tools/jungle](https://github.com/puma/puma/tree/master/tools/jungle) for an init.d script.
## License
Puma is copyright 2011 Evan Phoenix and contributors. It is licensed under the BSD license. See the include LICENSE file for details.

0
docs/config.md Normal file
View File

85
docs/nginx.md Normal file
View File

@ -0,0 +1,85 @@
# Nginx configuration example file
This is a very common setup using an upstream. It was adapted from some Capistrano recipe I found on the Internet a while ago.
```
upstream myapp {
server unix:///myapp/tmp/puma.sock;
}
server {
listen 80;
server_name myapp.com;
# ~2 seconds is often enough for most folks to parse HTML/CSS and
# retrieve needed images/icons/frames, connections are cheap in
# nginx so increasing this is generally safe...
keepalive_timeout 5;
# path for static files
root /myapp/public;
access_log /myapp/log/nginx.access.log;
error_log /myapp/log/nginx.error.log info;
# this rewrites all the requests to the maintenance.html
# page if it exists in the doc root. This is for capistrano's
# disable web task
if (-f $document_root/maintenance.html) {
rewrite ^(.*)$ /maintenance.html last;
break;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
# If the file exists as a static file serve it directly without
# running all the other rewite tests on it
if (-f $request_filename) {
break;
}
# check for index.html for directory index
# if its there on the filesystem then rewite
# the url to add /index.html to the end of it
# and then break to send it to the next config rules.
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
# this is the meat of the rack page caching config
# it adds .html to the end of the url and then checks
# the filesystem for that file. If it exists, then we
# rewite the url to have explicit .html on the end
# and then send it on its way to the next config rule.
# if there is no file on the fs then it sets all the
# necessary headers and proxies to our upstream mongrels
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://myapp;
break;
}
}
# Now this supposedly should work as it gets the filenames with querystrings that Rails provides.
# BUT there's a chance it could break the ajax calls.
location ~* \.(ico|css|gif|jpe?g|png)(\?[0-9]+)?$ {
expires max;
break;
}
location ~ ^/javascripts/.*\.js(\?[0-9]+)?$ {
expires max;
break;
}
# Error pages
# error_page 500 502 503 504 /500.html;
location = /500.html {
root /myapp/current/public;
}
}
```

View File

@ -231,5 +231,19 @@ module Puma
false
end
def write_400
begin
@io << ERROR_400_RESPONSE
rescue StandardError
end
end
def write_500
begin
@io << ERROR_500_RESPONSE
rescue StandardError
end
end
end
end

View File

@ -25,7 +25,7 @@ module Puma
# too taxing on performance.
module Const
PUMA_VERSION = VERSION = "1.6.0".freeze
PUMA_VERSION = VERSION = "1.6.3".freeze
FAST_TRACK_KA_TIMEOUT = 0.2
@ -49,11 +49,17 @@ module Puma
PUMA_TMP_BASE = "puma".freeze
# Indicate that we couldn't parse the request
ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
ERROR_404_RESPONSE = "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze
CONTENT_LENGTH = "CONTENT_LENGTH".freeze
# Indicate that there was an internal error, obviously.
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
# A common header for indicating the server is too busy. Not used yet.
ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze

View File

@ -16,6 +16,9 @@ module Puma
def initialize(stdout, stderr)
@stdout = stdout
@stderr = stderr
@stdout.sync = true
@stderr.sync = true
end
attr_reader :stdout, :stderr

View File

@ -12,10 +12,12 @@ module Puma
@input = []
@sleep_for = DefaultSleepFor
@timeouts = []
@sockets = [@ready]
end
def run
sockets = [@ready]
sockets = @sockets
while true
ready = IO.select sockets, nil, nil, @sleep_for
@ -28,6 +30,15 @@ module Puma
when "*"
sockets += @input
@input.clear
when "c"
sockets.delete_if do |s|
if s == @ready
false
else
s.close
true
end
end
when "!"
return
end
@ -48,13 +59,16 @@ module Puma
# The client doesn't know HTTP well
rescue HttpParserError => e
c.write_400
c.close
sockets.delete c
@events.parse_error @server, c.env, e
rescue IOError => e
rescue StandardError => e
c.write_500
c.close
sockets.delete c
end
end
@ -79,12 +93,14 @@ module Puma
def run_in_thread
@thread = Thread.new {
begin
run
rescue Exception => e
puts "MAJOR ERROR DETECTED"
p e
puts e.backtrace
while true
begin
run
break
rescue StandardError => e
STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
puts e.backtrace
end
end
}
end
@ -117,8 +133,14 @@ module Puma
end
end
# Close all watched sockets and clear them from being watched
def clear!
@trigger << "c"
end
def shutdown
@trigger << "!"
@thread.join
end
end
end

View File

@ -124,7 +124,9 @@ module Puma
begin
process_now = client.eagerly_finish
rescue HttpParserError => e
client.write_400
client.close
@events.parse_error self, client.env, e
rescue IOError
client.close
@ -184,6 +186,8 @@ module Puma
end
end
@reactor.clear! if @status == :restart
@reactor.shutdown
graceful_shutdown if @status == :stop
ensure
@ -248,10 +252,14 @@ module Puma
# The client doesn't know HTTP well
rescue HttpParserError => e
client.write_400
@events.parse_error self, client.env, e
# Server error
rescue StandardError => e
client.write_500
@events.unknown_error self, e, "Read"
ensure

View File

@ -2,23 +2,23 @@
Gem::Specification.new do |s|
s.name = "puma"
s.version = "1.5.0"
s.version = "1.6.3"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Evan Phoenix"]
s.date = "2012-07-23"
s.date = "2012-09-04"
s.description = "Puma is a simple, fast, and highly concurrent HTTP 1.1 server for Ruby web applications. It can be used with any application that supports Rack, and is considered the replacement for Webrick and Mongrel. It was designed to be the go-to server for [Rubinius](http://rubini.us), but also works well with JRuby and MRI. Puma is intended for use in both development and production environments.\n\nUnder the hood, Puma processes requests using a C-optimized Ragel extension (inherited from Mongrel) that provides fast, accurate HTTP 1.1 protocol parsing in a portable way. Puma then serves the request in a thread from an internal thread pool (which you can control). This allows Puma to provide real concurrency for your web application!\n\nWith Rubinius 2.0, Puma will utilize all cores on your CPU with real threads, meaning you won't have to spawn multiple processes to increase throughput. You can expect to see a similar benefit from JRuby.\n\nOn MRI, there is a Global Interpreter Lock (GIL) that ensures only one thread can be run at a time. But if you're doing a lot of blocking IO (such as HTTP calls to external APIs like Twitter), Puma still improves MRI's throughput by allowing blocking IO to be run concurrently (EventMachine-based servers such as Thin turn off this ability, requiring you to use special libraries). Your mileage may vary. In order to get the best throughput, it is highly recommended that you use a Ruby implementation with real threads like [Rubinius](http://rubini.us) or [JRuby](http://jruby.org)."
s.email = ["evan@phx.io"]
s.executables = ["puma", "pumactl"]
s.extensions = ["ext/puma_http11/extconf.rb"]
s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
s.files = [".travis.yml", "COPYING", "Gemfile", "History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "TODO", "bin/puma", "bin/pumactl", "examples/CA/cacert.pem", "examples/CA/newcerts/cert_1.pem", "examples/CA/newcerts/cert_2.pem", "examples/CA/private/cakeypair.pem", "examples/CA/serial", "examples/config.rb", "examples/puma/cert_puma.pem", "examples/puma/csr_puma.pem", "examples/puma/puma_keypair.pem", "examples/qc_config.rb", "ext/puma_http11/PumaHttp11Service.java", "ext/puma_http11/ext_help.h", "ext/puma_http11/extconf.rb", "ext/puma_http11/http11_parser.c", "ext/puma_http11/http11_parser.h", "ext/puma_http11/http11_parser.java.rl", "ext/puma_http11/http11_parser.rl", "ext/puma_http11/http11_parser_common.rl", "ext/puma_http11/org/jruby/puma/Http11.java", "ext/puma_http11/org/jruby/puma/Http11Parser.java", "ext/puma_http11/puma_http11.c", "lib/puma.rb", "lib/puma/app/status.rb", "lib/puma/cli.rb", "lib/puma/client.rb", "lib/puma/compat.rb", "lib/puma/configuration.rb", "lib/puma/const.rb", "lib/puma/control_cli.rb", "lib/puma/events.rb", "lib/puma/jruby_restart.rb", "lib/puma/null_io.rb", "lib/puma/rack_patch.rb", "lib/puma/reactor.rb", "lib/puma/server.rb", "lib/puma/thread_pool.rb", "lib/rack/handler/puma.rb", "puma.gemspec", "test/ab_rs.rb", "test/config/app.rb", "test/hello-post.ru", "test/hello.ru", "test/lobster.ru", "test/mime.yaml", "test/slow.ru", "test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb", "test/testhelp.rb", "tools/trickletest.rb"]
s.files = ["COPYING", "Gemfile", "Gemfile.lock", "History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "TODO", "bin/puma", "bin/pumactl", "examples/CA/cacert.pem", "examples/CA/newcerts/cert_1.pem", "examples/CA/newcerts/cert_2.pem", "examples/CA/private/cakeypair.pem", "examples/CA/serial", "examples/config.rb", "examples/puma/cert_puma.pem", "examples/puma/csr_puma.pem", "examples/puma/puma_keypair.pem", "examples/qc_config.rb", "ext/puma_http11/PumaHttp11Service.java", "ext/puma_http11/ext_help.h", "ext/puma_http11/extconf.rb", "ext/puma_http11/http11_parser.c", "ext/puma_http11/http11_parser.h", "ext/puma_http11/http11_parser.java.rl", "ext/puma_http11/http11_parser.rl", "ext/puma_http11/http11_parser_common.rl", "ext/puma_http11/org/jruby/puma/Http11.java", "ext/puma_http11/org/jruby/puma/Http11Parser.java", "ext/puma_http11/puma_http11.c", "lib/puma.rb", "lib/puma/app/status.rb", "lib/puma/cli.rb", "lib/puma/client.rb", "lib/puma/compat.rb", "lib/puma/configuration.rb", "lib/puma/const.rb", "lib/puma/control_cli.rb", "lib/puma/detect.rb", "lib/puma/events.rb", "lib/puma/jruby_restart.rb", "lib/puma/null_io.rb", "lib/puma/rack_patch.rb", "lib/puma/reactor.rb", "lib/puma/server.rb", "lib/puma/thread_pool.rb", "lib/rack/handler/puma.rb", "puma.gemspec", "test/ab_rs.rb", "test/config/app.rb", "test/hello-post.ru", "test/hello.ru", "test/lobster.ru", "test/mime.yaml", "test/slow.ru", "test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb", "test/testhelp.rb", "tools/trickletest.rb"]
s.homepage = "http://puma.io"
s.rdoc_options = ["--main", "README.md"]
s.require_paths = ["lib"]
s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
s.rubyforge_project = "puma"
s.rubygems_version = "1.8.22"
s.rubygems_version = "1.8.24"
s.summary = "Puma is a simple, fast, and highly concurrent HTTP 1.1 server for Ruby web applications"
s.test_files = ["test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]

View File

@ -1,6 +1,9 @@
require "rbconfig"
require 'test/unit'
require 'socket'
require 'timeout'
require 'net/http'
require 'tempfile'
require 'puma/cli'
require 'puma/control_cli'
@ -10,12 +13,48 @@ class TestIntegration < Test::Unit::TestCase
@state_path = "test/test_puma.state"
@bind_path = "test/test_server.sock"
@control_path = "test/test_control.sock"
@tcp_port = 9998
@server = nil
@script = nil
end
def teardown
File.unlink @state_path rescue nil
File.unlink @bind_path rescue nil
File.unlink @control_path rescue nil
if @server
Process.kill "INT", @server.pid
Process.wait @server.pid
@server.close
end
if @script
@script.close!
end
end
def server(opts)
core = "#{Gem.ruby} -rubygems -Ilib bin/puma"
cmd = "#{core} --restart-cmd '#{core}' -b tcp://127.0.0.1:#{@tcp_port} #{opts}"
tf = Tempfile.new "puma-test"
tf.puts "exec #{cmd}"
tf.close
@script = tf
@server = IO.popen("sh #{tf.path}", "r")
true while @server.gets =~ /Ctrl-C/
sleep 1
@server
end
def signal(which)
Process.kill which, @server.pid
end
def test_stop_via_pumactl
@ -45,4 +84,39 @@ class TestIntegration < Test::Unit::TestCase
assert_kind_of Thread, t.join(1), "server didn't stop"
end
def test_restart_closes_keepalive_sockets
server("-q test/hello.ru")
s = TCPSocket.new "localhost", @tcp_port
s << "GET / HTTP/1.1\r\n\r\n"
true until s.gets == "\r\n"
s.readpartial(20)
signal :USR2
true while @server.gets =~ /Ctrl-C/
sleep 1
s.write "GET / HTTP/1.1\r\n\r\n"
assert_raises Errno::ECONNRESET do
Timeout.timeout(2) do
s.read(2)
end
end
s = TCPSocket.new "localhost", @tcp_port
s << "GET / HTTP/1.0\r\n\r\n"
assert_equal "Hello World", s.read.split("\r\n").last
end
def test_bad_query_string_outputs_400
server "-q test/hello.ru 2>&1"
s = TCPSocket.new "localhost", @tcp_port
s << "GET /?h=% HTTP/1.0\r\n\r\n"
data = s.read
assert_equal "HTTP/1.1 400 Bad Request\r\n\r\n", data
end
end

54
tools/jungle/README.md Normal file
View File

@ -0,0 +1,54 @@
# Puma daemon service
Init script to manage multiple Puma servers on the same box using start-stop-daemon.
## Installation
# Copy the init script to services directory
sudo cp puma /etc/init.d
sudo chmod +x /etc/init.d/puma
# Make it start at boot time.
sudo update-rc.d -f puma defaults
# Copy the Puma runner to an accessible location
sudo cp run-puma /usr/local/bin
sudo chmod +x /usr/local/bin/run-puma
# Create an empty configuration file
sudo touch /etc/puma.conf
## Managing the jungle
Puma apps are held in /etc/puma.conf by default. It's mainly a CSV file and every line represents one app. Here's the syntax:
app-path,user,config-file-path,log-file-path
You can add an instance by editing the file or running the following command:
sudo /etc/init.d/puma add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log
The config and log paths are optional parameters and default to:
* config: /path/to/app/*config/puma.rb*
* log: /path/to/app/*config/puma.log*
To remove an app, simply delete the line from the config file or run:
sudo /etc/init.d/puma remove /path/to/app
The command will make sure the Puma instance stops before removing it from the jungle.
## Assumptions
* The script expects a temporary folder named /path/to/app/*tmp/puma* to exist. Create it if it's not there by default.
The pid and state files should live there and must be called: *tmp/puma/pid* and *tmp/puma/state*.
You can change those if you want but you'll have to adapt the script for it to work.
* Here's what a minimal app's config file should have:
```
pidfile "/path/to/app/tmp/puma/pid"
state_path "/path/to/app/tmp/puma/state"
activate_control_app
```

332
tools/jungle/puma Executable file
View File

@ -0,0 +1,332 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: puma
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Example initscript
# Description: This file should be used to construct scripts to be
# placed in /etc/init.d.
### END INIT INFO
# Author: Darío Javier Cravero <dario@exordo.com>
#
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/usr/local/bin:/usr/local/sbin/:/sbin:/usr/sbin:/bin:/usr/bin
DESC="Puma rack web server"
NAME=puma
DAEMON=$NAME
SCRIPTNAME=/etc/init.d/$NAME
CONFIG=/etc/puma.conf
JUNGLE=`cat $CONFIG`
RUNPUMA=/usr/local/bin/run-puma
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
#
# Function that starts the jungle
#
do_start() {
log_daemon_msg "=> Running the jungle..."
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
user=`echo $i | cut -d , -f 2`
config_file=`echo $i | cut -d , -f 3`
if [ "$config_file" = "" ]; then
config_file="$dir/config/puma.rb"
fi
log_file=`echo $i | cut -d , -f 4`
if [ "$log_file" = "" ]; then
log_file="$dir/log/puma.log"
fi
do_start_one $dir $user $config_file $log_file
done
}
do_start_one() {
PIDFILE=$1/tmp/puma/pid
if [ -e $PIDFILE ]; then
PID=`cat $PIDFILE`
# If the puma isn't running, run it, otherwise restart it.
if [ "`ps -A -o pid= | grep -c $PID`" -eq 0 ]; then
do_start_one_do $1 $2 $3 $4
else
do_restart_one $1
fi
else
do_start_one_do $1 $2 $3 $4
fi
}
do_start_one_do() {
log_daemon_msg "--> Woke up puma $1"
log_daemon_msg "user $2"
log_daemon_msg "log to $4"
start-stop-daemon --verbose --start --chdir $1 --chuid $2 --background --exec $RUNPUMA -- $1 $3 $4
}
#
# Function that stops the jungle
#
do_stop() {
log_daemon_msg "=> Putting all the beasts to bed..."
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
do_stop_one $dir
done
}
#
# Function that stops the daemon/service
#
do_stop_one() {
log_daemon_msg "--> Stopping $1"
PIDFILE=$1/tmp/puma/pid
STATEFILE=$1/tmp/puma/state
if [ -e $PIDFILE ]; then
PID=`cat $PIDFILE`
if [ "`ps -A -o pid= | grep -c $PID`" -eq 0 ]; then
log_daemon_msg "---> Puma $1 isn't running."
else
log_daemon_msg "---> About to kill PID `cat $PIDFILE`"
pumactl --state $STATEFILE stop
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE $STATEFILE
fi
else
log_daemon_msg "---> No puma here..."
fi
return 0
}
#
# Function that restarts the jungle
#
do_restart() {
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
do_restart_one $dir
done
}
#
# Function that sends a SIGUSR2 to the daemon/service
#
do_restart_one() {
PIDFILE=$1/tmp/puma/pid
i=`grep $1 $CONFIG`
dir=`echo $i | cut -d , -f 1`
if [ -e $PIDFILE ]; then
log_daemon_msg "--> About to restart puma $1"
pumactl --state $dir/tmp/puma/state restart
# kill -s USR2 `cat $PIDFILE`
# TODO Check if process exist
else
log_daemon_msg "--> Your puma was never playing... Let's get it out there first"
user=`echo $i | cut -d , -f 2`
config_file=`echo $i | cut -d , -f 3`
if [ "$config_file" = "" ]; then
config_file="$dir/config/puma.rb"
fi
log_file=`echo $i | cut -d , -f 4`
if [ "$log_file" = "" ]; then
log_file="$dir/log/puma.log"
fi
do_start_one $dir $user $config_file $log_file
fi
return 0
}
#
# Function that statuss the jungle
#
do_status() {
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
do_status_one $dir
done
}
#
# Function that sends a SIGUSR2 to the daemon/service
#
do_status_one() {
PIDFILE=$1/tmp/puma/pid
i=`grep $1 $CONFIG`
dir=`echo $i | cut -d , -f 1`
if [ -e $PIDFILE ]; then
log_daemon_msg "--> About to status puma $1"
pumactl --state $dir/tmp/puma/state stats
# kill -s USR2 `cat $PIDFILE`
# TODO Check if process exist
else
log_daemon_msg "--> $1 isn't there :(..."
fi
return 0
}
do_add() {
str=""
# App's directory
if [ -d "$1" ]; then
if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
str=$1
else
echo "The app is already being managed. Remove it if you want to update its config."
exit 1
fi
else
echo "The directory $1 doesn't exist."
exit 1
fi
# User to run it as
if [ "`grep -c "^$2:" /etc/passwd`" -eq 0 ]; then
echo "The user $2 doesn't exist."
exit 1
else
str="$str,$2"
fi
# Config file
if [ "$3" != "" ]; then
if [ -e $3 ]; then
str="$str,$3"
else
echo "The config file $3 doesn't exist."
exit 1
fi
fi
# Log file
if [ "$4" != "" ]; then
str="$str,$4"
fi
# Add it to the jungle
echo $str >> $CONFIG
log_daemon_msg "Added a Puma to the jungle: $str. You still have to start it though."
}
do_remove() {
if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
echo "There's no app $1 to remove."
else
# Stop it first.
do_stop_one $1
# Remove it from the config.
sed -i "\\:^$1:d" $CONFIG
log_daemon_msg "Removed a Puma from the jungle: $1."
fi
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_start
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
user=`echo $i | cut -d , -f 2`
config_file=`echo $i | cut -d , -f 3`
if [ "$config_file" = "" ]; then
config_file="$dir/config/puma.rb"
fi
log_file=`echo $i | cut -d , -f 4`
if [ "$log_file" = "" ]; then
log_file="$dir/log/puma.log"
fi
do_start_one $dir $user $config_file $log_file
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_stop
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
do_stop_one $dir
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
# TODO Implement.
log_daemon_msg "Status $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_status
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
do_status_one $dir
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_restart
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
do_restart_one $dir
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
add)
if [ "$#" -lt 3 ]; then
echo "Please, specifiy the app's directory and the user that will run it at least."
echo " Usage: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
echo " config and log are optionals."
exit 1
else
do_add $2 $3 $4 $5
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
remove)
if [ "$#" -lt 2 ]; then
echo "Please, specifiy the app's directory to remove."
exit 1
else
do_remove $2
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
*)
echo "Usage:" >&2
echo " Run the jungle: $SCRIPTNAME {start|stop|status|restart}" >&2
echo " Add a Puma: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
echo " config and log are optionals."
echo " Remove a Puma: $SCRIPTNAME remove /path/to/app"
echo " On a Puma: $SCRIPTNAME {start|stop|status|restart} PUMA-NAME" >&2
exit 3
;;
esac
:

3
tools/jungle/run-puma Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
app=$1; config=$2; log=$3;
cd $app && exec bundle exec puma -C $config 2>&1 >> $log