eycloud-recipe-delayed_job 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg
2
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,16 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ eycloud-recipe-delayed_job (1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ rake (0.9.2.2)
10
+
11
+ PLATFORMS
12
+ ruby
13
+
14
+ DEPENDENCIES
15
+ eycloud-recipe-delayed_job!
16
+ rake
data/README.markdown ADDED
@@ -0,0 +1,100 @@
1
+ # Delayed Job recipe for EY Cloud
2
+
3
+ This cookbook can serve as a good starting point for setting up Delayed Job support in your application.
4
+ In this recipe your Delayed Job workers will be set up to run under monit. The number of workers will
5
+ vary based on the size of the instance running Delayed Job.
6
+
7
+ This recipe will setup `delayed_job` on a Solo instance environment or on named Utility instances in a cluster environment.
8
+
9
+ Name your Utility instances with prefixes: `dj`, `delayed_job`, `delayedjob`. For example, `dj1`, `delayedjob4`.
10
+
11
+ If you want `delayed_job` to run on App instances instead of Utility, you will need to modify the recipe.
12
+
13
+ TODO: if no corresponding dj|delayed_job|delayedjob named Utility instances, then install DJ on the app instances.
14
+
15
+ ## How many workers do I get?
16
+
17
+ The number of parallel workers is based on the instance type.
18
+
19
+ Solo instances allocate a single Delayed Job worker because the instance is primarily for running the Application and its database.
20
+
21
+ For each `dj` utility instance the number of workers is determined by the `delayed_job_worker_count` library helper:
22
+
23
+ ``` ruby
24
+ def delayed_job_worker_count(instance_type)
25
+ case instance_type
26
+ when "m1.small"
27
+ 3
28
+ when "m1.medium"
29
+ 4
30
+ when "m1.large"
31
+ 8
32
+ when "m1.xlarge"
33
+ 12
34
+ when "m2.xlarge"
35
+ 10
36
+ when "m2.2xlarge"
37
+ 10
38
+ when "m2.4xlarge"
39
+ 24
40
+ when "c1.medium"
41
+ 4
42
+ when "c1.xlarge"
43
+ 24
44
+ else
45
+ 2
46
+ end
47
+ end
48
+ ```
49
+
50
+ To learn more about each instance type, see the [AWS EC2 Instance Type](http://aws.amazon.com/ec2/instance-types/ "Amazon EC2 Instance Types") page.
51
+
52
+ ## Installation
53
+
54
+ This recipe must be installed as `delayed_job` and not `eycloud-recipe-delayed_job`.
55
+
56
+ ## Simple Installation
57
+
58
+ To add this recipe to your collection of recipes, or as your first recipe, you can use the helpful `ey-recipes` command line tool:
59
+
60
+ cd myapp
61
+ gem install engineyard engineyard-recipes
62
+ ey-recipes init
63
+ ey-recipes clone git://github.com/engineyard/eycloud-recipe-delayed_job.git -n delayed_job
64
+ ey recipes upload --apply
65
+
66
+ If you want to have your recipes run during deploy (rather than the separate `ey recipes upload --apply` step):
67
+
68
+ ey-recipes init -d
69
+ ey-recipes clone git://github.com/engineyard/eycloud-recipe-delayed_job.git -n delayed_job
70
+ git add .; git commit -m "added delayed job recipe"; git push origin master
71
+ ey deploy
72
+
73
+ ## Manual Installation
74
+
75
+ Clone/copy this repository into a `cookbooks/delayed_job` folder (such that you have a `cookbooks/delayed_job/recipes/default.rb` file).
76
+
77
+ Then add the following to `cookbooks/main/recipes/default.rb`:
78
+
79
+ require_recipe "delayed_job"
80
+
81
+ Make sure this and any customizations to the recipe are committed to your own fork of this
82
+ repository.
83
+
84
+ Then to upload and apply to EY Cloud for a given environment:
85
+
86
+ ey recipes upload --apply -e target-environment
87
+
88
+ ## Restarting your workers
89
+
90
+ This recipe does NOT restart your workers. The reason for this is that shipping your application and
91
+ rebuilding your instances (i.e. running chef) are not always done at the same time. It is best to
92
+ restart your Delayed Job workers when you ship (deploy) your application code. To do this, add a
93
+ deploy hook to perform the following:
94
+
95
+ sudo "monit -g dj_<app_name> restart all"
96
+
97
+ Make sure to replace <app_name> with the name of your application. You likely want to use the
98
+ after_restart hook for this. See our [Deploy Hook](http://docs.engineyard.com/appcloud/howtos/deployment/use-deploy-hooks-with-engine-yard-appcloud) documentation
99
+ for more information on using deploy hooks.
100
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ version = "1.0" # get from metadata.json or .rb
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "eycloud-recipe-delayed_job"
8
+ s.version = version
9
+ s.authors = ["Dr Nic Williams"]
10
+ s.email = ["drnicwilliams@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Delayed Job for EY Cloud} # from metadata
13
+ s.description = %q{Delayed Job for EY Cloud} # from metadata long_description
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_development_dependency("rake")
21
+ end
@@ -0,0 +1,8 @@
1
+ class Chef
2
+ class Recipe
3
+ # Only Rails applications (containing script/runner) are valid
4
+ def delayed_job_applications
5
+ node[:applications]
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ class Chef
2
+ class Recipe
3
+ # If you change this, please copy it back into the README as documentation
4
+ def delayed_job_worker_count(instance_type)
5
+ case instance_type
6
+ when "m1.small"
7
+ 3
8
+ when "m1.medium"
9
+ 4
10
+ when "m1.large"
11
+ 8
12
+ when "m1.xlarge"
13
+ 12
14
+ when "m2.xlarge"
15
+ 10
16
+ when "m2.2xlarge"
17
+ 10
18
+ when "m2.4xlarge"
19
+ 24
20
+ when "c1.medium"
21
+ 4
22
+ when "c1.xlarge"
23
+ 24
24
+ else
25
+ 2
26
+ end
27
+ end
28
+ end
29
+ end
data/metadata.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "license": "MIT",
3
+ "maintainer": "Engine Yard LLC",
4
+ "maintainer_email": "drnic@engineyard.com",
5
+ "version": "1.0",
6
+ "description": "Delayed Job for EY Cloud",
7
+ "name": "delayed_job",
8
+ "attributes": {
9
+
10
+ },
11
+ "long_description": ""
12
+ }
data/metadata.rb ADDED
@@ -0,0 +1,6 @@
1
+ name "delayed_job"
2
+ description "Delayed Job for EY Cloud"
3
+ long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
4
+ maintainer "Engine Yard LLC"
5
+ maintainer_email "drnic@engineyard.com"
6
+ version "1.0"
@@ -0,0 +1,35 @@
1
+ #
2
+ # Cookbook Name:: delayed_job
3
+ # Recipe:: configure
4
+ #
5
+
6
+ # This recipe will setup `delayed_job` on a Solo instance environment or on named Utility instances in a cluster environment.
7
+ # Name your Utility instances with prefixes: `dj`, `delayed_job`, `delayedjob`. For example, `dj1`, `delayedjob4`.
8
+ if node[:instance_role] == "solo" || node[:instance_role] == "eylocal" ||
9
+ (node[:instance_role] == "util" && node[:name] =~ /^(dj|delayed_job|delayedjob)/)
10
+ delayed_job_applications().each do |app_name,data|
11
+
12
+ # determine the number of workers to run based on instance size
13
+ if node[:instance_role] == 'solo' || node[:instance_role] == 'eylocal'
14
+ worker_count = 1
15
+ else
16
+ worker_count = delayed_job_worker_count(node[:ec2][:instance_type])
17
+ end
18
+
19
+ worker_count.times do |count|
20
+ template "/etc/monit.d/delayed_job#{count+1}.#{app_name}.monitrc" do
21
+ source "dj.monitrc.erb"
22
+ owner "root"
23
+ group "root"
24
+ mode 0644
25
+ variables({
26
+ :app_name => app_name,
27
+ :user => node[:owner_name],
28
+ :worker_name => "delayed_job#{count+1}",
29
+ :framework_env => node[:environment][:framework_env]
30
+ })
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ #
2
+ # Cookbook Name:: delayed_job
3
+ # Recipe:: default
4
+ #
5
+
6
+ require_recipe "delayed_job::install"
7
+ require_recipe "delayed_job::configure"
8
+ require_recipe "delayed_job::restart"
9
+
@@ -0,0 +1,26 @@
1
+ #
2
+ # Cookbook Name:: delayed_job
3
+ # Recipe:: install
4
+ #
5
+
6
+ # This recipe will setup `delayed_job` on a Solo instance environment or on named Utility instances in a cluster environment.
7
+ # Name your Utility instances with prefixes: `dj`, `delayed_job`, `delayedjob`. For example, `dj1`, `delayedjob4`.
8
+ if node[:instance_role] == "solo" || node[:instance_role] == "eylocal" ||
9
+ (node[:instance_role] == "util" && node[:name] =~ /^(dj|delayed_job|delayedjob)/)
10
+ delayed_job_applications().each do |app_name,data|
11
+
12
+ directory "/engineyard/bin" do
13
+ owner "root"
14
+ group "root"
15
+ mode 0755
16
+ end
17
+
18
+ template "/engineyard/bin/dj" do
19
+ source "dj.erb"
20
+ owner "root"
21
+ group "root"
22
+ mode 0755
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Cookbook Name:: delayed_job
3
+ # Recipe:: restart
4
+ #
5
+
6
+ # This recipe will setup `delayed_job` on a Solo instance environment or on named Utility instances in a cluster environment.
7
+ # Name your Utility instances with prefixes: `dj`, `delayed_job`, `delayedjob`. For example, `dj1`, `delayedjob4`.
8
+ if node[:instance_role] == "solo" || node[:instance_role] == "eylocal" ||
9
+ (node[:instance_role] == "util" && node[:name] =~ /^(dj|delayed_job|delayedjob)/)
10
+ delayed_job_applications().each do |app_name,data|
11
+
12
+ execute "monit-reload-restart" do
13
+ command "sleep 30 && monit reload && monit restart all -g dj_#{app_name}"
14
+ action :run
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,182 @@
1
+ #!/bin/sh
2
+ #
3
+ # This script starts and stops the Dj daemon
4
+ # This script belongs in /engineyard/bin/dj
5
+ #
6
+ PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
7
+ CURDIR=`pwd`
8
+
9
+ usage() {
10
+ echo "Usage: $0 <appname> {start|stop} enviroment [name maximum_priority minimum_priority]"
11
+ exit 1
12
+ }
13
+
14
+ if [ $# -lt 3 ]; then usage; fi
15
+
16
+ if [ $4 ]; then
17
+ NAME="_$4"
18
+
19
+ if [ $5 ]; then
20
+ OPTIONS=":max_priority=>$5"
21
+ if [ $6 ]; then
22
+ OPTIONS="$OPTIONS,:min_priority=>$6"
23
+ fi
24
+ OPTIONS="{$OPTIONS}"
25
+ fi
26
+ fi
27
+
28
+ if [ "`whoami`" != "root" ]; then
29
+ logger -t `basename $0` -s "Must be run as root"
30
+ exit 1
31
+ fi
32
+ ## Function definitions - casual readers encouraged to skip this ##
33
+ rm_lockfile(){
34
+ if [ -e $LOCK_FILE ]; then
35
+ logger -t "monit_dj:$WORKER[$$]" "removing $LOCK_FILE for `cat $LOCK_FILE`"
36
+ rm $LOCK_FILE
37
+ fi
38
+ }
39
+
40
+ lock(){
41
+ RESULT=0
42
+ if [ -e $LOCK_FILE ]; then
43
+ LAST_LOCK_PID=`cat $LOCK_FILE`
44
+ if [ -n $LAST_LOCK_PID -a -z "`ps axo pid|grep $LAST_LOCK_PID`" -a -f $LOCK_FILE ];then
45
+ sleep 1
46
+ logger -t "monit-dj:$WORKER[$$]" "Removing stale lock file for $WORKER ($LAST_LOCK_PID)"
47
+ rm $LOCK_FILE 2>&1
48
+ else
49
+ logger -t "monit-dj:$WORKER[$$]" "Monit already messing with $WORKER ($LAST_LOCK_PID)"
50
+ RESULT=1
51
+ exit_cleanly
52
+ fi
53
+ fi
54
+ echo $$ > $LOCK_FILE
55
+ }
56
+
57
+ exit_cleanly() {
58
+ cd $CURDIR
59
+ logger -t "mont-dj:$WORKER[$$]" "exiting wrapper cleanly with $RESULT"
60
+ exit $RESULT
61
+ }
62
+
63
+ unlock_and_exit_cleanly(){
64
+ rm_lockfile
65
+ exit_cleanly
66
+ }
67
+
68
+ ## End function definitions ##
69
+ #set -x
70
+ WORKER=$1$NAME
71
+ LOCK_FILE="/tmp/$WORKER.monit-lock"
72
+ BUNDLER_COMMAND="ruby"
73
+ RAILS_ROOT=/data/$1/current
74
+ if [ -d $RAILS_ROOT ]; then
75
+ if [ -f $RAILS_ROOT/Gemfile ]; then
76
+ if [ -d $RAILS_ROOT/ey_bundler_binstubs ]; then
77
+ PATH=$RAILS_ROOT/ey_bundler_binstubs:$PATH
78
+ else
79
+ BUNDLER_COMMAND="bundle exec ruby"
80
+ fi
81
+ fi
82
+
83
+ RAILS_ENV=$3
84
+ export $RAILS_ENV
85
+ RUNNER='runner'
86
+ #RAILS_SCRIPTS="$RAILS_ROOT/script"
87
+ RAILS_SCRIPTS="$RAILS_ROOT/script"
88
+ [ -f $RAILS_SCRIPTS/rails ] && chmod a+x $RAILS_SCRIPTS/rails && RUNNER="rails runner"
89
+ cd $RAILS_ROOT
90
+ PID_FILE=/var/run/engineyard/dj/$1/dj$NAME.pid
91
+ USER=`stat -c"%U" /data/$1/current/`
92
+ HOME="/home/$USER" ; export HOME
93
+ RESULT=0
94
+ GRACE_TIME=${GRACE_TIME:-60}
95
+ let "GRACE_TIME=$GRACE_TIME*4"
96
+
97
+ mkdir -p /var/run/engineyard/dj/$1
98
+
99
+ case "$2" in
100
+ start)
101
+ lock
102
+ cd $RAILS_ROOT
103
+ HAS_DJ_COMMAND="RAILS_ENV=$RAILS_ENV $BUNDLER_COMMAND $RAILS_SCRIPTS/$RUNNER -e $RAILS_ENV \"require 'delayed/command';puts defined?(Delayed::Command)\" 2>/dev/null"
104
+ has_dj_command=$(sudo -u $USER -H /bin/bash -c "$HAS_DJ_COMMAND")
105
+ if [[ $has_dj_command == "constant" ]];then
106
+ OPTIONS=${OPTIONS:-[]}
107
+ HAS_BEFORE_FORK="RAILS_ENV=$RAILS_ENV $BUNDLER_COMMAND $RAILS_SCRIPTS/$RUNNER -e $RAILS_ENV \"require 'delayed/worker';puts Delayed::Worker.respond_to?(:before_fork)\" 2>/dev/null"
108
+ has_before_fork=$(sudo -u $USER -H /bin/bash -c "$HAS_BEFORE_FORK")
109
+ if [[ $has_before_fork == "true" ]];then
110
+ RUNNER_COMMAND="require 'delayed/command';Delayed::Worker.before_fork;Delayed::Command.new($OPTIONS).run"
111
+ else
112
+ RUNNER_COMMAND="require 'delayed/command';Delayed::Command.new($OPTIONS).run"
113
+ fi
114
+ else
115
+ RUNNER_COMMAND="Delayed::Worker.new($OPTIONS).start"
116
+ fi
117
+
118
+ COMMAND="$BUNDLER_COMMAND $RAILS_SCRIPTS/$RUNNER -e $RAILS_ENV \"$RUNNER_COMMAND\""
119
+ logger -t "monit-dj:$WORKER[$$]" "DJ Worker starting from $PPID"
120
+ if [ -f $PID_FILE ]; then
121
+ PID=`cat $PID_FILE`
122
+ if [ -n "$PID" ];then
123
+ logger -t "monit-dj:$WORKER[$$]" "There is already a PID file for delayed Job [$PID]"
124
+ if [ -d /proc/$PID ]; then
125
+ logger -t "monit-dj:$WORKER[$$]" "Dj worker is already running with PID of $PID"
126
+ RESULT=1
127
+ else
128
+ logger -t "monit-dj:$WORKER[$$]" "Removing stale pid file for $WORKER"
129
+ rm -f $PID_FILE
130
+ fi
131
+ fi
132
+ fi
133
+
134
+ if [ $RESULT -eq 0 ]; then
135
+ logger -t "monit-dj:$WORKER[$$]" "issuing command $COMMAND in $PWD for $USER"
136
+ sudo -u $USER -H /bin/bash -c "$COMMAND" &
137
+ NEW_PID=$!
138
+ RESULT=$?
139
+ logger -t "monit-dj:$WORKER[$$]" "$WORKER started as $NEW_PID : $RESULT"
140
+ echo $NEW_PID > $PID_FILE
141
+ fi
142
+ unlock_and_exit_cleanly
143
+ ;;
144
+ stop)
145
+ lock
146
+ logger -t "monit_dj:$WORKER[$$]" "Stopping DJ worker:"
147
+ if [ -f $PID_FILE ]; then
148
+ PID=$(cat $PID_FILE)
149
+
150
+ # Find children
151
+ WORKER_PID=$(ps axo pid,ppid,command|awk '$2=='$PID' {print $1}')
152
+
153
+ kill -15 $PID $PPID; # kill worker and any child that it may have at this very moment
154
+ logger -t "monit-dj:$WORKER[$$]" "Stopping DJ Worker Process $PID $PPID"
155
+
156
+ SLEEP_COUNT=0
157
+ while [ -e /proc/$PID ]; do
158
+ sleep .25
159
+ let "SLEEP_COUNT+=1"
160
+ let "REPORT_TIME = $SLEEP_COUNT%4"
161
+ if(( "$SLEEP_COUNT" > $GRACE_TIME )); then
162
+ logger -t "monit-dj:$WORKER[$$]" "Stopping DJ Worker Child Process $PID wait exceeded, killing it"
163
+ kill -9 $PID 2>/dev/null; true
164
+ break
165
+ elif(( $REPORT_TIME == 0 ));then
166
+ let "RUNTIME = $SLEEP_COUNT/4"
167
+ logger -t "monit-dj:$WORKER[$$]" "Waiting for $PID to die ( for $RUNTIME seconds now)"
168
+ fi
169
+ done
170
+ fi
171
+
172
+ [ -e "$PID_FILE" ] && rm -f $PID_FILE
173
+ unlock_and_exit_cleanly
174
+ ;;
175
+ *)
176
+ usage
177
+ ;;
178
+ esac
179
+ else
180
+ echo "/data/$1/current doesn't exist."
181
+ usage
182
+ fi
@@ -0,0 +1,6 @@
1
+ check process <%= @worker_name %>
2
+ with pidfile /var/run/engineyard/dj/<%= @app_name %>/dj_<%= @worker_name %>.pid
3
+ start program = "/engineyard/bin/dj <%= @app_name %> start <%= @framework_env %> <%= @worker_name %>" with timeout 60 seconds
4
+ stop program = "/engineyard/bin/dj <%= @app_name %> stop <%= @framework_env %> <%= @worker_name %>" with timeout 60 seconds
5
+ if totalmem is greater than 300 MB then restart # eating up memory?
6
+ group dj_<%= @app_name %>
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eycloud-recipe-delayed_job
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dr Nic Williams
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70271935729380 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70271935729380
25
+ description: Delayed Job for EY Cloud
26
+ email:
27
+ - drnicwilliams@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - Gemfile.lock
35
+ - README.markdown
36
+ - Rakefile
37
+ - eycloud-recipe-delayed_job.gemspec
38
+ - libraries/delayed_job_applications.rb
39
+ - libraries/delayed_job_worker_count.rb
40
+ - metadata.json
41
+ - metadata.rb
42
+ - recipes/configure.rb
43
+ - recipes/default.rb
44
+ - recipes/install.rb
45
+ - recipes/restart.rb
46
+ - templates/default/dj.erb
47
+ - templates/default/dj.monitrc.erb
48
+ homepage: ''
49
+ licenses: []
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ segments:
61
+ - 0
62
+ hash: -3120554601435364730
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ segments:
70
+ - 0
71
+ hash: -3120554601435364730
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 1.8.17
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Delayed Job for EY Cloud
78
+ test_files: []