right_aws 1.10.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/History.txt +53 -15
  2. data/Manifest.txt +16 -0
  3. data/README.txt +10 -9
  4. data/Rakefile +13 -15
  5. data/lib/acf/right_acf_interface.rb +224 -118
  6. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  7. data/lib/acf/right_acf_streaming_interface.rb +236 -0
  8. data/lib/acw/right_acw_interface.rb +249 -0
  9. data/lib/as/right_as_interface.rb +699 -0
  10. data/lib/awsbase/right_awsbase.rb +232 -51
  11. data/lib/awsbase/support.rb +4 -0
  12. data/lib/ec2/right_ec2.rb +33 -1375
  13. data/lib/ec2/right_ec2_ebs.rb +452 -0
  14. data/lib/ec2/right_ec2_images.rb +373 -0
  15. data/lib/ec2/right_ec2_instances.rb +755 -0
  16. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  17. data/lib/ec2/right_ec2_reserved_instances.rb +170 -0
  18. data/lib/ec2/right_ec2_security_groups.rb +280 -0
  19. data/lib/ec2/right_ec2_spot_instances.rb +399 -0
  20. data/lib/ec2/right_ec2_vpc.rb +571 -0
  21. data/lib/elb/right_elb_interface.rb +496 -0
  22. data/lib/rds/right_rds_interface.rb +998 -0
  23. data/lib/right_aws.rb +18 -4
  24. data/lib/s3/right_s3.rb +39 -7
  25. data/lib/s3/right_s3_interface.rb +77 -53
  26. data/lib/sdb/active_sdb.rb +203 -11
  27. data/lib/sdb/right_sdb_interface.rb +68 -45
  28. data/lib/sqs/right_sqs_gen2.rb +73 -16
  29. data/lib/sqs/right_sqs_gen2_interface.rb +131 -51
  30. data/lib/sqs/right_sqs_interface.rb +2 -4
  31. data/test/acf/test_right_acf.rb +10 -18
  32. data/test/rds/test_helper.rb +2 -0
  33. data/test/rds/test_right_rds.rb +120 -0
  34. data/test/s3/test_right_s3.rb +10 -8
  35. data/test/s3/test_right_s3_stubbed.rb +6 -4
  36. data/test/sdb/test_active_sdb.rb +70 -12
  37. data/test/sdb/test_right_sdb.rb +13 -7
  38. data/test/sqs/test_right_sqs_gen2.rb +104 -49
  39. metadata +103 -14
@@ -32,6 +32,7 @@ module RightAws
32
32
  DEFAULT_HOST = 'sdb.amazonaws.com'
33
33
  DEFAULT_PORT = 443
34
34
  DEFAULT_PROTOCOL = 'https'
35
+ DEFAULT_PATH = '/'
35
36
  API_VERSION = '2007-11-07'
36
37
  DEFAULT_NIL_REPRESENTATION = 'nil'
37
38
 
@@ -47,7 +48,7 @@ module RightAws
47
48
  # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
48
49
  # :port => 443 # Amazon service port: 80 or 443(default)
49
50
  # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
50
- # :signature_version => '0' # The signature version : '0' or '1'(default)
51
+ # :signature_version => '0' # The signature version : '0','1 or '2'(default)
51
52
  # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
52
53
  # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
53
54
  # :nil_representation => 'mynil'} # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')
@@ -61,10 +62,12 @@ module RightAws
61
62
  def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
62
63
  @nil_rep = params[:nil_representation] ? params[:nil_representation] : DEFAULT_NIL_REPRESENTATION
63
64
  params.delete(:nil_representation)
64
- init({ :name => 'SDB',
65
- :default_host => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).host : DEFAULT_HOST,
66
- :default_port => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).port : DEFAULT_PORT,
67
- :default_protocol => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).scheme : DEFAULT_PROTOCOL },
65
+ init({ :name => 'SDB',
66
+ :default_host => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).host : DEFAULT_HOST,
67
+ :default_port => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).port : DEFAULT_PORT,
68
+ :default_service => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).path : DEFAULT_PATH,
69
+ :default_protocol => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).scheme : DEFAULT_PROTOCOL,
70
+ :default_api_version => ENV['SDB_API_VERSION'] || API_VERSION },
68
71
  aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
69
72
  aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
70
73
  params)
@@ -73,66 +76,55 @@ module RightAws
73
76
  #-----------------------------------------------------------------
74
77
  # Requests
75
78
  #-----------------------------------------------------------------
79
+
76
80
  def generate_request(action, params={}) #:nodoc:
