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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/.travis.yml +6 -0
  4. data/LICENSE +1 -1
  5. data/README.md +52 -15
  6. data/examples/basic.rb +26 -0
  7. data/examples/cucumber_worker.rb +20 -0
  8. data/examples/local_worker.rb +8 -0
  9. data/examples/ssh_worker.rb +13 -0
  10. data/examples/worker.rb +12 -0
  11. data/lib/simpleworker/abstract_listener.rb +41 -0
  12. data/lib/simpleworker/event_monitor.rb +50 -0
  13. data/lib/simpleworker/event_server.rb +48 -0
  14. data/lib/simpleworker/local_worker.rb +17 -10
  15. data/lib/simpleworker/logging_listener.rb +65 -0
  16. data/lib/simpleworker/redis_support.rb +33 -0
  17. data/lib/simpleworker/retry_listener.rb +32 -0
  18. data/lib/simpleworker/runner.rb +85 -26
  19. data/lib/simpleworker/scripts/expired_tasks.lua +13 -0
  20. data/lib/simpleworker/scripts/lpopall.lua +9 -0
  21. data/lib/simpleworker/scripts/reliable_queue.lua +12 -0
  22. data/lib/simpleworker/ssh_worker.rb +40 -19
  23. data/lib/simpleworker/task_queue.rb +78 -0
  24. data/lib/simpleworker/version.rb +1 -1
  25. data/lib/simpleworker.rb +12 -1
  26. data/simpleworker.gemspec +1 -1
  27. data/spec/simpleworker/event_monitor_spec.rb +92 -0
  28. data/spec/simpleworker/event_server_spec.rb +24 -0
  29. data/spec/simpleworker/local_worker_spec.rb +11 -18
  30. data/spec/simpleworker/logging_listener_spec.rb +44 -0
  31. data/spec/simpleworker/retry_listener_spec.rb +25 -0
  32. data/spec/simpleworker/runner_spec.rb +74 -12
  33. data/spec/simpleworker/ssh_worker_spec.rb +20 -31
  34. data/spec/simpleworker/task_queue_spec.rb +76 -0
  35. metadata +42 -5
  36. data/lib/simpleworker/abstract_worker.rb +0 -42
  37. data/lib/simpleworker/bash/simple-localworker +0 -8
  38. 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
- attr_accessor :directory, :user, :host
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
- def initialize
9
- @script = %W[bash #{File.expand_path(File.dirname(__FILE__))}/bash/ssh-remoteworker]
10
- @env = {}
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
- def self.create(config = {})
14
- worker = new
15
- worker.script = config['script'] if config.has_key? 'script'
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 set_process_env
26
- env['user'] = user || `whoami`
27
- env['host'] = host || 'localhost'
28
- env['directory'] = directory || Dir.pwd
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
- super
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
@@ -1,4 +1,4 @@
1
1
 
2
2
  module SimpleWorker
3
- VERSION = '0.0.1'
3
+ VERSION = '0.1.0'
4
4
  end
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/abstract_worker'
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(:mock_process) { double(ChildProcess, :start => nil, :stop => nil, :wait => nil, :io => double('io').as_null_object, :environment => mock_env) }
7
+ let(:local_worker) { LocalWorker.new }
8
+ let(:jobid) { 'my_jobid' }
7
9
 
8
- it 'can start local worker with defaults' do
9
- ChildProcess.should_receive(:build).with('bash', end_with('simple-localworker')).and_return(mock_process)
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
- worker = LocalWorker.new
14
- worker.cmd = 'rake'
15
- worker.start
16
+ local_worker.on_start(jobid)
16
17
  end
17
18
 
18
- it 'can start local worker with customization' do
19
- ChildProcess.should_receive(:build).with('bash', end_with('simple-localworker')).and_return(mock_process)
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
- worker = LocalWorker.new
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(:mock_worker) { double(AbstractWorker, :start => nil, :wait => nil, :stop => nil) }
6
-
7
- it 'can run workers' do
8
- mock_worker.should_receive(:start)
9
- mock_worker.should_receive(:cmd=).with('cuke')
10
- mock_worker.should_receive(:wait)
11
- mock_worker.should_receive(:stop)
12
- IO.should_receive(:read).with('simpleworker.yml').and_return('hi')
13
- YAML.should_receive(:load).with('hi').and_return({'workers' => [{'type' => 'ssh'}]})
14
- SshWorker.should_receive(:create).and_return(mock_worker)
15
-
16
- Runner.run 'cuke'
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(:mock_env) { double(Hash) }
6
- let(:mock_process) { double(ChildProcess, :start => nil, :stop => nil, :wait => nil, :io => double('io').as_null_object, :environment => mock_env) }
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 with defaults' do
9
- ChildProcess.should_receive(:build).with('bash', end_with('ssh-remoteworker')).and_return(mock_process)
10
- mock_process.should_receive(:start)
11
- mock_env.should_receive(:[]=).once.with('cmd', 'rake')
12
- mock_env.should_receive(:[]=).once.with('user', `whoami`)
13
- mock_env.should_receive(:[]=).once.with('host', 'localhost')
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
- worker = SshWorker.new
17
- worker.cmd = 'rake'
18
- worker.start
22
+ ssh_worker.on_start(jobid)
19
23
  end
20
24
 
21
- it 'can start ssh worker with customization' do
22
- ChildProcess.should_receive(:build).with('bash', end_with('ssh-remoteworker')).and_return(mock_process)
23
- mock_process.should_receive(:start)
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
- mock_process.should_receive(:wait)
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
-