stormtroopers 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.travis.yml +7 -0
- data/README.md +2 -2
- data/Vagrantfile +10 -0
- data/bin/stormtroopers +11 -0
- data/config/stormtroopers.yml +3 -2
- data/lib/stormtroopers.rb +7 -11
- data/lib/stormtroopers/already_running.rb +4 -0
- data/lib/stormtroopers/army.rb +28 -4
- data/lib/stormtroopers/factory.rb +5 -1
- data/lib/stormtroopers/factory/delayed_job.rb +4 -2
- data/lib/stormtroopers/factory/dummy.rb +1 -1
- data/lib/stormtroopers/manager.rb +27 -12
- data/lib/stormtroopers/version.rb +1 -1
- data/spec/stormtroopers/manager_spec.rb +104 -0
- data/stormtroopers.gemspec +4 -1
- data/vagrant-provision +30 -0
- metadata +46 -19
- data/bin/stormtroopers.rb +0 -0
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format documentation
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# Stormtroopers
|
1
|
+
# Stormtroopers [![Build Status](https://secure.travis-ci.org/socialreferral/stormtroopers.png)](http://travis-ci.org/socialreferral/stormtroopers)
|
2
2
|
|
3
|
-
Stormtroopers is a jruby execution environment for delayed jobs
|
3
|
+
Stormtroopers is a jruby execution environment for delayed jobs, Work In Progress!
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
data/Vagrantfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# vi: set ft=ruby :
|
3
|
+
|
4
|
+
Vagrant::Config.run do |config|
|
5
|
+
config.vm.host_name = "stormtroopers"
|
6
|
+
config.vm.box = "precise64"
|
7
|
+
config.vm.box_url = "http://files.vagrantup.com/precise64.box"
|
8
|
+
config.vm.share_folder "v-root", "/vagrant", "."
|
9
|
+
config.vm.provision :shell, :path => "vagrant-provision"
|
10
|
+
end
|
data/bin/stormtroopers
ADDED
data/config/stormtroopers.yml
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
armies:
|
2
2
|
- factory:
|
3
3
|
type: :dummy
|
4
|
-
name: "Dummy
|
4
|
+
name: "Dummy Trooper 1"
|
5
5
|
sleep_duration: 10
|
6
6
|
max_threads: 2
|
7
|
+
name: "Dad's Army"
|
7
8
|
- factory:
|
8
9
|
type: :dummy
|
9
|
-
name: "Dummy
|
10
|
+
name: "Dummy Trooper 2"
|
10
11
|
sleep_duration: 5
|
11
12
|
max_threads: 2
|
data/lib/stormtroopers.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require_relative "./stormtroopers/version"
|
2
|
+
require_relative "./stormtroopers/trooper"
|
3
|
+
require_relative "./stormtroopers/manager"
|
4
|
+
require_relative "./stormtroopers/factory"
|
5
|
+
require_relative "./stormtroopers/army"
|
6
6
|
|
7
|
-
Dir[
|
8
|
-
Dir[
|
9
|
-
|
10
|
-
module Stormtroopers
|
11
|
-
|
12
|
-
end
|
7
|
+
Dir["#{File.dirname(__FILE__)}/stormtroopers/factory/*.rb"].each{ |f| require f }
|
8
|
+
Dir["#{File.dirname(__FILE__)}/stormtroopers/trooper/*.rb"].each{ |f| require f }
|
data/lib/stormtroopers/army.rb
CHANGED
@@ -1,30 +1,54 @@
|
|
1
1
|
module Stormtroopers
|
2
2
|
class Army
|
3
|
-
attr_reader :factory, :threads, :max_threads
|
3
|
+
attr_reader :factory, :threads, :max_threads, :name
|
4
4
|
|
5
5
|
def initialize(config)
|
6
|
-
@
|
6
|
+
@name = config[:name] || factory_class(config).name
|
7
|
+
@factory = factory_class(config).new(config[:factory])
|
7
8
|
@max_threads = config[:max_threads] || 1
|
8
9
|
@threads = []
|
9
10
|
end
|
10
11
|
|
12
|
+
def factory_class(config)
|
13
|
+
@factory_class ||= begin
|
14
|
+
raise ArgumentError, "Factory class or type must be defined" if config[:factory][:class].blank? && config[:factory][:type].blank?
|
15
|
+
class_name ||= config[:factory].delete(:class)
|
16
|
+
class_name ||= "stormtroopers/#{config[:factory].delete(:type)}_factory".camelize
|
17
|
+
class_name.constantize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
11
21
|
def manage
|
12
22
|
cleanup
|
13
23
|
if threads.count < max_threads
|
14
24
|
if trooper = factory.produce
|
15
|
-
threads << Thread.new
|
25
|
+
threads << Thread.new do
|
26
|
+
begin
|
27
|
+
trooper.run
|
28
|
+
ensure
|
29
|
+
if defined?(::Mongoid)
|
30
|
+
::Mongoid::IdentityMap.clear
|
31
|
+
::Mongoid.session(:default).disconnect
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
16
35
|
end
|
17
36
|
end
|
18
37
|
end
|
19
38
|
|
20
39
|
def finish
|
40
|
+
logger.debug("#{name}: Finishing")
|
21
41
|
threads.each(&:join)
|
22
42
|
end
|
23
43
|
|
44
|
+
def logger
|
45
|
+
Stormtroopers::Manager.logger
|
46
|
+
end
|
47
|
+
|
24
48
|
private
|
25
49
|
|
26
50
|
def cleanup
|
27
51
|
threads.reject!{ |thread| !thread.alive? }
|
28
52
|
end
|
29
53
|
end
|
30
|
-
end
|
54
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module Stormtroopers
|
2
2
|
class DelayedJobFactory < Factory
|
3
3
|
def produce
|
4
|
-
worker =
|
4
|
+
worker = Delayed::Worker.new(options)
|
5
|
+
worker.name = "rand #{Time.now.utc.to_f} #{rand(1000)}"
|
5
6
|
if job = Delayed::Job.reserve(worker)
|
7
|
+
logger.info("#{options[:name]} producing trooper to run #{job.queue} job #{job.id}")
|
6
8
|
DelayedJobTrooper.new(job)
|
7
9
|
end
|
8
10
|
end
|
9
11
|
end
|
10
|
-
end
|
12
|
+
end
|
@@ -1,24 +1,37 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'yaml'
|
1
3
|
require 'singleton'
|
2
4
|
require 'active_support/core_ext/hash'
|
3
5
|
require 'active_support/hash_with_indifferent_access'
|
6
|
+
require_relative "./already_running"
|
4
7
|
|
5
8
|
module Stormtroopers
|
6
9
|
class Manager
|
7
10
|
include Singleton
|
8
|
-
# This class is dependant on rails and active support
|
9
11
|
|
10
12
|
def manage
|
13
|
+
raise AlreadyRunning if managing?
|
14
|
+
@managing = true
|
15
|
+
|
16
|
+
Signal.trap("INT") do
|
17
|
+
logger.info "Stopping, waiting for running jobs to complete"
|
18
|
+
@managing = false
|
19
|
+
end
|
20
|
+
|
11
21
|
logger.info "Starting"
|
12
|
-
|
22
|
+
while managing? do
|
13
23
|
armies.each(&:manage)
|
14
24
|
sleep 0.1
|
15
25
|
end
|
16
|
-
|
17
|
-
logger.info "Stopping, waiting for running jobs to complete"
|
26
|
+
|
18
27
|
armies.each(&:finish)
|
19
28
|
logger.info "Stopped, all running jobs completed"
|
20
29
|
end
|
21
30
|
|
31
|
+
def managing?
|
32
|
+
@managing || false
|
33
|
+
end
|
34
|
+
|
22
35
|
def armies
|
23
36
|
@armies ||= config[:armies].map do |army_config|
|
24
37
|
Army.new(army_config)
|
@@ -29,20 +42,22 @@ module Stormtroopers
|
|
29
42
|
@config ||= HashWithIndifferentAccess.new(YAML.load_file(config_file))
|
30
43
|
end
|
31
44
|
|
32
|
-
def
|
45
|
+
def working_directory
|
33
46
|
if defined?(Rails)
|
34
|
-
|
47
|
+
Rails.root
|
35
48
|
else
|
36
|
-
|
49
|
+
Dir.getwd
|
37
50
|
end
|
38
51
|
end
|
39
52
|
|
53
|
+
def config_file
|
54
|
+
File.join(working_directory, "config", "stormtroopers.yml")
|
55
|
+
end
|
56
|
+
|
40
57
|
def logger
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@logger ||= Logger.new(STDOUT)
|
45
|
-
end
|
58
|
+
log_directory = File.join(working_directory, "log")
|
59
|
+
Dir.mkdir(log_directory) unless File.directory?(log_directory)
|
60
|
+
@logger ||= Logger.new(File.join(working_directory, "log", "stormtroopers.log"))
|
46
61
|
end
|
47
62
|
|
48
63
|
private
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require "stormtroopers/manager"
|
2
|
+
|
3
|
+
describe Stormtroopers::Manager do
|
4
|
+
let(:manager) { Stormtroopers::Manager.instance }
|
5
|
+
|
6
|
+
before(:each) {
|
7
|
+
stub_const("Stormtroopers::Army", Class.new)
|
8
|
+
}
|
9
|
+
|
10
|
+
describe "#working_directory" do
|
11
|
+
context "with Rails" do
|
12
|
+
it "uses the Rails root" do
|
13
|
+
stub_const("Rails", Class.new)
|
14
|
+
Rails.should_receive(:root).and_return("/path/to/app")
|
15
|
+
manager.working_directory.should eq("/path/to/app")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "without Rails" do
|
20
|
+
it "uses the current working directory" do
|
21
|
+
manager.working_directory.should eq(Dir.pwd)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#logger" do
|
27
|
+
it "creates a logger to log/stormtroopers.log" do
|
28
|
+
logger = stub.as_null_object
|
29
|
+
Logger.should_receive(:new).with(File.join(manager.working_directory, "log", "stormtroopers.log")).and_return(logger)
|
30
|
+
manager.logger.should equal(logger)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#config_file" do
|
35
|
+
it "uses the config_file relative to the current working directory" do
|
36
|
+
manager.config_file.should eq(File.join(manager.working_directory, "config", "stormtroopers.yml"))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#config" do
|
41
|
+
it "loads the config_file into a HashWithIndifferentAccess" do
|
42
|
+
YAML.should_receive(:load_file).and_return({dummy: "value"})
|
43
|
+
manager.config.should eq(HashWithIndifferentAccess.new({dummy: "value"}))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#armies" do
|
48
|
+
it "returns an array of armies matching using #config" do
|
49
|
+
army1_config = {factory: {type: :dummy, name: "Trooper 1", sleep_duration: 2}, name: "Dad's Army", max_threads: 1}
|
50
|
+
army1 = stub
|
51
|
+
Stormtroopers::Army.should_receive(:new).with(HashWithIndifferentAccess.new(army1_config)).and_return(army1)
|
52
|
+
army2_config = {factory: {type: :dummy, name: "Trooper 2", sleep_duration: 5}, name: "Mom's Army", max_threads: 2}
|
53
|
+
army2 = stub
|
54
|
+
Stormtroopers::Army.should_receive(:new).with(HashWithIndifferentAccess.new(army2_config)).and_return(army2)
|
55
|
+
config = HashWithIndifferentAccess.new(
|
56
|
+
{
|
57
|
+
armies: [
|
58
|
+
army1_config,
|
59
|
+
army2_config,
|
60
|
+
]
|
61
|
+
}
|
62
|
+
)
|
63
|
+
manager.should_receive(:config).and_return(config)
|
64
|
+
armies = manager.armies
|
65
|
+
manager.armies.size.should eq(2)
|
66
|
+
manager.armies.should include(army1)
|
67
|
+
manager.armies.should include(army2)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#manage" do
|
72
|
+
let(:army1) { stub(manage: nil, finish: nil) }
|
73
|
+
let(:army2) { stub(manage: nil, finish: nil) }
|
74
|
+
|
75
|
+
before(:each) do
|
76
|
+
manager.stub(:armies).and_return([army1, army2])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "calls manage on each army while managing" do
|
80
|
+
manager.stub(:managing?).and_return(false, true, true, false)
|
81
|
+
army1.should_receive(:manage).exactly(2).times
|
82
|
+
army2.should_receive(:manage).exactly(2).times
|
83
|
+
manager.manage
|
84
|
+
end
|
85
|
+
|
86
|
+
it "calls finish on each army when no longer managing" do
|
87
|
+
manager.stub(:managing?).and_return(false)
|
88
|
+
army1.should_receive(:finish)
|
89
|
+
army2.should_receive(:finish)
|
90
|
+
manager.manage
|
91
|
+
end
|
92
|
+
|
93
|
+
it "raises an error when already running" do
|
94
|
+
manager.stub(:managing?).and_return(true)
|
95
|
+
expect { manager.manage }.to raise_error(Stormtroopers::AlreadyRunning)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe ".logger" do
|
100
|
+
it "returns the singleton instance's logger" do
|
101
|
+
Stormtroopers::Manager.logger.should eq(manager.logger)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/stormtroopers.gemspec
CHANGED
@@ -7,13 +7,16 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.name = "stormtroopers"
|
8
8
|
gem.version = Stormtroopers::VERSION
|
9
9
|
gem.authors = ["Andre Meij", "Mark Kremer"]
|
10
|
-
gem.email = ["andre@socialreferral.com"]
|
10
|
+
gem.email = ["andre@socialreferral.com", "mark@socialreferral.com"]
|
11
11
|
gem.description = %q{Stormtroopers is a jruby execution environment for delayed jobs }
|
12
12
|
gem.summary = %q{Execute delayed jobs in a threaded jruby environment}
|
13
13
|
gem.homepage = "http://github.com/socialreferral/stormtroopers"
|
14
14
|
|
15
15
|
gem.add_dependency('activesupport', '>= 3.2.0')
|
16
16
|
|
17
|
+
gem.add_development_dependency('rspec')
|
18
|
+
|
19
|
+
gem.bindir = 'bin'
|
17
20
|
gem.files = `git ls-files`.split($/)
|
18
21
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
22
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
data/vagrant-provision
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
export DEBIAN_FRONTEND=noninteractive
|
3
|
+
|
4
|
+
apt-get update > /dev/null
|
5
|
+
apt-get -y install build-essential git-core openjdk-7-jre-headless
|
6
|
+
|
7
|
+
# Create directory for local installs
|
8
|
+
mkdir -p /home/vagrant/local
|
9
|
+
chown -R vagrant:vagrant /home/vagrant/local
|
10
|
+
|
11
|
+
if [ -f "/home/vagrant/local/jruby-1.7.0/bin/jruby" ]; then
|
12
|
+
echo "Already installed jruby 1.7"
|
13
|
+
else
|
14
|
+
echo "Downloading jruby 1.7"
|
15
|
+
wget -q http://jruby.org.s3.amazonaws.com/downloads/1.7.0/jruby-bin-1.7.0.tar.gz
|
16
|
+
echo "Unpacking jruby 1.7"
|
17
|
+
tar -xzf jruby-bin-1.7.0.tar.gz
|
18
|
+
echo "Installing jruby 1.7"
|
19
|
+
chown -R vagrant:vagrant jruby-1.7.0 && mv jruby-1.7.0 /home/vagrant/local
|
20
|
+
echo "export PATH=\"/home/vagrant/local/jruby-1.7.0/bin:\$PATH\"" >> /home/vagrant/.bashrc
|
21
|
+
echo "export PATH=\"/home/vagrant/local/jruby-1.7.0/bin:\$PATH\"" >> /home/vagrant/.zshenv
|
22
|
+
ln -s /home/vagrant/local/jruby-1.7.0/bin/jruby /home/vagrant/local/jruby-1.7.0/bin/ruby
|
23
|
+
fi
|
24
|
+
|
25
|
+
echo "Activating jruby 1.7"
|
26
|
+
export PATH=/home/vagrant/local/jruby-1.7.0/bin:$PATH
|
27
|
+
|
28
|
+
gem install bundler pry
|
29
|
+
|
30
|
+
su -c 'cd /vagrant && bundle check || bundle' vagrant
|
metadata
CHANGED
@@ -1,49 +1,72 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stormtroopers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Andre Meij
|
9
9
|
- Mark Kremer
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-11-
|
13
|
+
date: 2012-11-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
17
|
-
|
17
|
+
version_requirements: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.0
|
18
22
|
none: false
|
23
|
+
requirement: !ruby/object:Gem::Requirement
|
19
24
|
requirements:
|
20
25
|
- - ! '>='
|
21
26
|
- !ruby/object:Gem::Version
|
22
27
|
version: 3.2.0
|
23
|
-
|
28
|
+
none: false
|
24
29
|
prerelease: false
|
30
|
+
type: :runtime
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rspec
|
25
33
|
version_requirements: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: !binary |-
|
38
|
+
MA==
|
26
39
|
none: false
|
40
|
+
requirement: !ruby/object:Gem::Requirement
|
27
41
|
requirements:
|
28
42
|
- - ! '>='
|
29
43
|
- !ruby/object:Gem::Version
|
30
|
-
version:
|
44
|
+
version: !binary |-
|
45
|
+
MA==
|
46
|
+
none: false
|
47
|
+
prerelease: false
|
48
|
+
type: :development
|
31
49
|
description: ! 'Stormtroopers is a jruby execution environment for delayed jobs '
|
32
50
|
email:
|
33
51
|
- andre@socialreferral.com
|
52
|
+
- mark@socialreferral.com
|
34
53
|
executables:
|
35
|
-
- stormtroopers
|
54
|
+
- stormtroopers
|
36
55
|
extensions: []
|
37
56
|
extra_rdoc_files: []
|
38
57
|
files:
|
39
58
|
- .gitignore
|
59
|
+
- .rspec
|
60
|
+
- .travis.yml
|
40
61
|
- Gemfile
|
41
62
|
- LICENSE.txt
|
42
63
|
- README.md
|
43
64
|
- Rakefile
|
44
|
-
-
|
65
|
+
- Vagrantfile
|
66
|
+
- bin/stormtroopers
|
45
67
|
- config/stormtroopers.yml
|
46
68
|
- lib/stormtroopers.rb
|
69
|
+
- lib/stormtroopers/already_running.rb
|
47
70
|
- lib/stormtroopers/army.rb
|
48
71
|
- lib/stormtroopers/factory.rb
|
49
72
|
- lib/stormtroopers/factory/delayed_job.rb
|
@@ -53,30 +76,34 @@ files:
|
|
53
76
|
- lib/stormtroopers/trooper/delayed_job.rb
|
54
77
|
- lib/stormtroopers/trooper/dummy.rb
|
55
78
|
- lib/stormtroopers/version.rb
|
79
|
+
- spec/stormtroopers/manager_spec.rb
|
56
80
|
- stormtroopers.gemspec
|
81
|
+
- vagrant-provision
|
57
82
|
homepage: http://github.com/socialreferral/stormtroopers
|
58
83
|
licenses: []
|
59
|
-
post_install_message:
|
84
|
+
post_install_message:
|
60
85
|
rdoc_options: []
|
61
86
|
require_paths:
|
62
87
|
- lib
|
63
88
|
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
-
none: false
|
65
89
|
requirements:
|
66
90
|
- - ! '>='
|
67
91
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
69
|
-
|
92
|
+
version: !binary |-
|
93
|
+
MA==
|
70
94
|
none: false
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
96
|
requirements:
|
72
97
|
- - ! '>='
|
73
98
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
99
|
+
version: !binary |-
|
100
|
+
MA==
|
101
|
+
none: false
|
75
102
|
requirements: []
|
76
|
-
rubyforge_project:
|
77
|
-
rubygems_version: 1.8.
|
78
|
-
signing_key:
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.8.24
|
105
|
+
signing_key:
|
79
106
|
specification_version: 3
|
80
107
|
summary: Execute delayed jobs in a threaded jruby environment
|
81
|
-
test_files:
|
82
|
-
|
108
|
+
test_files:
|
109
|
+
- spec/stormtroopers/manager_spec.rb
|
data/bin/stormtroopers.rb
DELETED
File without changes
|