diff --git a/cookbooks/delayed_job4/files/default/dj.sh b/cookbooks/delayed_job4/files/default/dj.sh new file mode 100755 index 00000000..1304c233 --- /dev/null +++ b/cookbooks/delayed_job4/files/default/dj.sh @@ -0,0 +1,162 @@ +#!/bin/sh +# +# This script starts and stops the Dj daemon +# This script is created by the delayed_job4 recipe +# This script belongs in /engineyard/custom/dj +# +# Updated for Rails 4.x + +PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH +CURDIR=`pwd` +export NEW_RELIC_DISPATCHER=delayed_job + +usage() { + echo "Usage: $0 {start|stop} enviroment [-n WORKER_NAME] [-p MIN_PRIORITY] [-P MAX_PRIORITY] [-q comma,separated,queues]" + exit 1 +} + +die() { + echo -e "fatal error: ${1}" 1>&2 + ! [ "$(echo ${0} | awk -F/ '{print $NF}')" == "bash" ] && exit 255 || return 255 +} + +defined() { + [ -n "${1}" ] +} + +exists() { + defined "${1}" || die "exists - No path given." + [ -e "${1}" ] +} + +is_file() { + defined "${1}" || die "is_file - No path given." + exists "${1}" && [ -f "${1}" ] +} + +is_directory() { + defined "${1}" || die "is_directory - No path given." + exists "${1}" && [ -d "${1}" ] +} + +add_arg() { + local argname="${1}" + local argcontent="${2}" + local original="${3}" + + # Make no changes if the content to add is empty + if ! defined "${argcontent}" + then + echo -n "${original}" + return + fi + + local arg="'${argname}=${argcontent}'" + + if defined "${original}" + then + original="${arg}, ${original}" + else + original="${arg}" + fi + + echo -n "${original}" +} + +start() { + local app_name=${1} + local app_root="/data/${app_name}/current" + local rails_env=${3} + + # Clear out the non-option stuff so getopts doesn't freak out + shift 3 + + local queues="${QUEUES}" + local worker_name="" + local min_priority="" + local max_priority="" + local OPTIND=1 + + while getopts ":n:p:P:q:" opt + do + case $opt in + n) + worker_name="${OPTARG}" + ;; + q) + if defined "${queues}" + then + queues="${queues},${OPTARG}" + else + queues=${OPTARG} + fi + ;; + p) + min_priority=${OPTARG} + ;; + P) + max_priority=${OPTARG} + ;; + :) + echo "Option -${OPTARG} requires an argument." + exit 1 + ;; + esac + done + + # Sanitize the worker name + if ! defined "${worker_name}" + then + worker_name = 'nil' + fi + + # Sanitize the queues (whitespace -> comma) + if defined "${queues}" + then + queues="$(echo "${queues}" | sed -e 's/[[:space:]]/,/g')" + fi + + local worker_options="" + worker_options="$(add_arg "--queues" "${queues}" "${worker_options}")" + worker_options="$(add_arg '--max-priority' "${max_priority}" "${worker_options}")" + worker_options="$(add_arg '--min-priority' "${min_priority}" "${worker_options}")" + + echo "worker_options == \"${worker_options}\"" +} + +stop() { + echo "stop got '${@}'" +} + +main() { + if [ $# -lt 3 ] + then + usage + fi + + local app_name=$1 + local action=$2 + local app_root="/data/${app_name}/current" + local rails_env=$3 + + if ! is_directory "${app_root}" + then + echo "${app_root} doesn't exist" + usage + fi + + case "${action}" in + start) + start ${@} + ;; + stop) + stop ${@} + ;; + *) + echo "Unknown action '${action}'" + usage + ;; + esac +} + +main $@ diff --git a/cookbooks/delayed_job4/recipes/default.rb b/cookbooks/delayed_job4/recipes/default.rb index 3ced93ff..9a520e00 100644 --- a/cookbooks/delayed_job4/recipes/default.rb +++ b/cookbooks/delayed_job4/recipes/default.rb @@ -10,8 +10,8 @@ mode 0755 end - template "/engineyard/custom/dj" do - source "dj.erb" + cookbook_file "/engineyard/custom/dj" do + source "dj.sh" owner "root" group "root" mode 0755 diff --git a/cookbooks/delayed_job4/templates/default/dj.erb b/cookbooks/delayed_job4/templates/default/dj.erb deleted file mode 100644 index 554ae5d8..00000000 --- a/cookbooks/delayed_job4/templates/default/dj.erb +++ /dev/null @@ -1,210 +0,0 @@ -#!/bin/sh -# -# This script starts and stops the Dj daemon -# This script is created by the delayed_job4 recipe -# This script belongs in /engineyard/custom/dj -# -# Updated for Rails 4.x - -PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH -CURDIR=`pwd` -export NEW_RELIC_DISPATCHER=delayed_job - -usage() { - echo "Usage: $0 {start|stop} enviroment [name maximum-priority minimum-priority]" - exit 1 -} - -if [ $# -lt 3 ]; then usage; fi - -if [ $4 ]; then - NAME="_$4" - - if [ $5 ]; then - OPTIONS="--max-priority=$5" - if [ $6 ]; then - OPTIONS="$OPTIONS --min-priority=$6" - fi - fi -fi - -# QUEUES passed as comma seperated list -# E.G. QUEUES=one,two,three -if [ $QUEUES ]; then - OPTIONS="$OPTIONS --queues=${QUEUES}" -fi - -if [ "`whoami`" != "root" ]; then - logger -t `basename $0` -s "Must be run as root" - exit 1 -fi -## Function definitions - casual readers encouraged to skip this ## -rm_lockfile(){ - if [ -e $LOCK_FILE ]; then - logger -t "monit_dj:$WORKER[$$]" "removing $LOCK_FILE for `cat $LOCK_FILE`" - rm $LOCK_FILE - fi -} - -lock(){ - RESULT=0 - if [ -e $LOCK_FILE ]; then - LAST_LOCK_PID=`cat $LOCK_FILE` - # Test if the lock file does not match a running process - if [ -n $LAST_LOCK_PID -a -z "`ps axo pid|grep $LAST_LOCK_PID`" -a -f $LOCK_FILE ];then - sleep 1 - logger -t "monit-dj:$WORKER[$$]" "Removing stale lock file for $WORKER ($LAST_LOCK_PID)" - rm $LOCK_FILE 2>&1 - # Test if the lock file matches a running process, but it's a zombie - elif [ -n $LAST_LOCK_PID ] && [ ! -z "`ps aux | grep $LAST_LOCK_PID | awk '{print $8}' | grep Z`" ];then - sleep 1 - logger -t "monit-dj:$WORKER[$$]" "Removing stale lock file for zombie $WORKER ($LAST_LOCK_PID)" - rm $LOCK_FILE 2>&1 - else - logger -t "monit-dj:$WORKER[$$]" "Monit already messing with $WORKER ($LAST_LOCK_PID)" - RESULT=1 - exit_cleanly - fi - fi - echo $$ > $LOCK_FILE -} - -exit_cleanly() { - cd $CURDIR - logger -t "mont-dj:$WORKER[$$]" "exiting wrapper cleanly with $RESULT" - exit $RESULT -} - -unlock_and_exit_cleanly(){ - rm_lockfile - exit_cleanly -} - -## End function definitions ## -#set -x -WORKER=$1$NAME -LOCK_FILE="/tmp/$WORKER.monit-lock" -BUNDLER_COMMAND="ruby" -RAILS_ROOT=/data/$1/current -if [ -d $RAILS_ROOT ]; then - if [ -f $RAILS_ROOT/Gemfile ]; then - BUNDLER_COMMAND="bundle exec ruby" - if [ -d $RAILS_ROOT/ey_bundler_binstubs ]; then - PATH=$RAILS_ROOT/ey_bundler_binstubs:$PATH - fi - fi - - RAILS_ENV=$3 - export $RAILS_ENV - - # Rails 2.x uses $RAILS_ROOT/script/runner - # Rails 3.x uses $RAILS_ROOT/script rails runner - # Rails 4.x uses $RAILS_ROOT/bin/rails runner - RAILS_SCRIPTS="$RAILS_ROOT/script" - RUNNER='runner' # 2.x - - [ -f $RAILS_ROOT/script/rails ] && chmod a+x $RAILS_SCRIPTS/rails && RUNNER="rails runner" # 3.x - [ -f $RAILS_ROOT/bin/rails ] && RAILS_SCRIPTS="$RAILS_ROOT/bin" && chmod a+x $RAILS_SCRIPTS/rails && RUNNER="rails runner" # 4.x - - cd $RAILS_ROOT - PID_FILE=/var/run/engineyard/dj/$1/dj$NAME.pid - USER=`stat -c"%U" /data/$1/current/` - HOME="/home/$USER" ; export HOME - RESULT=0 - GRACE_TIME=${GRACE_TIME:-60} - let "GRACE_TIME=$GRACE_TIME*4" - - mkdir -p /var/run/engineyard/dj/$1 - - case "$2" in - start) - lock - cd $RAILS_ROOT - OPTIONS=${OPTIONS:-[]} - # Delayed Job takes a array of switches as options - # Split into array of quoted strings - # E.g. '--one=one --two=two' > ['--one=one', '--two=two'] - OPTIONS_ARGS="['${OPTIONS// /', '}']" - - RUNNER_COMMAND="require 'delayed/command';Delayed::Worker.before_fork;Delayed::Command.new($OPTIONS_ARGS).run" - COMMAND="$BUNDLER_COMMAND $RAILS_SCRIPTS/$RUNNER -e $RAILS_ENV \"$RUNNER_COMMAND\"" - logger -t "monit-dj:$WORKER[$$]" "DJ Worker starting from $PPID" - if [ -f $PID_FILE ]; then - PID=`cat $PID_FILE` - if [ -n "$PID" ];then - logger -t "monit-dj:$WORKER[$$]" "There is already a PID file for delayed Job [$PID]" - if [ -d /proc/$PID ]; then - logger -t "monit-dj:$WORKER[$$]" "Dj worker is already running with PID of $PID" - RESULT=1 - else - logger -t "monit-dj:$WORKER[$$]" "Removing stale pid file for $WORKER" - rm -f $PID_FILE - fi - fi - fi - - if [ $RESULT -eq 0 ]; then - logger -t "monit-dj:$WORKER[$$]" "issuing command $COMMAND in $PWD for $USER" - start-stop-daemon --start -b -m -u $USER -d $RAILS_ROOT -p $PID_FILE --exec /bin/bash -- -c "$COMMAND" - RESULT=$? - logger -t "monit-dj:$WORKER[$$]" "$WORKER started as `cat $PID_FILE` : $RESULT" - fi - unlock_and_exit_cleanly - ;; - stop) - lock - logger -t "monit_dj:$WORKER[$$]" "Stopping DJ worker:" - if [ -f $PID_FILE ]; then - PID=$(cat $PID_FILE) - - # Find children - WORKER_PID=$(ps axo pid,ppid,command|awk '$2=='$PID' {print $1}') - - kill -15 $PID $WORKER_PID; # kill worker and any child that it may have at this very moment - logger -t "monit-dj:$WORKER[$$]" "Stopping DJ Worker Process $PID $PPID" - - # In case the worker stubbornly ignored the kill -15 signal - SLEEP_COUNT=0 - while [ -e /proc/$WORKER_PID ]; do - sleep .25 - let "SLEEP_COUNT+=1" - let "REPORT_TIME = $SLEEP_COUNT%4" - if(( "$SLEEP_COUNT" > $GRACE_TIME )); then - logger -t "monit-dj:$WORKER[$$]" "Stopping DJ Worker Child Process $WORKER_PID wait exceeded, killing it" - kill -9 $WORKER_PID 2>/dev/null; true - break - elif(( $REPORT_TIME == 0 ));then - let "RUNTIME = $SLEEP_COUNT/4" - logger -t "monit-dj:$WORKER[$$]" "Waiting for $WORKER_PID to die ( for $RUNTIME seconds now)" - fi - done - - # In case the bash process that started the dj worker is still around - SLEEP_COUNT=0 - while [ -e /proc/$PID ]; do - sleep .25 - let "SLEEP_COUNT+=1" - let "REPORT_TIME = $SLEEP_COUNT%4" - if(( "$SLEEP_COUNT" > $GRACE_TIME )); then - logger -t "monit-dj:$WORKER[$$]" "Stopping DJ bash process $PID wait exceeded, killing it" - kill -9 $PID 2>/dev/null; true - break - elif(( $REPORT_TIME == 0 ));then - let "RUNTIME = $SLEEP_COUNT/4" - logger -t "monit-dj:$WORKER[$$]" "Waiting for $PID to die ( for $RUNTIME seconds now)" - fi - done - fi - - [ -e "$PID_FILE" ] && rm -f $PID_FILE - unlock_and_exit_cleanly - ;; - *) - usage - ;; - esac -else - echo "/data/$1/current doesn't exist." - usage -fi - diff --git a/cookbooks/delayed_job4/templates/default/dj.monitrc.erb b/cookbooks/delayed_job4/templates/default/dj.monitrc.erb index e24acf57..190673db 100644 --- a/cookbooks/delayed_job4/templates/default/dj.monitrc.erb +++ b/cookbooks/delayed_job4/templates/default/dj.monitrc.erb @@ -1,6 +1,6 @@ check process <%= @worker_name %> with pidfile /var/run/engineyard/dj/<%= @app_name %>/dj_<%= @worker_name %>.pid - start program = "/engineyard/custom/dj <%= @app_name %> start <%= @framework_env %> <%= @worker_name %>" with timeout 90 seconds - stop program = "/engineyard/custom/dj <%= @app_name %> stop <%= @framework_env %> <%= @worker_name %>" with timeout 90 seconds + start program = "/engineyard/custom/dj <%= @app_name %> start <%= @framework_env %> -n <%= @worker_name %>" with timeout 90 seconds + stop program = "/engineyard/custom/dj <%= @app_name %> stop <%= @framework_env %> -n <%= @worker_name %>" with timeout 90 seconds if totalmem is greater than <%= @worker_memory %> MB then restart # eating up memory? group dj_<%= @app_name %>