rbbt-rest 1.8.8 → 1.8.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cff29021a179c10bc6cc157b4b68c167f0d0cbce
4
- data.tar.gz: bd58e81210656647a893ce423b3afacd1959f36a
3
+ metadata.gz: 3efb056fd9e11310126a6cbec3dbdc9fea9ee093
4
+ data.tar.gz: e7fb374a165521846f5e10a2491f8dcceaaef8a5
5
5
  SHA512:
6
- metadata.gz: 3813b05fb8974c6b8ca3141bd0f2eb7108f8e979e68f932a888dc9ce8c9e505c978120037e11e2d9a4087bf2bcd1c21056aff5f95b941c9425b11c1de6d84d6a
7
- data.tar.gz: eb7519b674f9c552f771d6fcd7f4ac75c8a244820a28820c80b5c42742d0b7fb1b0138727fd3a5a23439a88a9430884ac1c62f68279f363f77a277e7eb95814f
6
+ metadata.gz: a0e7197f529598c35dc7f2f0f33931d48cd208205f8b068df899007eaa905cf7550de75255d2e1c052f54b409acc483f5781ddf92a2ecf83003c0dd8217168d9
7
+ data.tar.gz: d73a4246be17d0b5056c80b686ac24776c51192fdfbc4bcaccbbd0360ef0647a374a4810435d104c2dfabf44879554c703f1f30ff0a3e7f549a84ced1eb2c2a1
@@ -109,9 +109,10 @@ module RbbtRESTHelpers
109
109
 
