hirefire-resource 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/README.md +80 -0
- data/bin/hirefire +4 -0
- data/bin/hirefireapp +5 -0
- data/hirefire-resource.gemspec +17 -0
- data/lib/hirefire-resource.rb +14 -0
- data/lib/hirefire/cli.rb +48 -0
- data/lib/hirefire/macro/delayed_job.rb +44 -0
- data/lib/hirefire/macro/qc.rb +24 -0
- data/lib/hirefire/macro/qu.rb +25 -0
- data/lib/hirefire/macro/resque.rb +26 -0
- data/lib/hirefire/middleware.rb +73 -0
- data/lib/hirefire/railtie.rb +9 -0
- data/lib/hirefire/resource.rb +33 -0
- metadata +61 -0
data/.gitignore
ADDED
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# [HireFire](http://hirefireapp.com/) - The Heroku Dyno Manager
|
2
|
+
|
3
|
+
*Note: This gem currently only applies to users that are using the new HireFire beta program.
|
4
|
+
Once out of beta, this will become the default gem for new users.*
|
5
|
+
|
6
|
+
HireFire is a hosted service that manages / autoscales your [Heroku](http://heroku.com/) dynos.
|
7
|
+
|
8
|
+
It supports the following stacks:
|
9
|
+
|
10
|
+
* Celadon Cedar
|
11
|
+
* Badious Bamboo
|
12
|
+
* Argent Aspen
|
13
|
+
|
14
|
+
It supports practically any worker library. We provide out-of-the-box support for:
|
15
|
+
|
16
|
+
* Delayed Job
|
17
|
+
* Resque
|
18
|
+
* Qu
|
19
|
+
* QueueClassic
|
20
|
+
|
21
|
+
*Note that you can write your own worker queue logic for almost any other worker library as well.
|
22
|
+
HireFire can scale multiple individual worker libraries at the same time, as well as multiple individual queues for any worker library.*
|
23
|
+
|
24
|
+
It supports practically any Rack-based application or framework, such as:
|
25
|
+
|
26
|
+
* Ruby on Rails
|
27
|
+
* Sinatra
|
28
|
+
* Padrino
|
29
|
+
* Bare Rack Apps
|
30
|
+
|
31
|
+
We provide convenient macros for the above mentioned worker libraries to calculate the queue size for each of them.
|
32
|
+
If you wish to conribute more macros for other existing worker libraries feel free to send us a pull request.
|
33
|
+
|
34
|
+
Here is an example with Ruby on Rails 3. First, add the gem to your `Gemfile`:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
gem "hirefire-resource"
|
38
|
+
```
|
39
|
+
|
40
|
+
Then, all you have to do is create an initializer in `config/initializers/hirefire.rb` and add the following:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
HireFire::Resource.configure do |config|
|
44
|
+
config.dyno(:resque_worker) do
|
45
|
+
HireFire::Macro::Resque.queue
|
46
|
+
end
|
47
|
+
|
48
|
+
config.dyno(:dj_worker) do
|
49
|
+
HireFire::Macro::Delayed::Job.queue
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
This will allow HireFire to read out the queues for both Resque and Delayed Job. By default these macros will count all the queues combined if you are using multiple
|
55
|
+
different queues for each worker library. You can also pass in specific queues to count, like so:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
HireFire::Resource.configure do |config|
|
59
|
+
config.dyno(:resque_worker) do
|
60
|
+
HireFire::Macro::Resque.queue(:mail, :backup)
|
61
|
+
end
|
62
|
+
|
63
|
+
config.dyno(:dj_worker) do
|
64
|
+
HireFire::Macro::Delayed::Job.queue(:encode, :compress)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
This will tell HireFire to count the total amount of jobs from the `mail` and `backup` queue for the `resque_worker` dyno, and the `encode` and `compress` queues for the `dj_worker` dyno.
|
70
|
+
The `resque_worker` refers to the `resque_worker` in your `Procfile`, and the `dj_worker` refers to the `dj_worker` in the `Procfile`. In this case the `Procfile` would look something like this:
|
71
|
+
|
72
|
+
```
|
73
|
+
resque_worker: QUEUE=mail,backup bundle exec rake resque:work
|
74
|
+
dj_worker: QUEUES=encode,compress bundle exec rake jobs:work
|
75
|
+
```
|
76
|
+
|
77
|
+
Now that HireFire will scale both of the these dyno types based on their individual queue sizes. To customize how they scale, log in to the HireFire web interface.
|
78
|
+
|
79
|
+
Visit the [official website](http://hirefireapp.com/) for more information!
|
80
|
+
|
data/bin/hirefire
ADDED
data/bin/hirefireapp
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "hirefire-resource"
|
5
|
+
gem.version = "0.0.1"
|
6
|
+
gem.platform = Gem::Platform::RUBY
|
7
|
+
gem.authors = "Michael van Rooijen"
|
8
|
+
gem.email = "michael@hirefireapp.com"
|
9
|
+
gem.homepage = "http://hirefireapp.com/"
|
10
|
+
gem.summary = "HireFire - The Heroku Dyno Manager"
|
11
|
+
gem.description = "HireFire - The Heroku Dyno Manager"
|
12
|
+
|
13
|
+
gem.files = %x[git ls-files].split("\n")
|
14
|
+
gem.executables = ["hirefire", "hirefireapp"]
|
15
|
+
gem.require_path = "lib"
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
HIREFIRE_PATH = File.expand_path("../hirefire", __FILE__)
|
4
|
+
|
5
|
+
%w[middleware resource].each do |file|
|
6
|
+
require "#{HIREFIRE_PATH}/#{file}"
|
7
|
+
end
|
8
|
+
|
9
|
+
%w[delayed_job resque qu qc].each do |file|
|
10
|
+
require "#{HIREFIRE_PATH}/macro/#{file}"
|
11
|
+
end
|
12
|
+
|
13
|
+
require "#{HIREFIRE_PATH}/railtie" if defined?(Rails::Railtie)
|
14
|
+
|
data/lib/hirefire/cli.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "open-uri"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
OpenSSL::SSL.send(:remove_const, :VERIFY_PEER)
|
7
|
+
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
|
8
|
+
|
9
|
+
def usage
|
10
|
+
puts(<<-EOS)
|
11
|
+
|
12
|
+
Usage:
|
13
|
+
|
14
|
+
hirefire http://mydomain.com/
|
15
|
+
|
16
|
+
Or locally:
|
17
|
+
|
18
|
+
gem install thin
|
19
|
+
[bundle exec] thin start -p 3000
|
20
|
+
hirefire http://127.0.0.1:3000/
|
21
|
+
|
22
|
+
SSL Enabled URLs:
|
23
|
+
|
24
|
+
hirefire https://mydomain.com/
|
25
|
+
|
26
|
+
EOS
|
27
|
+
end
|
28
|
+
|
29
|
+
if (url = ARGV[0]).nil?
|
30
|
+
usage
|
31
|
+
else
|
32
|
+
begin
|
33
|
+
response = open(File.join(url, "hirefire", "test")).read
|
34
|
+
rescue
|
35
|
+
puts
|
36
|
+
puts "Error: Could not connect to: #{url}"
|
37
|
+
usage
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
if response =~ /HireFire/
|
42
|
+
puts response
|
43
|
+
else
|
44
|
+
puts "Error: Could not find HireFire at #{url}."
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Macro
|
5
|
+
module Delayed
|
6
|
+
module Job
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Determines whether `ActiveRecord (3)` or `Mongoid` is being used.
|
10
|
+
# Once determined, it will build the appropriate query criteria in order
|
11
|
+
# to count the amount of jobs in a given queue and return the result.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# HireFire::Macro::Delayed::Job.queue # all queues
|
16
|
+
# HireFire::Macro::Delayed::Job.queue("email") # only email queue
|
17
|
+
# HireFire::Macro::Delayed::Job.queue("audio", "video") # audio and video queues
|
18
|
+
#
|
19
|
+
# @param [Array] queues provide one or more queue names, or none for "all".
|
20
|
+
# @return [Integer] the number of jobs in the queue(s).
|
21
|
+
def queue(*queues)
|
22
|
+
queues.flatten!
|
23
|
+
|
24
|
+
if defined?(ActiveRecord)
|
25
|
+
c = ::Delayed::Job
|
26
|
+
c = c.where(:failed_at => nil)
|
27
|
+
c = c.where("run_at <= ?", Time.now.utc)
|
28
|
+
c = c.where(:queue => queues) unless queues.empty?
|
29
|
+
c.count
|
30
|
+
elsif defined?(Mongoid)
|
31
|
+
c = ::Delayed::Job
|
32
|
+
c = c.where(:failed_at => nil)
|
33
|
+
c = c.where(:run_at.lte => Time.now.utc)
|
34
|
+
c = c.where(:queue.in => queues) unless queues.empty?
|
35
|
+
c.count
|
36
|
+
else
|
37
|
+
raise "HireFire could not detect ActiveRecord or Mongoid for HireFire::Macro::Delayed::Job."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Macro
|
5
|
+
module QC
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Queries the PostgreSQL database through QueueClassic in order to
|
9
|
+
# count the amount of jobs in the specified queue.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# HireFire::Macro::QC.queue # counts the `default` queue.
|
14
|
+
# HireFire::Macro::QC.queue("email") # counts the `email` queue.
|
15
|
+
#
|
16
|
+
# @param [String, Symbol, nil] queue the queue name to count. (default: `default`)
|
17
|
+
# @return [Integer] the number of jobs in the queue(s).
|
18
|
+
def queue(queue = "default")
|
19
|
+
::QC::Queries.count(queue.to_s)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Macro
|
5
|
+
module Qu
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Counts the amount of jobs in the (provided) Qu queue(s).
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# HireFire::Macro::Qu.queue # all queues
|
13
|
+
# HireFire::Macro::Qu.queue("email") # only email queue
|
14
|
+
# HireFire::Macro::Qu.queue("audio", "video") # audio and video queues
|
15
|
+
#
|
16
|
+
# @param [Array] queues provide one or more queue names, or none for "all".
|
17
|
+
# @return [Integer] the number of jobs in the queue(s).
|
18
|
+
def queue(*queues)
|
19
|
+
queues = ::Qu.backend.queues if queues.empty?
|
20
|
+
queues.flatten.inject(0) { |memo, queue| memo += ::Qu.backend.length(queue); memo }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Macro
|
5
|
+
module Resque
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Counts the amount of jobs in the (provided) Resque queue(s).
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# HireFire::Macro::Resque.queue # all queues
|
13
|
+
# HireFire::Macro::Resque.queue("email") # only email queue
|
14
|
+
# HireFire::Macro::Resque.queue("audio", "video") # audio and video queues
|
15
|
+
#
|
16
|
+
# @param [Array] queues provide one or more queue names, or none for "all".
|
17
|
+
# @return [Integer] the number of jobs in the queue(s).
|
18
|
+
def queue(*queues)
|
19
|
+
return ::Resque.info[:pending].to_i if queues.empty?
|
20
|
+
queues = queues.flatten.map(&:to_s)
|
21
|
+
queues.inject(0) { |memo, queue| memo += ::Resque.size(queue); memo }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
class Middleware
|
5
|
+
|
6
|
+
# Initialize the HireFire::Middleware and store the `app` in `@app`
|
7
|
+
# and `ENV["HIREFIRE_TOKEN"]` in `@token` for convenience.
|
8
|
+
#
|
9
|
+
# @param [ActionDispatch::Routing::RouteSet] app.
|
10
|
+
def initialize(app)
|
11
|
+
@app = app
|
12
|
+
@token = ENV["HIREFIRE_TOKEN"]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Will respond to the request here if either the `test` or the `info` url was requested.
|
16
|
+
# Otherwise, fall through to the rest of the middleware below HireFire::Middleware.
|
17
|
+
#
|
18
|
+
# @param [Hash] env containing request information.
|
19
|
+
def call(env)
|
20
|
+
@env = env
|
21
|
+
|
22
|
+
if test?
|
23
|
+
[ 200, {"Content-Type" => "text/html"}, self ]
|
24
|
+
elsif info?
|
25
|
+
[ 200, {"Content-Type" => "application/json"}, self ]
|
26
|
+
else
|
27
|
+
@app.call(env)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns text/html when the `test` url is requested.
|
32
|
+
# This is purely to see whether the URL works through the HireFire command-line utility.
|
33
|
+
#
|
34
|
+
# Returns a JSON String when the `info` url is requested.
|
35
|
+
# This url will be requested every minute by HireFire in order to fetch dyno data.
|
36
|
+
#
|
37
|
+
# @return [text/html, application/json] based on whether the `test` or `info` url was requested.
|
38
|
+
def each(&block)
|
39
|
+
if test?
|
40
|
+
block.call "HireFire Middleware Found!"
|
41
|
+
elsif info?
|
42
|
+
block.call(dynos)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Generates a JSON string based on the dyno data.
|
49
|
+
#
|
50
|
+
# @return [String] in JSON format.
|
51
|
+
def dynos
|
52
|
+
dyno_data = HireFire::Resource.dynos.inject(String.new) do |json, dyno|
|
53
|
+
json << %|,{"name":"#{dyno[:name]}","quantity":#{dyno[:quantity].call || "null"}}|; json
|
54
|
+
end
|
55
|
+
|
56
|
+
"[#{dyno_data.sub(",","")}]"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns true if the PATH_INFO matches the test url.
|
60
|
+
#
|
61
|
+
# @return [Boolean] true if the requested url matches the test url.
|
62
|
+
def test?
|
63
|
+
@env["PATH_INFO"] == "/hirefire/test"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns true if the PATH_INFO matches the info url.
|
67
|
+
#
|
68
|
+
# @return [Boolean] true if the requested url matches the info url.
|
69
|
+
def info?
|
70
|
+
@env["PATH_INFO"] == "/hirefire/#{@token || "development"}/info"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Resource
|
5
|
+
extend self
|
6
|
+
attr_accessor :dynos
|
7
|
+
|
8
|
+
# Sets the `@dynos` instance variable to an empty Array to hold all the dyno configuration.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# HireFire::Resource.configure do |config|
|
13
|
+
# config.dyno(:worker) do
|
14
|
+
# # Macro or Custom logic for the :worker dyno here..
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @yields [HireFire::Resource] to allow for block-style configuration.
|
19
|
+
def configure
|
20
|
+
@dynos ||= []
|
21
|
+
yield self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Will be used through block-style configuration with the `configure` method.
|
25
|
+
#
|
26
|
+
# @param [Symbol, String] name the name of the dyno as defined in the Procfile.
|
27
|
+
# @param [Proc] block an Integer containing the quantity calculation logic.
|
28
|
+
def dyno(name, &block)
|
29
|
+
@dynos << { :name => name, :quantity => block }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hirefire-resource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Michael van Rooijen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-20 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: HireFire - The Heroku Dyno Manager
|
15
|
+
email: michael@hirefireapp.com
|
16
|
+
executables:
|
17
|
+
- hirefire
|
18
|
+
- hirefireapp
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- README.md
|
24
|
+
- bin/hirefire
|
25
|
+
- bin/hirefireapp
|
26
|
+
- hirefire-resource.gemspec
|
27
|
+
- lib/hirefire-resource.rb
|
28
|
+
- lib/hirefire/cli.rb
|
29
|
+
- lib/hirefire/macro/delayed_job.rb
|
30
|
+
- lib/hirefire/macro/qc.rb
|
31
|
+
- lib/hirefire/macro/qu.rb
|
32
|
+
- lib/hirefire/macro/resque.rb
|
33
|
+
- lib/hirefire/middleware.rb
|
34
|
+
- lib/hirefire/railtie.rb
|
35
|
+
- lib/hirefire/resource.rb
|
36
|
+
homepage: http://hirefireapp.com/
|
37
|
+
licenses: []
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.8.16
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: HireFire - The Heroku Dyno Manager
|
60
|
+
test_files: []
|
61
|
+
has_rdoc:
|