t2-server 0.9.1 → 0.9.2
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.
- 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
|