110
110
  pid = @step.child{
111
111
  begin
112
- res = capture_haml &block
112
+ Log.low("Fragment started: #{ fragment_file } - #{Process.pid}")
113
+ res = capture_haml fragment_code, &block
113
114
  Open.write(fragment_file, res)
114
- Log.low("Fragment done: #{ fragment_file }")
115
+ Log.low("Fragment done: #{ fragment_file } - #{Process.pid}")
115
116
  rescue Exception
116
117
  Open.write(fragment_file + '.error', [$!.class.to_s, $!.message] * ": ")
117
118
  Open.write(fragment_file + '.backtrace', $!.backtrace * "\n") if $!.backtrace
@@ -129,7 +130,11 @@ module RbbtRESTHelpers
129
130
  if link.nil?
130
131
  html_tag('a', "", :href => fragment_url, :class => 'fragment', "data-text" => text)
131
132
  else
132
- if link =~ / href=/
133
+ if FalseClass === link
134
+ return fragment_code
135
+ elsif TrueClass === link
136
+ return fragment_url
137
+ elsif link =~ / href=/
133
138
  link = link.sub(/ href=('|")/," href='#{fragment_url}'")
134
139
  else
135
140
  link = link.sub(/<a /,"<a href='#{fragment_url}' ")
@@ -1,6 +1,5 @@
1
1
  require 'rbbt'
2
2
  require 'sinatra/base'
3
- require 'sinatra/streaming'
4
3
 
5
4
 
6
5
  module Sinatra
@@ -8,7 +7,6 @@ module Sinatra
8
7
 
9
8
  def self.registered(base)
10
9
  base.module_eval do
11
- helpers Sinatra::Streaming
12
10
 
13
11
  get '/resource/:resource/get_directory' do
14
12
  directory, resource, create = params.values_at :directory, :resource, :create
@@ -8,8 +8,11 @@ require 'sinatra/base'
8
8
  require 'sinatra/cross_origin'
9
9
  require "sinatra/multi_route"
10
10
  require "sinatra/cookies"
11
+ require 'sinatra/streaming'
11
12
  require 'json'
12
13
 
14
+ require 'nakayoshi_fork'
15
+
13
16
  module Sinatra
14
17
  module RbbtRESTMain
15
18
 
@@ -46,6 +49,7 @@ module Sinatra
46
49
  helpers RbbtRESTHelpers
47
50
  register Sinatra::RbbtAuth
48
51
  helpers Sinatra::Cookies
52
+ helpers Sinatra::Streaming
49
53
 
50
54
  add_sass_load_path Rbbt.share.views.compass.find(:lib)
51
55
 
@@ -66,7 +70,7 @@ module Sinatra
66
70
  if production?
67
71
  set :haml, { :ugly => true }
68
72
  set :clean_trace, true
69
- set :static_cache_control , [:public, {:max_age => 360000}]
73
+ set :static_cache_control , [:public, {:max_age => 360_000}]
70
74
  else
71
75
  set :static_cache_control , [:public, {:max_age => 0}]
72
76
  end
@@ -122,6 +126,7 @@ module Sinatra
122
126
  printer.print(:path => dir, :profile => 'profile')
123
127
  Log.info{ "Profile saved at #{ dir }: #{request.env["REQUEST_URI"]}" }
124
128
  end
129
+
125
130
  response.header["URI"] = request.env["REQUEST_URI"]
126
131
  end
127
132
 
@@ -239,14 +244,11 @@ module Sinatra
239
244
  end
240
245
 
241
246
  require 'rbbt/rest/monitor'
247
+
242
248
  register Sinatra::RbbtRESTMonitor
243
249
  end
244
250
  end
245
251
 
246
252
  end
247
-
248
-
249
- #require 'rack/stream'
250
- #use Rack::Stream
251
253
  end
252
254
 
@@ -46,7 +46,9 @@ module Sinatra
46
46
  workflow_render('tasks', workflow, nil, @clean_params)
47
47
  when :json
48
48
  content_type "application/json"
49
- {:exec => workflow.exec_exports, :synchronous => workflow.synchronous_exports, :asynchronous => workflow.asynchronous_exports}.to_json
49
+
50
+ @can_stream = ENV["RBBT_WORKFLOW_TASK_STREAM"] == 'true'
51
+ {:exec => workflow.exec_exports, :synchronous => workflow.synchronous_exports, :asynchronous => workflow.asynchronous_exports, :can_stream => !!@can_stream}.to_json
50
52
  else
51
53
  raise "Unsupported format specified: #{ format }"
52
54
  end
@@ -63,6 +65,7 @@ module Sinatra
63
65
  raise "Unsupported format specified: #{ format }"
64
66
  end
65
67
  end
68
+
66
69
  get "/#{workflow.to_s}/:task/info" do
67
70
  task = consume_parameter(:task)
68
71
 
@@ -157,7 +160,7 @@ module Sinatra
157
160
  raise RbbtRESTHelpers::Retry
158
161
  end
159
162
  else
160
- halt 404, "Job not found"
163
+ halt 404, "Job not found: #{job.status}"
161
164
  end
162
165
  end
163
166
  rescue RbbtRESTHelpers::Retry
@@ -264,6 +267,15 @@ module Sinatra
264
267
  def self.registered(base)
265
268
  base.module_eval do
266
269
  helpers WorkflowRESTHelpers
270
+
271
+ if ENV["RBBT_WORKFLOW_TASK_STREAM"] == 'true'
272
+ require 'rbbt/rest/workflow/stream_task'
273
+ Log.info "Preparing server for streaming workflow tasks"
274
+ use StreamWorkflowTask if not @can_stream
275
+ @can_stream = true
276
+ else
277
+ @can_stream = false
278
+ end
267
279
  end
268
280
  end
269
281
  end
@@ -77,17 +77,20 @@ module WorkflowRESTHelpers
77
77
  halt 200, result.to_json
78
78
  when :tsv
79
79
  content_type "text/tab-separated-values"
80
- halt 200, result.to_s
80
+ result = result.to_s unless String === result or result.respond_to? :gets
81
+ halt 200, result
81
82
  when :literal, :raw
82
83
  content_type "text/plain"
83
84
  case workflow.task_info(task)[:result_type]
84
85
  when :array
85
86
  halt 200, result * "\n"
86
87
  else
87
- halt 200, result.to_s
88
+ result = result.to_s unless String === result or result.respond_to? :gets
89
+ halt 200, result
88
90
  end
89
91
  when :binary
90
92
  content_type "application/octet-stream"
93
+ result = result.to_s unless String === result or result.respond_to? :gets
91
94
  halt 200, result.to_s
92
95
  when :jobname
93
96
  halt 200, nil
@@ -146,6 +149,21 @@ module WorkflowRESTHelpers
146
149
  end
147
150
  end
148
151
 
152
+ def stream_job(job)
153
+ unless job.started? or job.done?
154
+ job.run(:stream)
155
+ job.soft_grace
156
+ end
157
+
158
+ s = TSV.get_stream job
159
+
160
+ sout, sin = Misc.pipe
161
+
162
+ Misc.consume_stream(s, true, sin)
163
+
164
+ halt 200, sout
165
+ end
166
+
149
167
  def issue_job(workflow, task, jobname = nil, params = {})
150
168
  inputs = prepare_job_inputs(workflow, task, params)
151
169
  job = workflow.job(task, jobname, inputs)
@@ -157,7 +175,15 @@ module WorkflowRESTHelpers
157
175
 
158
176
  case execution_type.to_sym
159
177
  when :exec
160
- show_exec_result job.exec, workflow, task
178
+ show_exec_result job.exec(:stream), workflow, task
179
+ when :stream
180
+ if update == :reload
181
+ job.abort
182
+ job.clean
183
+ end
184
+
185
+ stream_job(job)
186
+
161
187
  when :synchronous, :sync
162
188
  if update == :reload
163
189
  job.abort
@@ -169,11 +195,10 @@ module WorkflowRESTHelpers
169
195
  job_url += "?_format=#{@format}" if @format
170
196
 
171
197
  if not job.started?
172
- job.run true
198
+ job.run
173
199
  job.join
174
200
  end
175
201
 
176
- #halt 200, job.name if format == :jobname
177
202
  if format == :jobname
178
203
  job.name
179
204
  else
@@ -22,7 +22,7 @@ module WorkflowRESTHelpers
22
22
  end
23
23
 
24
24
  if job
25
- locals[:job] = job
25
+ locals[:job] = job
26
26
  @step = job
27
27
  cache_type = execution_type(workflow, task)
28
28
  cache_file = job.file('html')
@@ -0,0 +1,169 @@
1
+ class StreamWorkflowTask
2
+ def initialize(app)
3
+ @app = app
4
+ end
5
+
6
+ def read_normal_inputs(io, boundary, stream_input)
7
+ content = ""
8
+ content_start = false
9
+ variable = nil
10
+ filename = nil
11
+ inputs = {}
12
+
13
+ while line = io.gets
14
+ line.chomp!
15
+
16
+ if line == "--" + boundary
17
+ if variable
18
+ inputs[variable] = content
19
+ end
20
+ content_start = false
21
+ content = ""
22
+ elsif line =~ /^Content.* name="([^\s;"]*)"/
23
+ variable = $1
24
+ filename = line.match(/filename="([^"]+)"/)[1] if line =~ /filename/
25
+ content = ""
26
+ elsif line.empty?
27
+ content_start = true
28
+ break if variable == stream_input
29
+ else
30
+ content << line if content_start
31
+ end
32
+ end
33
+
34
+ [inputs, filename]
35
+ end
36
+
37
+ def parse_uri(env)
38
+ uri = env["REQUEST_URI"]
39
+ _n, workflow, task = uri.split("/")
40
+ workflow = begin
41
+ Kernel.const_get(workflow)
42
+ rescue
43
+ raise "Could not accept task for workflow: #{ workflow }"
44
+ end
45
+ [workflow, task]
46
+ end
47
+
48
+ EOL = "\r\n"
49
+ def read_chunk(sin, rest = "")
50
+ parts = []
51
+ c = sin.gets
52
+ c = rest << c unless rest.empty?
53
+ c = c[2..-1] if c[0..1] == EOL
54
+ index = c.index EOL
55
+ while index
56
+ part = c[0..index-1]
57
+ parts << part
58
+ c = c[index+2..-1]
59
+ index = c.index EOL
60
+ end
61
+ rest = c
62
+ [parts, rest]
63
+ end
64
+
65
+
66
+ def copy_chunked_stream(sin, sout, boundary)
67
+
68
+ rest = ""
69
+ done = false
70
+ content = true
71
+
72
+ while not done
73
+ parts, rest = read_chunk(sin, rest)
74
+ while parts.any?
75
+ part = parts.shift
76
+ if content
77
+ part.split("\n").each do |line|
78
+ sout.puts line
79
+ if line.include? boundary
80
+ done = true
81
+ break
82
+ end
83
+ end
84
+ content = false
85
+ else
86
+ content = true
87
+ end
88
+ end
89
+ end
90
+
91
+ sout.write rest
92
+ sout.close
93
+ end
94
+
95
+ def call(env)
96
+ if env["REQUEST_METHOD"] == "POST" and env["rack.hijack"] and env["CONTENT_TYPE"] and env["CONTENT_TYPE"].include? "Rbbt_Param_Stream" and env["HTTP_TRANSFER_ENCODING"] == 'chunked'
97
+ Log.high "Hijacking post data"
98
+ inputs = {}
99
+ content_type = env["CONTENT_TYPE"]
100
+ boundary = content_type.match(/boundary=([^\s;]*)/)[1]
101
+ stream_input = content_type.match(/stream=([^\s;]*)/)[1]
102
+ post_stream = env["rack.hijack"].call
103
+ begin
104
+ inputs, filename = read_normal_inputs(post_stream, boundary, stream_input)
105
+
106
+ input_stream_out, input_stream_in = Misc.pipe
107
+ Misc.add_stream_filename(input_stream_out, filename) if filename
108
+ inputs[stream_input] = input_stream_out
109
+
110
+ workflow, task = parse_uri(env)
111
+ name = inputs.delete "jobname"
112
+ job = workflow.job(task, name, inputs)
113
+
114
+ task = task.to_sym
115
+ execution_type = case
116
+ when workflow.exec_exports.include?(task)
117
+ "exec"
118
+ when workflow.synchronous_exports.include?(task)
119
+ "synchronous"
120
+ when workflow.asynchronous_exports.include?(task)
121
+ "asynchronous"
122
+ else
123
+ raise "No known export type for #{ workflow } #{ task }. Accesses denied"
124
+ end
125
+
126
+ execution_type = "exec" if inputs["_cache_type"] == 'exec'
127
+ Log.info "Streaming task with execution_type: #{ execution_type }"
128
+
129
+ case execution_type
130
+ when "exec", nil
131
+ job.exec(:stream)
132
+ when "sync", "synchronous", "async", "asynchronous"
133
+ job.run(:stream)
134
+ else
135
+ raise "Unknown execution_type: #{execution_type}"
136
+ end
137
+
138
+ t_in = Thread.new do
139
+ begin
140
+ copy_chunked_stream(post_stream, input_stream_in, boundary)
141
+ rescue
142
+ Log.exception $!
143
+ end
144
+ end
145
+
146
+ job_output = TSV.get_stream job
147
+ t_out = Thread.new do
148
+ begin
149
+ post_stream.write "HTTP/1.1 200\r\n\r\n"
150
+ while c = job_output.read(1024)
151
+ post_stream.write c
152
+ end
153
+ job_output.join if job_output.respond_to? :join
154
+ post_stream.close
155
+ rescue
156
+ Log.exception $!
157
+ job.abort
158
+ end
159
+ end
160
+
161
+ end
162
+ [200, {}, nil]
163
+ else
164
+ Log.high "NOT Hijacking post data"
165
+ @app.call(env)
166
+ end
167
+ end
168
+ end
169
+
@@ -7,10 +7,10 @@
7
7
 