77
- # remove empty params from request
78
- params.delete_if {|key,value| value.nil? }
79
- #params_string = params.to_a.collect{|key,val| key + "=#{CGI::escape(val.to_s)}" }.join("&")
80
- # prepare service data
81
- service = '/'
82
- service_hash = {"Action" => action,
83
- "AWSAccessKeyId" => @aws_access_key_id,
84
- "Version" => API_VERSION }
85
- service_hash.update(params)
86
- service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], service)
87
- #
88
- # use POST method if the length of the query string is too large
89
- # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/MakingRESTRequests.html
90
- if service_params.size > 2000
91
- if signature_version == '2'
92
- # resign the request because HTTP verb is included into signature
93
- service_params = signed_service_params(@aws_secret_access_key, service_hash, :post, @params[:server], service)
94
- end
95
- request = Net::HTTP::Post.new(service)
96
- request.body = service_params
97
- request['Content-Type'] = 'application/x-www-form-urlencoded'
98
- else
99
- request = Net::HTTP::Get.new("#{service}?#{service_params}")
100
- end
101
- # prepare output hash
102
- { :request => request,
103
- :server => @params[:server],
104
- :port => @params[:port],
105
- :protocol => @params[:protocol] }
81
+ generate_request_impl(:get, action, params )
106
82
  end
107
83
 
108
84
  # Sends request to Amazon and parses the response
109
85
  # Raises AwsError if any banana happened
110
86
  def request_info(request, parser) #:nodoc:
111
- thread = @params[:multi_thread] ? Thread.current : Thread.main
112
- thread[:sdb_connection] ||= Rightscale::HttpConnection.new(:exception => AwsError, :logger => @logger)
113
- request_info_impl(thread[:sdb_connection], @@bench, request, parser)
87
+ request_info_impl(:sdb_connection, @@bench, request, parser)
114
88
  end
115
89
 
116
90
  # Prepare attributes for putting.
117
91
  # (used by put_attributes)
118
- def pack_attributes(attributes, replace = false) #:nodoc:
92
+ def pack_attributes(items_or_attributes, replace = false, batch = false) #:nodoc:
93
+ if batch
94
+ index = 0
95
+ items_or_attributes.inject({}){|result, (item_name, attributes)|
96
+ item_prefix = "Item.#{index}."
97
+ result["#{item_prefix}ItemName"] = item_name.to_s
98
+ result.merge!(
99
+ pack_single_item_attributes(attributes, replace, item_prefix))
100
+ index += 1
101
+ result
102
+ }
103
+ else
104
+ pack_single_item_attributes(items_or_attributes, replace)
105
+ end
106
+ end
107
+
108
+ def pack_single_item_attributes(attributes, replace, prefix = "")
119
109
  result = {}
120
110
  if attributes
121
111
  idx = 0
122
112
  skip_values = attributes.is_a?(Array)
123
113
  attributes.each do |attribute, values|
124
114
  # set replacement attribute
125
- result["Attribute.#{idx}.Replace"] = 'true' if replace
115
+ result["#{prefix}Attribute.#{idx}.Replace"] = 'true' if replace
126
116
  # pack Name/Value
127
117
  unless values.nil?
