taverna-player 0.9.0 → 0.10.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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MmM4NWJkN2Y1MDc1ZTYzNDc2OTZmZmNlMWRjZjg5ODMzMzUxNjU2Yw==
4
+ NDYxMDEwYjhkZTE0OGJmZDk5Y2FkNGQwZWI5MDk1MjNkY2MzMDljYw==
5
5
  data.tar.gz: !binary |-
6
- ZDhhYjNiMDkxMjFhNTA2ZjMzZDFhYTZjNWVhODFmM2JkY2ZjMGFmOA==
6
+ NGRiYTBjMTY0NjFhZTEyZWNlMWIwNTZlMzllNDgzNzE0N2M5YzRiOQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NTdhOTUzZDkxYjVlZmY5MGI0NGM3NDAxZDNjZDI4NTc2NDg0N2NhMjVjMDA0
10
- NjFmZjIxYmI5MzYxNDFlNzYwNmNiYTMzYTJkOWIwNjQzNWY4MmI1MzJkMDc0
11
- NGVmMjQ1ODZkNzNkNDgyN2IwNDEwODBhZGUzMTA0YjMxODY5ZjU=
9
+ NTgxMWRkY2ZmMzI1NTQwYjI4YzA4NGNhNzEyNGI1MGQyZmUyYTRmMjZmNDVh
10
+ NTg1MjdhZDUwZmZkMTNlMjgzMTFiYjEyODlkMmZhMDgzZGIwMjE2ZDdiMWU4
11
+ ZDFhNjNiMTk5YTEwMjQ1NjE3NGEzMzM3NDg5YzhmYTliNzc3YTU=
12
12
  data.tar.gz: !binary |-
13
- YmMxYTRmNDNjM2ViYjc5MjcwMjE0MWZjODY5ZTBlYmI5Yjk0MzE1MDVmYjRk
14
- NzFjMTRjNTdmNzE3NjJjOGFlYjIxYzlkMDQzM2UwZGU0ZjBhMmQxYTNiOWUx
15
- MDlhODI4YzVhZTFhNDNhYTJhY2QzZGI2Y2IxNGZjZjA5OGZiYWI=
13
+ YWRlNWU1MmVkMTkyZGJhMDNmN2MwNDI0ZGIxMmM5YmM5YjQyZTExMGJiZjg4
14
+ ZTQ3N2ZmZDE0ZTQzNTE5YjgwZjg0M2U1ZTZmNTQxNDhjZTA0NzY1OGU4Njk5
15
+ OTZhZWU0NDFmYmQ4NTdiNDU1NzczZGFlZDc0MzVkOTUwMDQ1ZDY=
@@ -1,5 +1,15 @@
1
1
  = Changes log for Taverna Player
2
2
 
3
+ == Version 0.10.0
4
+
5
+ * Use the latest t2-server client library.
6
+ * Factor out run creation in the worker.
7
+ * Fix the "fail in post run callback" worker test.
8
+ * [TAV-522] Handle network level errors (retry).
9
+ * [TAV-522] Configure number of network error retries.
10
+ * Document the new connection configuration option.
11
+ * Factor out the interactions handler in the worker code.
12
+
3
13
  == Version 0.9.0
4
14
 
5
15
  * Add a test to check if a Run's delayed_job has failed.
@@ -313,7 +313,19 @@ that make use of interactions (so you have users watching the workflows
313
313
  running) then it should probably be set lower.
314
314
 
315
315
  There are a number of options for configuring the connection to the Taverna
