sidejob 3.0.1 → 4.0.1
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/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
|