rbbt-util 5.26.157 → 5.26.158
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/lib/rbbt/util/misc/development.rb +7 -0
- data/lib/rbbt/workflow/remote_workflow/driver/rest.rb +230 -0
- data/lib/rbbt/workflow/remote_workflow/driver/ssh.rb +237 -0
- data/lib/rbbt/workflow/remote_workflow/driver.rb +75 -0
- data/lib/rbbt/workflow/remote_workflow/remote_step/rest.rb +148 -0
- data/lib/rbbt/workflow/remote_workflow/remote_step/ssh.rb +73 -0
- data/lib/rbbt/workflow/remote_workflow/remote_step.rb +329 -0
- data/lib/rbbt/workflow/{remote/client.rb → remote_workflow.rb} +9 -16
- data/lib/rbbt/workflow/util/archive.rb +2 -3
- data/lib/rbbt/workflow.rb +4 -4
- data/share/rbbt_commands/workflow/remote/add +28 -2
- data/share/rbbt_commands/workflow/task +4 -3
- data/test/rbbt/workflow/test_remote_workflow.rb +86 -0
- data/test/test_helper.rb +26 -23
- metadata +11 -14
- data/lib/rbbt/rest/client/adaptor.rb +0 -67
- data/lib/rbbt/rest/client/get.rb +0 -166
- data/lib/rbbt/rest/client/run.rb +0 -133
- data/lib/rbbt/rest/client/step.rb +0 -397
- data/lib/rbbt/rest/client.rb +0 -58
- data/lib/rbbt/workflow/remote/remote_step.rb +0 -324
- data/lib/rbbt/workflow/remote/rest/adaptor.rb +0 -161
- data/lib/rbbt/workflow/remote/rest/get.rb +0 -310
- data/lib/rbbt/workflow/remote/ssh/adaptor.rb +0 -61
- data/lib/rbbt/workflow/remote/ssh/driver.rb +0 -147
- data/lib/rbbt/workflow/remote/ssh/get.rb +0 -193
@@ -1,324 +0,0 @@
|
|
1
|
-
class WorkflowRemoteClient
|
2
|
-
class RemoteStep < Step
|
3
|
-
|
4
|
-
attr_accessor :url, :base_url, :task, :base_name, :inputs, :result_type, :result_description, :is_exec, :is_stream, :stream_input
|
5
|
-
|
6
|
-
def initialize(base_url, task = nil, base_name = nil, inputs = nil, result_type = nil, result_description = nil, is_exec = false, is_stream = false, stream_input = nil)
|
7
|
-
@base_url, @task, @base_name, @inputs, @result_type, @result_description, @is_exec, @is_stream, @stream_input = base_url, task, base_name, inputs, result_type, result_description, is_exec, is_stream, stream_input
|
8
|
-
@base_url = "http://" << @base_url unless @base_url =~ /^[a-z]+:\/\//
|
9
|
-
@mutex = Mutex.new
|
10
|
-
rest = base_url.include?('ssh:') ? false : true
|
11
|
-
|
12
|
-
if rest
|
13
|
-
@adaptor = WorkflowRESTClient
|
14
|
-
else
|
15
|
-
@adaptor = WorkflowSSHClient
|
16
|
-
end
|
17
|
-
|
18
|
-
self.extend @adaptor
|
19
|
-
end
|
20
|
-
|
21
|
-
def clean_name
|
22
|
-
@base_name
|
23
|
-
end
|
24
|
-
|
25
|
-
def cache_file
|
26
|
-
digest = Misc.obj2digest([base_url, task, base_name, inputs])
|
27
|
-
Rbbt.var.cache.REST[[task, clean_name, digest] * "."].find
|
28
|
-
end
|
29
|
-
|
30
|
-
def cache_files
|
31
|
-
Dir.glob(cache_file + '.*')
|
32
|
-
end
|
33
|
-
|
34
|
-
def run(no_load = false)
|
35
|
-
no_load = @is_stream ? :stream : true if no_load
|
36
|
-
|
37
|
-
@result ||= @mutex.synchronize do
|
38
|
-
begin
|
39
|
-
if @is_exec
|
40
|
-
exec(no_load)
|
41
|
-
elsif no_load == :stream
|
42
|
-
_run_job(:stream)
|
43
|
-
elsif no_load
|
44
|
-
init_job
|
45
|
-
nil
|
46
|
-
else
|
47
|
-
if ! done?
|
48
|
-
init_job
|
49
|
-
join
|
50
|
-
end
|
51
|
-
self.load
|
52
|
-
end
|
53
|
-
ensure
|
54
|
-
@started = true
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
return @result if no_load == :stream
|
59
|
-
no_load ? Misc.add_GET_param(path, "_format", "raw") : @result
|
60
|
-
end
|
61
|
-
|
62
|
-
|
63
|
-
def self.get_streams(inputs, stream_input = nil)
|
64
|
-
new_inputs = {}
|
65
|
-
inputs.each do |k,v|
|
66
|
-
stream = stream_input.to_s == k.to_s
|
67
|
-
if Step === v
|
68
|
-
unless (v.done? or v.streaming?) # or RestClient::Step === v)
|
69
|
-
v.run(true) and v.grace
|
70
|
-
end
|
71
|
-
|
72
|
-
begin
|
73
|
-
if stream
|
74
|
-
new_inputs[k] = TSV.get_stream(v)
|
75
|
-
else
|
76
|
-
new_inputs[k] = v.load
|
77
|
-
end
|
78
|
-
rescue Exception
|
79
|
-
raise $!
|
80
|
-
end
|
81
|
-
else
|
82
|
-
new_inputs[k] = v
|
83
|
-
end
|
84
|
-
end
|
85
|
-
new_inputs
|
86
|
-
end
|
87
|
-
|
88
|
-
def get_streams
|
89
|
-
return if @inputs_done
|
90
|
-
@inputs = RemoteStep.get_streams @inputs, @stream_input
|
91
|
-
@inputs_done = true
|
92
|
-
@inputs
|
93
|
-
end
|
94
|
-
|
95
|
-
def dup_inputs
|
96
|
-
return if @dupped or ENV["RBBT_NO_STREAM"] == 'true'
|
97
|
-
Log.low "Dupping inputs for #{path}"
|
98
|
-
dupped_inputs = {}
|
99
|
-
@inputs.collect do |k,input|
|
100
|
-
dupped_inputs[k] = Step.dup_stream input
|
101
|
-
end
|
102
|
-
@inputs = dupped_inputs
|
103
|
-
@dupped = true
|
104
|
-
end
|
105
|
-
|
106
|
-
def name
|
107
|
-
return nil if @is_exec
|
108
|
-
return @path if @url.nil?
|
109
|
-
(Array === @url ? @url.first : @url).split("/").last
|
110
|
-
end
|
111
|
-
|
112
|
-
def name=(name)
|
113
|
-
@url = [base_url,task, name] * "/"
|
114
|
-
end
|
115
|
-
|
116
|
-
def task_name
|
117
|
-
return task if task
|
118
|
-
init_job
|
119
|
-
(Array === @url ? @url.first : @url).split("/")[-2]
|
120
|
-
end
|
121
|
-
|
122
|
-
def nopid?
|
123
|
-
false
|
124
|
-
end
|
125
|
-
|
126
|
-
def info(check_lock=false)
|
127
|
-
@done = @info && @info[:status] && (@info[:status].to_sym == :done || @info[:status].to_sym == :error)
|
128
|
-
|
129
|
-
if !@done && (@last_info_time.nil? || (Time.now - @last_info_time) > 0.5)
|
130
|
-
update = true
|
131
|
-
else
|
132
|
-
update = false
|
133
|
-
end
|
134
|
-
|
135
|
-
@info = Persist.memory("RemoteSteps Info", :url => @url, :persist => true, :update => update) do
|
136
|
-
@last_info_time = Time.now
|
137
|
-
init_job unless @url
|
138
|
-
info = @adaptor.get_json(File.join(@url, 'info'))
|
139
|
-
info = @adaptor.fix_hash(info)
|
140
|
-
info[:status] = info[:status].to_sym if String === info[:status]
|
141
|
-
info
|
142
|
-
end
|
143
|
-
@info
|
144
|
-
end
|
145
|
-
|
146
|
-
def status
|
147
|
-
return :done if @done
|
148
|
-
return nil unless url or started?
|
149
|
-
#return :streaming if @streaming
|
150
|
-
begin
|
151
|
-
status = info[:status]
|
152
|
-
@done = true if status and status.to_sym == :done
|
153
|
-
status
|
154
|
-
rescue
|
155
|
-
nil
|
156
|
-
ensure
|
157
|
-
@info = nil
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def started?
|
162
|
-
@result != nil || @started || @streaming
|
163
|
-
end
|
164
|
-
|
165
|
-
def done?
|
166
|
-
return true if cache_files.any?
|
167
|
-
init_job unless @url
|
168
|
-
@done || status.to_s == 'done' || status.to_s == 'noinfo'
|
169
|
-
end
|
170
|
-
|
171
|
-
def files
|
172
|
-
@adaptor.get_json(File.join(url, 'files'))
|
173
|
-
end
|
174
|
-
|
175
|
-
def file(file)
|
176
|
-
@adaptor.get_raw(File.join(url, 'file', file))
|
177
|
-
end
|
178
|
-
|
179
|
-
def get_stream
|
180
|
-
case @result
|
181
|
-
when IO
|
182
|
-
@result
|
183
|
-
when String
|
184
|
-
StringIO.new @result
|
185
|
-
else
|
186
|
-
nil
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def grace
|
191
|
-
produce unless @started
|
192
|
-
sleep 0.1 unless started?
|
193
|
-
sleep 0.5 unless started?
|
194
|
-
sleep 1 unless started?
|
195
|
-
while not (done? or started?)
|
196
|
-
sleep 1
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
#{{{ MANAGEMENT
|
201
|
-
|
202
|
-
|
203
|
-
def path
|
204
|
-
if @url
|
205
|
-
Misc.add_GET_param(@url, "_format", "raw")
|
206
|
-
elsif @base_name
|
207
|
-
[base_url, task, @base_name + '-' + Misc.fingerprint(inputs)] * "/"
|
208
|
-
else
|
209
|
-
nil
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def fork(noload=false, semaphore=nil)
|
214
|
-
init_job(:asynchronous)
|
215
|
-
end
|
216
|
-
|
217
|
-
def running?
|
218
|
-
! %w(done error aborted noinfo).include? status.to_s
|
219
|
-
end
|
220
|
-
|
221
|
-
def exec(noload = false)
|
222
|
-
@result ||= begin
|
223
|
-
if noload == :stream
|
224
|
-
_run_job(:exec)
|
225
|
-
else
|
226
|
-
exec_job
|
227
|
-
end
|
228
|
-
ensure
|
229
|
-
@started = true
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
def join
|
234
|
-
return true if cache_files.any?
|
235
|
-
init_job unless @url
|
236
|
-
Log.debug{ "Joining RestClient: #{path}" }
|
237
|
-
if IO === @result
|
238
|
-
res = @result
|
239
|
-
@result = nil
|
240
|
-
Misc.consume_stream(res, true)
|
241
|
-
end
|
242
|
-
|
243
|
-
if not (self.done? || self.aborted? || self.error?)
|
244
|
-
self.info
|
245
|
-
return self if self.done? || self.aborted? || self.error?
|
246
|
-
sleep 0.2 unless self.done? || self.aborted? || self.error?
|
247
|
-
sleep 1 unless self.done? || self.aborted? || self.error?
|
248
|
-
while not (self.done? || self.aborted? || self.error?)
|
249
|
-
sleep 3
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
self
|
254
|
-
end
|
255
|
-
|
256
|
-
def load_res(res, result_type = nil)
|
257
|
-
|
258
|
-
stream = true if res.respond_to? :read
|
259
|
-
join unless stream
|
260
|
-
result_type ||= self.result_type
|
261
|
-
|
262
|
-
case result_type.to_sym
|
263
|
-
when :string
|
264
|
-
stream ? res.read : res
|
265
|
-
when :boolean
|
266
|
-
(stream ? res.read : res) == 'true'
|
267
|
-
when :tsv
|
268
|
-
if stream
|
269
|
-
TSV.open(res, :monitor => true)
|
270
|
-
else
|
271
|
-
TSV.open(StringIO.new(res))
|
272
|
-
end
|
273
|
-
when :annotations
|
274
|
-
if stream
|
275
|
-
Annotated.load_tsv(TSV.open(res))
|
276
|
-
else
|
277
|
-
Annotated.load_tsv(TSV.open(StringIO.new(res)))
|
278
|
-
end
|
279
|
-
when :array
|
280
|
-
(stream ? res.read : res).split("\n")
|
281
|
-
else
|
282
|
-
json_text = if IO === res
|
283
|
-
res.read
|
284
|
-
else
|
285
|
-
res
|
286
|
-
end
|
287
|
-
begin
|
288
|
-
JSON.parse json_text
|
289
|
-
rescue
|
290
|
-
case
|
291
|
-
when json_text =~ /^\d+$/
|
292
|
-
json_text.to_i
|
293
|
-
when json_text =~ /^\d+\.\d/
|
294
|
-
json_text.to_f
|
295
|
-
else
|
296
|
-
raise $!
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
def _restart
|
303
|
-
@done = nil
|
304
|
-
@name = nil
|
305
|
-
@started = nil
|
306
|
-
@aborted = nil
|
307
|
-
new_inputs = {}
|
308
|
-
inputs.each do |k,i|
|
309
|
-
if File === i
|
310
|
-
new_inputs[k] = File.open(i.path)
|
311
|
-
else
|
312
|
-
new_inputs[k] = i
|
313
|
-
end
|
314
|
-
end
|
315
|
-
@inputs = new_inputs
|
316
|
-
@info = nil
|
317
|
-
end
|
318
|
-
|
319
|
-
def input_checks
|
320
|
-
[]
|
321
|
-
end
|
322
|
-
|
323
|
-
end
|
324
|
-
end
|
@@ -1,161 +0,0 @@
|
|
1
|
-
require 'rest-client'
|
2
|
-
|
3
|
-
module WorkflowRESTClient
|
4
|
-
def self.__prepare_inputs_for_restclient(inputs)
|
5
|
-
inputs.each do |k,v|
|
6
|
-
if v.respond_to? :path and not v.respond_to? :original_filename
|
7
|
-
class << v
|
8
|
-
def original_filename
|
9
|
-
File.expand_path(path)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
if Array === v and v.empty?
|
15
|
-
inputs[k] = "EMPTY_ARRAY"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def workflow_description
|
21
|
-
WorkflowRESTClient.get_raw(File.join(url, 'description'))
|
22
|
-
end
|
23
|
-
|
24
|
-
def documentation
|
25
|
-
@documention ||= IndiferentHash.setup(WorkflowRESTClient.get_json(File.join(url, "documentation"),{}))
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.task_info(url, task)
|
29
|
-
@@task_info ||= {}
|
30
|
-
|
31
|
-
key = [url, task] * "#"
|
32
|
-
@@task_info[key] ||= begin
|
33
|
-
task_info = WorkflowRESTClient.get_json(File.join(url, task.to_s, 'info'))
|
34
|
-
task_info = WorkflowRESTClient.fix_hash(task_info)
|
35
|
-
|
36
|
-
task_info[:result_type] = task_info[:result_type].to_sym
|
37
|
-
task_info[:export] = task_info[:export].to_sym
|
38
|
-
task_info[:input_types] = WorkflowRESTClient.fix_hash(task_info[:input_types], true)
|
39
|
-
task_info[:inputs] = task_info[:inputs].collect{|input| input.to_sym }
|
40
|
-
|
41
|
-
@@task_info[key] = task_info
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def task_info(task)
|
46
|
-
WorkflowRESTClient.task_info(url, task)
|
47
|
-
end
|
48
|
-
|
49
|
-
def exported_tasks
|
50
|
-
(@asynchronous_exports + @synchronous_exports + @exec_exports).compact.flatten
|
51
|
-
end
|
52
|
-
|
53
|
-
def tasks
|
54
|
-
@tasks ||= Hash.new do |hash,task_name|
|
55
|
-
info = task_info(task_name)
|
56
|
-
task = Task.setup info do |*args|
|
57
|
-
raise "This is a remote task"
|
58
|
-
end
|
59
|
-
task.name = task_name.to_sym
|
60
|
-
hash[task_name] = task
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def load_tasks
|
65
|
-
exported_tasks.each{|name| tasks[name]}
|
66
|
-
nil
|
67
|
-
end
|
68
|
-
|
69
|
-
def task_dependencies
|
70
|
-
@task_dependencies ||= Hash.new do |hash,task|
|
71
|
-
hash[task] = if exported_tasks.include? task
|
72
|
-
WorkflowRESTClient.get_json(File.join(url, task.to_s, 'dependencies'))
|
73
|
-
else
|
74
|
-
[]
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def init_remote_tasks
|
80
|
-
task_exports = WorkflowRESTClient.get_json(url)
|
81
|
-
@asynchronous_exports = (task_exports["asynchronous"] || []).collect{|task| task.to_sym }
|
82
|
-
@synchronous_exports = (task_exports["synchronous"] || []).collect{|task| task.to_sym }
|
83
|
-
@exec_exports = (task_exports["exec"] || []).collect{|task| task.to_sym }
|
84
|
-
@stream_exports = (task_exports["stream"] || []).collect{|task| task.to_sym }
|
85
|
-
@can_stream = task_exports["can_stream"]
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.execute_job(base_url, task, task_params, cache_type)
|
89
|
-
self.capture_exception do
|
90
|
-
task_url = URI.encode(File.join(base_url, task.to_s))
|
91
|
-
|
92
|
-
sout, sin = Misc.pipe
|
93
|
-
|
94
|
-
post_thread = Thread.new(Thread.current) do |parent|
|
95
|
-
bl = lambda do |rok|
|
96
|
-
if Net::HTTPOK === rok
|
97
|
-
_url = rok["RBBT-STREAMING-JOB-URL"]
|
98
|
-
@url = File.join(task_url, File.basename(_url)) if _url
|
99
|
-
rok.read_body do |c,_a, _b|
|
100
|
-
sin.write c
|
101
|
-
end
|
102
|
-
sin.close
|
103
|
-
else
|
104
|
-
err = StringIO.new
|
105
|
-
rok.read_body do |c,_a, _b|
|
106
|
-
err.write c
|
107
|
-
end
|
108
|
-
text = begin
|
109
|
-
reader = Zlib::GzipReader.new(err)
|
110
|
-
reader.read
|
111
|
-
rescue
|
112
|
-
err.rewind
|
113
|
-
err.read
|
114
|
-
end
|
115
|
-
ne = @adaptor.parse_exception text
|
116
|
-
case ne
|
117
|
-
when String
|
118
|
-
parent.raise e.class, ne
|
119
|
-
when Exception
|
120
|
-
parent.raise ne
|
121
|
-
else
|
122
|
-
parent.raise "Error in RestClient: " << rok.message
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
task_params.each do |k,v|
|
128
|
-
task_params[k] = v.read if IO === v
|
129
|
-
end
|
130
|
-
|
131
|
-
Log.debug{ "RestClient execute: #{ task_url } - #{Misc.fingerprint task_params}" }
|
132
|
-
RestClient::Request.execute(:method => :post, :url => task_url, :payload => task_params, :block_response => bl)
|
133
|
-
end
|
134
|
-
|
135
|
-
# It seems like now response body are now decoded by Net::HTTP after 2.1
|
136
|
-
# https://github.com/rest-client/rest-client/blob/cf3e5a115bcdb8f3344aeac0e45b44d67fac1a42/history.md
|
137
|
-
decode = Gem.loaded_specs["rest-client"].version < Gem::Version.create('2.1')
|
138
|
-
if decode
|
139
|
-
reader = Zlib::GzipReader.new(sout)
|
140
|
-
res_io = Misc.open_pipe do |sin|
|
141
|
-
while c = reader.read(Misc::BLOCK_SIZE)
|
142
|
-
sin.write c
|
143
|
-
end
|
144
|
-
sin.close
|
145
|
-
@done = true
|
146
|
-
end
|
147
|
-
ConcurrentStream.setup(res_io, :threads => [post_thread]) do
|
148
|
-
@done = true
|
149
|
-
@streaming = false
|
150
|
-
end
|
151
|
-
else
|
152
|
-
ConcurrentStream.setup(sout, :threads => [post_thread]) do
|
153
|
-
@done = true
|
154
|
-
@streaming = false
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
end
|