128
- Array(values).each do |value|
129
- result["Attribute.#{idx}.Name"] = attribute
130
- result["Attribute.#{idx}.Value"] = ruby_to_sdb(value) unless skip_values
118
+ # Array(values) does not work here:
119
+ # - Array('') => [] but we wanna get here ['']
120
+ [values].flatten.each do |value|
121
+ result["#{prefix}Attribute.#{idx}.Name"] = attribute
122
+ result["#{prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(value) unless skip_values
131
123
  idx += 1
132
124
  end
133
125
  else
134
- result["Attribute.#{idx}.Name"] = attribute
135
- result["Attribute.#{idx}.Value"] = ruby_to_sdb(nil) unless skip_values
126
+ result["#{prefix}Attribute.#{idx}.Name"] = attribute
127
+ result["#{prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(nil) unless skip_values
136
128
  idx += 1
137
129
  end
138
130
  end
@@ -333,6 +325,37 @@ module RightAws
333
325
  rescue Exception
334
326
  on_exception
335
327
  end
328
+
329
+ # Add/Replace attributes for multiple items at a time.
330
+ #
331
+ # Params:
332
+ # domain_name = DomainName
333
+ # items = {
334
+ # 'Item1' => {
335
+ # 'nameA' => [valueA1, valueA2,..., valueAN],
336
+ # ...
337
+ # 'nameB' => [valueB1, valueB2,..., valueBN]
338
+ # },
339
+ # 'Item2' => {
340
+ # 'nameC' => [valueC1, valueC2,..., valueCN],
341
+ # ...
342
+ # 'nameD' => [valueD1, valueD2,..., valueDN]
343
+ # }
344
+ # }
345
+ # replace = :replace | any other value to skip replacement
346
+ #
347
+ # Usage of batch_put_attributes is similar to put_attributes except that
348
+ # instead of supplying an item_name and a hash of attributes, you supply a
349
+ # hash of item names to attributes.
350
+ #
351
+ # See: http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?SDB_API_BatchPutAttributes.html
352
+ def batch_put_attributes(domain_name, items, replace = false)
353
+ params = { 'DomainName' => domain_name }.merge(pack_attributes(items, replace, true))
354
+ link = generate_request("BatchPutAttributes", params)
355
+ request_info( link, QSdbSimpleParser.new)
356
+ rescue Exception
357
+ on_exception
358
+ end
336
359
 
337
360
  # Retrieve SDB item's attribute(s).
338
361
  #
@@ -502,7 +525,7 @@ module RightAws
502
525
  # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_QueryWithAttributes.html
503
526
  #
504
527
  def query_with_attributes(domain_name, attributes=[], query_expression = nil, max_number_of_items = nil, next_token = nil)
505
- attributes = attributes.to_a
528
+ attributes = Array(attributes)
506
529
  query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
507
530
  @last_query_expression = query_expression
508
531
  #
@@ -136,7 +136,7 @@ module RightAws
136
136
  # queue.clear() #=> true
137
137
  #
138
138
  def clear()
139
- @sqs.interface.clear_queue(@url)
139
+ @sqs.interface.clear_queue(@url)
140
140
  end
141
141
 
142
142
  # Deletes queue. Any messages in the queue will be permanently lost.
@@ -167,11 +167,10 @@ module RightAws
167
167
  #
168
168
  # queue.receive_messages(2,10) #=> array of messages
169
169
  #
170
- def receive_messages(number_of_messages=1, visibility=nil)
171
- list = @sqs.interface.receive_message(@url, number_of_messages, visibility)
170
+ def receive_messages(number_of_messages=1, visibility=nil, attributes=nil)
171
+ list = @sqs.interface.receive_message(@url, number_of_messages, visibility, attributes)
172
172
  list.map! do |entry|
173
- msg = Message.new(self, entry['MessageId'], entry['ReceiptHandle'],
174
- entry['Body'], visibility)
173
+ msg = Message.new(self, entry['MessageId'], entry['ReceiptHandle'], entry['Body'], visibility, entry['Attributes'])
175
174
  msg.received_at = Time.now
176
175
  msg.receive_checksum = entry['MD5OfBody']
177
176
  msg
@@ -183,8 +182,8 @@ module RightAws
183
182
  #
184
183
  # queue.receive #=> #<RightAws::SqsGen2::Message:0xb7bf0884 ... >
185
184
  #
186
- def receive(visibility=nil)
187
- list = receive_messages(1, visibility)
185
+ def receive(visibility=nil, attributes=nil)
186
+ list = receive_messages(1, visibility, attributes)
188
187
  list.empty? ? nil : list[0]
189
188
  end
190
189
 
@@ -193,12 +192,15 @@ module RightAws
193
192
  #
194
193
  # queue.pop #=> #<RightAws::SqsGen2::Message:0xb7bf0884 ... >
195
194
  #
196
- def pop
197
- list = @sqs.interface.pop_messages(@url, 1)
195
+ # # pop a message with custom attributes
196
+ # m = queue.pop(['SenderId', 'SentTimestamp']) #=> #<RightAws::SqsGen2::Message:0xb7bf1884 ... >
197
+ # m.attributes #=> {"SentTimestamp"=>"1240991906937", "SenderId"=>"800000000005"}
198
+ #
199
+ def pop(attributes=nil)
200
+ list = @sqs.interface.pop_messages(@url, 1, attributes)
198
201
  return nil if list.empty?
199
202
  entry = list[0]
200
- msg = Message.new(self, entry['MessageId'], entry['ReceiptHandle'],
201
- entry['Body'], visibility)
203
+ msg = Message.new(self, entry['MessageId'], entry['ReceiptHandle'], entry['Body'], visibility, entry['Attributes'])
202
204
  msg.received_at = Time.now
203
205
  msg.receive_checksum = entry['MD5OfBody']
204
206
  msg
@@ -241,27 +243,77 @@ module RightAws
241
243
  end
242
244
 
243
245
  # Retrieves queue attributes.
244
- # At this moment Amazon supports +VisibilityTimeout+ and +ApproximateNumberOfMessages+ only.
245
246
  # If the name of attribute is set, returns its value. Otherwise, returns a hash of attributes.
246
247
  #
247
248
  # queue.get_attribute('VisibilityTimeout') #=> {"VisibilityTimeout"=>"45"}
248
249
  #
250
+ # P.S. This guy is deprecated. Use +get_attributes+ instead.
249
251
  def get_attribute(attribute='All')
250
- attributes = @sqs.interface.get_queue_attributes(@url, attribute)
252
+ attributes = get_attributes(attribute)
251
253
  attribute=='All' ? attributes : attributes[attribute]
252
254
  end
255
+
256
+ # Retrieves queue attributes.
257
+ #
258
+ # queue.get_attributes #=>
259
+ # {"ApproximateNumberOfMessages" => "0",
260
+ # "LastModifiedTimestamp" => "1240946032",
261
+ # "CreatedTimestamp" => "1240816887",
262
+ # "VisibilityTimeout" => "30",
263
+ # "Policy" => "{"Version":"2008-10-17","Id":...}"}
264
+ #
265
+ # queue.get_attributes("LastModifiedTimestamp", "VisibilityTimeout") #=>
266
+ # {"LastModifiedTimestamp" => "1240946032",
267
+ # "VisibilityTimeout" => "30"}
268
+ #
269
+ def get_attributes(*attributes)
270
+ @sqs.interface.get_queue_attributes(@url, attributes)
271
+ end
272
+
273
+ # Add permission to the queue.
274
+ #
275
+ # queue.add_permissions('testLabel',['125074342641', '125074342642'],
276
+ # ['SendMessage','SendMessage','ReceiveMessage']) #=> true
277
+ #
278
+ def add_permissions(label, grantees, actions)
279
+ @sqs.interface.add_permissions(@url, label, grantees, actions)
280
+ end
281
+
282
+ # Revoke any permissions in the queue policy that matches the +label+ parameter.
283
+ #
284
+ # sqs.remove_permissions('testLabel') # => true
285
+ #
286
+ def remove_permissions(label)
287
+ @sqs.interface.remove_permissions(@url, label)
288
+ end
289
+
290
+ # Get current permissions set. The set is JSON packed.
291
+ #
292
+ # sqs.get_permissions #=>
293
+ # '{"Version":"2008-10-17","Id":"/826693181925/kd-test-gen-2_5/SQSDefaultPolicy",
294
+ # "Statement":[{"Sid":"kd-perm-04","Effect":"Allow","Principal":{"AWS":"100000000001",
295
+ # "AWS":"100000000001","AWS":"100000000002"},"Action":["SQS:SendMessage","SQS:DeleteMessage",
296
+ # "SQS:ReceiveMessage"],"Resource":"/826693181925/kd-test-gen-2_5"},{"Sid":"kd-perm-03",
297
+ # "Effect":"Allow","Principal":{"AWS":"648772224137"},"Action":"SQS:SendMessage",
298
+ # "Resource":"/826693181925/kd-test-gen-2_5"}]}'
299
+ #
300
+ def get_permissions
301
+ get_attributes('Policy')['Policy']
302
+ end
303
+
253
304
  end
254
305
 
255
306
  class Message
256
- attr_reader :queue, :id, :body, :visibility, :receipt_handle
307
+ attr_reader :queue, :id, :body, :visibility, :receipt_handle, :attributes
257
308
  attr_accessor :sent_at, :received_at, :send_checksum, :receive_checksum
258
309
 
259
- def initialize(queue, id=nil, rh = nil, body=nil, visibility=nil)
310
+ def initialize(queue, id=nil, rh = nil, body=nil, visibility=nil, attributes=nil)
260
311
  @queue = queue
261
312
  @id = id
262
313
  @receipt_handle = rh
263
314
  @body = body
264
315
  @visibility = visibility
316
+ @attributes = attributes
265
317
  @sent_at = nil
266
318
  @received_at = nil
267
319
  @send_checksum = nil
@@ -273,6 +325,12 @@ module RightAws
273
325
  @body
274
326
  end
275
327
 
328
+ # Set message visibility timeout.
329
+ def visibility=(visibility_timeout)
330
+ @queue.sqs.interface.change_message_visibility(@queue.url, @receipt_handle, visibility_timeout)
331
+ @visibility = visibility_timeout
332
+ end
333
+
276
334
  # Removes message from queue.
277
335
  # Returns +true+.
278
336
  def delete
@@ -281,6 +339,5 @@ module RightAws
281
339
 
282
340
  end
283
341
 
284
-
285
342
  end
286
343
  end
@@ -38,7 +38,7 @@ module RightAws
38
38
  class SqsGen2Interface < RightAwsBase
39
39
  include RightAwsBaseInterface
40
40
 
41
- API_VERSION = "2008-01-01"
41
+ API_VERSION = "2009-02-01"
42
42
  DEFAULT_HOST = "queue.amazonaws.com"
43
43
  DEFAULT_PORT = 443
44
44
  DEFAULT_PROTOCOL = 'https'
@@ -72,7 +72,7 @@ module RightAws
72
72
  # {:server => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com' (default)
73
73
  # :port => 443 # Amazon service port: 80 or 443 (default)
74
74
  # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false (default)
75
- # :signature_version => '0' # The signature version : '0' or '1'(default)
75
+ # :signature_version => '0' # The signature version : '0', '1' or '2'(default)
76
76
  # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
77
77
  #
78
78
  def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
@@ -127,7 +127,7 @@ module RightAws
127
127
  #
128
128
  service_params = signed_service_params(@aws_secret_access_key, service_hash, :post, @params[:server], service)
129
129
  request = Net::HTTP::Post.new(AwsUtils::URLencode(service))
130
- request['Content-Type'] = 'application/x-www-form-urlencoded'
130
+ request['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
131
131
  request.body = service_params
132
132
  # prepare output hash
133
133
  { :request => request,
@@ -140,15 +140,12 @@ module RightAws
140
140
  # Sends request to Amazon and parses the response
141
141
  # Raises AwsError if any banana happened
142
142
  def request_info(request, parser) # :nodoc:
143
- thread = @params[:multi_thread] ? Thread.current : Thread.main
144
- thread[:sqs_connection] ||= Rightscale::HttpConnection.new(:exception => AwsError, :logger => @logger)
145
- request_info_impl(thread[:sqs_connection], @@bench, request, parser)
143
+ request_info_impl(:sqs_connection, @@bench, request, parser)
146
144
  end
147
145
 
148
-
149
146
  # Creates a new queue, returning its URI.
150
147
  #
151
- # sqs.create_queue('my_awesome_queue') #=> 'http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'
148
+ # sqs.create_queue('my_awesome_queue') #=> 'https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'
152
149
  #
153
150
  def create_queue(queue_name, default_visibility_timeout=nil)
154
151
  req_hash = generate_request('CreateQueue', 'QueueName' => queue_name,
@@ -164,7 +161,7 @@ module RightAws
164
161
  #
165
162
  # sqs.create_queue('my_awesome_queue')
166
163
  # sqs.create_queue('my_awesome_queue_2')
167
- # sqs.list_queues('my_awesome') #=> ['http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue','http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2']
164
+ # sqs.list_queues('my_awesome') #=> ['https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue','https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2']
168
165
  #
169
166
  def list_queues(queue_name_prefix=nil)
170
167
  req_hash = generate_request('ListQueues', 'QueueNamePrefix' => queue_name_prefix)
@@ -179,11 +176,10 @@ module RightAws
179
176
  # may still show the deleted queue. It is not unusual within the 60 s window to see the deleted queue absent from
180
177
  # one list_queues call but present in the subsequent one. Deletion is eventual.
181
178
  #
182
- # sqs.delete_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2') #=> true
183
- #
179
+ # sqs.delete_queue('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2') #=> true
184
180
  #
185
181
  def delete_queue(queue_url)
186
- req_hash = generate_request('DeleteQueue', :queue_url => queue_url)
182
+ req_hash = generate_request('DeleteQueue', :queue_url => queue_url)
187
183
  request_info(req_hash, SqsStatusParser.new(:logger => @logger))
188
184
  rescue
189
185
  on_exception
@@ -191,11 +187,24 @@ module RightAws
191
187
 
192
188
  # Retrieves the queue attribute(s). Returns a hash of attribute(s) or an exception.
193
189
  #
194
- # sqs.get_queue_attributes('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue')
195
- # #=> {"ApproximateNumberOfMessages"=>"0", "VisibilityTimeout"=>"30"}
196
- #
197
- def get_queue_attributes(queue_url, attribute='All')
198
- req_hash = generate_request('GetQueueAttributes', 'AttributeName' => attribute, :queue_url => queue_url)
190
+ # sqs.get_queue_attributes('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>
191
+ # {"ApproximateNumberOfMessages" => "0",
192
+ # "LastModifiedTimestamp" => "1240946032",
193
+ # "CreatedTimestamp" => "1240816887",
194
+ # "VisibilityTimeout" => "30",
195
+ # "Policy" => "{"Version":"2008-10-17","Id":...}"}
196
+ #
197
+ # queue.get_queue_attributes('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', "LastModifiedTimestamp", "VisibilityTimeout") #=>
198
+ # {"LastModifiedTimestamp" => "1240946032",
199
+ # "VisibilityTimeout" => "30"}
200
+ #
201
+ # http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryGetQueueAttributes.html
202
+ def get_queue_attributes(queue_url, *attributes)
203
+ attributes.flatten!
204
+ attributes << 'All' if attributes.blank?
205
+ params = amazonize_list('AttributeName', attributes)
206
+ params.merge!(:queue_url => queue_url)
207
+ req_hash = generate_request('GetQueueAttributes', params)
199
208
  request_info(req_hash, SqsGetQueueAttributesParser.new(:logger => @logger))
200
209
  rescue
201
210
  on_exception
@@ -203,48 +212,108 @@ module RightAws
203
212
 
204
213
  # Sets queue attribute. Returns +true+ or an exception.
205
214
  #
206
- # sqs.set_queue_attributes('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', "VisibilityTimeout", 10) #=> true
215
+ # sqs.set_queue_attributes('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', "VisibilityTimeout", 10) #=> true
207
216
  #
208
217
  # From the SQS Dev Guide:
209
- # "Currently, you can set only the
210
- # VisibilityTimeout attribute for a queue...
211
- # When you change a queue's attributes, the change can take up to 60 seconds to propagate
218
+ # "When you change a queue's attributes, the change can take up to 60 seconds to propagate
212
219
  # throughout the SQS system."
213
220
  #
214
221
  # NB: Attribute values may not be immediately available to other queries
215
222
  # for some time after an update. See the SQS documentation for
216
223
  # semantics, but in general propagation can take up to 60 s.
224
+ #
225
+ # see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QuerySetQueueAttributes.html
217
226
  def set_queue_attributes(queue_url, attribute, value)
218
- req_hash = generate_request('SetQueueAttributes', 'Attribute.Name' => attribute, 'Attribute.Value' => value, :queue_url => queue_url)
227
+ req_hash = generate_request('SetQueueAttributes',
228
+ 'Attribute.Name' => attribute,
229
+ 'Attribute.Value' => value,
230
+ :queue_url => queue_url)
231
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
232
+ rescue
233
+ on_exception
234
+ end
235
+
236
+ # Add permissions to a queue.
237
+ #
238
+ # sqs.add_permissions('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',
239
+ # 'testLabel', ['125074342641','125074342642'],
240
+ # ['SendMessage','SendMessage','ReceiveMessage']) #=> true
241
+ #
242
+ # +permissions+ is a hash of: AccountId => ActionName
243
+ # (valid ActionNames: * | SendMessage | ReceiveMessage | DeleteMessage | ChangeMessageVisibility | GetQueueAttributes )
244
+ #
245
+ # see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryAddPermission.html
246
+ # http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?acp-overview.html
247
+ def add_permissions(queue_url, label, grantees, actions)
248
+ params = amazonize_list('AWSAccountId', Array(grantees))
249
+ params.merge!(amazonize_list('ActionName', Array(actions)))
250
+ params.merge!('Label' => label,
251
+ :queue_url => queue_url )
252
+ req_hash = generate_request('AddPermission', params)
253
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
254
+ rescue
255
+ on_exception
256
+ end
257
+
258
+ # Revoke any permissions in the queue policy that matches the +label+ parameter.
259
+ #
260
+ # sqs.remove_permissions('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',
261
+ # 'testLabel') # => true
262
+ #
263
+ # see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryRemovePermission.html
264
+ def remove_permissions(queue_url, label)
265
+ req_hash = generate_request('RemovePermission',
266
+ 'Label' => label,
267
+ :queue_url => queue_url )
219
268
  request_info(req_hash, SqsStatusParser.new(:logger => @logger))
220
269
  rescue
221
270
  on_exception
222
271
  end
223
272
 
224
- # Retrieves a list of messages from queue. Returns an array of hashes in format: <tt>{:id=>'message_id', body=>'message_body'}</tt>
273
+ # Retrieves a list of messages from queue. Returns an array of hashes in format: <tt>{:id=>'message_id', :body=>'message_body'}</tt>
225
274
  #
226
- # sqs.receive_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',10, 5) #=>
275
+ # sqs.receive_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',10, 5) #=>
227
276
  # [{"ReceiptHandle"=>"Euvo62...kw==", "MD5OfBody"=>"16af2171b5b83cfa35ce254966ba81e3",
228
277
  # "Body"=>"Goodbyte World!", "MessageId"=>"MUM4WlAyR...pYOTA="}, ..., {}]
229
278
  #
230
279
  # Normally this call returns fewer messages than the maximum specified,
231
280
  # even if they are available.
232
281
  #
233
- def receive_message(queue_url, max_number_of_messages=1, visibility_timeout=nil)
282
+ def receive_message(queue_url, max_number_of_messages=1, visibility_timeout=nil, attributes=nil)
234
283
  return [] if max_number_of_messages == 0
235
- req_hash = generate_post_request('ReceiveMessage', 'MaxNumberOfMessages' => max_number_of_messages, 'VisibilityTimeout' => visibility_timeout,
236
- :queue_url => queue_url )
284
+ params = {}
285
+ params.merge!(amazonize_list('AttributeName', Array(attributes))) unless attributes.blank?
286
+ params.merge!('MaxNumberOfMessages' => max_number_of_messages,
287
+ 'VisibilityTimeout' => visibility_timeout,
288
+ :queue_url => queue_url )
289
+ req_hash = generate_post_request('ReceiveMessage', params)
237
290
  request_info(req_hash, SqsReceiveMessageParser.new(:logger => @logger))
238
291
  rescue
239
292
  on_exception
240
293
  end
294
+
295
+ # Change the visibility timeout of a specified message in a queue.
296
+ #
297
+ # sqs.change_message_visibility('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'Euvo62...kw==', 33) #=> true
298
+ #
299
+ # see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryChangeMessageVisibility.html
300
+ def change_message_visibility(queue_url, receipt_handle, visibility_timeout)
301
+ req_hash = generate_request('ChangeMessageVisibility',
302
+ 'ReceiptHandle' => receipt_handle,
303
+ 'VisibilityTimeout' => visibility_timeout,
304
+ :queue_url => queue_url )
305
+ request_info(req_hash, SqsStatusParser.new(:logger => @logger))
306
+ rescue
307
+ on_exception
308
+ end
241
309
 
242
310
  # Sends a new message to a queue. Message size is limited to 8 KB.
243
311
  # If successful, this call returns a hash containing key/value pairs for
244
312
  # "MessageId" and "MD5OfMessageBody":
245
313
  #
246
- # sqs.send_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'message_1') #=> "1234567890...0987654321"
247
- # => {"MessageId"=>"MEs4M0JKNlRCRTBBSENaMjROTk58QVFRNzNEREhDVFlFOVJDQ1JKNjF8UTdBRllCUlJUMjhKMUI1WDJSWDE=", "MD5OfMessageBody"=>"16af2171b5b83cfa35ce254966ba81e3"}
314
+ # sqs.send_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'message_1') #=>
315
+ # {"MessageId"=>"MEs4M0JKNlRCRTBBSENaMjROTk58QVFRNzNEREhDVFlFOVJDQ1JKNjF8UTdBRllCUlJUMjhKMUI1WDJSWDE=",
316
+ # "MD5OfMessageBody"=>"16af2171b5b83cfa35ce254966ba81e3"}
248
317
  #
249
318
  # On failure, send_message raises an exception.
250
319
  #
@@ -271,7 +340,7 @@ module RightAws
271
340
  # you again on a subsequent receive request. You should create your system to be
272
341
  # idempotent so that receiving a particular message more than once is not a problem. "
273
342
  #
274
- # sqs.delete_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'Euvo62/1nlIet...ao03hd9Sa0w==') #=> true
343
+ # sqs.delete_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'Euvo62/1nlIet...ao03hd9Sa0w==') #=> true
275
344
  #
276
345
  def delete_message(queue_url, receipt_handle)
277
346
  req_hash = generate_request('DeleteMessage', 'ReceiptHandle' => receipt_handle, :queue_url => queue_url)
@@ -281,7 +350,7 @@ module RightAws
281
350
  end
282
351
 
283
352
  # Given the queue's short name, this call returns the queue URL or +nil+ if queue is not found
284
- # sqs.queue_url_by_name('my_awesome_queue') #=> 'http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'
353
+ # sqs.queue_url_by_name('my_awesome_queue') #=> 'https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'
285
354
  #
286
355
  def queue_url_by_name(queue_name)
287
356
  return queue_name if queue_name.include?('/')
@@ -296,7 +365,7 @@ module RightAws
296
365
 
297
366
  # Returns short queue name by url.
298
367
  #
299
- # RightSqs.queue_name_by_url('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'
368
+ # RightSqs.queue_name_by_url('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'
300
369
  #
301
370
  def self.queue_name_by_url(queue_url)
302
371
  queue_url[/[^\/]*$/]
@@ -306,7 +375,7 @@ module RightAws
306
375
 
307
376
  # Returns short queue name by url.
308
377
  #
309
- # sqs.queue_name_by_url('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'
378
+ # sqs.queue_name_by_url('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'
310
379
  #
311
380
  def queue_name_by_url(queue_url)
312
381
  self.class.queue_name_by_url(queue_url)
@@ -316,17 +385,19 @@ module RightAws
316
385
 
317
386
  # Returns approximate number of messages in queue.
318
387
  #
319
- # sqs.get_queue_length('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 3
388
+ # sqs.get_queue_length('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 3
320
389
  #
321
390
  def get_queue_length(queue_url)
322
- get_queue_attributes(queue_url)['ApproximateNumberOfMessages'].to_i
391
+ attrs = get_queue_attributes(queue_url)
392
+ attrs['ApproximateNumberOfMessages'].to_i +
393
+ attrs['ApproximateNumberOfMessagesNotVisible'].to_i
323
394
  rescue
324
395
  on_exception
325
396
  end
326
397
 
327
398
  # Removes all visible messages from queue. Return +true+ or an exception.
328
399
  #
329
- # sqs.clear_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true
400
+ # sqs.clear_queue('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true
330
401
  #
331
402
  def clear_queue(queue_url)
332
403
  while (pop_messages(queue_url, 10).length > 0) ; end # delete all messages in queue
@@ -337,12 +408,12 @@ module RightAws
337
408
 
338
409
  # 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>.
339
410
  #
340
- # sqs.pop_messages('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 3) #=>
411
+ # sqs.pop_messages('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 3) #=>
341
412
  # [{"ReceiptHandle"=>"Euvo62/...+Zw==", "MD5OfBody"=>"16af2...81e3", "Body"=>"Goodbyte World!",
342
413
  # "MessageId"=>"MEZI...JSWDE="}, {...}, ... , {...} ]
343
414
  #
344
- def pop_messages(queue_url, number_of_messages=1)
345
- messages = receive_message(queue_url, number_of_messages)
415
+ def pop_messages(queue_url, number_of_messages=1, attributes=nil)
416
+ messages = receive_message(queue_url, number_of_messages, nil, attributes)
346
417
  messages.each do |message|
347
418
  delete_message(queue_url, message['ReceiptHandle'])
348
419
  end
@@ -353,11 +424,11 @@ module RightAws
353
424
 
354
425
  # Pops (retrieves and deletes) first accessible message from queue. Returns the message in format <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.
355
426
  #
356
- # sqs.pop_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>
427
+ # sqs.pop_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>
357
428
  # {:id=>"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321", :body=>"message_1"}
358
429
  #
359
- def pop_message(queue_url)
360
- messages = pop_messages(queue_url)
430
+ def pop_message(queue_url, attributes=nil)
431
+ messages = pop_messages(queue_url, 1, attributes)
361
432
  messages.blank? ? nil : messages[0]
362
433
  rescue
363
434
  on_exception
@@ -400,8 +471,8 @@ module RightAws
400
471
  end
401
472
  def tagend(name)
402
473
  case name
403
- when 'Name' ; @current_attribute = @text
404
- when 'Value' ; @result[@current_attribute] = @text
474
+ when 'Name' then @current_attribute = @text
475
+ when 'Value' then @result[@current_attribute] = @text
405
476
  end
406
477
  end
407
478
  end
@@ -415,14 +486,23 @@ module RightAws
415
486
  @result = []
416
487
  end
417
488
  def tagstart(name, attributes)
418
- @current_message = {} if name == 'Message'
489
+ case name
490
+ when 'Message' then @current_message = { }
491
+ when 'Attribute' then
492
+ @current_message['Attributes'] ||= {}
493
+ @current_attribute_name = ''
494
+ @current_attribute_value = ''
495
+ end
419
496
  end
420
497
  def tagend(name)
421
498
  case name
422
- when 'MessageId' ; @current_message['MessageId'] = @text
423
- when 'ReceiptHandle' ; @current_message['ReceiptHandle'] = @text
424
- when 'MD5OfBody' ; @current_message['MD5OfBody'] = @text
425
- when 'Body'; @current_message['Body'] = @text; @result << @current_message
499
+ when 'MessageId' then @current_message['MessageId'] = @text
500
+ when 'ReceiptHandle' then @current_message['ReceiptHandle'] = @text
501
+ when 'MD5OfBody' then @current_message['MD5OfBody'] = @text
502
+ when 'Name' then @current_attribute_name = @text
503
+ when 'Value' then @current_attribute_value = @text
504
+ when 'Attribute' then @current_message['Attributes'][@current_attribute_name] = @current_attribute_value
505
+ when 'Body' then @current_message['Body'] = @text; @result << @current_message
426
506
  end
427
507
  end
428
508
  end
@@ -433,8 +513,8 @@ module RightAws
433
513
  end
434
514
  def tagend(name)
435
515
  case name
436
- when 'MessageId' ; @result['MessageId'] = @text
437
- when 'MD5OfMessageBody' ; @result['MD5OfMessageBody'] = @text
516
+ when 'MessageId' then @result['MessageId'] = @text
517
+ when 'MD5OfMessageBody' then @result['MD5OfMessageBody'] = @text
438
518
  end
439
519
  end
440
520
  end