simpleworker 0.0.1 → 0.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.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.travis.yml +6 -0
- data/LICENSE +1 -1
- data/README.md +52 -15
- data/examples/basic.rb +26 -0
- data/examples/cucumber_worker.rb +20 -0
- data/examples/local_worker.rb +8 -0
- data/examples/ssh_worker.rb +13 -0
- data/examples/worker.rb +12 -0
- data/lib/simpleworker/abstract_listener.rb +41 -0
- data/lib/simpleworker/event_monitor.rb +50 -0
- data/lib/simpleworker/event_server.rb +48 -0
- data/lib/simpleworker/local_worker.rb +17 -10
- data/lib/simpleworker/logging_listener.rb +65 -0
- data/lib/simpleworker/redis_support.rb +33 -0
- data/lib/simpleworker/retry_listener.rb +32 -0
- data/lib/simpleworker/runner.rb +85 -26
- data/lib/simpleworker/scripts/expired_tasks.lua +13 -0
- data/lib/simpleworker/scripts/lpopall.lua +9 -0
- data/lib/simpleworker/scripts/reliable_queue.lua +12 -0
- data/lib/simpleworker/ssh_worker.rb +40 -19
- data/lib/simpleworker/task_queue.rb +78 -0
- data/lib/simpleworker/version.rb +1 -1
- data/lib/simpleworker.rb +12 -1
- data/simpleworker.gemspec +1 -1
- data/spec/simpleworker/event_monitor_spec.rb +92 -0
- data/spec/simpleworker/event_server_spec.rb +24 -0
- data/spec/simpleworker/local_worker_spec.rb +11 -18
- data/spec/simpleworker/logging_listener_spec.rb +44 -0
- data/spec/simpleworker/retry_listener_spec.rb +25 -0
- data/spec/simpleworker/runner_spec.rb +74 -12
- data/spec/simpleworker/ssh_worker_spec.rb +20 -31
- data/spec/simpleworker/task_queue_spec.rb +76 -0
- metadata +42 -5
- data/lib/simpleworker/abstract_worker.rb +0 -42
- data/lib/simpleworker/bash/simple-localworker +0 -8
- data/lib/simpleworker/bash/ssh-remoteworker +0 -21
@@ -0,0 +1,12 @@
|
|
1
|
+
local task = redis.call('LPOP', KEYS[1])
|
2
|
+
if not task then
|
3
|
+
return nil
|
4
|
+
end
|
5
|
+
|
6
|
+
local task_key = string.format("%s:active:%s:%s:%s", ARGV[1], ARGV[2], ARGV[3], task)
|
7
|
+
redis.pcall('SADD', KEYS[2], task_key)
|
8
|
+
redis.pcall('SET',
|
9
|
+
task_key,
|
10
|
+
task,
|
11
|
+
"EX", ARGV[4])
|
12
|
+
return task
|
@@ -1,33 +1,54 @@
|
|
1
1
|
|
2
2
|
module SimpleWorker
|
3
|
-
class SshWorker
|
4
|
-
include AbstractWorker
|
5
3
|
|
6
|
-
|
4
|
+
#
|
5
|
+
# SshWorker.new(opts)
|
6
|
+
#
|
7
|
+
# where 'opts' is a Hash of options:
|
8
|
+
#
|
9
|
+
# :user => String name of user on remote host (default: `whoami`)
|
10
|
+
# :host => String name of remote host (default: `hostname`)
|
11
|
+
# :port => String port to connect on the remote host (default: 22)
|
12
|
+
# :cmd => String bash string to execute on the remote host in the users login shell, in the remote 'dirname', and with the JOBID environment variable set to the current jobid, (default: `bundle install; rake;`)
|
13
|
+
# :dirname => String remote directory in the users home directory (default: dynamic jobid)
|
14
|
+
class SshWorker < AbstractListener
|
7
15
|
|
8
|
-
|
9
|
-
|
10
|
-
|
16
|
+
attr_reader :user, :host, :port, :dirname, :cmd
|
17
|
+
|
18
|
+
def initialize(opts = {})
|
19
|
+
@user = opts[:user] || `whoami`.strip
|
20
|
+
@host = opts[:host] || `hostname`.strip
|
21
|
+
@port = opts[:port] || '22'
|
22
|
+
@cmd = opts[:cmd] || 'bundle install; rake;'
|
23
|
+
@dirname = opts[:dirname]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Destructive rsync to remote and start a process in the background on the remote server.
|
27
|
+
def on_start(jobid)
|
28
|
+
@dirname ||= jobid
|
29
|
+
@jobid = jobid
|
30
|
+
|
31
|
+
sync_to_remote
|
32
|
+
async_cmd
|
11
33
|
end
|
12
34
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
worker.cmd = config['cmd'] if config.has_key? 'cmd'
|
17
|
-
worker.user = config['user'] if config.has_key? 'user'
|
18
|
-
worker.host = config['host'] if config.has_key? 'host'
|
19
|
-
worker.directory = config['directory'] if config.has_key? 'directory'
|
20
|
-
worker
|
35
|
+
# Rsync from remote.
|
36
|
+
def on_stop
|
37
|
+
sync_from_remote
|
21
38
|
end
|
22
39
|
|
23
40
|
private
|
24
41
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
42
|
+
def sync_to_remote
|
43
|
+
`rsync -a --delete -e "ssh -p #{port}" "${PWD}/" "#{user}@#{host}:~/#{dirname}"`
|
44
|
+
end
|
45
|
+
|
46
|
+
def sync_from_remote
|
47
|
+
`rsync -a -e "ssh -p #{port}" "#{user}@#{host}:~/#{dirname}/" "${PWD}"`
|
48
|
+
end
|
29
49
|
|
30
|
-
|
50
|
+
def async_cmd
|
51
|
+
`ssh -p #{port} "#{user}@#{host}" "/bin/bash -lc 'cd ~/#{dirname}; export JOBID=#{@jobid}; #{cmd}' </dev/null >/dev/null 2>&1 &"`
|
31
52
|
end
|
32
53
|
end
|
33
54
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
module SimpleWorker
|
3
|
+
|
4
|
+
# TaskQueue.new(redis, hostname, jobid, opts)
|
5
|
+
#
|
6
|
+
# where hostname is the machines hostname or a unique identifier
|
7
|
+
# and 'opts' is a Hash of options:
|
8
|
+
#
|
9
|
+
# :namespace => String prefix to keys in redis used by SimpleWorker (default: simpleworker)
|
10
|
+
# :task_timeout => Fixnum time after which a task expires, this should be > timout set in Runner (default: 10 seconds)
|
11
|
+
class TaskQueue
|
12
|
+
include RedisSupport
|
13
|
+
|
14
|
+
DEFAULT_OPTIONS = {
|
15
|
+
:namespace => 'simpleworker'
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize(redis, hostname, jobid, opts = {})
|
19
|
+
opts = DEFAULT_OPTIONS.dup.merge(opts)
|
20
|
+
@redis = redis
|
21
|
+
@namespace = opts[:namespace]
|
22
|
+
@task_timeout = opts[:task_timeout]
|
23
|
+
@jobid = jobid
|
24
|
+
@hostname = hostname
|
25
|
+
@active_key_prefix = "#{active_tasks_key}:#{@hostname}"
|
26
|
+
@task_timeout = JSON.parse(@redis.get(config_key))['task_timeout']
|
27
|
+
load_lua_scripts
|
28
|
+
end
|
29
|
+
|
30
|
+
def fire_start
|
31
|
+
push_to_log('on_node_start', @hostname)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fire_stop
|
35
|
+
push_to_log('on_node_stop', @hostname)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fire_task_start(task)
|
39
|
+
push_to_log('on_task_start', @hostname, task)
|
40
|
+
end
|
41
|
+
|
42
|
+
def fire_task_stop(task)
|
43
|
+
push_to_log('on_task_stop', @hostname, task)
|
44
|
+
end
|
45
|
+
|
46
|
+
def expire_current_task
|
47
|
+
@redis.del "#{@active_key_prefix}:#{@current_task}" if @current_task
|
48
|
+
@current_task = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def fire_log_message(msg)
|
52
|
+
push_to_log('on_log', @hostname, msg)
|
53
|
+
end
|
54
|
+
|
55
|
+
def each_task
|
56
|
+
until pop.nil?
|
57
|
+
local_task = @current_task
|
58
|
+
fire_task_start local_task
|
59
|
+
yield local_task
|
60
|
+
fire_task_stop local_task
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def pop
|
65
|
+
@redis.srem(active_tasks_key, "#{@active_key_prefix}:#{@current_task}") if @current_task
|
66
|
+
@current_task = @redis.evalsha(@reliable_queue_sha,
|
67
|
+
:keys => [tasks_key, active_tasks_key],
|
68
|
+
:argv => [namespace, jobid, @hostname, @task_timeout])
|
69
|
+
@current_task
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def push_to_log(*args)
|
75
|
+
@redis.rpush(log_key, args.to_json)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/simpleworker/version.rb
CHANGED
data/lib/simpleworker.rb
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
|
2
2
|
require 'childprocess'
|
3
3
|
require 'yaml'
|
4
|
+
require 'redis'
|
5
|
+
require 'observer'
|
6
|
+
require 'json'
|
7
|
+
require 'securerandom'
|
8
|
+
require 'logger'
|
4
9
|
|
5
10
|
module SimpleWorker
|
6
11
|
end
|
7
12
|
|
13
|
+
require 'simpleworker/redis_support'
|
8
14
|
require 'simpleworker/runner'
|
9
|
-
require 'simpleworker/
|
15
|
+
require 'simpleworker/abstract_listener'
|
16
|
+
require 'simpleworker/event_monitor'
|
17
|
+
require 'simpleworker/event_server'
|
18
|
+
require 'simpleworker/task_queue'
|
10
19
|
require 'simpleworker/local_worker'
|
11
20
|
require 'simpleworker/ssh_worker'
|
21
|
+
require 'simpleworker/logging_listener'
|
22
|
+
require 'simpleworker/retry_listener'
|
data/simpleworker.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
$:.push File.expand_path("../lib", __FILE__)
|
3
2
|
require "simpleworker/version"
|
4
3
|
|
@@ -16,6 +15,7 @@ Gem::Specification.new do |s|
|
|
16
15
|
s.rubyforge_project = "simpleworker"
|
17
16
|
|
18
17
|
s.add_dependency 'childprocess', '>= 0.5.3'
|
18
|
+
s.add_dependency 'redis', '>= 0.0.1'
|
19
19
|
|
20
20
|
s.add_development_dependency "rspec", "~> 2.5"
|
21
21
|
s.add_development_dependency "rake", "~> 0.9.2"
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path('../../spec_helper.rb', __FILE__)
|
2
|
+
|
3
|
+
module SimpleWorker
|
4
|
+
describe EventMonitor do
|
5
|
+
let(:start_time) { Time.now }
|
6
|
+
let(:event_monitor) { EventMonitor.new(start_time = start_time) }
|
7
|
+
let(:jobid) { 'my_jobid' }
|
8
|
+
let(:hostname) { 'my_hostname' }
|
9
|
+
let(:task) { 'my_task' }
|
10
|
+
|
11
|
+
it 'can initialize event monitor' do
|
12
|
+
event_monitor = EventMonitor.new(start_time = start_time)
|
13
|
+
|
14
|
+
expect(event_monitor.start_time).to eq(start_time)
|
15
|
+
expect(event_monitor.latest_time).to eq(start_time)
|
16
|
+
expect(event_monitor.expired_tasks).to be_empty
|
17
|
+
expect(event_monitor.done?(1)).to be false
|
18
|
+
expect(event_monitor.done?(0)).to be true
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can start node' do
|
22
|
+
event_monitor.on_start(jobid)
|
23
|
+
event_monitor.on_node_start(hostname)
|
24
|
+
|
25
|
+
expect(event_monitor.done?(0)).to be false
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'can stop node' do
|
29
|
+
event_monitor.on_start(jobid)
|
30
|
+
event_monitor.on_node_start(hostname)
|
31
|
+
event_monitor.on_node_stop(hostname)
|
32
|
+
|
33
|
+
expect(event_monitor.done?(0)).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'can start task' do
|
37
|
+
event_monitor.on_start(jobid)
|
38
|
+
event_monitor.on_node_start(hostname)
|
39
|
+
event_monitor.on_task_start(hostname, task)
|
40
|
+
|
41
|
+
expect(event_monitor.done?(0)).to be false
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'can stop task' do
|
45
|
+
event_monitor.on_start(jobid)
|
46
|
+
event_monitor.on_node_start(hostname)
|
47
|
+
event_monitor.on_task_start(hostname, task)
|
48
|
+
event_monitor.on_task_stop(hostname, task)
|
49
|
+
|
50
|
+
expect(event_monitor.done?(0)).to be false
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'can expire task' do
|
54
|
+
event_monitor.on_start(jobid)
|
55
|
+
event_monitor.on_node_start(hostname)
|
56
|
+
event_monitor.on_task_start(hostname, task)
|
57
|
+
event_monitor.on_task_expire(hostname, task)
|
58
|
+
|
59
|
+
expect(event_monitor.done?(0)).to be false
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'can expire task and stop node' do
|
63
|
+
event_monitor.on_start(jobid)
|
64
|
+
event_monitor.on_node_start(hostname)
|
65
|
+
event_monitor.on_task_start(hostname, task)
|
66
|
+
event_monitor.on_task_expire(hostname, task)
|
67
|
+
event_monitor.on_node_stop(hostname)
|
68
|
+
|
69
|
+
expect(event_monitor.done?(0)).to be true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'can complete on happy path' do
|
73
|
+
event_monitor.on_start(jobid)
|
74
|
+
event_monitor.on_node_start(hostname)
|
75
|
+
event_monitor.on_task_start(hostname, task)
|
76
|
+
event_monitor.on_task_stop(hostname, task)
|
77
|
+
event_monitor.on_node_stop(hostname)
|
78
|
+
|
79
|
+
expect(event_monitor.done?(0)).to be true
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'can complete with expired task' do
|
83
|
+
event_monitor.on_start(jobid)
|
84
|
+
event_monitor.on_node_start(hostname)
|
85
|
+
event_monitor.on_task_start(hostname, task)
|
86
|
+
event_monitor.on_task_expire(hostname, task)
|
87
|
+
event_monitor.on_node_stop(hostname)
|
88
|
+
|
89
|
+
expect(event_monitor.done?(0)).to be true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path('../../spec_helper.rb', __FILE__)
|
2
|
+
|
3
|
+
module SimpleWorker
|
4
|
+
describe EventServer do
|
5
|
+
let(:redis) { double(Redis, :script => nil) }
|
6
|
+
let(:namespace) { 'my_namespace' }
|
7
|
+
let(:jobid) { 'my_jobid' }
|
8
|
+
let(:event_server) { EventServer.new(redis, namespace, jobid) }
|
9
|
+
let(:log_key) { "#{namespace}:log:#{jobid}" }
|
10
|
+
let(:active_tasks_key) { "#{namespace}:active:#{jobid}" }
|
11
|
+
let(:tasks_key) { "#{namespace}:tasks:#{jobid}" }
|
12
|
+
|
13
|
+
it 'can pull events' do
|
14
|
+
expect(redis).to receive(:multi).and_return([[["event"].to_json], [["#{namespace}:active:#{jobid}:my_hostname:my_task"],["#{namespace}:active:#{jobid}:my2_hostname:my2_task"]], 0])
|
15
|
+
|
16
|
+
expect(event_server).to receive(:fire).with("event")
|
17
|
+
expect(event_server).to receive(:fire).with('on_task_expire', 'my_hostname', 'my_task')
|
18
|
+
expect(event_server).to receive(:fire).with('on_task_active', 'my2_hostname', 'my2_task')
|
19
|
+
|
20
|
+
result = event_server.pull_events
|
21
|
+
expect(result).to eq(1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -2,32 +2,25 @@ require File.expand_path('../../spec_helper.rb', __FILE__)
|
|
2
2
|
|
3
3
|
module SimpleWorker
|
4
4
|
describe LocalWorker do
|
5
|
+
let(:mock_process) { double(ChildProcess, :io => double('io').as_null_object) }
|
5
6
|
let(:mock_env) { double(Hash) }
|
6
|
-
let(:
|
7
|
+
let(:local_worker) { LocalWorker.new }
|
8
|
+
let(:jobid) { 'my_jobid' }
|
7
9
|
|
8
|
-
it 'can start local worker
|
9
|
-
ChildProcess.should_receive(:build).
|
10
|
+
it 'can start local worker' do
|
11
|
+
ChildProcess.should_receive(:build).and_return(mock_process)
|
12
|
+
mock_process.should_receive(:environment).and_return(mock_env)
|
13
|
+
mock_env.should_receive(:[]=).with('JOBID', jobid)
|
10
14
|
mock_process.should_receive(:start)
|
11
|
-
mock_env.should_receive(:[]=).with('cmd', 'rake')
|
12
15
|
|
13
|
-
|
14
|
-
worker.cmd = 'rake'
|
15
|
-
worker.start
|
16
|
+
local_worker.on_start(jobid)
|
16
17
|
end
|
17
18
|
|
18
|
-
it 'can
|
19
|
-
ChildProcess.should_receive(:build).
|
20
|
-
mock_process.should_receive(:start)
|
21
|
-
mock_env.should_receive(:[]=).with('cmd', 'cuke')
|
22
|
-
mock_process.should_receive(:wait)
|
19
|
+
it 'can stop local worker' do
|
20
|
+
ChildProcess.should_receive(:build).and_return(mock_process)
|
23
21
|
mock_process.should_receive(:stop)
|
24
22
|
|
25
|
-
|
26
|
-
worker.cmd = 'cuke'
|
27
|
-
worker.start
|
28
|
-
worker.wait
|
29
|
-
worker.stop
|
23
|
+
local_worker.on_stop
|
30
24
|
end
|
31
25
|
end
|
32
26
|
end
|
33
|
-
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path('../../spec_helper.rb', __FILE__)
|
2
|
+
|
3
|
+
module SimpleWorker
|
4
|
+
describe LoggingListener do
|
5
|
+
let(:stdout) { StringIO.new }
|
6
|
+
let(:listener) { LoggingListener.new stdout }
|
7
|
+
|
8
|
+
it 'logs all events' do
|
9
|
+
Time.stub(:now => Time.now)
|
10
|
+
|
11
|
+
jobid = 'my_jobid'
|
12
|
+
host = 'my_host'
|
13
|
+
task = 'my_task'
|
14
|
+
|
15
|
+
listener.on_start jobid
|
16
|
+
listener.on_node_start host
|
17
|
+
listener.on_task_start host, task
|
18
|
+
listener.on_task_active host, task
|
19
|
+
listener.on_task_expire host, task
|
20
|
+
listener.on_task_stop host, task
|
21
|
+
listener.on_node_stop host
|
22
|
+
listener.on_timeout
|
23
|
+
listener.on_log host, 'message'
|
24
|
+
listener.on_interrupted
|
25
|
+
listener.on_stop
|
26
|
+
|
27
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S##{Process.pid}")
|
28
|
+
|
29
|
+
stdout.string.should == <<-OUTPUT
|
30
|
+
I, [#{timestamp}] INFO -- : start: my_jobid
|
31
|
+
I, [#{timestamp}] INFO -- : start node: my_host
|
32
|
+
I, [#{timestamp}] INFO -- : start host: my_host task: my_task
|
33
|
+
I, [#{timestamp}] INFO -- : active host: my_host task: my_task
|
34
|
+
I, [#{timestamp}] INFO -- : expire host: my_host task: my_task
|
35
|
+
I, [#{timestamp}] INFO -- : stop host: my_host task: my_task
|
36
|
+
I, [#{timestamp}] INFO -- : stop node: my_host
|
37
|
+
I, [#{timestamp}] INFO -- : timeout
|
38
|
+
I, [#{timestamp}] INFO -- : host: my_host message
|
39
|
+
I, [#{timestamp}] INFO -- : interrupted
|
40
|
+
I, [#{timestamp}] INFO -- : stop
|
41
|
+
OUTPUT
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path('../../spec_helper.rb', __FILE__)
|
2
|
+
|
3
|
+
module SimpleWorker
|
4
|
+
describe RetryListener do
|
5
|
+
let(:redis) { double(Redis) }
|
6
|
+
let(:namespace) { 'my_namespace' }
|
7
|
+
let(:jobid) { 'my_jobid' }
|
8
|
+
let(:hostname) { 'my_hostname' }
|
9
|
+
let(:jobid) { 'my_jobid' }
|
10
|
+
|
11
|
+
it 'does not retry if max retries is 0' do
|
12
|
+
listener = RetryListener.new(redis, 0, namespace, jobid)
|
13
|
+
redis.should_not_receive(:rpush)
|
14
|
+
|
15
|
+
listener.on_task_expire(hostname, jobid)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'does retry once if max retries is 1' do
|
19
|
+
listener = RetryListener.new(redis, 1, namespace, jobid)
|
20
|
+
redis.should_receive(:rpush).once
|
21
|
+
|
22
|
+
2.times { listener.on_task_expire(hostname, jobid) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,18 +2,80 @@ require File.expand_path('../../spec_helper.rb', __FILE__)
|
|
2
2
|
|
3
3
|
module SimpleWorker
|
4
4
|
describe Runner do
|
5
|
-
let(:
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
let(:redis) { double(Redis, :script => nil) }
|
6
|
+
let(:tasks) { ['ask1', 'task2'] }
|
7
|
+
let(:hostname) { 'my_hostname' }
|
8
|
+
let(:jobid) { 'my_jobid' }
|
9
|
+
let(:runner) { Runner.new(redis, tasks, :timeout => 120, :interval => 0) }
|
10
|
+
let(:mock_event_monitor) { double(EventMonitor, :update => nil) }
|
11
|
+
let(:mock_event_server) { double(EventServer, :add_observer => nil) }
|
12
|
+
let(:mock_retry_listener) { double(RetryListener, :update => nil, :tasks => [] ) }
|
13
|
+
let(:listener) { double(AbstractListener, :update => nil) }
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
::SecureRandom.should_receive(:hex).with(6).and_return(jobid)
|
17
|
+
|
18
|
+
EventServer.should_receive(:new).and_return(mock_event_server)
|
19
|
+
EventMonitor.should_receive(:new).and_return(mock_event_monitor)
|
20
|
+
RetryListener.should_receive(:new).and_return(mock_retry_listener)
|
21
|
+
redis.should_receive(:rpush).with("simpleworker:tasks:#{jobid}", tasks)
|
22
|
+
redis.should_receive(:set).with("simpleworker:config:my_jobid", {'task_timeout' => 10}.to_json)
|
23
|
+
|
24
|
+
redis.should_receive(:multi)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can run' do
|
28
|
+
mock_event_monitor.should_receive(:done?).and_return(false, true)
|
29
|
+
mock_event_server.should_receive(:pull_events).and_return(2, 0)
|
30
|
+
mock_event_monitor.should_receive(:latest_time).and_return(Time.now)
|
31
|
+
runner.add_observer listener
|
32
|
+
listener.should_receive(:update).with("on_start", jobid)
|
33
|
+
listener.should_receive(:update).with("on_stop")
|
34
|
+
|
35
|
+
runner.run
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can timeout' do
|
39
|
+
mock_event_monitor.should_receive(:done?).and_return(false)
|
40
|
+
mock_event_server.should_receive(:pull_events).and_return(2, 2)
|
41
|
+
mock_event_monitor.should_receive(:latest_time).and_return(Time.now)
|
42
|
+
|
43
|
+
runner = Runner.new(redis, tasks, :timeout => 0, :interval => 0)
|
44
|
+
runner.add_observer listener
|
45
|
+
listener.should_receive(:update).with("on_timeout")
|
46
|
+
|
47
|
+
runner.run
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'fires on_interrupted and shuts down if interrupted' do
|
51
|
+
runner.add_observer listener
|
52
|
+
|
53
|
+
mock_event_server.should_receive(:pull_events).and_raise(Interrupt)
|
54
|
+
listener.should_receive(:update).with("on_interrupted")
|
55
|
+
|
56
|
+
runner.run
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'fires on_interrupted and shuts down if an error occurs' do
|
60
|
+
runner.add_observer listener
|
61
|
+
|
62
|
+
mock_event_server.should_receive(:pull_events).and_raise(StandardError)
|
63
|
+
listener.should_receive(:update).with("on_interrupted")
|
64
|
+
|
65
|
+
lambda { runner.run }.should raise_error(StandardError)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'continue to run if retries exist' do
|
69
|
+
mock_event_monitor.should_receive(:done?).twice.with(2).and_return(false)
|
70
|
+
mock_event_monitor.should_receive(:done?).with(0).and_return(true)
|
71
|
+
mock_event_server.should_receive(:pull_events).and_return(2, 2, 0)
|
72
|
+
|
73
|
+
mock_event_monitor.should_receive(:latest_time).twice.and_return(Time.now)
|
74
|
+
runner.add_observer listener
|
75
|
+
listener.should_receive(:update).with("on_start", jobid)
|
76
|
+
listener.should_receive(:update).with("on_stop")
|
77
|
+
|
78
|
+
runner.run
|
17
79
|
end
|
18
80
|
end
|
19
81
|
end
|
@@ -2,42 +2,31 @@ require File.expand_path('../../spec_helper.rb', __FILE__)
|
|
2
2
|
|
3
3
|
module SimpleWorker
|
4
4
|
describe SshWorker do
|
5
|
-
let(:
|
6
|
-
let(:
|
5
|
+
let(:mock_process) { double(ChildProcess, :io => double('io').as_null_object) }
|
6
|
+
let(:jobid) { 'my_jobid' }
|
7
|
+
let(:ssh_worker) { SshWorker.new(
|
8
|
+
:user => 'jesg',
|
9
|
+
:host => 'localhost',
|
10
|
+
:port => '22',
|
11
|
+
:cmd => 'my_cmd',
|
12
|
+
:dirname => 'work_dir')
|
13
|
+
}
|
7
14
|
|
8
|
-
it 'can start ssh worker
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
mock_env.should_receive(:[]=).once.with('directory', Dir.pwd)
|
15
|
+
it 'can start ssh worker' do
|
16
|
+
expected_sync_cmd ='rsync -a --delete -e "ssh -p 22" "${PWD}/" "jesg@localhost:~/work_dir"'
|
17
|
+
expected_async_cmd = %q[ssh -p 22 "jesg@localhost" "/bin/bash -lc 'cd ~/work_dir; export JOBID=my_jobid; my_cmd' </dev/null >/dev/null 2>&1 &"]
|
18
|
+
Kernel.stub('`')
|
19
|
+
ssh_worker.should_receive(:sync_to_remote)
|
20
|
+
ssh_worker.should_receive(:async_cmd)
|
15
21
|
|
16
|
-
|
17
|
-
worker.cmd = 'rake'
|
18
|
-
worker.start
|
22
|
+
ssh_worker.on_start(jobid)
|
19
23
|
end
|
20
24
|
|
21
|
-
it 'can
|
22
|
-
|
23
|
-
|
24
|
-
mock_env.should_receive(:[]=).once.with('cmd', 'cuke')
|
25
|
-
mock_env.should_receive(:[]=).once.with('user', 'bob')
|
26
|
-
mock_env.should_receive(:[]=).once.with('host', 'foo')
|
27
|
-
mock_env.should_receive(:[]=).once.with('directory', 'bar')
|
25
|
+
it 'can stop ssh worker' do
|
26
|
+
Kernel.stub('`')
|
27
|
+
ssh_worker.should_receive(:sync_from_remote)
|
28
28
|
|
29
|
-
|
30
|
-
mock_process.should_receive(:stop)
|
31
|
-
|
32
|
-
worker = SshWorker.new
|
33
|
-
worker.cmd = 'cuke'
|
34
|
-
worker.user = 'bob'
|
35
|
-
worker.host = 'foo'
|
36
|
-
worker.directory = 'bar'
|
37
|
-
worker.start
|
38
|
-
worker.wait
|
39
|
-
worker.stop
|
29
|
+
ssh_worker.on_stop
|
40
30
|
end
|
41
31
|
end
|
42
32
|
end
|
43
|
-
|