sidejob 3.0.1 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -5
- data/README.md +10 -14
- data/lib/sidejob.rb +29 -23
- data/lib/sidejob/job.rb +183 -213
- data/lib/sidejob/port.rb +112 -80
- data/lib/sidejob/server_middleware.rb +56 -50
- data/lib/sidejob/testing.rb +0 -2
- data/lib/sidejob/version.rb +1 -1
- data/lib/sidejob/worker.rb +28 -46
- data/spec/integration/fib_spec.rb +8 -4
- data/spec/integration/sum_spec.rb +0 -1
- data/spec/sidejob/job_spec.rb +323 -241
- data/spec/sidejob/port_spec.rb +152 -138
- data/spec/sidejob/server_middleware_spec.rb +27 -47
- data/spec/sidejob/worker_spec.rb +16 -84
- data/spec/sidejob_spec.rb +39 -16
- data/web/Gemfile +6 -0
- data/web/Gemfile.lock +43 -0
- data/web/app.rb +205 -0
- data/web/config.ru +14 -0
- metadata +6 -2
@@ -34,7 +34,6 @@ describe SideJob::ServerMiddleware do
|
|
34
34
|
worker = msg.klass.constantize.new
|
35
35
|
worker.jid = job.id
|
36
36
|
chain.invoke(worker, msg, @queue) { yield worker }
|
37
|
-
job.reload
|
38
37
|
worker
|
39
38
|
end
|
40
39
|
|
@@ -67,10 +66,7 @@ describe SideJob::ServerMiddleware do
|
|
67
66
|
@job.status = 'suspended'
|
68
67
|
child = SideJob.queue(@queue, 'TestWorker', parent: @job, name: 'child')
|
69
68
|
expect(@job.status).to eq 'suspended'
|
70
|
-
|
71
|
-
child.run_inline
|
72
|
-
}.to change {Sidekiq::Stats.new.enqueued}.by(1)
|
73
|
-
@job.reload
|
69
|
+
child.run_inline
|
74
70
|
expect(@job.status).to eq 'queued'
|
75
71
|
end
|
76
72
|
|
@@ -84,22 +80,27 @@ describe SideJob::ServerMiddleware do
|
|
84
80
|
end
|
85
81
|
|
86
82
|
describe 'prevents multiple threads running the same job' do
|
87
|
-
it '
|
88
|
-
|
89
|
-
|
90
|
-
process(@job) { @
|
91
|
-
expect(@
|
83
|
+
it 'does not run if the worker lock is set' do
|
84
|
+
SideJob.redis.set "#{@job.redis_key}:lock:worker", 1
|
85
|
+
@run = false
|
86
|
+
process(@job) { @run = true }
|
87
|
+
expect(@run).to be false
|
88
|
+
expect(SideJob.redis.exists("#{@job.redis_key}:lock:worker"))
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'obtains and releases a lock' do
|
92
|
+
process(@job) { @lock = SideJob.redis.get("#{@job.redis_key}:lock") }
|
93
|
+
expect(@lock).to_not be nil
|
92
94
|
expect(SideJob.redis.exists("#{@job.redis_key}:lock")).to be false
|
93
95
|
end
|
94
96
|
|
95
|
-
it '
|
96
|
-
|
97
|
-
allow(Time).to receive(:now) { now }
|
97
|
+
it 'does not run if the job is locked' do
|
98
|
+
token = @job.lock(100)
|
98
99
|
@run = false
|
99
|
-
SideJob.redis.set "#{@job.redis_key}:lock", (now-10).to_f
|
100
100
|
process(@job) { @run = true }
|
101
101
|
expect(@run).to be false
|
102
|
-
expect(SideJob.redis.
|
102
|
+
expect(SideJob.redis.exists("#{@job.redis_key}:lock"))
|
103
|
+
expect(@job.unlock(token)).to be true
|
103
104
|
end
|
104
105
|
|
105
106
|
it 'does not restart the worker unless another worker was locked out during the run' do
|
@@ -109,10 +110,11 @@ describe SideJob::ServerMiddleware do
|
|
109
110
|
expect(@job.status).to eq 'completed'
|
110
111
|
end
|
111
112
|
|
112
|
-
it '
|
113
|
+
it 'requeues the worker if it was locked out during the run' do
|
114
|
+
token = @job.lock(100)
|
113
115
|
expect {
|
114
|
-
process(@job) {
|
115
|
-
}.to change {Sidekiq::Stats.new.
|
116
|
+
process(@job) { }
|
117
|
+
}.to change {Sidekiq::Stats.new.scheduled_size}.by(1)
|
116
118
|
expect(@job.status).to eq 'queued'
|
117
119
|
end
|
118
120
|
end
|
@@ -122,7 +124,7 @@ describe SideJob::ServerMiddleware do
|
|
122
124
|
now = Time.now
|
123
125
|
allow(Time).to receive(:now) { now }
|
124
126
|
key = "#{@job.redis_key}:rate:#{Time.now.to_i/60}"
|
125
|
-
SideJob.redis.set key, SideJob::
|
127
|
+
SideJob.redis.set key, SideJob::CONFIGURATION[:max_runs_per_minute]
|
126
128
|
@run = false
|
127
129
|
process(@job) { @run = true }
|
128
130
|
expect(@run).to be false
|
@@ -133,27 +135,7 @@ describe SideJob::ServerMiddleware do
|
|
133
135
|
now = Time.now
|
134
136
|
allow(Time).to receive(:now) { now }
|
135
137
|
key = "#{@job.redis_key}:rate:#{Time.now.to_i/60}"
|
136
|
-
SideJob.redis.set key, SideJob::
|
137
|
-
@run = false
|
138
|
-
process(@job) { @run = true }
|
139
|
-
expect(@run).to be true
|
140
|
-
expect(@job.status).to eq 'completed'
|
141
|
-
end
|
142
|
-
|
143
|
-
it 'does not run if job is too deep' do
|
144
|
-
(SideJob::ServerMiddleware::CONFIGURATION[:max_depth]+1).times do |i|
|
145
|
-
@job = SideJob.queue(@queue, 'TestWorker', parent: @job, name: 'child')
|
146
|
-
end
|
147
|
-
@run = false
|
148
|
-
process(@job) { @run = true }
|
149
|
-
expect(@run).to be false
|
150
|
-
expect(@job.status).to eq 'terminating'
|
151
|
-
end
|
152
|
-
|
153
|
-
it 'does run if job is not too deep' do
|
154
|
-
SideJob::ServerMiddleware::CONFIGURATION[:max_depth].times do |i|
|
155
|
-
@job = SideJob.queue(@queue, 'TestWorker', parent: @job, name: 'child')
|
156
|
-
end
|
138
|
+
SideJob.redis.set key, SideJob::CONFIGURATION[:max_runs_per_minute]-1
|
157
139
|
@run = false
|
158
140
|
process(@job) { @run = true }
|
159
141
|
expect(@run).to be true
|
@@ -176,21 +158,19 @@ describe SideJob::ServerMiddleware do
|
|
176
158
|
expect(log[0]['backtrace']).to_not match(/sidekiq/)
|
177
159
|
end
|
178
160
|
|
179
|
-
it 'does not set status to failed if status is
|
161
|
+
it 'does not set status to failed if status is terminating' do
|
180
162
|
process(@job) do |worker|
|
181
|
-
worker.
|
163
|
+
worker.terminate
|
182
164
|
raise 'oops'
|
183
165
|
end
|
184
|
-
expect(@job.status).to eq '
|
166
|
+
expect(@job.status).to eq 'terminating'
|
185
167
|
end
|
186
168
|
|
187
169
|
it 'runs the parent job' do
|
188
170
|
@job.status = 'suspended'
|
189
171
|
child = SideJob.queue(@queue, 'TestWorker', parent: @job, name: 'child')
|
190
|
-
expect
|
191
|
-
|
192
|
-
}.to change {Sidekiq::Stats.new.enqueued}.by(1)
|
193
|
-
@job.reload
|
172
|
+
expect(@job.status).to eq 'suspended'
|
173
|
+
process(child) { raise 'oops' }
|
194
174
|
expect(@job.status).to eq 'queued'
|
195
175
|
end
|
196
176
|
end
|
data/spec/sidejob/worker_spec.rb
CHANGED
@@ -5,7 +5,6 @@ describe SideJob::Worker do
|
|
5
5
|
@job = SideJob.queue('testq', 'TestWorker', inports: {
|
6
6
|
in1: {},
|
7
7
|
in2: {},
|
8
|
-
memory: { mode: :memory },
|
9
8
|
default: { default: 'default' },
|
10
9
|
default_null: { default: nil },
|
11
10
|
}, outports: {out1: {}})
|
@@ -62,33 +61,6 @@ describe SideJob::Worker do
|
|
62
61
|
expect { @worker.suspend }.to raise_error(SideJob::Worker::Suspended)
|
63
62
|
end
|
64
63
|
|
65
|
-
describe '#queue' do
|
66
|
-
it 'can queue child jobs' do
|
67
|
-
expect(SideJob).to receive(:queue).with('testq', 'TestWorker', args: [1,2], inports: {'myport' => {'mode' => 'memory'}}, parent: @job, name: 'child', by: "job:#{@worker.id}").and_call_original
|
68
|
-
expect {
|
69
|
-
child = @worker.queue('testq', 'TestWorker', args: [1,2], inports: {'myport' => {'mode' => 'memory'}}, name: 'child')
|
70
|
-
expect(child.parent).to eq(@job)
|
71
|
-
expect(@job.children).to eq('child' => child)
|
72
|
-
}.to change {Sidekiq::Stats.new.enqueued}.by(1)
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'queues with by string set to self' do
|
76
|
-
child = @worker.queue('testq', 'TestWorker', name: 'child')
|
77
|
-
expect(child.get(:created_by)).to eq "job:#{@worker.id}"
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'groups initial port data' do
|
81
|
-
now = Time.now
|
82
|
-
allow(Time).to receive(:now) { now }
|
83
|
-
child = @worker.queue('testq', 'TestWorker', name: 'child', inports: {'inport1' => {data: [1,2]}}, outports: {'outport1' => {data: [3,4]}})
|
84
|
-
expect(SideJob.logs).to eq [{'timestamp' => SideJob.timestamp, 'job' => @worker.id,
|
85
|
-
'read' => [],
|
86
|
-
'write' => [{'job' => child.id, 'inport' => 'inport1', 'data' => [1,2]},
|
87
|
-
{'job' => child.id, 'outport' => 'outport1', 'data' => [3,4]},
|
88
|
-
]}]
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
64
|
describe '#for_inputs' do
|
93
65
|
it 'does nothing if no ports provided' do
|
94
66
|
expect {|block| @worker.for_inputs(&block)}.not_to yield_control
|
@@ -113,8 +85,8 @@ describe SideJob::Worker do
|
|
113
85
|
@worker.for_inputs(:in1, :in2) do |in1, in2|
|
114
86
|
@worker.output(:out1).write [in1, in2[0]]
|
115
87
|
end
|
116
|
-
expect(SideJob.logs).to eq([{'timestamp' => SideJob.timestamp, '
|
117
|
-
{'timestamp' => SideJob.timestamp, '
|
88
|
+
expect(SideJob.logs).to eq([{'timestamp' => SideJob.timestamp, 'read' => [{'job' => @job.id, 'inport' => 'in1', 'data' => [1]}, {'job' => @job.id, 'inport' => 'in2', 'data' => [['a', 'b']]}], 'write' => [{'job' => @job.id, 'outport' => 'out1', 'data' => [[1, 'a']]}]},
|
89
|
+
{'timestamp' => SideJob.timestamp, 'read' => [{'job' => @job.id, 'inport' => 'in1', 'data' => [2]}, {'job' => @job.id, 'inport' => 'in2', 'data' => [['c', 'd']]}], 'write' => [{'job' => @job.id, 'outport' => 'out1', 'data' => [[2, 'c']]}]},
|
118
90
|
])
|
119
91
|
end
|
120
92
|
|
@@ -127,16 +99,14 @@ describe SideJob::Worker do
|
|
127
99
|
}.to raise_error(SideJob::Worker::Suspended)
|
128
100
|
end
|
129
101
|
|
130
|
-
it 'returns
|
131
|
-
@job.input(:memory).write 1
|
102
|
+
it 'returns default values from ports' do
|
132
103
|
@job.input(:in2).write [2, 3]
|
133
104
|
@job.input(:in2).write 3
|
134
|
-
expect {|block| @worker.for_inputs(:
|
105
|
+
expect {|block| @worker.for_inputs(:default, :in2, &block)}.to yield_successive_args(['default', [2,3]], ['default', 3])
|
135
106
|
end
|
136
107
|
|
137
|
-
it 'does not
|
138
|
-
@
|
139
|
-
expect {|block| @worker.for_inputs(:memory, :in2, &block)}.not_to yield_control
|
108
|
+
it 'does not yield if there are only default values' do
|
109
|
+
expect {|block| @worker.for_inputs(:default, :in2, &block)}.not_to yield_control
|
140
110
|
end
|
141
111
|
|
142
112
|
it 'allows for null default values' do
|
@@ -146,56 +116,18 @@ describe SideJob::Worker do
|
|
146
116
|
expect {|block| @worker.for_inputs(:default_null, :in2, &block)}.to yield_successive_args([1, [2,3]], [nil, 3])
|
147
117
|
end
|
148
118
|
|
149
|
-
it '
|
150
|
-
@job.
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
|
155
|
-
describe '#set' do
|
156
|
-
it 'can save state in redis' do
|
157
|
-
@worker.set(test: 'data', test2: 123)
|
158
|
-
state = JSON.parse(SideJob.redis.hget('jobs', @worker.id))
|
159
|
-
expect(state['test']).to eq 'data'
|
160
|
-
expect(state['test2']).to eq 123
|
161
|
-
|
162
|
-
# test updating
|
163
|
-
@worker.set(test: 'data2')
|
164
|
-
state = JSON.parse(SideJob.redis.hget('jobs', @worker.id))
|
165
|
-
expect(state['test']).to eq 'data2'
|
166
|
-
end
|
167
|
-
|
168
|
-
it 'can update values' do
|
169
|
-
3.times do |i|
|
170
|
-
@worker.set key: i
|
171
|
-
@worker.reload
|
172
|
-
expect(@worker.get(:key)).to eq i
|
173
|
-
state = JSON.parse(SideJob.redis.hget('jobs', @worker.id))
|
174
|
-
expect(state['key']).to eq i
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'raises error if job no longer exists' do
|
179
|
-
@worker.status = 'terminated'
|
180
|
-
SideJob.find(@worker.id).delete
|
181
|
-
expect { @worker.set key: 123 }.to raise_error
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
describe '#unset' do
|
186
|
-
it 'unsets fields' do
|
187
|
-
@worker.set(a: 123, b: 456, c: 789)
|
188
|
-
@worker.unset('a', :b)
|
189
|
-
expect(@worker.get(:a)).to eq nil
|
190
|
-
expect(@worker.get(:b)).to eq nil
|
191
|
-
expect(@worker.get(:c)).to eq 789
|
119
|
+
it 'sets output default value for ports written in block if all inputs are default' do
|
120
|
+
expect(@job.output(:out1).default).to be SideJob::Port::None
|
121
|
+
@worker.for_inputs(:default, :default_null) { @job.output(:out1).write 1234 }
|
122
|
+
expect(@job.output(:out1).default).to eq 1234
|
192
123
|
end
|
193
124
|
|
194
|
-
it '
|
195
|
-
@worker.
|
196
|
-
@worker.
|
197
|
-
|
198
|
-
expect { @worker.
|
125
|
+
it 'yields exactly once until port defaults change' do
|
126
|
+
expect {|block| @worker.for_inputs(:default, :default_null, &block)}.to yield_successive_args(['default', nil])
|
127
|
+
expect {|block| @worker.for_inputs(:default, :default_null, &block)}.not_to yield_control
|
128
|
+
@job.input(:default).default = 'new'
|
129
|
+
expect {|block| @worker.for_inputs(:default, :default_null, &block)}.to yield_successive_args(['new', nil])
|
130
|
+
expect {|block| @worker.for_inputs(:default, :default_null, &block)}.not_to yield_control
|
199
131
|
end
|
200
132
|
end
|
201
133
|
end
|
data/spec/sidejob_spec.rb
CHANGED
@@ -45,9 +45,9 @@ describe SideJob do
|
|
45
45
|
|
46
46
|
it 'generates an incrementing job id from 1' do
|
47
47
|
job = SideJob.queue('testq', 'TestWorker')
|
48
|
-
expect(job.id).to
|
48
|
+
expect(job.id).to be 1
|
49
49
|
job = SideJob.queue('testq', 'TestWorker')
|
50
|
-
expect(job.id).to
|
50
|
+
expect(job.id).to be 2
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'stores created at timestamp' do
|
@@ -72,7 +72,6 @@ describe SideJob do
|
|
72
72
|
expect(job.status).to eq 'queued'
|
73
73
|
expect(job.parent).to eq(parent)
|
74
74
|
expect(parent.child('child1')).to eq job
|
75
|
-
expect(SideJob.redis.lrange("#{job.redis_key}:ancestors", 0, -1)).to eq([parent.id])
|
76
75
|
}.to change {Sidekiq::Stats.new.enqueued}.by(2)
|
77
76
|
end
|
78
77
|
|
@@ -87,25 +86,12 @@ describe SideJob do
|
|
87
86
|
expect { SideJob.queue('testq', 'TestWorker', parent: parent, name: 'child') }.to raise_error
|
88
87
|
end
|
89
88
|
|
90
|
-
it 'sets ancestor tree correctly' do
|
91
|
-
j1 = SideJob.queue('testq', 'TestWorker')
|
92
|
-
j2 = SideJob.queue('testq', 'TestWorker', parent: j1, name: 'child1')
|
93
|
-
j3 = SideJob.queue('testq', 'TestWorker', parent: j2, name: 'child1')
|
94
|
-
expect(SideJob.redis.lrange("#{j3.redis_key}:ancestors", 0, -1)).to eq([j2.id, j1.id])
|
95
|
-
end
|
96
|
-
|
97
89
|
it 'can add a port via inports configuration' do
|
98
90
|
job = SideJob.queue('testq', 'TestWorker', inports: {myport: {default: [1,2]}})
|
99
91
|
expect(job.status).to eq 'queued'
|
100
92
|
expect(job.input(:myport).read).to eq [1, 2]
|
101
93
|
end
|
102
94
|
|
103
|
-
it 'can set the port mode via inports configuration' do
|
104
|
-
job = SideJob.queue('testq', 'TestWorker', inports: {queue: {mode: 'queue'}})
|
105
|
-
expect(job.status).to eq 'queued'
|
106
|
-
expect(job.input(:queue).mode).to be :queue
|
107
|
-
end
|
108
|
-
|
109
95
|
it 'can add a port via outports configuration' do
|
110
96
|
job = SideJob.queue('testq', 'TestWorker', outports: {myport: {}})
|
111
97
|
expect(job.status).to eq 'queued'
|
@@ -179,4 +165,41 @@ describe SideJob do
|
|
179
165
|
{'xyz' => 456, 'timestamp' => SideJob.timestamp},])
|
180
166
|
end
|
181
167
|
end
|
168
|
+
|
169
|
+
describe '.log_context' do
|
170
|
+
before do
|
171
|
+
now = Time.now
|
172
|
+
allow(Time).to receive(:now) { now }
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'adds metadata to logs within the group' do
|
176
|
+
SideJob.log_context(data1: 1, data2: 2) do
|
177
|
+
SideJob.log({abc: 123})
|
178
|
+
expect(SideJob.logs).to eq([{'data1' => 1, 'data2' => 2, 'abc' => 123, 'timestamp' => SideJob.timestamp}])
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'does not add metadata to logs outside of the group' do
|
183
|
+
SideJob.log_context(data1: 1, data2: 2) {}
|
184
|
+
SideJob.log({abc: 123})
|
185
|
+
expect(SideJob.logs).to eq([{'abc' => 123, 'timestamp' => SideJob.timestamp}])
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'can be nested' do
|
189
|
+
SideJob.log_context(data1: 1) do
|
190
|
+
SideJob.log({x: 1})
|
191
|
+
SideJob.log_context(data2: 2) do
|
192
|
+
SideJob.log({x: 2})
|
193
|
+
end
|
194
|
+
SideJob.log({x: 3})
|
195
|
+
end
|
196
|
+
SideJob.log({x: 4})
|
197
|
+
|
198
|
+
expect(SideJob.logs).to eq([{'data1' => 1, 'timestamp' => SideJob.timestamp, 'x' => 1},
|
199
|
+
{'data1' => 1, 'data2' => 2, 'timestamp' => SideJob.timestamp, 'x' => 2},
|
200
|
+
{'data1' => 1, 'timestamp' => SideJob.timestamp, 'x' => 3},
|
201
|
+
{'timestamp' => SideJob.timestamp, 'x' => 4},
|
202
|
+
])
|
203
|
+
end
|
204
|
+
end
|
182
205
|
end
|
data/web/Gemfile
ADDED
data/web/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
sidejob (4.0.1)
|
5
|
+
sidekiq (~> 3.2.5)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
celluloid (0.15.2)
|
11
|
+
timers (~> 1.1.0)
|
12
|
+
connection_pool (2.2.0)
|
13
|
+
json (1.8.2)
|
14
|
+
puma (2.10.2)
|
15
|
+
rack (>= 1.1, < 2.0)
|
16
|
+
rack (1.6.0)
|
17
|
+
rack-cors (0.2.9)
|
18
|
+
rack-protection (1.5.3)
|
19
|
+
rack
|
20
|
+
redis (3.2.1)
|
21
|
+
redis-namespace (1.5.2)
|
22
|
+
redis (~> 3.0, >= 3.0.4)
|
23
|
+
sidekiq (3.2.6)
|
24
|
+
celluloid (= 0.15.2)
|
25
|
+
connection_pool (>= 2.0.0)
|
26
|
+
json
|
27
|
+
redis (>= 3.0.6)
|
28
|
+
redis-namespace (>= 1.3.1)
|
29
|
+
sinatra (1.4.5)
|
30
|
+
rack (~> 1.4)
|
31
|
+
rack-protection (~> 1.4)
|
32
|
+
tilt (~> 1.3, >= 1.3.4)
|
33
|
+
tilt (1.4.1)
|
34
|
+
timers (1.1.0)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
puma
|
41
|
+
rack-cors
|
42
|
+
sidejob!
|
43
|
+
sinatra
|
data/web/app.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'sidejob'
|
3
|
+
|
4
|
+
# Provide a limited web API to SideJob methods
|
5
|
+
class SideJob::Web < Sinatra::Base
|
6
|
+
before do
|
7
|
+
content_type :json
|
8
|
+
end
|
9
|
+
|
10
|
+
# for CORS
|
11
|
+
options '/' do
|
12
|
+
200
|
13
|
+
end
|
14
|
+
|
15
|
+
# provide some limited info for now
|
16
|
+
get '/' do
|
17
|
+
{ version: '1.0' }.to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
# queue a new job
|
21
|
+
post '/jobs' do
|
22
|
+
api_call do |params|
|
23
|
+
queue = params['queue'] or return { error: 'Missing queue' }
|
24
|
+
klass = params['class'] or return { error: 'Missing class' }
|
25
|
+
|
26
|
+
job = SideJob.queue(queue, klass, args: params['args'],
|
27
|
+
parent: SideJob.find(params['parent']), name: params['name'],
|
28
|
+
by: params['by'], inports: params['inports'], outports: params['outports'])
|
29
|
+
{ job: job.id }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# gets job state and port info
|
34
|
+
get '/jobs/:job' do
|
35
|
+
job_api do |job, params|
|
36
|
+
{ job: job.id, state: job.state, inports: ports_info(job.inports), outports: ports_info(job.outports) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# sets job state
|
41
|
+
post '/jobs/:job/state' do
|
42
|
+
job_api do |job, params|
|
43
|
+
job.set(params)
|
44
|
+
{ job: job.id, state: job.state }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# delete a job
|
49
|
+
delete '/jobs/:job' do
|
50
|
+
job_api do |job, params|
|
51
|
+
{ job: job.id, delete: job.delete }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# set job ports
|
56
|
+
post '/jobs/:job/ports' do
|
57
|
+
job_api do |job, params|
|
58
|
+
job.inports = params['inports'] if params['inports']
|
59
|
+
job.outports = params['outports'] if params['outports']
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# port operations
|
65
|
+
post '/jobs/:job/*ports/:port/:operation' do |job_id, type, port_name, operation|
|
66
|
+
job_api do |job, params|
|
67
|
+
case type
|
68
|
+
when 'in'
|
69
|
+
port = job.input(port_name)
|
70
|
+
when 'out'
|
71
|
+
port = job.output(port_name)
|
72
|
+
else
|
73
|
+
raise 'Invalid port type'
|
74
|
+
end
|
75
|
+
|
76
|
+
case operation
|
77
|
+
# read one value
|
78
|
+
when 'read'
|
79
|
+
data = port.read
|
80
|
+
if data == SideJob::Port::None
|
81
|
+
{}
|
82
|
+
else
|
83
|
+
{ data: data }
|
84
|
+
end
|
85
|
+
|
86
|
+
# read all and return an array of data
|
87
|
+
when 'entries'
|
88
|
+
{ entries: port.entries }
|
89
|
+
|
90
|
+
# port write
|
91
|
+
when 'write'
|
92
|
+
if params['list']
|
93
|
+
list = params['list']
|
94
|
+
else
|
95
|
+
list = [params['data']]
|
96
|
+
end
|
97
|
+
list.each {|x| port.write(x)}
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# run a job
|
104
|
+
post '/jobs/:job/run' do
|
105
|
+
job_api do |job, params|
|
106
|
+
job.run(force: params['force'], at: params['at'], wait: params['wait'])
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# terminate a job
|
112
|
+
post '/jobs/:job/terminate' do
|
113
|
+
job_api do |job, params|
|
114
|
+
job.terminate(recursive: params['recursive'])
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# lock job
|
120
|
+
post '/jobs/:job/lock' do
|
121
|
+
job_api do |job, params|
|
122
|
+
{ token: job.lock(params['ttl']) }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# refresh lock
|
127
|
+
post '/jobs/:job/refresh_lock' do
|
128
|
+
job_api do |job, params|
|
129
|
+
{ refresh: job.refresh_lock(params['ttl']) }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# unlock job
|
134
|
+
post '/jobs/:job/unlock' do
|
135
|
+
job_api do |job, params|
|
136
|
+
{ unlock: job.unlock(params['token']) }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# adopt another job
|
141
|
+
post '/jobs/:job/adopt' do
|
142
|
+
job_api do |job, params|
|
143
|
+
orphan = SideJob.find(params[:child])
|
144
|
+
raise 'Child job does not exist' unless orphan
|
145
|
+
job.adopt(orphan, params['name'])
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# disown a job
|
151
|
+
post '/jobs/:job/disown' do
|
152
|
+
job_api do |job, params|
|
153
|
+
job.disown(params['name'])
|
154
|
+
nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# return all logs
|
159
|
+
get '/logs' do
|
160
|
+
api_call do |params|
|
161
|
+
SideJob.logs(clear: params['clear'] || false)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# add a log entry
|
166
|
+
post '/logs' do
|
167
|
+
api_call do |params|
|
168
|
+
SideJob.log(params['entry'])
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def job_api(&block)
|
175
|
+
job = SideJob.find(params['job'])
|
176
|
+
halt 422, { error: "Job #{params['job']} does not exist" }.to_json unless job
|
177
|
+
api_call do |params|
|
178
|
+
result = yield(job, params)
|
179
|
+
job.run(parent: params['run']['parent'], force: params['run']['force']) if params['run']
|
180
|
+
result
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def api_call(&block)
|
185
|
+
begin
|
186
|
+
request.body.rewind
|
187
|
+
params = JSON.parse(request.body.read) rescue {}
|
188
|
+
SideJob.log_context(params['log_context'] || {}) do
|
189
|
+
(yield params).to_json
|
190
|
+
end
|
191
|
+
rescue => e
|
192
|
+
puts e.inspect
|
193
|
+
puts e.backtrace
|
194
|
+
halt 422, { error: e.to_s }.to_json
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# @param ports [Array<SideJob::Port>]
|
199
|
+
def ports_info(ports)
|
200
|
+
ports.each_with_object({}) do |port, hash|
|
201
|
+
hash[port.name] = {size: port.size}
|
202
|
+
hash[port.name]['default'] = port.default if port.default?
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|