kerryb-right_aws 1.7.3

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.
@@ -0,0 +1,604 @@
1
+ #
2
+ # Copyright (c) 2007-2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ module RightAws
25
+
26
+ class SqsInterface < RightAwsBase
27
+ include RightAwsBaseInterface
28
+
29
+ API_VERSION = "2007-05-01"
30
+ DEFAULT_HOST = "queue.amazonaws.com"
31
+ DEFAULT_PORT = 443
32
+ DEFAULT_PROTOCOL = 'https'
33
+ REQUEST_TTL = 30
34
+ DEFAULT_VISIBILITY_TIMEOUT = 30
35
+
36
+
37
+ @@bench = AwsBenchmarkingBlock.new
38
+ def self.bench_xml
39
+ @@bench.xml
40
+ end
41
+ def self.bench_sqs
42
+ @@bench.service
43
+ end
44
+
45
+ @@api = API_VERSION
46
+ def self.api
47
+ @@api
48
+ end
49
+
50
+ # Creates a new SqsInterface instance.
51
+ #
52
+ # sqs = RightAws::SqsInterface.new('1E3GDYEOGFJPIT75KDT40','hgTHt68JY07JKUY08ftHYtERkjgtfERn57DFE379', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')})
53
+ #
54
+ # Params is a hash:
55
+ #
56
+ # {:server => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com'(default)
57
+ # :port => 443 # Amazon service port: 80 or 443(default)
58
+ # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
59
+ # :signature_version => '0' # The signature version : '0' or '1'(default)
60
+ # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
61
+ #
62
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
63
+ init({ :name => 'SQS',
64
+ :default_host => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).host : DEFAULT_HOST,
65
+ :default_port => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).port : DEFAULT_PORT,
66
+ :default_protocol => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).scheme : DEFAULT_PROTOCOL },
67
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
68
+ aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
69
+ params)
70
+ end
71
+
72
+
73
+ #-----------------------------------------------------------------
74
+ # Requests
75
+ #-----------------------------------------------------------------
76
+
77
+ # Generates a request hash for the query API
78
+ def generate_request(action, params={}) # :nodoc:
79
+ # Sometimes we need to use queue uri (delete queue etc)
80
+ # In that case we will use Symbol key: 'param[:queue_url]'
81
+ queue_uri = params[:queue_url] ? URI(params[:queue_url]).path : '/'
82
+ # remove unset(=optional) and symbolyc keys
83
+ params.each{ |key, value| params.delete(key) if (value.nil? || key.is_a?(Symbol)) }
84
+ # prepare output hash
85
+ service_hash = { "Action" => action,
86
+ "Expires" => (Time.now + REQUEST_TTL).utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
87
+ "AWSAccessKeyId" => @aws_access_key_id,
88
+ "Version" => API_VERSION,
89
+ "SignatureVersion" => signature_version }
90
+ service_hash.update(params)
91
+ # prepare string to sight
92
+ string_to_sign = case signature_version
93
+ when '0' : service_hash["Action"] + service_hash["Expires"]
94
+ when '1' : service_hash.sort{|a,b| (a[0].to_s.downcase)<=>(b[0].to_s.downcase)}.to_s
95
+ end
96
+ service_hash['Signature'] = AwsUtils::sign(@aws_secret_access_key, string_to_sign)
97
+ request_params = service_hash.to_a.collect{|key,val| key.to_s + "=" + CGI::escape(val.to_s) }.join("&")
98
+ request = Net::HTTP::Get.new("#{queue_uri}?#{request_params}")
99
+ # prepare output hash
100
+ { :request => request,
101
+ :server => @params[:server],
102
+ :port => @params[:port],
103
+ :protocol => @params[:protocol],
104
+ :proxy => @params[:proxy] }
105
+ end
106
+
107
+ # Generates a request hash for the REST API
108
+ def generate_rest_request(method, param) # :nodoc:
109
+ queue_uri = param[:queue_url] ? URI(param[:queue_url]).path : '/'
110
+ message = param[:message] # extract message body if nesessary
111
+ # remove unset(=optional) and symbolyc keys
112
+ param.each{ |key, value| param.delete(key) if (value.nil? || key.is_a?(Symbol)) }
113
+ # created request
114
+ param_to_str = param.to_a.collect{|key,val| key.to_s + "=" + CGI::escape(val.to_s) }.join("&")
115
+ param_to_str = "?#{param_to_str}" unless param_to_str.blank?
116
+ request = "Net::HTTP::#{method.capitalize}".constantize.new("#{queue_uri}#{param_to_str}")
117
+ request.body = message if message
118
+ # set main headers
119
+ request['content-md5'] = ''
120
+ request['Content-Type'] = 'text/plain'
121
+ request['Date'] = Time.now.httpdate
122
+ # generate authorization string
123
+ auth_string = "#{method.upcase}\n#{request['content-md5']}\n#{request['Content-Type']}\n#{request['Date']}\n#{CGI::unescape(queue_uri)}"
124
+ signature = AwsUtils::sign(@aws_secret_access_key, auth_string)
125
+ # set other headers
126
+ request['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
127
+ request['AWS-Version'] = API_VERSION
128
+ # prepare output hash
129
+ { :request => request,
130
+ :server => @params[:server],
131
+ :port => @params[:port],
132
+ :protocol => @params[:protocol] }
133
+ end
134
+
135
+
136
+ # Sends request to Amazon and parses the response
137
+ # Raises AwsError if any banana happened
138
+ def request_info(request, parser) # :nodoc:
139
+ thread = @params[:multi_thread] ? Thread.current : Thread.main
140
+ thread[:sqs_connection] ||= Rightscale::HttpConnection.new(:exception => AwsError, :logger => @logger)
141
+ request_info_impl(thread[:sqs_connection], @@bench, request, parser)
142
+ end
143
+
144
+
145
+ # Creates new queue. Returns new queue link.
146
+ #
147
+ # sqs.create_queue('my_awesome_queue') #=> 'http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'
148
+ #
149
+ # PS Some queue based requests may not become available until a couple of minutes after queue creation
150
+ # (permission grant and removal for example)
151
+ #
152
+ def create_queue(queue_name, default_visibility_timeout=nil)
153
+ req_hash = generate_request('CreateQueue',
154
+ 'QueueName' => queue_name,
155
+ 'DefaultVisibilityTimeout' => default_visibility_timeout || DEFAULT_VISIBILITY_TIMEOUT )
156
+ request_info(req_hash, SqsCreateQueueParser.new(:logger => @logger))
157
+ end
158
+
159
+ # Lists all queues owned by this user that have names beginning with +queue_name_prefix+. If +queue_name_prefix+ is omitted then retrieves a list of all queues.
160
+ #
161
+ # sqs.create_queue('my_awesome_queue')
162
+ # sqs.create_queue('my_awesome_queue_2')
163
+ # sqs.list_queues('my_awesome') #=> ['http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue','http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2']
164
+ #
165
+ def list_queues(queue_name_prefix=nil)
166
+ req_hash = generate_request('ListQueues', 'QueueNamePrefix' => queue_name_prefix)
167
+ request_info(req_hash, SqsListQueuesParser.new(:logger => @logger))
168
+ rescue
169
+ on_exception
170
+ end
171
+
172
+ # Deletes queue (queue must be empty or +force_deletion+ must be set to true). Queue is identified by url. Returns +true+ or an exception.
173
+ #
174
+ # sqs.delete_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2') #=> true
175
+ #
176
+ def delete_queue(queue_url, force_deletion = false)
177
+ req_hash = generate_request('DeleteQueue',
178
+ 'ForceDeletion' => force_deletion.to_s,
179
+ :queue_url => queue_url)
180
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
181
+ rescue
182
+ on_exception
183
+ end
184
+
185
+ # Retrieves the queue attribute(s). Returns a hash of attribute(s) or an exception.
186
+ #
187
+ # sqs.get_queue_attributes('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> {"ApproximateNumberOfMessages"=>"0", "VisibilityTimeout"=>"30"}
188
+ #
189
+ def get_queue_attributes(queue_url, attribute='All')
190
+ req_hash = generate_request('GetQueueAttributes',
191
+ 'Attribute' => attribute,
192
+ :queue_url => queue_url)
193
+ request_info(req_hash, SqsGetQueueAttributesParser.new(:logger => @logger))
194
+ rescue
195
+ on_exception
196
+ end
197
+
198
+ # Sets queue attribute. Returns +true+ or an exception.
199
+ #
200
+ # sqs.set_queue_attributes('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', "VisibilityTimeout", 10) #=> true
201
+ #
202
+ # P.S. Amazon returns success even if the attribute does not exist. Also, attribute values may not be immediately available to other queries
203
+ # for some time after an update (see the SQS documentation for
204
+ # semantics).
205
+ def set_queue_attributes(queue_url, attribute, value)
206
+ req_hash = generate_request('SetQueueAttributes',
207
+ 'Attribute' => attribute,
208
+ 'Value' => value,
209
+ :queue_url => queue_url)
210
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
211
+ rescue
212
+ on_exception
213
+ end
214
+
215
+ # Sets visibility timeout. Returns +true+ or an exception.
216
+ #
217
+ # sqs.set_visibility_timeout('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 15) #=> true
218
+ #
219
+ # See also: +set_queue_attributes+
220
+ #
221
+ def set_visibility_timeout(queue_url, visibility_timeout=nil)
222
+ req_hash = generate_request('SetVisibilityTimeout',
223
+ 'VisibilityTimeout' => visibility_timeout || DEFAULT_VISIBILITY_TIMEOUT,
224
+ :queue_url => queue_url )
225
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
226
+ rescue
227
+ on_exception
228
+ end
229
+
230
+ # Retrieves visibility timeout.
231
+ #
232
+ # sqs.get_visibility_timeout('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 15
233
+ #
234
+ # See also: +get_queue_attributes+
235
+ #
236
+ def get_visibility_timeout(queue_url)
237
+ req_hash = generate_request('GetVisibilityTimeout', :queue_url => queue_url )
238
+ request_info(req_hash, SqsGetVisibilityTimeoutParser.new(:logger => @logger))
239
+ rescue
240
+ on_exception
241
+ end
242
+
243
+ # Adds grants for user (identified by email he registered at Amazon). Returns +true+ or an exception. Permission = 'FULLCONTROL' | 'RECEIVEMESSAGE' | 'SENDMESSAGE'.
244
+ #
245
+ # sqs.add_grant('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'my_awesome_friend@gmail.com', 'FULLCONTROL') #=> true
246
+ #
247
+ def add_grant(queue_url, grantee_email_address, permission = nil)
248
+ req_hash = generate_request('AddGrant',
249
+ 'Grantee.EmailAddress' => grantee_email_address,
250
+ 'Permission' => permission,
251
+ :queue_url => queue_url)
252
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
253
+ rescue
254
+ on_exception
255
+ end
256
+
257
+ # Retrieves hash of +grantee_id+ => +perms+ for this queue:
258
+ #
259
+ # sqs.list_grants('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>
260
+ # {"000000000000000000000001111111111117476c7fea6efb2c3347ac3ab2792a"=>{:name=>"root", :perms=>["FULLCONTROL"]},
261
+ # "00000000000000000000000111111111111e5828344600fc9e4a784a09e97041"=>{:name=>"myawesomefriend", :perms=>["FULLCONTROL"]}
262
+ #
263
+ def list_grants(queue_url, grantee_email_address=nil, permission = nil)
264
+ req_hash = generate_request('ListGrants',
265
+ 'Grantee.EmailAddress' => grantee_email_address,
266
+ 'Permission' => permission,
267
+ :queue_url => queue_url)
268
+ response = request_info(req_hash, SqsListGrantsParser.new(:logger => @logger))
269
+ # One user may have up to 3 permission records for every queue.
270
+ # We will join these records to one.
271
+ result = {}
272
+ response.each do |perm|
273
+ id = perm[:id]
274
+ # create hash for new user if unexisit
275
+ result[id] = {:perms=>[]} unless result[id]
276
+ # fill current grantee params
277
+ result[id][:perms] << perm[:permission]
278
+ result[id][:name] = perm[:name]
279
+ end
280
+ result
281
+ end
282
+
283
+ # Revokes permission from user. Returns +true+ or an exception.
284
+ #
285
+ # sqs.remove_grant('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'my_awesome_friend@gmail.com', 'FULLCONTROL') #=> true
286
+ #
287
+ def remove_grant(queue_url, grantee_email_address_or_id, permission = nil)
288
+ grantee_key = grantee_email_address_or_id.include?('@') ? 'Grantee.EmailAddress' : 'Grantee.ID'
289
+ req_hash = generate_request('RemoveGrant',
290
+ grantee_key => grantee_email_address_or_id,
291
+ 'Permission' => permission,
292
+ :queue_url => queue_url)
293
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
294
+ rescue
295
+ on_exception
296
+ end
297
+
298
+ # Retrieves a list of messages from queue. Returns an array of hashes in format: <tt>{:id=>'message_id', body=>'message_body'}</tt>
299
+ #
300
+ # sqs.receive_messages('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',10, 5) #=>
301
+ # [{:id=>"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321", :body=>"message_1"}, ..., {}]
302
+ #
303
+ # P.S. Usually returns fewer messages than requested even if they are available.
304
+ #
305
+ def receive_messages(queue_url, number_of_messages=1, visibility_timeout=nil)
306
+ return [] if number_of_messages == 0
307
+ req_hash = generate_rest_request('GET',
308
+ 'NumberOfMessages' => number_of_messages,
309
+ 'VisibilityTimeout' => visibility_timeout,
310
+ :queue_url => "#{queue_url}/front" )
311
+ request_info(req_hash, SqsReceiveMessagesParser.new(:logger => @logger))
312
+ rescue
313
+ on_exception
314
+ end
315
+
316
+ # Peeks message from queue by message id. Returns message in format of <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.
317
+ #
318
+ # sqs.peek_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', '1234567890...0987654321') #=>
319
+ # {:id=>"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321", :body=>"message_1"}
320
+ #
321
+ def peek_message(queue_url, message_id)
322
+ req_hash = generate_rest_request('GET', :queue_url => "#{queue_url}/#{CGI::escape message_id}" )
323
+ messages = request_info(req_hash, SqsReceiveMessagesParser.new(:logger => @logger))
324
+ messages.blank? ? nil : messages[0]
325
+ rescue
326
+ on_exception
327
+ end
328
+
329
+ # Sends new message to queue.Returns 'message_id' or raises an exception.
330
+ #
331
+ # sqs.send_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'message_1') #=> "1234567890...0987654321"
332
+ #
333
+ def send_message(queue_url, message)
334
+ req_hash = generate_rest_request('PUT',
335
+ :message => message,
336
+ :queue_url => "#{queue_url}/back")
337
+ request_info(req_hash, SqsSendMessagesParser.new(:logger => @logger))
338
+ rescue
339
+ on_exception
340
+ end
341
+
342
+ # Deletes message from queue. Returns +true+ or an exception. Amazon
343
+ # returns +true+ on deletion of non-existent messages.
344
+ #
345
+ # sqs.delete_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', '12345678904...0987654321') #=> true
346
+ #
347
+ def delete_message(queue_url, message_id)
348
+ req_hash = generate_request('DeleteMessage',
349
+ 'MessageId' => message_id,
350
+ :queue_url => queue_url)
351
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
352
+ rescue
353
+ on_exception
354
+ end
355
+
356
+ # Changes message visibility timeout. Returns +true+ or an exception.
357
+ #
358
+ # sqs.change_message_visibility('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', '1234567890...0987654321', 10) #=> true
359
+ #
360
+ def change_message_visibility(queue_url, message_id, visibility_timeout=0)
361
+ req_hash = generate_request('ChangeMessageVisibility',
362
+ 'MessageId' => message_id,
363
+ 'VisibilityTimeout' => visibility_timeout.to_s,
364
+ :queue_url => queue_url)
365
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
366
+ rescue
367
+ on_exception
368
+ end
369
+
370
+ # Returns queue url by queue short name or +nil+ if queue is not found
371
+ #
372
+ # sqs.queue_url_by_name('my_awesome_queue') #=> 'http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'
373
+ #
374
+ def queue_url_by_name(queue_name)
375
+ return queue_name if queue_name.include?('/')
376
+ queue_urls = list_queues(queue_name)
377
+ queue_urls.each do |queue_url|
378
+ return queue_url if queue_name_by_url(queue_url) == queue_name
379
+ end
380
+ nil
381
+ rescue
382
+ on_exception
383
+ end
384
+
385
+ # Returns short queue name by url.
386
+ #
387
+ # RightSqs.queue_name_by_url('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'
388
+ #
389
+ def self.queue_name_by_url(queue_url)
390
+ queue_url[/[^\/]*$/]
391
+ rescue
392
+ on_exception
393
+ end
394
+
395
+ # Returns short queue name by url.
396
+ #
397
+ # sqs.queue_name_by_url('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'
398
+ #
399
+ def queue_name_by_url(queue_url)
400
+ self.class.queue_name_by_url(queue_url)
401
+ rescue
402
+ on_exception
403
+ end
404
+
405
+ # Returns approximate number of messages in queue.
406
+ #
407
+ # sqs.get_queue_length('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 3
408
+ #
409
+ def get_queue_length(queue_url)
410
+ get_queue_attributes(queue_url)['ApproximateNumberOfMessages'].to_i
411
+ rescue
412
+ on_exception
413
+ end
414
+
415
+ # Removes all visible messages from queue. Return +true+ or an exception.
416
+ #
417
+ # sqs.clear_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true
418
+ #
419
+ def clear_queue(queue_url)
420
+ while (m = pop_message(queue_url)) ; end # delete all messages in queue
421
+ true
422
+ rescue
423
+ on_exception
424
+ end
425
+
426
+ # Deletes queue then re-creates it (restores attributes also). The fastest method to clear big queue or queue with invisible messages. Return +true+ or an exception.
427
+ #
428
+ # sqs.force_clear_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true
429
+ #
430
+ # PS This function is no longer supported. Amazon has changed the SQS semantics to require at least 60 seconds between
431
+ # queue deletion and creation. Hence this method will fail with an exception.
432
+ #
433
+ def force_clear_queue(queue_url)
434
+ queue_name = queue_name_by_url(queue_url)
435
+ queue_attributes = get_queue_attributes(queue_url)
436
+ force_delete_queue(queue_url)
437
+ create_queue(queue_name)
438
+ # hmmm... The next line is a trick. Amazon do not want change attributes immediately after queue creation
439
+ # So we do 'empty' get_queue_attributes. Probably they need some time to allow attributes change.
440
+ get_queue_attributes(queue_url)
441
+ queue_attributes.each{ |attribute, value| set_queue_attributes(queue_url, attribute, value) }
442
+ true
443
+ rescue
444
+ on_exception
445
+ end
446
+
447
+ # Deletes queue even if it has messages. Return +true+ or an exception.
448
+ #
449
+ # force_delete_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true
450
+ #
451
+ # P.S. same as <tt>delete_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', true)</tt>
452
+ def force_delete_queue(queue_url)
453
+ delete_queue(queue_url, true)
454
+ rescue
455
+ on_exception
456
+ end
457
+
458
+ # Reads first accessible message from queue. Returns message as a hash: <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.
459
+ #
460
+ # sqs.receive_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 10) #=>
461
+ # {:id=>"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321", :body=>"message_1"}
462
+ #
463
+ def receive_message(queue_url, visibility_timeout=nil)
464
+ result = receive_messages(queue_url, 1, visibility_timeout)
465
+ result.blank? ? nil : result[0]
466
+ rescue
467
+ on_exception
468
+ end
469
+
470
+ # Same as send_message
471
+ alias_method :push_message, :send_message
472
+
473
+ # Pops (retrieves and deletes) up to 'number_of_messages' from queue. Returns an array of retrieved messages in format: <tt>[{:id=>'message_id', :body=>'message_body'}]</tt>.
474
+ #
475
+ # sqs.pop_messages('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 3) #=>
476
+ # [{:id=>"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321", :body=>"message_1"}, ..., {}]
477
+ #
478
+ def pop_messages(queue_url, number_of_messages=1)
479
+ messages = receive_messages(queue_url, number_of_messages)
480
+ messages.each do |message|
481
+ delete_message(queue_url, message[:id])
482
+ end
483
+ messages
484
+ rescue
485
+ on_exception
486
+ end
487
+
488
+ # Pops (retrieves and deletes) first accessible message from queue. Returns the message in format <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.
489
+ #
490
+ # sqs.pop_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>
491
+ # {:id=>"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321", :body=>"message_1"}
492
+ #
493
+ def pop_message(queue_url)
494
+ messages = pop_messages(queue_url)
495
+ messages.blank? ? nil : messages[0]
496
+ rescue
497
+ on_exception
498
+ end
499
+
500
+ #-----------------------------------------------------------------
501
+ # PARSERS: Status Response Parser
502
+ #-----------------------------------------------------------------
503
+
504
+ class SqsStatusParser < RightAWSParser # :nodoc:
505
+ def tagend(name)
506
+ if name == 'StatusCode'
507
+ @result = @text=='Success' ? true : false
508
+ end
509
+ end
510
+ end
511
+
512
+ #-----------------------------------------------------------------
513
+ # PARSERS: Queue
514
+ #-----------------------------------------------------------------
515
+
516
+ class SqsCreateQueueParser < RightAWSParser # :nodoc:
517
+ def tagend(name)
518
+ @result = @text if name == 'QueueUrl'
519
+ end
520
+ end
521
+
522
+ class SqsListQueuesParser < RightAWSParser # :nodoc:
523
+ def reset
524
+ @result = []
525
+ end
526
+ def tagend(name)
527
+ @result << @text if name == 'QueueUrl'
528
+ end
529
+ end
530
+
531
+ class SqsGetQueueAttributesParser < RightAWSParser # :nodoc:
532
+ def reset
533
+ @result = {}
534
+ end
535
+ def tagend(name)
536
+ case name
537
+ when 'Attribute' ; @current_attribute = @text
538
+ when 'Value' ; @result[@current_attribute] = @text
539
+ # when 'StatusCode'; @result['status_code'] = @text
540
+ # when 'RequestId' ; @result['request_id'] = @text
541
+ end
542
+ end
543
+ end
544
+
545
+ #-----------------------------------------------------------------
546
+ # PARSERS: Timeouts
547
+ #-----------------------------------------------------------------
548
+
549
+ class SqsGetVisibilityTimeoutParser < RightAWSParser # :nodoc:
550
+ def tagend(name)
551
+ @result = @text.to_i if name == 'VisibilityTimeout'
552
+ end
553
+ end
554
+
555
+ #-----------------------------------------------------------------
556
+ # PARSERS: Permissions
557
+ #-----------------------------------------------------------------
558
+
559
+ class SqsListGrantsParser < RightAWSParser # :nodoc:
560
+ def reset
561
+ @result = []
562
+ end
563
+ def tagstart(name, attributes)
564
+ @current_perms = {} if name == 'GrantList'
565
+ end
566
+ def tagend(name)
567
+ case name
568
+ when 'ID' ; @current_perms[:id] = @text
569
+ when 'DisplayName'; @current_perms[:name] = @text
570
+ when 'Permission' ; @current_perms[:permission] = @text
571
+ when 'GrantList' ; @result << @current_perms
572
+ end
573
+ end
574
+ end
575
+
576
+ #-----------------------------------------------------------------
577
+ # PARSERS: Messages
578
+ #-----------------------------------------------------------------
579
+
580
+ class SqsReceiveMessagesParser < RightAWSParser # :nodoc:
581
+ def reset
582
+ @result = []
583
+ end
584
+ def tagstart(name, attributes)
585
+ @current_message = {} if name == 'Message'
586
+ end
587
+ def tagend(name)
588
+ case name
589
+ when 'MessageId' ; @current_message[:id] = @text
590
+ when 'MessageBody'; @current_message[:body] = @text
591
+ when 'Message' ; @result << @current_message
592
+ end
593
+ end
594
+ end
595
+
596
+ class SqsSendMessagesParser < RightAWSParser # :nodoc:
597
+ def tagend(name)
598
+ @result = @text if name == 'MessageId'
599
+ end
600
+ end
601
+
602
+ end
603
+
604
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../../lib/right_aws'
@@ -0,0 +1,76 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ require 'pp'
3
+
4
+ class TestEc2 < Test::Unit::TestCase
5
+
6
+ # Some of RightEc2 instance methods concerning instance launching and image registration
7
+ # are not tested here due to their potentially risk.
8
+
9
+ def setup
10
+ @ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id,
11
+ TestCredentials.aws_secret_access_key)
12
+ @key = 'right_ec2_awesome_test_key'
13
+ @group = 'right_ec2_awesome_test_security_group'
14
+ end
15
+
16
+ def test_01_create_describe_key_pairs
17
+ new_key = @ec2.create_key_pair(@key)
18
+ assert new_key[:aws_material][/BEGIN RSA PRIVATE KEY/], "New key material is absent"
19
+ keys = @ec2.describe_key_pairs
20
+ assert keys.map{|key| key[:aws_key_name] }.include?(@key), "#{@key} must exist"
21
+ end
22
+
23
+ def test_02_create_security_group
24
+ assert @ec2.create_security_group(@group,'My awesone test group'), 'Create_security_group fail'
25
+ group = @ec2.describe_security_groups([@group])[0]
26
+ assert_equal @group, group[:aws_group_name], 'Group must be created but does not exist'
27
+ end
28
+
29
+ def test_03_perms_add
30
+ assert @ec2.authorize_security_group_named_ingress(@group, TestCredentials.account_number, 'default')
31
+ assert @ec2.authorize_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')
32
+ end
33
+
34
+ def test_04_check_new_perms_exist
35
+ assert_equal 2, @ec2.describe_security_groups([@group])[0][:aws_perms].size
36
+ end
37
+
38
+ def test_05_perms_remove
39
+ assert @ec2.revoke_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')
40
+ assert @ec2.revoke_security_group_named_ingress(@group,
41
+ TestCredentials.account_number, 'default')
42
+ end
43
+
44
+ def test_06_describe_images
45
+ images = @ec2.describe_images
46
+ assert images.size>0, 'Amazon must have at least some public images'
47
+ # unknown image
48
+ assert_raise(Rightscale::AwsError){ @ec2.describe_images(['ami-ABCDEFGH'])}
49
+ end
50
+
51
+ def test_07_describe_instanses
52
+ assert @ec2.describe_instances
53
+ # unknown image
54
+ assert_raise(Rightscale::AwsError){ @ec2.describe_instances(['i-ABCDEFGH'])}
55
+ end
56
+
57
+ def test_08_delete_security_group
58
+ assert @ec2.delete_security_group(@group), 'Delete_security_group fail'
59
+ end
60
+
61
+ def test_09_delete_key_pair
62
+ assert @ec2.delete_key_pair(@key), 'Delete_key_pair fail'
63
+ ## Hmmm... Amazon does not through the exception any more. It now just returns a 'true' if the key does not exist any more...
64
+ ## # key must be deleted already
65
+ ## assert_raise(Rightscale::AwsError) { @ec2.delete_key_pair(@key) }
66
+ end
67
+
68
+ def test_10_signature_version_0
69
+ ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '0')
70
+ images = ec2.describe_images
71
+ assert images.size>0, 'Amazon must have at least some public images'
72
+ # check that the request has correct signature version
73
+ assert ec2.last_request.path.include?('SignatureVersion=0')
74
+ end
75
+
76
+ end