stormtroopers 0.0.1 → 0.0.2
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/.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 [](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
|