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.
@@ -1,5 +1,22 @@
1
1
  = Changes log for the T2 Ruby Gem
2
2
 
3
+ == Version 0.9.2
4
+
5
+ * Cleanup of the README and some delete additions.
6
+ * Complete documentation coverage.
7
+ * Update the parsing of the server version.
8
+ * Optimize Server object creation.
9
+ * Add URI manipulating methods to T2Server::Util.
10
+ * Internal updates to use full URIs rather than relative paths.
11
+ * Updates to the administrator interface to use full URIs.
12
+ * URI code updates for secure workflow and permissions support.
13
+ * Remove (deprecate) Server#delete_run.
14
+ * Support direct PUT of data for newer versions of server.
15
+ * Update the documentation of the Connection classes.
16
+ * Minor updates to the top-level README.
17
+ * Cleanup of t2-server-info script.
18
+ * t2-run-workflow: Only print outputs if there are any.
19
+
3
20
  == Version 0.9.1
4
21
 
5
22
  * Update unit test code for Ruby 1.9 compatibility.
@@ -1,7 +1,7 @@
1
1
  = Taverna[http://www.taverna.org.uk/] 2 Server Interaction Gem
2
2
 
3
3
  Authors:: Robert Haines
4
- Contact:: mailto:rhaines@manchester.ac.uk
4
+ Contact:: mailto:support@mygrid.org.uk
5
5
  URL:: http://www.taverna.org.uk/
6
6
  Licence:: BSD (See LICENCE or http://www.opensource.org/licenses/bsd-license.php)
7
7
  Copyright:: (c) 2010-2012 The University of Manchester, UK
@@ -37,8 +37,8 @@ This version of the library should be compatible with any Ruby code that you
37
37
  may have already written against earlier versions but you may see some warnings
38
38
  about API deprecations. These are clearly marked [DEPRECATION] and will appear
39
39
  on your console output. Anything marked as deprecated will be removed in
40
- version 1.0.0 so you are advised to update your code at your earliest
41
- convenience.
40
+ version 1.0.0 (and are not guaranteed to continue working even until then) so
41
+ you are advised to update your code at your earliest convenience.
42
42
 
43
43
  == Compatibility with Ruby versions
44
44
 
@@ -49,7 +49,7 @@ This library is known to work with the following versions of Ruby:
49
49
  * jruby 1.6.4 (in Ruby 1.8 mode)
50
50
 
51
51
  Those marked with an asterisk (*) are supported and bugs found against them
52
- will be fixed. Other versions may work but are not supported.
52
+ will be fixed. Other versions may work but are not tested or supported.
53
53
 
54
54
  == Usage
55
55
 
@@ -60,7 +60,7 @@ There are two entry points for the T2Server API:
60
60
 
61
61
  In both cases the gem should be initialized by requiring the top level ruby
62
62
  file:
63
- require 't2-server.rb'
63
+ require 't2-server'
64
64
 
65
65
  === Configuring a Server connection
66
66
 
@@ -71,11 +71,11 @@ connection.
71
71
 
72
72
  Connection configuration settings are passed in to various methods using the
73
73
  +ConnectionParameters+ class. Parameters that can be set are:
74
- * <tt>:ca_file</tt>
75
- * <tt>:ca_path</tt>
76
- * <tt>:verify_peer</tt>
77
- * <tt>:client_certificate</tt>
78
- * <tt>:client_password</tt>
74
+ * <tt>:ca_file</tt> - A file to use as a Certificate Authority (CA) for self-signed server certificates.
75
+ * <tt>:ca_path</tt> - Path or list of paths to directories of CA certificates.
76
+ * <tt>:verify_peer</tt> - Use a CA to verify that the Taverna Server you are connecting to has a valid server certificate and that it is the correct one.
77
+ * <tt>:client_certificate</tt> - A certificate to use for client certificate authentication.
78
+ * <tt>:client_password</tt> - The password to unlock the private key of the client certificate.
79
79
 
80
80
  And can be set like this for a standard https connection:
81
81
 
@@ -83,7 +83,7 @@ And can be set like this for a standard https connection:
83
83
  conn_params[:verify_peer] = true
84
84
 
85
85
  This will ensure that the identity of the Taverna Server you are connecting to
86
- will be verified using the set of certificates in <tt>/etc/ssl/certs</tt>.
86
+ will be verified using the default set of certificates for your platform.
87
87
  <tt>:ca_path</tt> can also be set to a list of paths if required. You do not
88
88
  need to include your platform's default certificate paths as these are included
89
89
  automatically.
@@ -117,6 +117,21 @@ Note that credentials are not required by default to simply connect to a
117
117
  Taverna Server. Further operations (such as creating and starting runs) may
118
118
  require authorization depending on how your server has been set up.
119
119
 
120
+ To create a Run on a Server simply pass the workflow you wish to run and your
121
+ credentials:
122
+
123
+ server.create_run(workflow, credentials) do |run|
124
+ ...
125
+ end
126
+
127
+ An individual run can be deleted with its own <tt>delete</tt> method (see below)
128
+ but all runs on a server can be deleted in one go:
129
+
130
+ server.delete_all_runs(credentials)
131
+
132
+ Note that you can only delete runs for which your credentials have delete
133
+ authorization. See later for details.
134
+
120
135
  === Run API example
121
136
 
122
137
  You can bypass the Server API if you know you are only going to be dealing with
@@ -169,6 +184,10 @@ But make sure you request baclava output *before* starting the run:
169
184
  run.wait
170
185
  output = run.baclava_output
171
186
 
187
+ A run can be deleted when no longer needed, like so:
188
+
189
+ run.delete
190
+
172
191
  See the rdoc for more information. Many methods and classes have much more
173
192
  functionality than the defaults described above. Please note that anything
174
193
  which does not appear in the documentation is not intended to be part of the
@@ -239,9 +258,9 @@ WSDL address of the service with your username and password:
239
258
 
240
259
  ==== R Servers (via Rshells)
241
260
 
242
- You can authenticate to R Servers in almost exactly the same as for
261
+ You can authenticate to R Servers in almost exactly the same way as for
243
262
  REST services - only the protocol scheme is different. So instead of
244
- http or https it is rserve:
263
+ +http+ or +https+ it is +rserve+:
245
264
 
246
265
  run.add_password_credential("rserve://example.com:6311", "username", "password")
247
266
 
@@ -175,13 +175,16 @@ begin
175
175
  puts "Zip file written to '#{options[:zip_out]}'"
176
176
  else
177
177
  # go through the outputs and print them out
178
- puts "Outputs:"
179
- run.output_ports.each_value do |port|
180
- print " #{port.name} (#{port.depth}) -> "
181
- if options[:output_refs]
182
- p port.reference
183
- else
184
- p port.value
178
+ outputs = run.output_ports
179
+ unless outputs.empty?
180
+ puts "Outputs:"
181
+ outputs.each_value do |port|
182
+ print " #{port.name} (#{port.depth}) -> "
183
+ if options[:output_refs]
184
+ p port.reference
185
+ else
186
+ p port.value
187
+ end
185
188
  end
186
189
  end
187
190
  end
@@ -56,11 +56,11 @@ uri, creds = parse_address(ARGV.shift, creds)
56
56
  # connect to server and output information
57
57
  begin
58
58
  T2Server::Server.new(uri, conn_params) do |server|
59
- print " Server: #{server.uri}\n"
60
- print " Version: #{server.version}\n"
61
- print " Run limit: #{server.run_limit(creds)}\n"
59
+ puts " Server: #{server.uri}"
60
+ puts " Version: #{server.version}"
61
+ puts " Run limit: #{server.run_limit(creds)}"
62
62
  runs = server.runs(creds)
63
- print "No. of runs: #{runs.length}\n"
63
+ puts "No. of runs: #{runs.length}"
64
64
  if options[:list] && runs.length > 0
65
65
  puts (Hirb::Helpers::ObjectTable.render runs,
66
66
  :fields=>[:identifier, :status, :expiry],
@@ -49,6 +49,8 @@ require 't2-server/admin'
49
49
  # * T2Server::Server - Use this if you are providing a web interface to a
50
50
  # Taverna 2 Server instance.
51
51
  module T2Server
52
+
53
+ # Library version information.
52
54
  module Version
53
55
  # Version information in a Hash
54
56
  INFO = YAML.load_file(File.join(File.dirname(__FILE__), "..",
@@ -46,11 +46,14 @@ module T2Server
46
46
  attr_reader :resources
47
47
 
48
48
  # :stopdoc:
49
+ ADMIN_ENDPOINT = "admin"
50
+
49
51
  def initialize(server, credentials = nil)
50
52
  @server = server
53
+ @uri = Util.append_to_uri_path(server.uri, ADMIN_ENDPOINT)
51
54
  @credentials = credentials
52
55
 
53
- admin_description = xml_document(@server.get_admin_attribute("",
56
+ admin_description = xml_document(@server.read(@uri, "application/xml",
54
57
  @credentials))
55
58
  @resources = get_resources(admin_description)
56
59
  #@resources.each {|key, value| puts "#{key}: #{value}"}
@@ -68,12 +71,12 @@ module T2Server
68
71
  end
69
72
 
70
73
  # :stopdoc:
71
- def get_resource_value(path)
72
- @server.get_admin_attribute(path, @credentials)
74
+ def get_resource_value(uri)
75
+ @server.read(uri, "text/plain", @credentials)
73
76
  end
74
77
 
75
- def set_resource_value(path, val)
76
- @server.set_admin_attribute(path, val.to_s, @credentials)
78
+ def set_resource_value(uri, val)
79
+ @server.update(uri, val.to_s, "text/plain", @credentials)
77
80
  end
78
81
  # :startdoc:
79
82
 
@@ -82,10 +85,9 @@ module T2Server
82
85
  links = {}
83
86
 
84
87
  xml_children(doc.root) do |res|
85
- path = xml_node_attribute(res, 'href').split('/')[-1]
86
- write = @server.admin_resource_writable?(path, @credentials)
87
- links[res.name.downcase] = AdminResource.new(res.name, path,
88
- write, self)
88
+ uri = URI.parse(xml_node_attribute(res, 'href'))
89
+ write = @server.is_resource_writable?(uri, @credentials)
90
+ links[res.name.downcase] = AdminResource.new(res.name, uri, write, self)
89
91
  end
90
92
 
91
93
  links
@@ -100,13 +102,13 @@ module T2Server
100
102
  # The name of this resource.
101
103
  attr_reader :name
102
104
 
103
- # The path to this resource on the server.
104
- attr_reader :path
105
+ # The URI of this resource on the server.
106
+ attr_reader :uri
105
107
 
106
108
  # :stopdoc:
107
- def initialize(name, path, writeable, parent)
109
+ def initialize(name, uri, writeable, parent)
108
110
  @name = name
109
- @path = path
111
+ @uri = uri
110
112
  @admin = parent
111
113
  @writeable = writeable
112
114
 
@@ -123,7 +125,7 @@ module T2Server
123
125
  #
124
126
  # The resource can only be set if it is writable.
125
127
  def value
126
- @admin.get_resource_value(@path)
128
+ @admin.get_resource_value(@uri)
127
129
  end
128
130
 
129
131
  # :call-seq:
@@ -138,7 +140,7 @@ module T2Server
138
140
  def make_writable
139
141
  (class << self; self; end).instance_eval do
140
142
  define_method "value=" do |value|
141
- @admin.set_resource_value(@path, value)
143
+ @admin.set_resource_value(@uri, value)
142
144
  end
143
145
  end
144
146
  end
@@ -66,6 +66,8 @@ module T2Server
66
66
  # way. This could be due to the server not accepting the connection, the
67
67
  # connection being dropped unexpectedly or a timeout of some sort.
68
68
  class ConnectionError < T2ServerError
69
+
70
+ # The internal cause of this connection error.
69
71
  attr_reader :cause
70
72
 
71
73
  # Create a new ConnectionError with the specified cause. The cause to be
@@ -94,6 +96,8 @@ module T2Server
94
96
  # expectation is that the run exists then it could have been destroyed by
95
97
  # a timeout or another user.
96
98
  class RunNotFoundError < T2ServerError
99
+
100
+ # The identifier of the run that was not found on the server.
97
101
  attr_reader :identifier
98
102
 
99
103
  # Create a new RunNotFoundError with the specified identifier.
@@ -106,6 +110,8 @@ module T2Server
106
110
  # Indicates that the attribute that the user is trying to read/change does
107
111
  # not exist. The attribute could be a server or run attribute.
108
112
  class AttributeNotFoundError < T2ServerError
113
+
114
+ # The path of the attribute that was not found on the server.
109
115
  attr_reader :path
110
116
 
111
117
  # Create a new AttributeNotFoundError with the path to the erroneous
@@ -128,6 +134,8 @@ module T2Server
128
134
  # Access to the entity (run or attribute) is denied. The credentials
129
135
  # supplied are not sufficient or the server does not allow the operation.
130
136
  class AccessForbiddenError < T2ServerError
137
+
138
+ # The path of the attribute that the user is forbidden to access.
131
139
  attr_reader :path
132
140
 
133
141
  # Create a new AccessForbiddenError with the path to the restricted
@@ -142,6 +150,8 @@ module T2Server
142
150
 
143
151
  # Access to the server is denied to this username
144
152
  class AuthorizationError < T2ServerError
153
+
154
+ # The username that has failed authorization.
145
155
  attr_reader :username
146
156
 
147
157
  # Create a new AuthorizationError with the rejected username
@@ -98,84 +98,18 @@ module T2Server
98
98
  end
99
99
 
100
100
  # :call-seq:
101
- # POST_run(path, value, credentials) -> String
101
+ # GET(uri, type, range, credentials) -> String
102
102
  #
103
- # Initialize a T2Server::Run on a server by uploading its workflow.
104
- # The new run's identifier (in String form) is returned.
105
- def POST_run(path, value, credentials)
106
- response = _POST(path, value, "application/xml", credentials)
107
-
108
- case response
109
- when Net::HTTPCreated
110
- # return the identifier of the newly created run
111
- path_leaf_from_uri(response['location'])
112
- when Net::HTTPForbidden
113
- raise ServerAtCapacityError.new
114
- when Net::HTTPUnauthorized
115
- raise AuthorizationError.new(credentials)
116
- else
117
- raise UnexpectedServerResponse.new(response)
118
- end
119
- end
120
-
121
- # :call-seq:
122
- # POST_file(path, value, run, credentials) -> bool
123
- #
124
- # Upload a file to a run. If successful, true is returned.
125
- def POST_file(path, value, run, credentials)
126
- response = _POST(path, value, "application/xml", credentials)
127
-
128
- case response
129
- when Net::HTTPCreated
130
- # OK, carry on...
131
- true
132
- when Net::HTTPNotFound
133
- raise RunNotFoundError.new(run)
134
- when Net::HTTPForbidden
135
- raise AccessForbiddenError.new("run #{run}")
136
- when Net::HTTPUnauthorized
137
- raise AuthorizationError.new(credentials)
138
- else
139
- raise UnexpectedServerResponse.new(response)
140
- end
141
- end
142
-
143
- # :call-seq:
144
- # POST_dir(path, value, run, dir, credentials) -> bool
145
- #
146
- # Create a directory in the scratch space of a run. If successful, true
147
- # is returned.
148
- def POST_dir(path, value, run, dir, credentials)
149
- response = _POST(path, value, "application/xml", credentials)
150
-
151
- case response
152
- when Net::HTTPCreated
153
- # OK, carry on...
154
- true
155
- when Net::HTTPNotFound
156
- raise RunNotFoundError.new(run)
157
- when Net::HTTPForbidden
158
- raise AccessForbiddenError.new("#{dir} on run #{run}")
159
- when Net::HTTPUnauthorized
160
- raise AuthorizationError.new(credentials)
161
- else
162
- raise UnexpectedServerResponse.new(response)
163
- end
164
- end
165
-
166
- # :call-seq:
167
- # GET(path, type, range, credentials) -> String
168
- #
169
- # HTTP GET a resource at _path_ of _type_ from the server. If successful
103
+ # HTTP GET a resource at _uri_ of _type_ from the server. If successful
170
104
  # the body of the response is returned. A portion of the data can be
171
105
  # retrieved by specifying a byte range, start..end, with the _range_
172
106
  # parameter.
173
- def GET(path, type, range, credentials)
174
- get = Net::HTTP::Get.new(path)
107
+ def GET(uri, type, range, credentials)
108
+ get = Net::HTTP::Get.new(uri.path)
175
109
  get["Accept"] = type
176
110
  get["Range"] = "bytes=#{range.min}-#{range.max}" unless range.nil?
177
111
 
178
- response = submit(get, path, credentials)
112
+ response = submit(get, uri, credentials)
179
113
 
180
114
  case response
181
115
  when Net::HTTPOK, Net::HTTPPartialContent
@@ -186,9 +120,9 @@ module T2Server
186
120
  new_conn = redirect(response["location"])
187
121
  raise ConnectionRedirectError.new(new_conn)
188
122
  when Net::HTTPNotFound
189
- raise AttributeNotFoundError.new(path)
123
+ raise AttributeNotFoundError.new(uri.path)
190
124
  when Net::HTTPForbidden
191
- raise AccessForbiddenError.new("attribute #{path}")
125
+ raise AccessForbiddenError.new("attribute #{uri.path}")
192
126
  when Net::HTTPUnauthorized
193
127
  raise AuthorizationError.new(credentials)
194
128
  else
@@ -197,25 +131,37 @@ module T2Server
197
131
  end
198
132
 
199
133
  # :call-seq:
200
- # PUT(path, value, type, credentials) -> bool
134
+ # PUT(uri, value, type, credentials) -> bool
135
+ # PUT(uri, value, type, credentials) -> URI
201
136
  #
202
- # Perform a HTTP PUT of _value_ to a path on the server. If successful
203
- # true is returned.
204
- def PUT(path, value, type, credentials)
205
- put = Net::HTTP::Put.new(path)
137
+ # Perform a HTTP PUT of _value_ to a location on the server specified by
138
+ # _uri_ . If successful _true_ , or a URI to the PUT resource, is returned
139
+ # depending on whether the operation has set a parameter (true) or uploaded
140
+ # data (URI).
141
+ def PUT(uri, value, type, credentials)
142
+ put = Net::HTTP::Put.new(uri.path)
206
143
  put.content_type = type
207
144
  put.body = value
208
145
 
209
- response = submit(put, path, credentials)
146
+ response = submit(put, uri, credentials)
210
147
 
211
148
  case response
212
149
  when Net::HTTPOK
213
- # OK, so carry on
150
+ # We've set a parameter so we get 200 back from the server. Return
151
+ # true to indicate success.
214
152
  true
153
+ when Net::HTTPCreated
154
+ # We've uploaded data so we get 201 back from the server. Return the
155
+ # uri of the created resource.
156
+ URI.parse(response['location'])
157
+ when Net::HTTPNoContent
158
+ # We've modified data so we get 204 back from the server. Return the
159
+ # uri of the modified resource.
160
+ uri
215
161
  when Net::HTTPNotFound
216
- raise AttributeNotFoundError.new(path)
162
+ raise AttributeNotFoundError.new(uri.path)
217
163
  when Net::HTTPForbidden
218
- raise AccessForbiddenError.new("attribute #{path}")
164
+ raise AccessForbiddenError.new("attribute #{uri.path}")
219
165
  when Net::HTTPUnauthorized
220
166
  raise AuthorizationError.new(credentials)
221
167
  else
@@ -224,21 +170,29 @@ module T2Server
224
170
  end
225
171
 
226
172
  # :call-seq:
227
- # POST(path, value, type, credentials)
173
+ # POST(uri, value, type, credentials)
228
174
  #
229
- # Perform an HTTP POST of _value_ to a path on the server and return the
230
- # identifier of the created attribute as a string.
231
- def POST(path, value, type, credentials)
232
- response = _POST(path, value, type, credentials)
175
+ # Perform an HTTP POST of _value_ to a location on the server specified by
176
+ # _uri_ and return the URI of the created attribute.
177
+ def POST(uri, value, type, credentials)
178
+ post = Net::HTTP::Post.new(uri.path)
179
+ post.content_type = type
180
+ post.body = value
181
+
182
+ response = submit(post, uri, credentials)
233
183
 
234
184
  case response
235
185
  when Net::HTTPCreated
236
- # return the identifier of the newly created item
237
- path_leaf_from_uri(response['location'])
186
+ # return the URI of the newly created item
187
+ URI.parse(response['location'])
238
188
  when Net::HTTPNotFound
239
- raise AttributeNotFoundError.new(path)
189
+ raise AttributeNotFoundError.new(uri.path)
240
190
  when Net::HTTPForbidden
241
- raise AccessForbiddenError.new("attribute #{path}")
191
+ if response.body.chomp.include? "server load exceeded"
192
+ raise ServerAtCapacityError.new
193
+ else
194
+ raise AccessForbiddenError.new("attribute #{uri.path}")
195
+ end
242
196
  when Net::HTTPUnauthorized
243
197
  raise AuthorizationError.new(credentials)
244
198
  else
@@ -247,24 +201,23 @@ module T2Server
247
201
  end
248
202
 
249
203
  # :call-seq:
250
- # DELETE(path, credentials) -> bool
204
+ # DELETE(uri, credentials) -> bool
251
205
  #
252
- # Perform an HTTP DELETE on a path on the server. If successful true
206
+ # Perform an HTTP DELETE on a _uri_ on the server. If successful true
253
207
  # is returned.
254
- def DELETE(path, credentials)
255
- run = path.split("/")[-1]
256
- delete = Net::HTTP::Delete.new(path)
208
+ def DELETE(uri, credentials)
209
+ delete = Net::HTTP::Delete.new(uri.path)
257
210
 
258
- response = submit(delete, path, credentials)
211
+ response = submit(delete, uri, credentials)
259
212
 
260
213
  case response
261
214
  when Net::HTTPNoContent
262
215
  # Success, carry on...
263
216
  true
264
217
  when Net::HTTPNotFound
265
- raise RunNotFoundError.new(run)
218
+ false
266
219
  when Net::HTTPForbidden
267
- raise AccessForbiddenError.new("run #{run}")
220
+ raise AccessForbiddenError.new(uri)
268
221
  when Net::HTTPUnauthorized
269
222
  raise AuthorizationError.new(credentials)
270
223
  else
@@ -273,20 +226,20 @@ module T2Server
273
226
  end
274
227
 
275
228
  # :call-seq:
276
- # OPTIONS(path, credentials) -> Hash
229
+ # OPTIONS(uri, credentials) -> Hash
277
230
  #
278
- # Perform the HTTP OPTIONS command on the given _path_ and return a hash
231
+ # Perform the HTTP OPTIONS command on the given _uri_ and return a hash
279
232
  # of the headers returned.
280
- def OPTIONS(path, credentials)
281
- options = Net::HTTP::Options.new(path)
233
+ def OPTIONS(uri, credentials)
234
+ options = Net::HTTP::Options.new(uri.path)
282
235
 
283
- response = submit(options, path, credentials)
236
+ response = submit(options, uri, credentials)
284
237
 
285
238
  case response
286
239
  when Net::HTTPOK
287
240
  response.to_hash
288
241
  when Net::HTTPForbidden
289
- raise AccessForbiddenError.new("resource #{path}")
242
+ raise AccessForbiddenError.new("resource #{uri.path}")
290
243
  when Net::HTTPUnauthorized
291
244
  raise AuthorizationError.new(credentials)
292
245
  else
@@ -295,26 +248,13 @@ module T2Server
295
248
  end
296
249
 
297
250
  private
298
- def _POST(path, value, type, credentials)
299
- post = Net::HTTP::Post.new(path)
300
- post.content_type = type
301
- post.body = value
302
-
303
- submit(post, path, credentials)
304
- end
305
-
306
- def path_leaf_from_uri(uri)
307
- URI.parse(uri).path.split('/')[-1]
308
- end
309
251
 
310
- def submit(request, path, credentials)
311
- full_uri = @uri.clone
312
- full_uri.path = path
252
+ def submit(request, uri, credentials)
313
253
 
314
254
  credentials.authenticate(request) unless credentials.nil?
315
255
 
316
256
  begin
317
- @http.request(full_uri, request)
257
+ @http.request(uri, request)
318
258
  rescue InternalHTTPError => e
319
259
  raise ConnectionError.new(e)
320
260
  end