Skip to content

Commit

Permalink
Implemented an init.d script to manage the Jungle.
Browse files Browse the repository at this point in the history
The script allows running Puma apps as daemons using start-stop-daemon and adds an easy way to log its activity.
  • Loading branch information
Darío Javier Cravero committed Aug 11, 2012
1 parent ca21111 commit f2cb62c
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -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" 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.
52 changes: 52 additions & 0 deletions tools/jungle/README.md
@@ -0,0 +1,52 @@
# 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 changes: 332 additions & 0 deletions tools/jungle/puma
@@ -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
:

0 comments on commit f2cb62c

Please sign in to comment.