puma 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/History.md +1532 -0
- data/LICENSE +26 -0
- data/README.md +291 -0
- data/bin/puma +10 -0
- data/bin/puma-wild +31 -0
- data/bin/pumactl +12 -0
- data/docs/architecture.md +37 -0
- data/docs/deployment.md +111 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/nginx.md +80 -0
- data/docs/plugins.md +38 -0
- data/docs/restart.md +41 -0
- data/docs/signals.md +96 -0
- data/docs/systemd.md +290 -0
- data/docs/tcp_mode.md +96 -0
- data/ext/puma_http11/PumaHttp11Service.java +19 -0
- data/ext/puma_http11/ext_help.h +15 -0
- data/ext/puma_http11/extconf.rb +28 -0
- data/ext/puma_http11/http11_parser.c +1044 -0
- data/ext/puma_http11/http11_parser.h +65 -0
- data/ext/puma_http11/http11_parser.java.rl +145 -0
- data/ext/puma_http11/http11_parser.rl +147 -0
- data/ext/puma_http11/http11_parser_common.rl +54 -0
- data/ext/puma_http11/io_buffer.c +155 -0
- data/ext/puma_http11/mini_ssl.c +553 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +226 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +363 -0
- data/ext/puma_http11/puma_http11.c +502 -0
- data/lib/puma.rb +31 -0
- data/lib/puma/accept_nonblock.rb +29 -0
- data/lib/puma/app/status.rb +80 -0
- data/lib/puma/binder.rb +385 -0
- data/lib/puma/cli.rb +239 -0
- data/lib/puma/client.rb +494 -0
- data/lib/puma/cluster.rb +554 -0
- data/lib/puma/commonlogger.rb +108 -0
- data/lib/puma/configuration.rb +362 -0
- data/lib/puma/const.rb +235 -0
- data/lib/puma/control_cli.rb +289 -0
- data/lib/puma/detect.rb +15 -0
- data/lib/puma/dsl.rb +740 -0
- data/lib/puma/events.rb +156 -0
- data/lib/puma/io_buffer.rb +4 -0
- data/lib/puma/jruby_restart.rb +84 -0
- data/lib/puma/launcher.rb +475 -0
- data/lib/puma/minissl.rb +278 -0
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/null_io.rb +44 -0
- data/lib/puma/plugin.rb +120 -0
- data/lib/puma/plugin/tmp_restart.rb +36 -0
- data/lib/puma/rack/builder.rb +301 -0
- data/lib/puma/rack/urlmap.rb +93 -0
- data/lib/puma/rack_default.rb +9 -0
- data/lib/puma/reactor.rb +400 -0
- data/lib/puma/runner.rb +192 -0
- data/lib/puma/server.rb +1030 -0
- data/lib/puma/single.rb +123 -0
- data/lib/puma/state_file.rb +31 -0
- data/lib/puma/tcp_logger.rb +41 -0
- data/lib/puma/thread_pool.rb +328 -0
- data/lib/puma/util.rb +124 -0
- data/lib/rack/handler/puma.rb +115 -0
- data/tools/docker/Dockerfile +16 -0
- data/tools/jungle/README.md +19 -0
- data/tools/jungle/init.d/README.md +61 -0
- data/tools/jungle/init.d/puma +421 -0
- data/tools/jungle/init.d/run-puma +18 -0
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/jungle/upstart/README.md +61 -0
- data/tools/jungle/upstart/puma-manager.conf +31 -0
- data/tools/jungle/upstart/puma.conf +69 -0
- data/tools/trickletest.rb +44 -0
- metadata +144 -0
data/lib/puma/util.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri/common'
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
module Util
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def pipe
|
10
|
+
IO.pipe
|
11
|
+
end
|
12
|
+
|
13
|
+
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
|
14
|
+
# target encoding of the string returned, and it defaults to UTF-8
|
15
|
+
if defined?(::Encoding)
|
16
|
+
def unescape(s, encoding = Encoding::UTF_8)
|
17
|
+
URI.decode_www_form_component(s, encoding)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
def unescape(s, encoding = nil)
|
21
|
+
URI.decode_www_form_component(s, encoding)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
module_function :unescape
|
25
|
+
|
26
|
+
DEFAULT_SEP = /[&;] */n
|
27
|
+
|
28
|
+
# Stolen from Mongrel, with some small modifications:
|
29
|
+
# Parses a query string by breaking it up at the '&'
|
30
|
+
# and ';' characters. You can also use this to parse
|
31
|
+
# cookies by changing the characters used in the second
|
32
|
+
# parameter (which defaults to '&;').
|
33
|
+
def parse_query(qs, d = nil, &unescaper)
|
34
|
+
unescaper ||= method(:unescape)
|
35
|
+
|
36
|
+
params = {}
|
37
|
+
|
38
|
+
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
|
39
|
+
next if p.empty?
|
40
|
+
k, v = p.split('=', 2).map(&unescaper)
|
41
|
+
|
42
|
+
if cur = params[k]
|
43
|
+
if cur.class == Array
|
44
|
+
params[k] << v
|
45
|
+
else
|
46
|
+
params[k] = [cur, v]
|
47
|
+
end
|
48
|
+
else
|
49
|
+
params[k] = v
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
return params
|
54
|
+
end
|
55
|
+
|
56
|
+
# A case-insensitive Hash that preserves the original case of a
|
57
|
+
# header when set.
|
58
|
+
class HeaderHash < Hash
|
59
|
+
def self.new(hash={})
|
60
|
+
HeaderHash === hash ? hash : super(hash)
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(hash={})
|
64
|
+
super()
|
65
|
+
@names = {}
|
66
|
+
hash.each { |k, v| self[k] = v }
|
67
|
+
end
|
68
|
+
|
69
|
+
def each
|
70
|
+
super do |k, v|
|
71
|
+
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_hash
|
76
|
+
hash = {}
|
77
|
+
each { |k,v| hash[k] = v }
|
78
|
+
hash
|
79
|
+
end
|
80
|
+
|
81
|
+
def [](k)
|
82
|
+
super(k) || super(@names[k.downcase])
|
83
|
+
end
|
84
|
+
|
85
|
+
def []=(k, v)
|
86
|
+
canonical = k.downcase
|
87
|
+
delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
|
88
|
+
@names[k] = @names[canonical] = k
|
89
|
+
super k, v
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete(k)
|
93
|
+
canonical = k.downcase
|
94
|
+
result = super @names.delete(canonical)
|
95
|
+
@names.delete_if { |name,| name.downcase == canonical }
|
96
|
+
result
|
97
|
+
end
|
98
|
+
|
99
|
+
def include?(k)
|
100
|
+
@names.include?(k) || @names.include?(k.downcase)
|
101
|
+
end
|
102
|
+
|
103
|
+
alias_method :has_key?, :include?
|
104
|
+
alias_method :member?, :include?
|
105
|
+
alias_method :key?, :include?
|
106
|
+
|
107
|
+
def merge!(other)
|
108
|
+
other.each { |k, v| self[k] = v }
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def merge(other)
|
113
|
+
hash = dup
|
114
|
+
hash.merge! other
|
115
|
+
end
|
116
|
+
|
117
|
+
def replace(other)
|
118
|
+
clear
|
119
|
+
other.each { |k, v| self[k] = v }
|
120
|
+
self
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/handler'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
module Handler
|
7
|
+
module Puma
|
8
|
+
DEFAULT_OPTIONS = {
|
9
|
+
:Verbose => false,
|
10
|
+
:Silent => false
|
11
|
+
}
|
12
|
+
|
13
|
+
def self.config(app, options = {})
|
14
|
+
require 'puma'
|
15
|
+
require 'puma/configuration'
|
16
|
+
require 'puma/events'
|
17
|
+
require 'puma/launcher'
|
18
|
+
|
19
|
+
default_options = DEFAULT_OPTIONS.dup
|
20
|
+
|
21
|
+
# Libraries pass in values such as :Port and there is no way to determine
|
22
|
+
# if it is a default provided by the library or a special value provided
|
23
|
+
# by the user. A special key `user_supplied_options` can be passed. This
|
24
|
+
# contains an array of all explicitly defined user options. We then
|
25
|
+
# know that all other values are defaults
|
26
|
+
if user_supplied_options = options.delete(:user_supplied_options)
|
27
|
+
(options.keys - user_supplied_options).each do |k|
|
28
|
+
default_options[k] = options.delete(k)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
|
33
|
+
user_config.quiet
|
34
|
+
|
35
|
+
if options.delete(:Verbose)
|
36
|
+
app = Rack::CommonLogger.new(app, STDOUT)
|
37
|
+
end
|
38
|
+
|
39
|
+
if options[:environment]
|
40
|
+
user_config.environment options[:environment]
|
41
|
+
end
|
42
|
+
|
43
|
+
if options[:Threads]
|
44
|
+
min, max = options.delete(:Threads).split(':', 2)
|
45
|
+
user_config.threads min, max
|
46
|
+
end
|
47
|
+
|
48
|
+
if options[:Host] || options[:Port]
|
49
|
+
host = options[:Host] || default_options[:Host]
|
50
|
+
port = options[:Port] || default_options[:Port]
|
51
|
+
self.set_host_port_to_config(host, port, user_config)
|
52
|
+
end
|
53
|
+
|
54
|
+
if default_options[:Host]
|
55
|
+
file_config.set_default_host(default_options[:Host])
|
56
|
+
end
|
57
|
+
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
58
|
+
|
59
|
+
user_config.app app
|
60
|
+
end
|
61
|
+
conf
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.run(app, options = {})
|
65
|
+
conf = self.config(app, options)
|
66
|
+
|
67
|
+
events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
|
68
|
+
|
69
|
+
launcher = ::Puma::Launcher.new(conf, :events => events)
|
70
|
+
|
71
|
+
yield launcher if block_given?
|
72
|
+
begin
|
73
|
+
launcher.run
|
74
|
+
rescue Interrupt
|
75
|
+
puts "* Gracefully stopping, waiting for requests to finish"
|
76
|
+
launcher.stop
|
77
|
+
puts "* Goodbye!"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.valid_options
|
82
|
+
{
|
83
|
+
"Host=HOST" => "Hostname to listen on (default: localhost)",
|
84
|
+
"Port=PORT" => "Port to listen on (default: 8080)",
|
85
|
+
"Threads=MIN:MAX" => "min:max threads to use (default 0:16)",
|
86
|
+
"Verbose" => "Don't report each request (default: false)"
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.set_host_port_to_config(host, port, config)
|
91
|
+
config.clear_binds! if host || port
|
92
|
+
|
93
|
+
if host && (host[0,1] == '.' || host[0,1] == '/')
|
94
|
+
config.bind "unix://#{host}"
|
95
|
+
elsif host && host =~ /^ssl:\/\//
|
96
|
+
uri = URI.parse(host)
|
97
|
+
uri.port ||= port || ::Puma::Configuration::DefaultTCPPort
|
98
|
+
config.bind uri.to_s
|
99
|
+
else
|
100
|
+
|
101
|
+
if host
|
102
|
+
port ||= ::Puma::Configuration::DefaultTCPPort
|
103
|
+
end
|
104
|
+
|
105
|
+
if port
|
106
|
+
host ||= ::Puma::Configuration::DefaultTCPHost
|
107
|
+
config.port port, host
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
register :puma, Puma
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Use this Dockerfile to create minimal reproductions of issues
|
2
|
+
|
3
|
+
FROM ruby:2.6
|
4
|
+
|
5
|
+
# throw errors if Gemfile has been modified since Gemfile.lock
|
6
|
+
RUN bundle config --global frozen 1
|
7
|
+
|
8
|
+
WORKDIR /usr/src/app
|
9
|
+
|
10
|
+
COPY . .
|
11
|
+
RUN gem install bundler
|
12
|
+
RUN bundle install
|
13
|
+
RUN bundle exec rake compile
|
14
|
+
|
15
|
+
EXPOSE 9292
|
16
|
+
CMD bundle exec bin/puma test/rackup/hello.ru
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Puma as a service
|
2
|
+
|
3
|
+
## Upstart
|
4
|
+
|
5
|
+
See `/tools/jungle/upstart` for Ubuntu's upstart scripts.
|
6
|
+
|
7
|
+
## Systemd
|
8
|
+
|
9
|
+
See [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md).
|
10
|
+
|
11
|
+
## Init.d
|
12
|
+
|
13
|
+
Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS.
|
14
|
+
|
15
|
+
See `/tools/jungle/init.d` for tools to use with init.d and start-stop-daemon.
|
16
|
+
|
17
|
+
## rc.d
|
18
|
+
|
19
|
+
See `/tools/jungle/rc.d` for FreeBSD's rc.d scripts
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Puma daemon service
|
2
|
+
|
3
|
+
Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS.
|
4
|
+
|
5
|
+
Init script to manage multiple Puma servers on the same box using start-stop-daemon.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
# Copy the init script to services directory
|
10
|
+
sudo cp puma /etc/init.d
|
11
|
+
sudo chmod +x /etc/init.d/puma
|
12
|
+
|
13
|
+
# Make it start at boot time.
|
14
|
+
sudo update-rc.d -f puma defaults
|
15
|
+
|
16
|
+
# Copy the Puma runner to an accessible location
|
17
|
+
sudo cp run-puma /usr/local/bin
|
18
|
+
sudo chmod +x /usr/local/bin/run-puma
|
19
|
+
|
20
|
+
# Create an empty configuration file
|
21
|
+
sudo touch /etc/puma.conf
|
22
|
+
|
23
|
+
## Managing the jungle
|
24
|
+
|
25
|
+
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:
|
26
|
+
|
27
|
+
app-path,user,config-file-path,log-file-path,environment-variables
|
28
|
+
|
29
|
+
You can add an instance by editing the file or running the following command:
|
30
|
+
|
31
|
+
sudo /etc/init.d/puma add /path/to/app user /path/to/app/config/puma.rb /path/to/app/log/puma.log
|
32
|
+
|
33
|
+
The config and log paths, as well as the environment variables, are optional parameters and default to:
|
34
|
+
|
35
|
+
* config: /path/to/app/*config/puma.rb*
|
36
|
+
* log: /path/to/app/*log/puma.log*
|
37
|
+
* environment: (empty)
|
38
|
+
|
39
|
+
Multiple environment variables need to be separated by a semicolon, e.g.
|
40
|
+
|
41
|
+
FOO=1;BAR=2
|
42
|
+
|
43
|
+
To remove an app, simply delete the line from the config file or run:
|
44
|
+
|
45
|
+
sudo /etc/init.d/puma remove /path/to/app
|
46
|
+
|
47
|
+
The command will make sure the Puma instance stops before removing it from the jungle.
|
48
|
+
|
49
|
+
## Assumptions
|
50
|
+
|
51
|
+
* The script expects a temporary folder named /path/to/app/*tmp/puma* to exist. Create it if it's not there by default.
|
52
|
+
The pid and state files should live there and must be called: *tmp/puma/pid* and *tmp/puma/state*.
|
53
|
+
You can change those if you want but you'll have to adapt the script for it to work.
|
54
|
+
|
55
|
+
* Here's what a minimal app's config file should have:
|
56
|
+
|
57
|
+
```
|
58
|
+
pidfile "/path/to/app/tmp/puma/pid"
|
59
|
+
state_path "/path/to/app/tmp/puma/state"
|
60
|
+
activate_control_app
|
61
|
+
```
|
@@ -0,0 +1,421 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
### BEGIN INIT INFO
|
3
|
+
# Provides: puma
|
4
|
+
# Required-Start: $remote_fs $syslog
|
5
|
+
# Required-Stop: $remote_fs $syslog
|
6
|
+
# Default-Start: 2 3 4 5
|
7
|
+
# Default-Stop: 0 1 6
|
8
|
+
# Short-Description: Puma web server
|
9
|
+
# Description: A ruby web server built for concurrency http://puma.io
|
10
|
+
# initscript to be placed in /etc/init.d.
|
11
|
+
### END INIT INFO
|
12
|
+
|
13
|
+
# Author: Darío Javier Cravero <dario@exordo.com>
|
14
|
+
#
|
15
|
+
# Do NOT "set -e"
|
16
|
+
|
17
|
+
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
18
|
+
PATH=/usr/local/bin:/usr/local/sbin/:/sbin:/usr/sbin:/bin:/usr/bin
|
19
|
+
DESC="Puma rack web server"
|
20
|
+
NAME=puma
|
21
|
+
DAEMON=$NAME
|
22
|
+
SCRIPTNAME=/etc/init.d/$NAME
|
23
|
+
CONFIG=/etc/puma.conf
|
24
|
+
JUNGLE=`cat $CONFIG`
|
25
|
+
RUNPUMA=/usr/local/bin/run-puma
|
26
|
+
USE_LOCAL_BUNDLE=0
|
27
|
+
|
28
|
+
# Load the VERBOSE setting and other rcS variables
|
29
|
+
. /lib/init/vars.sh
|
30
|
+
|
31
|
+
# Define LSB log_* functions.
|
32
|
+
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
|
33
|
+
. /lib/lsb/init-functions
|
34
|
+
|
35
|
+
#
|
36
|
+
# Function that starts the jungle
|
37
|
+
#
|
38
|
+
do_start() {
|
39
|
+
log_daemon_msg "=> Running the jungle..."
|
40
|
+
for i in $JUNGLE; do
|
41
|
+
dir=`echo $i | cut -d , -f 1`
|
42
|
+
do_start_one $dir
|
43
|
+
done
|
44
|
+
}
|
45
|
+
|
46
|
+
do_start_one() {
|
47
|
+
PIDFILE=$1/tmp/puma/pid
|
48
|
+
if [ -e $PIDFILE ]; then
|
49
|
+
PID=`cat $PIDFILE`
|
50
|
+
# If the puma is running, restart it, otherwise run it.
|
51
|
+
if ps -p $PID > /dev/null; then
|
52
|
+
do_restart_one $1
|
53
|
+
else
|
54
|
+
do_start_one_do $1
|
55
|
+
fi
|
56
|
+
else
|
57
|
+
do_start_one_do $1
|
58
|
+
fi
|
59
|
+
}
|
60
|
+
|
61
|
+
do_start_one_do() {
|
62
|
+
i=`grep $1 $CONFIG`
|
63
|
+
dir=`echo $i | cut -d , -f 1`
|
64
|
+
user=`echo $i | cut -d , -f 2`
|
65
|
+
config_file=`echo $i | cut -d , -f 3`
|
66
|
+
if [ "$config_file" = "" ]; then
|
67
|
+
config_file="$dir/config/puma.rb"
|
68
|
+
fi
|
69
|
+
log_file=`echo $i | cut -d , -f 4`
|
70
|
+
if [ "$log_file" = "" ]; then
|
71
|
+
log_file="$dir/log/puma.log"
|
72
|
+
fi
|
73
|
+
environment=`echo $i | cut -d , -f 5`
|
74
|
+
|
75
|
+
log_daemon_msg "--> Woke up puma $dir"
|
76
|
+
log_daemon_msg "user $user"
|
77
|
+
log_daemon_msg "log to $log_file"
|
78
|
+
|
79
|
+
if [ ! -z "$environment" ]; then
|
80
|
+
for e in $(echo "$environment" | tr ';' '\n'); do
|
81
|
+
log_daemon_msg "environment $e"
|
82
|
+
v=${e%%\=*} ; eval "$e" ; export $v
|
83
|
+
done
|
84
|
+
fi
|
85
|
+
|
86
|
+
start-stop-daemon --verbose --start --chdir $dir --chuid $user --background --exec $RUNPUMA -- $dir $config_file $log_file
|
87
|
+
}
|
88
|
+
|
89
|
+
#
|
90
|
+
# Function that stops the jungle
|
91
|
+
#
|
92
|
+
do_stop() {
|
93
|
+
log_daemon_msg "=> Putting all the beasts to bed..."
|
94
|
+
for i in $JUNGLE; do
|
95
|
+
dir=`echo $i | cut -d , -f 1`
|
96
|
+
do_stop_one $dir
|
97
|
+
done
|
98
|
+
}
|
99
|
+
#
|
100
|
+
# Function that stops the daemon/service
|
101
|
+
#
|
102
|
+
do_stop_one() {
|
103
|
+
log_daemon_msg "--> Stopping $1"
|
104
|
+
PIDFILE=$1/tmp/puma/pid
|
105
|
+
STATEFILE=$1/tmp/puma/state
|
106
|
+
if [ -e $PIDFILE ]; then
|
107
|
+
PID=`cat $PIDFILE`
|
108
|
+
if ps -p $PID > /dev/null; then
|
109
|
+
log_daemon_msg "---> About to kill PID `cat $PIDFILE`"
|
110
|
+
if [ "$USE_LOCAL_BUNDLE" -eq 1 ]; then
|
111
|
+
cd $1 && bundle exec pumactl --state $STATEFILE stop
|
112
|
+
else
|
113
|
+
pumactl --state $STATEFILE stop
|
114
|
+
fi
|
115
|
+
# Many daemons don't delete their pidfiles when they exit.
|
116
|
+
rm -f $PIDFILE $STATEFILE
|
117
|
+
else
|
118
|
+
log_daemon_msg "---> Puma $1 isn't running."
|
119
|
+
fi
|
120
|
+
else
|
121
|
+
log_daemon_msg "---> No puma here..."
|
122
|
+
fi
|
123
|
+
return 0
|
124
|
+
}
|
125
|
+
|
126
|
+
#
|
127
|
+
# Function that restarts the jungle
|
128
|
+
#
|
129
|
+
do_restart() {
|
130
|
+
for i in $JUNGLE; do
|
131
|
+
dir=`echo $i | cut -d , -f 1`
|
132
|
+
do_restart_one $dir
|
133
|
+
done
|
134
|
+
}
|
135
|
+
|
136
|
+
#
|
137
|
+
# Function that sends a SIGUSR2 to the daemon/service
|
138
|
+
#
|
139
|
+
do_restart_one() {
|
140
|
+
PIDFILE=$1/tmp/puma/pid
|
141
|
+
|
142
|
+
if [ -e $PIDFILE ]; then
|
143
|
+
log_daemon_msg "--> About to restart puma $1"
|
144
|
+
kill -s USR2 `cat $PIDFILE`
|
145
|
+
# TODO Check if process exist
|
146
|
+
else
|
147
|
+
log_daemon_msg "--> Your puma was never playing... Let's get it out there first"
|
148
|
+
do_start_one $1
|
149
|
+
fi
|
150
|
+
return 0
|
151
|
+
}
|
152
|
+
|
153
|
+
#
|
154
|
+
# Function that phased restarts the jungle
|
155
|
+
#
|
156
|
+
do_phased_restart() {
|
157
|
+
for i in $JUNGLE; do
|
158
|
+
dir=`echo $i | cut -d , -f 1`
|
159
|
+
do_phased_restart_one $dir
|
160
|
+
done
|
161
|
+
}
|
162
|
+
|
163
|
+
#
|
164
|
+
# Function that sends a SIGUSR1 to the daemon/service
|
165
|
+
#
|
166
|
+
do_phased_restart_one() {
|
167
|
+
PIDFILE=$1/tmp/puma/pid
|
168
|
+
|
169
|
+
if [ -e $PIDFILE ]; then
|
170
|
+
log_daemon_msg "--> About to restart puma $1"
|
171
|
+
kill -s USR1 `cat $PIDFILE`
|
172
|
+
# TODO Check if process exist
|
173
|
+
else
|
174
|
+
log_daemon_msg "--> Your puma was never playing... Let's get it out there first"
|
175
|
+
do_start_one $1
|
176
|
+
fi
|
177
|
+
return 0
|
178
|
+
}
|
179
|
+
|
180
|
+
#
|
181
|
+
# Function that statuss the jungle
|
182
|
+
#
|
183
|
+
do_status() {
|
184
|
+
for i in $JUNGLE; do
|
185
|
+
dir=`echo $i | cut -d , -f 1`
|
186
|
+
do_status_one $dir
|
187
|
+
done
|
188
|
+
}
|
189
|
+
|
190
|
+
#
|
191
|
+
# Function that sends a SIGUSR2 to the daemon/service
|
192
|
+
#
|
193
|
+
do_status_one() {
|
194
|
+
PIDFILE=$1/tmp/puma/pid
|
195
|
+
i=`grep $1 $CONFIG`
|
196
|
+
dir=`echo $i | cut -d , -f 1`
|
197
|
+
|
198
|
+
if [ -e $PIDFILE ]; then
|
199
|
+
log_daemon_msg "--> About to status puma $1"
|
200
|
+
if [ "$USE_LOCAL_BUNDLE" -eq 1 ]; then
|
201
|
+
cd $1 && bundle exec pumactl --state $dir/tmp/puma/state stats
|
202
|
+
else
|
203
|
+
pumactl --state $dir/tmp/puma/state stats
|
204
|
+
fi
|
205
|
+
# kill -s USR2 `cat $PIDFILE`
|
206
|
+
# TODO Check if process exist
|
207
|
+
else
|
208
|
+
log_daemon_msg "--> $1 isn't there :(..."
|
209
|
+
fi
|
210
|
+
return 0
|
211
|
+
}
|
212
|
+
|
213
|
+
do_add() {
|
214
|
+
str=""
|
215
|
+
# App's directory
|
216
|
+
if [ -d "$1" ]; then
|
217
|
+
if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
|
218
|
+
str=$1
|
219
|
+
else
|
220
|
+
echo "The app is already being managed. Remove it if you want to update its config."
|
221
|
+
exit 1
|
222
|
+
fi
|
223
|
+
else
|
224
|
+
echo "The directory $1 doesn't exist."
|
225
|
+
exit 1
|
226
|
+
fi
|
227
|
+
# User to run it as
|
228
|
+
if [ "`grep -c "^$2:" /etc/passwd`" -eq 0 ]; then
|
229
|
+
echo "The user $2 doesn't exist."
|
230
|
+
exit 1
|
231
|
+
else
|
232
|
+
str="$str,$2"
|
233
|
+
fi
|
234
|
+
# Config file
|
235
|
+
if [ "$3" != "" ]; then
|
236
|
+
if [ -e $3 ]; then
|
237
|
+
str="$str,$3"
|
238
|
+
else
|
239
|
+
echo "The config file $3 doesn't exist."
|
240
|
+
exit 1
|
241
|
+
fi
|
242
|
+
fi
|
243
|
+
# Log file
|
244
|
+
if [ "$4" != "" ]; then
|
245
|
+
str="$str,$4"
|
246
|
+
fi
|
247
|
+
|
248
|
+
# Environment variables
|
249
|
+
if [ "$5" != "" ]; then
|
250
|
+
str="$str,$5"
|
251
|
+
fi
|
252
|
+
|
253
|
+
# Add it to the jungle
|
254
|
+
echo $str >> $CONFIG
|
255
|
+
log_daemon_msg "Added a Puma to the jungle: $str. You still have to start it though."
|
256
|
+
}
|
257
|
+
|
258
|
+
do_remove() {
|
259
|
+
if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
|
260
|
+
echo "There's no app $1 to remove."
|
261
|
+
else
|
262
|
+
# Stop it first.
|
263
|
+
do_stop_one $1
|
264
|
+
# Remove it from the config.
|
265
|
+
sed -i "\\:^$1:d" $CONFIG
|
266
|
+
log_daemon_msg "Removed a Puma from the jungle: $1."
|
267
|
+
fi
|
268
|
+
}
|
269
|
+
|
270
|
+
config_bundler() {
|
271
|
+
HOME="$(eval echo ~$(id -un))"
|
272
|
+
|
273
|
+
if [ -d "$1/.rbenv/bin" ]; then
|
274
|
+
PATH="$1/.rbenv/bin:$1/.rbenv/shims:$1"
|
275
|
+
eval "$(rbenv init -)"
|
276
|
+
USE_LOCAL_BUNDLE=1
|
277
|
+
return 0
|
278
|
+
|
279
|
+
elif [ -d "/usr/local/rbenv/bin" ]; then
|
280
|
+
PATH="/usr/local/rbenv/bin:/usr/local/rbenv/shims:$PATH"
|
281
|
+
eval "$(rbenv init -)"
|
282
|
+
USE_LOCAL_BUNDLE=1
|
283
|
+
return 0
|
284
|
+
|
285
|
+
elif [ -d "$HOME/.rbenv/bin" ]; then
|
286
|
+
PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
|
287
|
+
eval "$(rbenv init -)"
|
288
|
+
USE_LOCAL_BUNDLE=1
|
289
|
+
return 0
|
290
|
+
|
291
|
+
# TODO: test rvm
|
292
|
+
# elif [ -f /etc/profile.d/rvm.sh ]; then
|
293
|
+
# source /etc/profile.d/rvm.sh
|
294
|
+
# elif [ -f /usr/local/rvm/scripts/rvm ]; then
|
295
|
+
# source /etc/profile.d/rvm.sh
|
296
|
+
# elif [ -f "$HOME/.rvm/scripts/rvm" ]; then
|
297
|
+
# source "$HOME/.rvm/scripts/rvm"
|
298
|
+
# TODO: don't know what to do with chruby
|
299
|
+
# elif [ -f /usr/local/share/chruby/chruby.sh ]; then
|
300
|
+
# source /usr/local/share/chruby/chruby.sh
|
301
|
+
# if [ -f /usr/local/share/chruby/auto.sh ]; then
|
302
|
+
# source /usr/local/share/chruby/auto.sh
|
303
|
+
# fi
|
304
|
+
# if you aren't using auto, set your version here
|
305
|
+
# chruby 2.0.0
|
306
|
+
fi
|
307
|
+
|
308
|
+
return 1
|
309
|
+
}
|
310
|
+
|
311
|
+
config_bundler
|
312
|
+
|
313
|
+
case "$1" in
|
314
|
+
start)
|
315
|
+
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
316
|
+
if [ "$#" -eq 1 ]; then
|
317
|
+
do_start
|
318
|
+
else
|
319
|
+
i=`grep $2 $CONFIG`
|
320
|
+
dir=`echo $i | cut -d , -f 1`
|
321
|
+
do_start_one $dir
|
322
|
+
fi
|
323
|
+
case "$?" in
|
324
|
+
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
325
|
+
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
326
|
+
esac
|
327
|
+
;;
|
328
|
+
stop)
|
329
|
+
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
330
|
+
if [ "$#" -eq 1 ]; then
|
331
|
+
do_stop
|
332
|
+
else
|
333
|
+
i=`grep $2 $CONFIG`
|
334
|
+
dir=`echo $i | cut -d , -f 1`
|
335
|
+
do_stop_one $dir
|
336
|
+
fi
|
337
|
+
case "$?" in
|
338
|
+
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
339
|
+
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
340
|
+
esac
|
341
|
+
;;
|
342
|
+
status)
|
343
|
+
# TODO Implement.
|
344
|
+
log_daemon_msg "Status $DESC" "$NAME"
|
345
|
+
if [ "$#" -eq 1 ]; then
|
346
|
+
do_status
|
347
|
+
else
|
348
|
+
i=`grep $2 $CONFIG`
|
349
|
+
dir=`echo $i | cut -d , -f 1`
|
350
|
+
do_status_one $dir
|
351
|
+
fi
|
352
|
+
case "$?" in
|
353
|
+
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
354
|
+
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
355
|
+
esac
|
356
|
+
;;
|
357
|
+
restart)
|
358
|
+
log_daemon_msg "Restarting $DESC" "$NAME"
|
359
|
+
if [ "$#" -eq 1 ]; then
|
360
|
+
do_restart
|
361
|
+
else
|
362
|
+
i=`grep $2 $CONFIG`
|
363
|
+
dir=`echo $i | cut -d , -f 1`
|
364
|
+
do_restart_one $dir
|
365
|
+
fi
|
366
|
+
case "$?" in
|
367
|
+
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
368
|
+
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
369
|
+
esac
|
370
|
+
;;
|
371
|
+
phased-restart)
|
372
|
+
log_daemon_msg "Restarting (phased) $DESC" "$NAME"
|
373
|
+
if [ "$#" -eq 1 ]; then
|
374
|
+
do_phased_restart
|
375
|
+
else
|
376
|
+
i=`grep $2 $CONFIG`
|
377
|
+
dir=`echo $i | cut -d , -f 1`
|
378
|
+
do_phased_restart_one $dir
|
379
|
+
fi
|
380
|
+
case "$?" in
|
381
|
+
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
382
|
+
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
383
|
+
esac
|
384
|
+
;;
|
385
|
+
add)
|
386
|
+
if [ "$#" -lt 3 ]; then
|
387
|
+
echo "Please, specify the app's directory and the user that will run it at least."
|
388
|
+
echo " Usage: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
|
389
|
+
echo " config and log are optionals."
|
390
|
+
exit 1
|
391
|
+
else
|
392
|
+
do_add $2 $3 $4 $5
|
393
|
+
fi
|
394
|
+
case "$?" in
|
395
|
+
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
396
|
+
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
397
|
+
esac
|
398
|
+
;;
|
399
|
+
remove)
|
400
|
+
if [ "$#" -lt 2 ]; then
|
401
|
+
echo "Please, specify the app's directory to remove."
|
402
|
+
exit 1
|
403
|
+
else
|
404
|
+
do_remove $2
|
405
|
+
fi
|
406
|
+
case "$?" in
|
407
|
+
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
408
|
+
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
409
|
+
esac
|
410
|
+
;;
|
411
|
+
*)
|
412
|
+
echo "Usage:" >&2
|
413
|
+
echo " Run the jungle: $SCRIPTNAME {start|stop|status|restart|phased-restart}" >&2
|
414
|
+
echo " Add a Puma: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
|
415
|
+
echo " config and log are optionals."
|
416
|
+
echo " Remove a Puma: $SCRIPTNAME remove /path/to/app"
|
417
|
+
echo " On a Puma: $SCRIPTNAME {start|stop|status|restart|phased-restart} PUMA-NAME" >&2
|
418
|
+
exit 3
|
419
|
+
;;
|
420
|
+
esac
|
421
|
+
:
|