eycloud-recipe-resque 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []