t2-server 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.rdoc +17 -0
- data/README.rdoc +32 -13
- data/bin/t2-run-workflow +10 -7
- data/bin/t2-server-info +4 -4
- data/lib/t2-server.rb +2 -0
- data/lib/t2-server/admin.rb +17 -15
- data/lib/t2-server/exceptions.rb +10 -0
- data/lib/t2-server/net/connection.rb +60 -120
- data/lib/t2-server/net/parameters.rb +4 -0
- data/lib/t2-server/port.rb +3 -3
- data/lib/t2-server/run.rb +125 -151
- data/lib/t2-server/server.rb +130 -161
- data/lib/t2-server/util.rb +39 -0
- data/t2-server.gemspec +1 -1
- data/test/tc_admin.rb +5 -2
- data/test/tc_run.rb +11 -0
- data/test/tc_server.rb +2 -2
- data/test/tc_util.rb +44 -0
- data/test/ts_t2server.rb +1 -0
- data/test/workflows/no-ports.t2flow +76 -0
- data/version.yml +1 -1
- metadata +3 -2
data/lib/t2-server/server.rb
CHANGED
@@ -40,10 +40,11 @@ module T2Server
|
|
40
40
|
class Server
|
41
41
|
include XML::Methods
|
42
42
|
|
43
|
-
# The version of the remote Taverna Server instance.
|
44
|
-
attr_reader :version
|
45
|
-
|
46
43
|
# :stopdoc:
|
44
|
+
# Internal references to the main rest and admin top-level resource
|
45
|
+
# endpoints.
|
46
|
+
REST_ENDPOINT = "rest/"
|
47
|
+
|
47
48
|
XPaths = {
|
48
49
|
# Server top-level XPath queries
|
49
50
|
:server => XML::Methods.xpath_compile("//nsr:serverDescription"),
|
@@ -85,13 +86,12 @@ module T2Server
|
|
85
86
|
# setup connection
|
86
87
|
@connection = ConnectionFactory.connect(uri, params)
|
87
88
|
|
88
|
-
#
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
@
|
93
|
-
@links =
|
94
|
-
@links[:admin] = "#{uri.path}/admin"
|
89
|
+
# The following four fields hold cached data about the server that is
|
90
|
+
# only downloaded the first time it is requested.
|
91
|
+
@server_doc = nil
|
92
|
+
@version = nil
|
93
|
+
@version_components = nil
|
94
|
+
@links = nil
|
95
95
|
|
96
96
|
# initialize run object cache
|
97
97
|
@runs = {}
|
@@ -149,8 +149,34 @@ module T2Server
|
|
149
149
|
user = credentials.nil? ? :all : credentials.username
|
150
150
|
@runs[user] = {} unless @runs[user]
|
151
151
|
|
152
|
-
@connection.
|
153
|
-
|
152
|
+
@connection.POST(links[:runs], workflow,
|
153
|
+
"application/vnd.taverna.t2flow+xml", credentials)
|
154
|
+
end
|
155
|
+
|
156
|
+
# :call-seq:
|
157
|
+
# version -> String
|
158
|
+
#
|
159
|
+
# The version string of the remote Taverna Server.
|
160
|
+
def version
|
161
|
+
if @version.nil?
|
162
|
+
@version = _get_version
|
163
|
+
end
|
164
|
+
|
165
|
+
@version
|
166
|
+
end
|
167
|
+
|
168
|
+
# :call-seq:
|
169
|
+
# version_components -> Array
|
170
|
+
#
|
171
|
+
# An array of the major, minor and patch version components of the remote
|
172
|
+
# Taverna Server.
|
173
|
+
def version_components
|
174
|
+
if @version_components.nil?
|
175
|
+
comps = version.split(".")
|
176
|
+
@version_components = comps.map { |v| v.to_i }
|
177
|
+
end
|
178
|
+
|
179
|
+
@version_components
|
154
180
|
end
|
155
181
|
|
156
182
|
# :call-seq:
|
@@ -168,7 +194,7 @@ module T2Server
|
|
168
194
|
# Runs in any state (+Initialized+, +Running+ and +Finished+) are counted
|
169
195
|
# against this maximum.
|
170
196
|
def run_limit(credentials = nil)
|
171
|
-
|
197
|
+
read(links[:runlimit], "text/plain", credentials).to_i
|
172
198
|
end
|
173
199
|
|
174
200
|
# :call-seq:
|
@@ -187,29 +213,32 @@ module T2Server
|
|
187
213
|
get_runs(credentials)[identifier]
|
188
214
|
end
|
189
215
|
|
190
|
-
# :
|
191
|
-
# delete_run(run, credentials = nil) -> bool
|
192
|
-
#
|
193
|
-
# Delete the specified run from the server, discarding all of its state.
|
194
|
-
# _run_ can be either a Run instance or a identifier.
|
216
|
+
# :stopdoc:
|
195
217
|
def delete_run(run, credentials = nil)
|
218
|
+
warn "[DEPRECATION] 'Server#delete_run' is deprecated and will be " +
|
219
|
+
"removed in 1.0. Please use 'Run#delete' to delete a run."
|
220
|
+
|
196
221
|
# get the identifier from the run if that is what is passed in
|
197
222
|
if run.instance_of? Run
|
198
223
|
run = run.identifier
|
199
224
|
end
|
200
225
|
|
201
|
-
|
226
|
+
run_uri = Util.append_to_uri_path(links[:runs], run)
|
227
|
+
if delete(run_uri, credentials)
|
202
228
|
# delete cached run object - this must be done per user
|
203
229
|
user = credentials.nil? ? :all : credentials.username
|
204
230
|
@runs[user].delete(run) if @runs[user]
|
205
231
|
true
|
206
232
|
end
|
207
233
|
end
|
234
|
+
# :startdoc:
|
208
235
|
|
209
236
|
# :call-seq:
|
210
237
|
# delete_all_runs(credentials = nil)
|
211
238
|
#
|
212
|
-
# Delete all runs on this server, discarding all of their state.
|
239
|
+
# Delete all runs on this server, discarding all of their state. Note that
|
240
|
+
# only those runs that the provided credentials have permission to delete
|
241
|
+
# will be deleted.
|
213
242
|
def delete_all_runs(credentials = nil)
|
214
243
|
# first refresh run list
|
215
244
|
runs(credentials).each {|run| run.delete}
|
@@ -244,15 +273,9 @@ module T2Server
|
|
244
273
|
run.input_port(input).remote_file = filename
|
245
274
|
end
|
246
275
|
|
247
|
-
def
|
248
|
-
|
249
|
-
|
250
|
-
run = run.identifier
|
251
|
-
end
|
252
|
-
|
253
|
-
raise AccessForbiddenError.new("subdirectories (#{dir})") if dir.include? ?/
|
254
|
-
@connection.POST_dir("#{@links[:runs]}/#{run}/#{root}",
|
255
|
-
XML::Fragments::MKDIR % dir, run, dir, credentials)
|
276
|
+
def mkdir(uri, dir, credentials = nil)
|
277
|
+
@connection.POST(uri, XML::Fragments::MKDIR % dir, "application/xml",
|
278
|
+
credentials)
|
256
279
|
end
|
257
280
|
|
258
281
|
def make_run_dir(run, root, dir, credentials = nil)
|
@@ -262,25 +285,26 @@ module T2Server
|
|
262
285
|
create_dir(run, root, dir, credentials)
|
263
286
|
end
|
264
287
|
|
265
|
-
def upload_file(
|
288
|
+
def upload_file(filename, uri, remote_name, credentials = nil)
|
266
289
|
contents = IO.read(filename)
|
267
|
-
|
290
|
+
remote_name = filename.split('/')[-1] if remote_name == ""
|
268
291
|
|
269
|
-
|
270
|
-
rename
|
271
|
-
end
|
292
|
+
upload_data(contents, remote_name, uri, credentials)
|
272
293
|
end
|
273
294
|
|
274
|
-
def upload_data(
|
275
|
-
#
|
276
|
-
|
277
|
-
run = run.identifier
|
278
|
-
end
|
295
|
+
def upload_data(data, remote_name, uri, credentials = nil)
|
296
|
+
# Different Server versions support different upload methods
|
297
|
+
(major, minor, patch) = version_components
|
279
298
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
299
|
+
if minor == 4 && patch >= 1
|
300
|
+
put_uri = Util.append_to_uri_path(uri, remote_name)
|
301
|
+
@connection.PUT(put_uri, data, "application/octet-stream", credentials)
|
302
|
+
else
|
303
|
+
contents = Base64.encode64(data)
|
304
|
+
@connection.POST(uri,
|
305
|
+
XML::Fragments::UPLOAD % [remote_name, contents], "application/xml",
|
306
|
+
credentials)
|
307
|
+
end
|
284
308
|
end
|
285
309
|
|
286
310
|
def upload_run_file(run, filename, location, rename, credentials = nil)
|
@@ -291,105 +315,16 @@ module T2Server
|
|
291
315
|
upload_file(run, filename, location, rename, credentials)
|
292
316
|
end
|
293
317
|
|
294
|
-
def
|
295
|
-
|
296
|
-
if run.instance_of? Run
|
297
|
-
run = run.identifier
|
298
|
-
end
|
299
|
-
|
300
|
-
create_attribute("#{@links[:runs]}/#{run}/#{path}", value, type,
|
301
|
-
credentials)
|
302
|
-
rescue AttributeNotFoundError => e
|
303
|
-
if get_runs(credentials).has_key? run
|
304
|
-
raise e
|
305
|
-
else
|
306
|
-
raise RunNotFoundError.new(run)
|
307
|
-
end
|
308
|
-
end
|
309
|
-
|
310
|
-
def get_run_attribute(run, path, type, credentials = nil)
|
311
|
-
# get the identifier from the run if that is what is passed in
|
312
|
-
if run.instance_of? Run
|
313
|
-
run = run.identifier
|
314
|
-
end
|
315
|
-
|
316
|
-
get_attribute("#{@links[:runs]}/#{run}/#{path}", type, credentials)
|
317
|
-
rescue AttributeNotFoundError => e
|
318
|
-
if get_runs(credentials).has_key? run
|
319
|
-
raise e
|
320
|
-
else
|
321
|
-
raise RunNotFoundError.new(run)
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
def set_run_attribute(run, path, value, type, credentials = nil)
|
326
|
-
# get the identifier from the run if that is what is passed in
|
327
|
-
if run.instance_of? Run
|
328
|
-
run = run.identifier
|
329
|
-
end
|
330
|
-
|
331
|
-
set_attribute("#{@links[:runs]}/#{run}/#{path}", value, type,
|
332
|
-
credentials)
|
333
|
-
rescue AttributeNotFoundError => e
|
334
|
-
if get_runs(credentials).has_key? run
|
335
|
-
raise e
|
336
|
-
else
|
337
|
-
raise RunNotFoundError.new(run)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
def delete_run_attribute(run, path, credentials = nil)
|
342
|
-
# get the identifier from the run if that is what is passed in
|
343
|
-
if run.instance_of? Run
|
344
|
-
run = run.identifier
|
345
|
-
end
|
346
|
-
|
347
|
-
delete_attribute("#{@links[:runs]}/#{run}/#{path}", credentials)
|
348
|
-
rescue AttributeNotFoundError => e
|
349
|
-
if get_runs(credentials).has_key? run
|
350
|
-
raise e
|
351
|
-
else
|
352
|
-
raise RunNotFoundError.new(run)
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
def download_run_file(run, path, range, credentials = nil)
|
357
|
-
# get the identifier from the run if that is what is passed in
|
358
|
-
if run.instance_of? Run
|
359
|
-
run = run.identifier
|
360
|
-
end
|
361
|
-
|
362
|
-
get_attribute("#{@links[:runs]}/#{run}/#{path}",
|
363
|
-
"application/octet-stream", range, credentials)
|
364
|
-
rescue AttributeNotFoundError => e
|
365
|
-
if get_runs(credentials).has_key? run
|
366
|
-
raise e
|
367
|
-
else
|
368
|
-
raise RunNotFoundError.new(run)
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
def get_admin_attribute(path, credentials = nil)
|
373
|
-
get_attribute("#{@links[:admin]}/#{path}", "*/*", credentials)
|
374
|
-
end
|
375
|
-
|
376
|
-
def set_admin_attribute(path, value, credentials = nil)
|
377
|
-
set_attribute("#{@links[:admin]}/#{path}", value, "text/plain",
|
378
|
-
credentials)
|
379
|
-
end
|
380
|
-
|
381
|
-
def admin_resource_writable?(path, credentials = nil)
|
382
|
-
headers = @connection.OPTIONS("#{@links[:admin]}/#{path}", credentials)
|
318
|
+
def is_resource_writable?(uri, credentials = nil)
|
319
|
+
headers = @connection.OPTIONS(uri, credentials)
|
383
320
|
headers["allow"][0].split(",").include? "PUT"
|
384
321
|
end
|
385
|
-
# :startdoc:
|
386
322
|
|
387
|
-
|
388
|
-
|
389
|
-
@connection.POST(path, value, type, credentials)
|
323
|
+
def create(uri, value, type, credentials = nil)
|
324
|
+
@connection.POST(uri, value, type, credentials)
|
390
325
|
end
|
391
326
|
|
392
|
-
def
|
327
|
+
def read(uri, type, *rest)
|
393
328
|
credentials = nil
|
394
329
|
range = nil
|
395
330
|
|
@@ -405,77 +340,111 @@ module T2Server
|
|
405
340
|
end
|
406
341
|
|
407
342
|
begin
|
408
|
-
@connection.GET(
|
343
|
+
@connection.GET(uri, type, range, credentials)
|
409
344
|
rescue ConnectionRedirectError => cre
|
345
|
+
# We've been redirected so save the new connection object with the new
|
346
|
+
# server URI and try again with the new URI.
|
410
347
|
@connection = cre.redirect
|
348
|
+
uri = Util.replace_uri_path(@connection.uri, uri.path)
|
411
349
|
retry
|
412
350
|
end
|
413
351
|
end
|
414
352
|
|
415
|
-
def
|
416
|
-
@connection.PUT(
|
353
|
+
def update(uri, value, type, credentials = nil)
|
354
|
+
@connection.PUT(uri, value, type, credentials)
|
417
355
|
end
|
418
356
|
|
419
|
-
def
|
420
|
-
@connection.DELETE(
|
357
|
+
def delete(uri, credentials = nil)
|
358
|
+
@connection.DELETE(uri, credentials)
|
421
359
|
end
|
360
|
+
# :startdoc:
|
361
|
+
|
362
|
+
private
|
422
363
|
|
423
|
-
def
|
364
|
+
def links
|
365
|
+
@links = _get_server_links if @links.nil?
|
366
|
+
|
367
|
+
@links
|
368
|
+
end
|
369
|
+
|
370
|
+
def _get_server_description
|
371
|
+
if @server_doc.nil?
|
372
|
+
rest_uri = Util.append_to_uri_path(uri, REST_ENDPOINT)
|
373
|
+
@server_doc = xml_document(read(rest_uri, "application/xml"))
|
374
|
+
end
|
375
|
+
|
376
|
+
@server_doc
|
377
|
+
end
|
378
|
+
|
379
|
+
def _get_version
|
380
|
+
doc = _get_server_description
|
424
381
|
version = xpath_attr(doc, XPaths[:server], "serverVersion")
|
425
382
|
if version == nil
|
426
383
|
raise RuntimeError.new("Taverna Servers prior to version 2.3 " +
|
427
384
|
"are no longer supported.")
|
428
385
|
else
|
429
|
-
|
386
|
+
# Remove SNAPSHOT tag if it's there.
|
387
|
+
if version.end_with? "-SNAPSHOT"
|
388
|
+
version.gsub!("-SNAPSHOT", "")
|
389
|
+
end
|
390
|
+
|
391
|
+
# Add .0 if we only have a major and minor component.
|
392
|
+
if version.split(".").length == 2
|
393
|
+
version += ".0"
|
394
|
+
end
|
395
|
+
|
396
|
+
return version
|
430
397
|
end
|
431
398
|
end
|
432
399
|
|
433
|
-
def
|
400
|
+
def _get_server_links
|
401
|
+
doc = _get_server_description
|
434
402
|
links = {}
|
435
|
-
links[:runs] = URI.parse(xpath_attr(doc, XPaths[:runs], "href"))
|
403
|
+
links[:runs] = URI.parse(xpath_attr(doc, XPaths[:runs], "href"))
|
436
404
|
|
437
|
-
links[:policy] = URI.parse(xpath_attr(doc, XPaths[:policy], "href"))
|
438
|
-
doc = xml_document(
|
405
|
+
links[:policy] = URI.parse(xpath_attr(doc, XPaths[:policy], "href"))
|
406
|
+
doc = xml_document(read(links[:policy], "application/xml"))
|
439
407
|
|
440
408
|
links[:permlisteners] =
|
441
|
-
URI.parse(xpath_attr(doc, XPaths[:permlstt], "href"))
|
409
|
+
URI.parse(xpath_attr(doc, XPaths[:permlstt], "href"))
|
442
410
|
links[:notifications] =
|
443
|
-
URI.parse(xpath_attr(doc, XPaths[:notify], "href"))
|
411
|
+
URI.parse(xpath_attr(doc, XPaths[:notify], "href"))
|
444
412
|
|
445
413
|
links[:runlimit] =
|
446
|
-
URI.parse(xpath_attr(doc, XPaths[:runlimit], "href"))
|
414
|
+
URI.parse(xpath_attr(doc, XPaths[:runlimit], "href"))
|
447
415
|
links[:permworkflows] =
|
448
|
-
URI.parse(xpath_attr(doc, XPaths[:permwkf], "href"))
|
416
|
+
URI.parse(xpath_attr(doc, XPaths[:permwkf], "href"))
|
449
417
|
|
450
418
|
links
|
451
419
|
end
|
452
420
|
|
453
421
|
def get_runs(credentials = nil)
|
454
|
-
run_list =
|
455
|
-
credentials)
|
422
|
+
run_list = read(links[:runs], "application/xml", credentials)
|
456
423
|
|
457
424
|
doc = xml_document(run_list)
|
458
425
|
|
459
426
|
# get list of run identifiers
|
460
|
-
|
427
|
+
run_list = {}
|
461
428
|
xpath_find(doc, XPaths[:run]).each do |run|
|
462
|
-
|
429
|
+
uri = URI.parse(xml_node_attribute(run, "href"))
|
430
|
+
id = xml_node_content(run)
|
431
|
+
run_list[id] = uri
|
463
432
|
end
|
464
433
|
|
465
434
|
# cache run objects - this must be done per user
|
466
435
|
user = credentials.nil? ? :all : credentials.username
|
467
436
|
@runs[user] = {} unless @runs[user]
|
468
437
|
|
469
|
-
# add new runs
|
470
|
-
|
438
|
+
# add new runs to the user cache
|
439
|
+
run_list.each_key do |id|
|
471
440
|
if !@runs[user].has_key? id
|
472
|
-
@runs[user][id] = Run.create(self, "", credentials, id)
|
441
|
+
@runs[user][id] = Run.create(self, "", credentials, run_list[id])
|
473
442
|
end
|
474
443
|
end
|
475
444
|
|
476
445
|
# clear out the expired runs
|
477
|
-
if @runs[user].length >
|
478
|
-
@runs[user].delete_if {|key, val| !
|
446
|
+
if @runs[user].length > run_list.length
|
447
|
+
@runs[user].delete_if {|key, val| !run_list.member? key}
|
479
448
|
end
|
480
449
|
|
481
450
|
@runs[user]
|
data/lib/t2-server/util.rb
CHANGED
@@ -31,6 +31,9 @@
|
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
33
|
module T2Server
|
34
|
+
|
35
|
+
# This module contains various utility methods that the library uses
|
36
|
+
# internally.
|
34
37
|
module Util
|
35
38
|
|
36
39
|
# :call-seq:
|
@@ -67,5 +70,41 @@ module T2Server
|
|
67
70
|
path.gsub(/^\//, "").chomp("/")
|
68
71
|
end
|
69
72
|
|
73
|
+
# :call-seq:
|
74
|
+
# Util.append_to_uri_path(uri, path) -> URI
|
75
|
+
#
|
76
|
+
# Appends a path to the end of the path of the given URI.
|
77
|
+
def self.append_to_uri_path(uri, path)
|
78
|
+
return uri if path == ""
|
79
|
+
|
80
|
+
new_uri = uri.clone
|
81
|
+
new_uri.path = "#{uri.path}/#{path}"
|
82
|
+
|
83
|
+
new_uri
|
84
|
+
end
|
85
|
+
|
86
|
+
# :call-seq:
|
87
|
+
# Util.replace_uri_path(uri, path) -> URI
|
88
|
+
#
|
89
|
+
# Replace the given URI's path with a new one. The new path must be an
|
90
|
+
# absolute path (start with a slash).
|
91
|
+
def self.replace_uri_path(uri, path)
|
92
|
+
new_uri = uri.clone
|
93
|
+
new_uri.path = path
|
94
|
+
|
95
|
+
new_uri
|
96
|
+
end
|
97
|
+
|
98
|
+
# :call_seq:
|
99
|
+
# Util.get_path_leaf_from_uri(uri) -> String
|
100
|
+
#
|
101
|
+
# Get the final component from the path of a URI. This method returns the
|
102
|
+
# empty string (not _nil_ ) if the URI does not have a path.
|
103
|
+
def self.get_path_leaf_from_uri(uri)
|
104
|
+
path = uri.path.split("/")[-1]
|
105
|
+
|
106
|
+
path.nil? ? "" : path
|
107
|
+
end
|
108
|
+
|
70
109
|
end
|
71
110
|
end
|