eycloud-recipe-resque 1.0

Sign up to get free protection for your applications and to get access to all the features.
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-resque (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-resque!
16
+ rake
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Resque recipe for EY Cloud
2
+
3
+ Resque is a Redis-backed Ruby library for creating background jobs, placing those jobs on multiple queues, and processing them later.
4
+
5
+ This recipe will setup `resque` on a Solo instance environment or on named Utility instances in a cluster environment.
6
+
7
+ Name your Utility instances with prefixes: `resque`. For example, `resque`, or `resque123`. Naming them all `resque` works just fine.
8
+
9
+ This recipe assumes that redis is running within the environment. Fortunately, it is. By default EY Cloud runs redis on your `solo` or `db_master` instance. Optionally, this recipe will use a custom `redis` utility instance that you might have in your environment. It's clever like that.
10
+
11
+ ## Installation
12
+
13
+ ## Simple Installation
14
+
15
+ To add this recipe to your collection of recipes, or as your first recipe, you can use the helpful `ey-recipes` command line tool:
16
+
17
+ cd myapp
18
+ gem install engineyard engineyard-recipes
19
+ ey-recipes init
20
+ ey-recipes clone git://github.com/engineyard/eycloud-recipe-resque.git -n resque
21
+ ey recipes upload --apply
22
+
23
+ If you want to have your recipes run during deploy (rather than the separate `ey recipes upload --apply` step):
24
+
25
+ ey-recipes init -d
26
+ ey-recipes clone git://github.com/engineyard/eycloud-recipe-resque.git -n resque
27
+ git add .; git commit -m "added delayed job recipe"; git push origin master
28
+ ey deploy
29
+
30
+ ## Manual Installation
31
+
32
+ Clone/copy this repository into a `cookbooks/resque` folder (such that you have a `cookbooks/resque/recipes/default.rb` file). This recipe must be installed as `resque` and not `eycloud-recipe-resque` or anything fancy.
33
+
34
+ Then add the following to `cookbooks/main/recipes/default.rb`:
35
+
36
+ require_recipe "resque"
37
+
38
+ Make sure this and any customizations to the recipe are committed to your own fork of this
39
+ repository.
40
+
41
+ Then to upload and apply to EY Cloud for a given environment:
42
+
43
+ ey recipes upload --apply -e target-environment
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ worker_memory(300) # Mb
@@ -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-resque"
8
+ s.version = version
9
+ s.authors = ["Dr Nic Williams"]
10
+ s.email = ["drnicwilliams@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Resque for EY Cloud} # from metadata
13
+ s.description = %q{Resque 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,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "syslog"
4
+ require "fileutils"
5
+
6
+ GRACE = 300
7
+
8
+ unless ARGV.size == 1
9
+ $stderr.puts "Usage: #{$0} ttl_files_directory"
10
+ exit 1
11
+ end
12
+
13
+ ttl_files_directory = ARGV.shift
14
+
15
+ unless File.directory?(ttl_files_directory)
16
+ $stderr.puts "ttl_files_directory `#{ttl_files_directory}` is not a directory"
17
+ exit 1
18
+ end
19
+
20
+ Syslog.open
21
+
22
+ Dir[File.join(ttl_files_directory, "*.ttl")].each do |file|
23
+ begin
24
+ pid = File.basename(file, ".ttl").to_i
25
+ if pid == 0
26
+ FileUtils.rm_f(file)
27
+ next
28
+ end
29
+
30
+ started = File.stat(file).mtime
31
+ elapsed = (Time.now - started).to_i
32
+ queue, ttl = File.readlines(file).map {|l| l.chomp }
33
+ ttl = ttl.to_i + GRACE
34
+
35
+ if elapsed > ttl
36
+ Syslog.notice("killing child #{pid} with HUP. processing #{queue.inspect}, elapsed #{elapsed} > ttl #{ttl}")
37
+ Process.kill("HUP", pid)
38
+ end
39
+ rescue Errno::ENOENT, Errno::ESRCH
40
+ FileUtils.rm_f(file)
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ class Chef
2
+ class Recipe
3
+ def get_resque_worker_count
4
+ # $ sudo cat /proc/meminfo
5
+ # MemTotal: 1759228 kB
6
+ # ...
7
+ mem_total_kb = `awk '/MemTotal/{print $2}' /proc/meminfo`.strip.to_i
8
+ mem_total_mb = mem_total_kb / 1024
9
+
10
+ # Want no more than ((total memory - 300) / est process size) workers + jobs to run
11
+ # in other words, if all workers on the instance are running jobs, there should
12
+ # be memory left over
13
+ result = (mem_total_mb - node[:worker_memory].to_i) / node[:worker_memory].to_i
14
+ result /= 2 if node[:instance_role] == 'solo' || node[:instance_role] == 'eylocal'
15
+ result
16
+ end
17
+ end
18
+ end
data/metadata.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "license": "MIT",
3
+ "maintainer": "Engine Yard LLC",
4
+ "maintainer_email": "drnic@engineyard.com",
5
+ "version": "1.0",
6
+ "description": "Resque for EY Cloud",
7
+ "name": "resque",
8
+ "attributes": {
9
+
10
+ },
11
+ "long_description": "",
12
+ "dependencies": {
13
+ "cron": [
14
+
15
+ ]
16
+ }
17
+ }
data/metadata.rb ADDED
@@ -0,0 +1,7 @@
1
+ name "resque"
2
+ description "Resque 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"
7
+ depends "crond"
@@ -0,0 +1,60 @@
1
+ #
2
+ # Cookbook Name:: resque
3
+ # Recipe:: configure
4
+ #
5
+
6
+ if node[:instance_role] == "solo" || node[:instance_role] == "eylocal" ||
7
+ (node[:instance_role] == "util" && node[:name] =~ /^(resque)/)
8
+ resque_workers_count = get_resque_worker_count()
9
+
10
+ remote_file "/usr/local/bin/resque_kill_stale" do
11
+ owner 'root'
12
+ group 'root'
13
+ mode 0755
14
+ source "resque_kill_stale"
15
+ end
16
+
17
+ directory "/tmp/resque_ttls" do
18
+ owner "root"
19
+ group "root"
20
+ mode 0755
21
+ end
22
+
23
+ # crond "Kill stale resque workers" do
24
+ # filename "resque_kill_stale"
25
+ # interval "* * * * *"
26
+ # command %Q{/usr/local/bin/resque_kill_stale /tmp/resque_ttls}
27
+ # end
28
+
29
+ directory "/var/log/resque" do
30
+ owner node[:owner_name]
31
+ group node[:owner_name]
32
+ mode 0755
33
+ end
34
+
35
+ node[:applications].each do |app_name, data|
36
+ # app-server specific recipes usually take care of this
37
+ link "/data/#{app_name}/shared/log" do
38
+ to "/var/log/resque"
39
+ end
40
+ end
41
+ end
42
+
43
+ if %w[solo app app_master util].include? node[:instance_role]
44
+ redis_instance = node.engineyard.environment.utility_instances.find {|x| x.name == "redis"} ||
45
+ node.engineyard.environment.db_master ||
46
+ node.engineyard.environment.instances.find { |x| x.role == "solo" }
47
+
48
+ node[:applications].each do |app, data|
49
+ template "/data/#{app}/shared/config/resque.yml" do
50
+ owner node[:owner_name]
51
+ group node[:owner_name]
52
+ mode 0655
53
+ source "resque.yml.erb"
54
+ variables(:framework_env => node[:environment][:framework_env],
55
+ :redis_host => redis_instance.public_hostname,
56
+ :redis_port => 6379)
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,8 @@
1
+ #
2
+ # Cookbook Name:: resque
3
+ # Recipe:: default
4
+ #
5
+
6
+ require_recipe "resque::configure"
7
+ require_recipe "resque::restart"
8
+
@@ -0,0 +1,14 @@
1
+ #
2
+ # Cookbook Name:: resque
3
+ # Recipe:: default
4
+ #
5
+
6
+ node[:applications].each do |app_name, data|
7
+ execute "ensure-resque-is-setup-with-monit" do
8
+ command %Q{monit reload}
9
+ end
10
+
11
+ execute "restart-resque" do
12
+ command %Q{echo "sleep 20 && monit -g #{app_name}_resque restart all" | at now }
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ rack_env = "<%= @node[:environment][:framework_env] %>"
2
+ app_root = "/data/awsm/current"
3
+ owner = "<%= @node[:owner_name] %>"
4
+ home = "/home/#{owner}"
5
+ instance_id = "<%= @node.engineyard.id %>"
6
+
7
+ <%= @resque_workers_count %>.times do |num|
8
+ inline = "#{home}/.ruby_inline/resque-#{num}"
9
+
10
+ God.watch do |w|
11
+ w.name = "resque-#{num}"
12
+ w.group = 'resque'
13
+ w.uid = owner
14
+ w.gid = owner
15
+ w.interval = 30.seconds
16
+ w.log = "#{app_root}/log/worker.#{num}.log"
17
+ w.dir = app_root
18
+ w.env = {
19
+ "VERBOSE" => "true",
20
+ "INSTANCE_ID" => instance_id,
21
+ "GOD_WATCH" => w.name,
22
+ "RACK_ENV" => rack_env,
23
+ "LANG" => "en_US.utf-8",
24
+ "MERB_ENV" => rack_env,
25
+ "HOME" => home,
26
+ "INLINEDIR" => inline
27
+ }
28
+
29
+ w.start = "bundle exec rake --trace resque:work"
30
+
31
+ w.behavior(:clean_pid_file)
32
+
33
+ w.start_grace = 2.minutes
34
+ w.restart_grace = 2.minutes
35
+
36
+ # retart if memory gets too high
37
+ w.transition(:up, :restart) do |on|
38
+ on.condition(:memory_usage) do |c|
39
+ c.above = 350.megabytes
40
+ c.times = 2
41
+ end
42
+ end
43
+
44
+ # determine the state on startup
45
+ w.transition(:init, { true => :up, false => :start }) do |on|
46
+ on.condition(:process_running) do |c|
47
+ c.running = true
48
+ end
49
+ end
50
+
51
+ # determine when process has finished starting
52
+ w.transition([:start, :restart], :up) do |on|
53
+ on.condition(:process_running) do |c|
54
+ c.running = true
55
+ c.interval = 5.seconds
56
+ end
57
+
58
+ # failsafe
59
+ on.condition(:tries) do |c|
60
+ c.times = 5
61
+ c.transition = :start
62
+ c.interval = 5.seconds
63
+ end
64
+ end
65
+
66
+ # start if process is not running
67
+ w.transition(:up, :start) do |on|
68
+ on.condition(:process_running) do |c|
69
+ c.running = false
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1 @@
1
+ <%= @framework_env %>: <%= @redis_host %>:<%= @redis_port %>
@@ -0,0 +1,71 @@
1
+ rack_env = "<%= @node[:environment][:framework_env] %>"
2
+ app_root = "/data/awsm/current"
3
+ owner = "<%= @node[:owner_name] %>"
4
+ home = "/home/#{owner}"
5
+ instance_id = "<%= @node.engineyard.id %>"
6
+
7
+ inline = "#{home}/.ruby_inline/resque-scheduler"
8
+
9
+ God.watch do |w|
10
+ w.name = "resque-scheduler"
11
+ w.group = 'resque'
12
+ w.uid = owner
13
+ w.gid = owner
14
+ w.interval = 30.seconds
15
+ w.log = "#{app_root}/log/resque_scheduler.log"
16
+ w.dir = app_root
17
+ w.env = {
18
+ "VERBOSE" => "true",
19
+ "INSTANCE_ID" => instance_id,
20
+ "GOD_WATCH" => w.name,
21
+ "RACK_ENV" => rack_env,
22
+ "MERB_ENV" => rack_env,
23
+ "LANG" => "en_US.utf-8",
24
+ "HOME" => home,
25
+ "INLINEDIR" => inline
26
+ }
27
+
28
+ w.start = "bundle exec rake --trace resque:scheduler"
29
+
30
+ w.behavior(:clean_pid_file)
31
+
32
+ w.start_grace = 2.minutes
33
+ w.restart_grace = 2.minutes
34
+
35
+ # retart if memory gets too high
36
+ w.transition(:up, :restart) do |on|
37
+ on.condition(:memory_usage) do |c|
38
+ c.above = 350.megabytes
39
+ c.times = 2
40
+ end
41
+ end
42
+
43
+ # determine the state on startup
44
+ w.transition(:init, { true => :up, false => :start }) do |on|
45
+ on.condition(:process_running) do |c|
46
+ c.running = true
47
+ end
48
+ end
49
+
50
+ # determine when process has finished starting
51
+ w.transition([:start, :restart], :up) do |on|
52
+ on.condition(:process_running) do |c|
53
+ c.running = true
54
+ c.interval = 5.seconds
55
+ end
56
+
57
+ # failsafe
58
+ on.condition(:tries) do |c|
59
+ c.times = 5
60
+ c.transition = :start
61
+ c.interval = 5.seconds
62
+ end
63
+ end
64
+
65
+ # start if process is not running
66
+ w.transition(:up, :start) do |on|
67
+ on.condition(:process_running) do |c|
68
+ c.running = false
69
+ end
70
+ end
71
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eycloud-recipe-resque
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: &70298707999080 !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: *70298707999080
25
+ description: Resque for EY Cloud
26
+ email:
27
+ - drnicwilliams@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - Gemfile
33
+ - Gemfile.lock
34
+ - README.md
35
+ - Rakefile
36
+ - attributes/default.rb
37
+ - eycloud-recipe-resque.gemspec
38
+ - files/default/resque_kill_stale
39
+ - libraries/get_resque_worker_count.rb
40
+ - metadata.json
41
+ - metadata.rb
42
+ - recipes/configure.rb
43
+ - recipes/default.rb
44
+ - recipes/restart.rb
45
+ - templates/default/resque.rb.erb
46
+ - templates/default/resque.yml.erb
47
+ - templates/default/resque_scheduler.rb.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: 2470771934776039979
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: 2470771934776039979
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 1.8.17
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Resque for EY Cloud
78
+ test_files: []