8
8
  - if defined? job.info_file and File.exists? job.info_file and request.env["REQUEST_URI"].include? job.name
9
9
  - clean_url = add_GET_param(request.env["REQUEST_URI"], "_update", "clean")
10
- %a.ui.button.blue(href=clean_url) Clean
10
+ %a.ui.button.blue.clean(href=clean_url) Clean
11
11
  - else
12
12
  - clean_url = add_GET_param(request.env["REQUEST_URI"], "_update", "reload")
13
- %a.ui.blue.button(href=clean_url) Reload
13
+ %a.ui.blue.button.reload(href=clean_url) Reload
14
14
 
15
15
  - backtrace = job.info[:backtrace]
16
16
  - if backtrace and backtrace.any?
@@ -1 +1,18 @@
1
- = hash2dl(info)
1
+ - new_info = {}
2
+ - info.each{|k,v| new_info[k] = v unless k.to_s == 'dependencies'}
3
+ = hash2dl(new_info)
4
+
5
+ - if info[:dependencies] and info[:dependencies].any?
6
+ %h3 Dependencies
7
+ %ul
8
+
9
+ - task_exports = workflow.synchronous_exports + workflow.asynchronous_exports
10
+ - task_exports = task_exports.collect{|t| t.to_s}
11
+ - info[:dependencies].each do |task,name,path|
12
+ - url = "/" + [workflow, task, name] * "/"
13
+ %li
14
+ (#{workflow} #{task})
15
+ - if task_exports.include? task.to_s
16
+ %a(href=url) #{ name }
17
+ - else
18
+ = name
@@ -6,7 +6,7 @@
6
6
  %a.ui.small.button(href="/#{[workflow.to_s, task, jobname, "info"] * "/"}") job info
7
7
  %a.ui.small.button(href="/#{[workflow.to_s, task, jobname] * "/"}?_format=raw") get raw result file
8
8
 
9
- - files = job.files - ["html", "html.info"]
9
+ - files = job.files - ["html", "html.files", "html.info", "html.pid"]
10
10
  - if files.any?
11
11
  %dt.ui.header Files
12
12
  %dd.job_files
@@ -115,7 +115,8 @@ function replace_object(object, href, embedd, complete){
115
115
  error_message = error.sub(/\n\n.*/,'')
116
116
 
117
117
  error_span = $('<span>').html(error_message).addClass('error')
118
- error = $('<div>').append(error_span)
118
+ error = $('<div>') //.append(error_span)
119
+ error.append(req.responseText)
119
120
  object.removeClass(_reloading_class).addClass("error").css('height', 0).html(error).css('height', 'auto').attr('target-href', clean_href);
120
121
  },
121
122
 
@@ -287,6 +287,10 @@ $.widget("rbbt.action_controller", {
287
287
  action_controller.addClass('loading');
288
288
 
289
289
  replace_object(action_div, href, true, this.options.complete);
290
+ var tool = this
291
+ action_div.on('click', '.reload', function(e){
292
+ update_embedded(action_div, true, tool.options.complete); return false
293
+ });
290
294
 
291
295
  return false
292
296
  }
@@ -1,5 +1,8 @@
1
1
  - @reload_page = true
2
2
 
3
+ :sass
4
+ span.progress_time
5
+ color: grey
3
6
  .wait.ui.segment.info.message
4
7
  %h3.ui.header Waiting on #{format_name File.basename(job.name)}
5
8
 
@@ -10,10 +13,29 @@
10
13
  .footer.ui.basic.segment
11
14
  - progress = job.file(:progress).yaml
12
15
  - if progress[:last_percent]
16
+ - start = progress[:start]
17
+ - last_time = progress[:last_time]
18
+ - if progress[:last_percent].to_i > 0
19
+ - ellapsed = last_time - start
20
+ - if progress[:mean]
21
+ - missing = progress[:max].to_f - progress[:ticks].to_f
22
+ - eta = missing / progress[:mean].to_f
23
+ - else
24
+ - ratio = progress[:ticks].to_f/progress[:max].to_f
25
+ - eta = ellapsed * (1 - ratio)
26
+ - eta = Misc.format_seconds(eta)
27
+ - ellapsed = Misc.format_seconds(ellapsed)
28
+ - else
29
+ - ellapsed = '?'
30
+ - eta = '?'
13
31
  .ui.teal.step.percent.progress.indicating.meta(data-percent="#{progress[:last_percent]}" data-total=100)
14
32
  .bar
15
33
  .progress
16
- .label= progress[:desc]
34
+ .label
35
+ = progress[:desc]
36
+ - if progress[:last_percent].to_i > 0
37
+ %span.progress_time
38
+ (#{ eta } &#8658; #{ellapsed})
17
39
  :deferjs
18
40
  $('.step.progress').progress()
19
41
  - else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbbt-rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.8
4
+ version: 1.8.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-22 00:00:00.000000000 Z
11
+ date: 2016-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -196,6 +196,7 @@ files:
196
196
  - lib/rbbt/rest/workflow/jobs.rb
197
197
  - lib/rbbt/rest/workflow/locate.rb
198
198
  - lib/rbbt/rest/workflow/render.rb
199
+ - lib/rbbt/rest/workflow/stream_task.rb
199
200
  - share/views/association/Default.haml
200
201
  - share/views/compass/app.sass
201
202
  - share/views/compass/card_templates.sass