316
- Server. These are actually provided by the underlying
316
+ Server. The first specifies how many times an initial connection to Taverna
317
+ Server will be retried if there are any low level network errors.
318
+
319
+ config.server_connection_error_retries = 5
320
+
321
+ These can be events such as timeouts, broken connections, refused connections
322
+ or anything else out of our control. If such an error is detected Taverna
323
+ Player will back off for the amount of time specified by
324
+ <tt>config.server_retry_interval</tt> before retrying. If the maximum number of
325
+ retries is reached and there is still an error then the run will fail; the
326
+ network error message will be recorded as the reason for the run's failure.
327
+
328
+ The rest of the connection options are actually provided by the underlying
317
329
  {t2-server client library}[https://rubygems.org/gems/t2-server] and surfaced
318
330
  here for extra control. They are
319
331
  {documented in more detail}[http://mygrid.github.io/t2-server-gem/] elsewhere
@@ -17,6 +17,7 @@ en:
17
17
  pre-callback: "Running pre-run tasks"
18
18
  connect: "Connecting to Taverna Server"
19
19
  full: "Server full - please wait; run will start soon"
20
+ network-error: "Network error - please wait; retrying"
20
21
  initialized: "Initializing new workflow run"
21
22
  inputs: "Uploading run inputs"
22
23
  start: "Starting run"
@@ -13,6 +13,7 @@ TavernaPlayer.setup do |config|
13
13
  config.server_retry_interval = 10
14
14
 
15
15
  # Taverna Server connection configuration.
16
+ #config.server_connection_error_retries = 5
16
17
  #config.server_connection[:verify_peer] = true
17
18
  #config.server_connection[:ca_file] = "/etc/certs/my-cert.crt"
18
19
  #config.server_connection[:ca_path] = "/etc/certs/"
@@ -121,6 +121,10 @@ module TavernaPlayer
121
121
  mattr_accessor :server_retry_interval
122
122
  @@server_retry_interval = 10
123
123
 
124
+ # Number of times to retry on a low-level connection error.
125
+ mattr_accessor :server_connection_error_retries
126
+ @@server_connection_error_retries = 5
127
+
124
128
  # Taverna Server connection parameters
125
129
  mattr_accessor :server_connection
126
130
  @@server_connection = T2Server::DefaultConnectionParameters.new
@@ -11,5 +11,5 @@
11
11
  #------------------------------------------------------------------------------
12
12
 
13
13
  module TavernaPlayer
14
- VERSION = "0.9.0"
14
+ VERSION = "0.10.0"
15
15
  end
@@ -50,23 +50,11 @@ module TavernaPlayer
50
50
 
51
51
  begin
52
52
  server = T2Server::Server.new(@server, conn_params)
53
- wkf = File.read(@workflow)
53
+ workflow = File.read(@workflow)
54
+ run = create_run(server, workflow, credentials)
54
55
 
55
- # Try and create the run bearing in mind that the server might be at
56
- # the limit of runs that it can hold at once.
57
- begin
58
- run = server.create_run(wkf, credentials)
59
- rescue T2Server::ServerAtCapacityError
60
- status_message("full")
61
-
62
- if cancelled?
63
- cancel
64
- return
65
- end
66
-
67
- sleep(TavernaPlayer.server_retry_interval)
68
- retry
69
- end
56
+ # If run is nil here then we could have failed or have been cancelled.
57
+ return if run.nil?
70
58
 
71
59
  status_message("initialized")
72
60
 
@@ -121,65 +109,7 @@ module TavernaPlayer
121
109
  return
122
110
  end
123
111
 
124
- run.notifications(:requests).each do |note|
125
- if @run.has_parent?
126
- next if note.has_reply? || note.is_notification?
127
- int = Interaction.find_by_run_id_and_serial(@run.parent_id, note.serial)
128
- new_int = Interaction.find_or_initialize_by_run_id_and_serial(@run.id, note.serial)
129
- if new_int.new_record?
130
- note.reply(int.feed_reply, int.data)
131
- new_int.displayed = true
132
- new_int.replied = true
133
- new_int.feed_reply = int.feed_reply
134
- new_int.data = int.data
135
- new_int.save
136
- end
137
- else
138
- int = Interaction.find_or_initialize_by_run_id_and_serial(@run.id, note.serial)
139
-
140
- # Need to catch this here in case some other process has replied.
141
- if note.has_reply? && !int.replied?
142
- int.replied = true
143
- int.save
144
- end
145
-
146
- unless int.replied?
147
- if int.page.blank?
148
- page = server.read(note.uri, "text/html", credentials)
149
-
150
- INTERACTION_REGEX.match(page) do
151
- page_uri = $1
152
-
153
- if page_uri.starts_with?(server.uri.to_s)
154
- page = server.read(URI.parse(page_uri), "text/html", credentials)
155
- int.page = page.gsub("#{run.interactions_uri.to_s}/pmrpc.js",
156
- "/assets/taverna_player/application.js")
157
- else
158
- int.page_uri = page_uri
159
- end
160
- end
161
- end
162
-
163
- if note.is_notification? && !int.new_record?
164
- int.replied = true
165
- end
166
-
167
- if int.data.blank?
168
- int.data = note.input_data.force_encoding("UTF-8")
169
- end
170
-
171
- if !int.feed_reply.blank? && !int.data.blank?
172
- note.reply(int.feed_reply, int.data)
173
-
174
- int.replied = true
175
- end
176
-
177
- int.save
178
- end
179
-
180
- waiting = true unless int.replied?
181
- end
182
- end
112
+ waiting = interactions(run, credentials)
183
113
 
184
114
  status_message(waiting ? "interact" : "running")
185
115
  end
@@ -220,6 +150,108 @@ module TavernaPlayer
220
150
  T2Server::HttpBasic.new(user, pass)
221
151
  end
222
152
 
153
+ # Try and create the run bearing in mind that the server might be at
154
+ # the limit of runs that it can hold at once.
155
+ def create_run(server, workflow, credentials)
156
+ retries ||= TavernaPlayer.server_connection_error_retries
157
+ server.create_run(workflow, credentials)
158
+ rescue T2Server::ServerAtCapacityError
159
+ status_message("full")
160
+
161
+ if cancelled?
162
+ cancel
163
+ return
164
+ end
165
+
166
+ sleep(TavernaPlayer.server_retry_interval)
167
+ retry
168
+ rescue T2Server::ConnectionError => ce
169
+ status_message("network-error")
170
+
171
+ if cancelled?
172
+ cancel
173
+ return
174
+ end
175
+
176
+ sleep(TavernaPlayer.server_retry_interval)
177
+ unless retries.zero?
178
+ retries -= 1
179
+ retry
180
+ end
181
+
182
+ # If we're out of retries, fail the run.
183
+ failed(ce)
184
+ end
185
+
186
+ def interactions(run, credentials)
187
+ wait = false
188
+
189
+ run.notifications(:requests).each do |note|
190
+ if @run.has_parent?
191
+ next if note.has_reply? || note.is_notification?
192
+
193
+ int = Interaction.find_by_run_id_and_serial(@run.parent_id, note.serial)
194
+ new_int = Interaction.find_or_initialize_by_run_id_and_serial(@run.id, note.serial)
195
+
196
+ if new_int.new_record?
197
+ note.reply(int.feed_reply, int.data)
198
+ new_int.displayed = true
199
+ new_int.replied = true
200
+ new_int.feed_reply = int.feed_reply
201
+ new_int.data = int.data
202
+ new_int.save
203
+ end
204
+ else
205
+ int = Interaction.find_or_initialize_by_run_id_and_serial(@run.id, note.serial)
206
+
207
+ # Need to catch this here in case some other process has replied.
208
+ if note.has_reply? && !int.replied?
209
+ int.replied = true
210
+ int.save
211
+ end
212
+
213
+ unless int.replied?
214
+ if int.page.blank?
215
+ server = run.server
216
+ page = server.read(note.uri, "text/html", credentials)
217
+
218
+ INTERACTION_REGEX.match(page) do
219
+ page_uri = $1
220
+
221
+ if page_uri.starts_with?(server.uri.to_s)
222
+ page = server.read(URI.parse(page_uri), "text/html", credentials)
223
+ int.page = page.gsub("#{run.interactions_uri.to_s}/pmrpc.js",
224
+ "/assets/taverna_player/application.js")
225
+ else
226
+ int.page_uri = page_uri
227
+ end
228
+ end
229
+ end
230
+
231
+ # If this is a pure notification that we've already seen then
232
+ # set it as replied as we don't want it blocking a proper
233
+ # interaction.
234
+ int.replied = true if note.is_notification? && !int.new_record?
235
+
236
+ if int.data.blank?
237
+ int.data = note.input_data.force_encoding("UTF-8")
238
+ end
239
+
240
+ if !int.feed_reply.blank? && !int.data.blank?
241
+ note.reply(int.feed_reply, int.data)
242
+ int.replied = true
243
+ end
244
+
245
+ int.save
246
+ end
247
+
248
+ wait = true unless int.replied?
249
+ end
250
+ end
251
+
252
+ wait
253
+ end
254
+
223
255
  # Run the specified callback and return false on error so that we know to
224
256
  # return out of the worker code completely.
225
257
  def run_callback(cb, message)
@@ -37,7 +37,7 @@ Gem::Specification.new do |s|
37
37
  s.add_dependency "jquery-rails", "~> 3.0"
38
38
  s.add_dependency "paperclip", "~> 4.1"
39
39
  s.add_dependency "taverna-t2flow", "~> 0.5.1"
40
- s.add_dependency "t2-server", "~> 1.1"
40
+ s.add_dependency "t2-server", "~> 1.2"
41
41
  s.add_dependency "delayed_job_active_record", "~> 4.0"
42
42
  s.add_dependency "daemons", "~> 1.1.9"
43
43
  s.add_dependency "rubyzip", "~> 1.1.4"
@@ -17,6 +17,7 @@ en:
17
17
  pre-callback: "Running pre-run tasks"
18
18
  connect: "Connecting to Taverna Server"
19
19
  full: "Server full - please wait; run will start soon"
20
+ network-error: "Network error - please wait; retrying"
20
21
  initialized: "Initializing new workflow run"
21
22
  inputs: "Uploading run inputs"
22
23
  start: "Starting run"
@@ -28,6 +28,7 @@ class WorkerTest < ActiveSupport::TestCase
28
28
  config.server_password = "taverna"
29
29
  config.server_poll_interval = 0
30
30
  config.server_retry_interval = 0
31
+ config.server_connection_error_retries = 2
31
32
  config.pre_run_callback = @noop_callback
32
33
  config.post_run_callback = @noop_callback
33
34
  config.run_cancelled_callback = @noop_callback
@@ -38,6 +39,7 @@ class WorkerTest < ActiveSupport::TestCase
38
39
 
39
40
  # Stuff we can't test yet in TavernaPlayer::Worker.
40
41
  flexmock(TavernaPlayer::Worker).new_instances do |w|
42
+ w.should_receive(:interactions).and_return(false)
41
43
  w.should_receive(:download_outputs).and_return_undefined
42
44
  w.should_receive(:process_outputs).and_return([])
43
45
  end
@@ -101,7 +103,6 @@ class WorkerTest < ActiveSupport::TestCase
101
103
  r.should_receive(:name=).once.and_return(true)
102
104
  r.should_receive(:start).twice.and_return(false, true)
103
105
  r.should_receive(:start_time).and_return(Time.now)
104
- r.should_receive(:notifications).and_return([])
105
106
  r.should_receive(:finish_time).and_return(Time.now)
106
107
  r.should_receive(:log).once.and_return(0)
107
108
  r.should_receive(:delete).and_return_undefined
@@ -281,6 +282,25 @@ class WorkerTest < ActiveSupport::TestCase
281
282
  # Set a failing post_run callback
282
283
  TavernaPlayer.post_run_callback = Proc.new { raise RuntimeError }
283
284
 
285
+ # Stub the creation of a run on a Taverna Server.
286
+ flexmock(T2Server::Server).new_instances do |s|
287
+ s.should_receive(:initialize_run).once.
288
+ and_return(URI.parse("http://localhost/run/01"))
289
+ end
290
+
291
+ # Stub the Taverna Server run calls.
292
+ flexmock(T2Server::Run).new_instances do |r|
293
+ r.should_receive(:status).times(3).and_return(:initialized, :running, :finished)
294
+ r.should_receive(:create_time).and_return(Time.now)
295
+ r.should_receive(:add_password_credential).and_return(true)
296
+ r.should_receive(:name=).once.and_return(true)
297
+ r.should_receive(:start).once.and_return(true)
298
+ r.should_receive(:start_time).and_return(Time.now)
299
+ r.should_receive(:finish_time).and_return(Time.now)
300
+ r.should_receive(:log).once.and_return(0)
301
+ r.should_receive(:delete).and_return_undefined
302
+ end
303
+
284
304
  assert_equal :pending, @run.state, "Initial run state not ':pending'"
285
305
 
286
306
  @worker.perform
@@ -302,4 +322,50 @@ class WorkerTest < ActiveSupport::TestCase
302
322
 
303
323
  assert_equal :timeout, @run.state, "Final run state not ':timeout'"
304
324
  end
325
+
326
+ test "network error with recovery" do
327
+ # Stub the creation of a run on a Taverna Server with a network error
328
+ # first.
329
+ flexmock(T2Server::Server).new_instances do |s|
330
+ s.should_receive(:initialize_run).twice.
331
+ and_raise(T2Server::ConnectionError, Timeout::Error.new).
332
+ and_return(URI.parse("http://localhost/run/01"))
333
+ end
334
+
335
+ # Stub the Taverna Server run calls.
336
+ flexmock(T2Server::Run).new_instances do |r|
337
+ r.should_receive(:status).times(3).and_return(:initialized, :running, :finished)
338
+ r.should_receive(:create_time).and_return(Time.now)
339
+ r.should_receive(:add_password_credential).and_return(true)
340
+ r.should_receive(:name=).once.and_return(true)
341
+ r.should_receive(:start).once.and_return(true)
342
+ r.should_receive(:start_time).and_return(Time.now)
343
+ r.should_receive(:finish_time).and_return(Time.now)
344
+ r.should_receive(:log).once.and_return(0)
345
+ r.should_receive(:delete).and_return_undefined
346
+ end
347
+
348
+ assert_equal :pending, @run.state, "Initial run state not ':pending'"
349
+
350
+ @worker.perform
351
+
352
+ assert_equal :finished, @run.state, "Final run state not ':finished'"
353
+ end
354
+
355
+ test "network error with no recovery" do
356
+ # Stub the creation of a run on a Taverna Server with a network error
357
+ # first.
358
+ flexmock(T2Server::Server).new_instances do |s|
359
+ # Connection retries are set to 2 so this should be called 3 times.
360
+ s.should_receive(:initialize_run).times(3).
361
+ and_raise(T2Server::ConnectionError, Timeout::Error.new)
362
+ end
363
+
364
+ assert_equal :pending, @run.state, "Initial run state not ':pending'"
365
+
366
+ @worker.perform
367
+
368
+ assert_equal :failed, @run.state, "Final run state not ':finished'"
369
+ end
370
+
305
371
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taverna-player
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Haines
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-19 00:00:00.000000000 Z
11
+ date: 2014-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - ~>
74
74
  - !ruby/object:Gem::Version
75
- version: '1.1'
75
+ version: '1.2'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
- version: '1.1'
82
+ version: '1.2'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: delayed_job_active_record
85
85
  requirement: !ruby/object:Gem::Requirement