openwferu-extras 0.9.16 → 0.9.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,581 +0,0 @@
1
- #
2
- #--
3
- # Copyright (c) 2007, John Mettraux, OpenWFE.org
4
- # All rights reserved.
5
- #
6
- # Redistribution and use in source and binary forms, with or without
7
- # modification, are permitted provided that the following conditions are met:
8
- #
9
- # . Redistributions of source code must retain the above copyright notice, this
10
- # list of conditions and the following disclaimer.
11
- #
12
- # . Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- #
16
- # . Neither the name of the "OpenWFE" nor the names of its contributors may be
17
- # used to endorse or promote products derived from this software without
18
- # specific prior written permission.
19
- #
20
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
- # POSSIBILITY OF SUCH DAMAGE.
31
- #++
32
- #
33
- # $Id$
34
- #
35
-
36
- #
37
- # Made in Japan
38
- #
39
- # John dot Mettraux at OpenWFE dot org
40
- #
41
-
42
- require 'base64'
43
- require 'cgi'
44
- require 'net/https'
45
- require 'rexml/document'
46
- require 'time'
47
- require 'pp'
48
-
49
-
50
- module SQS
51
-
52
- #
53
- # An SQS message (after its creation).
54
- #
55
- class Message
56
-
57
- attr_reader :queue, :message_id, :message_body
58
-
59
- def initialize (queue, xml_element)
60
-
61
- @queue = queue
62
- @message_id = SQS::get_element_text(xml_element, "MessageId")
63
- @message_body = SQS::get_element_text(xml_element, "MessageBody")
64
- end
65
-
66
- #
67
- # Connects to the queue service and deletes this message in its queue.
68
- #
69
- def delete
70
- @queue.queue_service.delete_message(@queue, @message_id)
71
- end
72
- end
73
-
74
- #
75
- # An SQS queue (gathering all the necessary info about it
76
- # in a single class).
77
- #
78
- class Queue
79
-
80
- attr_reader :queue_service, :host, :path, :name
81
-
82
- def initialize (queue_service, xml_element)
83
-
84
- @queue_service = queue_service
85
-
86
- s = xml_element.text.to_s
87
- m = Regexp.compile('^http://(.*)(/.*)(/.*$)').match(s)
88
- @host = m[1]
89
- @name = m[3][1..-1]
90
- @path = m[2] + m[3]
91
- end
92
- end
93
-
94
- class QueueService
95
-
96
- AWS_VERSION = "2006-04-01"
97
- DEFAULT_QUEUE_HOST = "queue.amazonaws.com"
98
-
99
- def initialize (queue_host=nil)
100
-
101
- @queue_host = queue_host
102
- @queue_host = DEFAULT_QUEUE_HOST unless @queue_host
103
- end
104
-
105
- #
106
- # Lists the queues for the active AWS account.
107
- # If 'prefix' is given, only queues whose name begin with that
108
- # prefix will be returned.
109
- #
110
- def list_queues (prefix=nil)
111
-
112
- queues = []
113
-
114
- path = "/"
115
- path = "#{path}?QueueNamePrefix=#{prefix}" if prefix
116
-
117
- doc = do_action :get, @queue_host, path
118
-
119
- doc.elements.each("//QueueUrl") do |e|
120
- queues << Queue.new(self, e)
121
- end
122
-
123
- return queues
124
- end
125
-
126
- #
127
- # Creates a queue.
128
- #
129
- # If the queue name doesn't comply with SQS requirements for it,
130
- # an error will be raised.
131
- #
132
- def create_queue (queue_name)
133
-
134
- doc = do_action :post, @queue_host, "/?QueueName=#{queue_name}"
135
-
136
- doc.elements.each("//QueueUrl") do |e|
137
- return e.text.to_s
138
- end
139
- end
140
-
141
- #
142
- # Given some content ('text/plain' content), send it as a message to
143
- # a queue.
144
- # Returns the SQS message id (a String).
145
- #
146
- # The queue might be a queue name (String) or a Queue instance.
147
- #
148
- def put_message (queue, content)
149
-
150
- queue = resolve_queue(queue)
151
-
152
- doc = do_action :put, queue.host, "#{queue.path}/back", content
153
-
154
- #puts doc.to_s
155
-
156
- #status_code = SQS::get_element_text(doc, '//StatusCode')
157
- #message_id = SQS::get_element_text(doc, '//MessageId')
158
- #request_id = SQS::get_element_text(doc, '//RequestId')
159
- #{ :status_code => status_code,
160
- # :message_id => message_id,
161
- # :request_id => request_id }
162
-
163
- SQS::get_element_text(doc, '//MessageId')
164
- end
165
-
166
- alias :send_message :put_message
167
-
168
- #
169
- # Retrieves a bunch of messages from a queue. Returns a list of
170
- # Message instances.
171
- #
172
- # There are actually two optional params that this method understands :
173
- #
174
- # - :timeout the duration in seconds of the message visibility in the
175
- # queue
176
- # - :count the max number of message to be returned by this call
177
- #
178
- # The queue might be a queue name (String) or a Queue instance.
179
- #
180
- def get_messages (queue, params={})
181
-
182
- queue = resolve_queue(queue)
183
-
184
- path = "#{queue.path}/front"
185
-
186
- path += "?" if params.size > 0
187
-
188
- timeout = params[:timeout]
189
- count = params[:count]
190
-
191
- path += "VisibilityTimeout=#{timeout}" if timeout
192
- path += "&" if timeout and count
193
- path += "NumberOfMessages=#{count}" if count
194
-
195
- doc = do_action :get, queue.host, path
196
-
197
- messages = []
198
-
199
- doc.elements.each("//Message") do |me|
200
- messages << Message.new(queue, me)
201
- end
202
-
203
- messages
204
- end
205
-
206
- #
207
- # Retrieves a single message from a queue. Returns an instance of
208
- # Message.
209
- #
210
- # The queue might be a queue name (String) or a Queue instance.
211
- #
212
- def get_message (queue, message_id)
213
-
214
- queue = resolve_queue(queue)
215
-
216
- path = "#{queue.path}/#{message_id}"
217
-
218
- begin
219
- doc = do_action :get, queue.host, path
220
- Message.new(queue, doc.root.elements[1])
221
- rescue Exception => e
222
- #puts e.message
223
- return nil if e.message.match "^404 .*$"
224
- raise e
225
- end
226
- end
227
-
228
- #
229
- # Deletes a given message.
230
- #
231
- # The queue might be a queue name (String) or a Queue instance.
232
- #
233
- def delete_message (queue, message_id)
234
-
235
- queue = resolve_queue(queue)
236
-
237
- path = "#{queue.path}/#{message_id}"
238
- #path = "#{queue.path}/#{CGI::escape(message_id)}"
239
-
240
- doc = do_action :delete, queue.host, path
241
-
242
- SQS::get_element_text(doc, "//StatusCode") == "Success"
243
- end
244
-
245
- #
246
- # Use with care !
247
- #
248
- # Attempts at deleting all the messages in a queue.
249
- # Returns the total count of messages deleted.
250
- #
251
- # A call on this method might take a certain time, as it has
252
- # to delete each message individually. AWS will perhaps
253
- # add a proper 'flush_queue' method later.
254
- #
255
- # The queue might be a queue name (String) or a Queue instance.
256
- #
257
- def flush_queue (queue)
258
-
259
- count = 0
260
-
261
- while true
262
-
263
- l = get_messages(queue, :timeout => 0, :count => 255)
264
- break if l.length < 1
265
-
266
- l.each do |m|
267
- m.delete
268
- count += 1
269
- end
270
- end
271
-
272
- return count
273
- end
274
-
275
- #
276
- # Deletes the queue. Returns true if the delete was successful.
277
- # You can empty a queue by called the method #flush_queue
278
- #
279
- # If 'force' is set to true, a flush will be performed on the
280
- # queue before the actual delete operation. It should ensure
281
- # a successful removal of the queue.
282
- #
283
- def delete_queue (queue, force=false)
284
-
285
- queue = resolve_queue(queue)
286
-
287
- flush_queue(queue) if force
288
-
289
- begin
290
-
291
- doc = do_action :delete, @queue_host, queue.path
292
-
293
- rescue Exception => e
294
-
295
- return false if e.message.match "^400 .*$"
296
- end
297
-
298
- SQS::get_element_text(doc, "//StatusCode") == "Success"
299
- end
300
-
301
- #
302
- # Given a queue name, a Queue instance is returned.
303
- #
304
- def get_queue (queue_name)
305
-
306
- l = list_queues(queue_name)
307
-
308
- l.each do |q|
309
- return q if q.name == queue_name
310
- end
311
-
312
- #return nil
313
- raise "found no queue named '#{queue_name}'"
314
- end
315
-
316
- protected
317
-
318
- #
319
- # 'queue' might be a Queue instance or a queue name.
320
- # If it's a Queue instance, it is immediately returned,
321
- # else the Queue instance is looked up and returned.
322
- #
323
- def resolve_queue (queue)
324
- return queue if queue.kind_of? Queue
325
- return get_queue(queue.to_s)
326
- end
327
-
328
- #
329
- # The actual http request/response job is done here.
330
- #
331
- def do_action (action, host, path, content=nil)
332
-
333
- #puts "___path : #{path}"
334
-
335
- doc = nil
336
-
337
- http = Net::HTTP.new(host)
338
- http.start do
339
-
340
- date = Time.now.httpdate
341
-
342
- req = if action == :get
343
- Net::HTTP::Get.new(path)
344
- elsif action == :post
345
- Net::HTTP::Post.new(path)
346
- elsif action == :put
347
- Net::HTTP::Put.new(path)
348
- else #action == :delete
349
- Net::HTTP::Delete.new(path)
350
- end
351
-
352
- req['AWS-Version'] = AWS_VERSION
353
- req['Date'] = date
354
- req['Content-type'] = 'text/plain'
355
-
356
- if action == :put or action == :post
357
- req.body = content
358
- req['Content-length'] = content.length.to_s if content
359
- end
360
-
361
- req['Authorization'] = generate_auth_header(
362
- action, path, date, "text/plain")
363
-
364
- #req.each_header do |k, v|
365
- # puts " - '#{k}' => '#{v}'"
366
- #end
367
-
368
- res = http.request(req)
369
-
370
- case res
371
- when Net::HTTPSuccess, Net::HTTPRedirection
372
- doc = REXML::Document.new(res.read_body)
373
- else
374
- res.error!
375
- end
376
- end
377
- raise_errors(doc)
378
- return doc
379
- end
380
-
381
- #
382
- # Scans the SQS XML reply for potential errors and raises an
383
- # error if he encounters one.
384
- #
385
- def raise_errors (doc)
386
-
387
- doc.elements.each("//Error") do |e|
388
-
389
- code = get_element_text(e, "Code")
390
- return unless code
391
-
392
- message = get_element_text(e, "Message")
393
- raise "SQS::#{code} : #{m.text.to_s}"
394
- end
395
- end
396
-
397
- #
398
- # Generates the 'AWS x:y" authorization header value.
399
- #
400
- def generate_auth_header (action, path, date, content_type)
401
-
402
- s = ""
403
- s << action.to_s.upcase
404
- s << "\n"
405
-
406
- #s << Base64.encode64(Digest::MD5.digest(content)).strip \
407
- # if content
408
- #
409
- # documented but not necessary (not working)
410
- s << "\n"
411
-
412
- s << content_type
413
- s << "\n"
414
-
415
- s << date
416
- s << "\n"
417
-
418
- i = path.index '?'
419
- path = path[0..i-1] if i
420
- s << path
421
-
422
- #puts ">>>#{s}<<<"
423
-
424
- digest = OpenSSL::Digest::Digest.new 'sha1'
425
-
426
- key = ENV['AMAZON_SECRET_ACCESS_KEY']
427
-
428
- raise "No $AMAZON_SECRET_ACCESS_KEY env variable found" \
429
- unless key
430
-
431
- sig = OpenSSL::HMAC.digest(digest, key, s)
432
- sig = Base64.encode64(sig).strip
433
-
434
- "AWS #{ENV['AMAZON_ACCESS_KEY_ID']}:#{sig}"
435
- end
436
-
437
- end
438
-
439
- #
440
- # A convenience method for returning the text of a sub element,
441
- # maybe there is something better in REXML, but I haven't found out
442
- # yet.
443
- #
444
- def SQS.get_element_text (parent_elt, elt_name)
445
- e = parent_elt.elements[elt_name]
446
- return nil unless e
447
- return e.text.to_s
448
- end
449
- end
450
-
451
-
452
- #
453
- # running directly...
454
-
455
- if $0 == __FILE__
456
-
457
- if ENV['AMAZON_ACCESS_KEY_ID'] == nil or ENV['AMAZON_SECRET_ACCESS_KEY'] == nil
458
-
459
- puts
460
- puts "env variables $AMAZON_ACCESS_KEY_ID and $AMAZON_SECRET_ACCESS_KEY are not set"
461
- puts
462
- exit 1
463
- end
464
-
465
- ACTIONS = {
466
- :list_queues => :list_queues,
467
- :lq => :list_queues,
468
- :create_queue => :create_queue,
469
- :cq => :create_queue,
470
- :delete_queue => :delete_queue,
471
- :dq => :delete_queue,
472
- :flush_queue => :flush_queue,
473
- :fq => :flush_queue,
474
- :get_message => :get_message,
475
- :gm => :get_message,
476
- :delete_message => :delete_message,
477
- :dm => :delete_message,
478
- :puts_message => :put_message,
479
- :pm => :put_message
480
- }
481
-
482
- b64 = false
483
- queue_host = nil
484
-
485
- require 'optparse'
486
-
487
- opts = OptionParser.new
488
-
489
- opts.banner = "Usage: sqs.rb [options] {action} [queue_name] [message_id]"
490
- opts.separator("")
491
- opts.separator(" known actions are :")
492
- opts.separator("")
493
-
494
- keys = ACTIONS.keys.collect { |k| k.to_s }.sort
495
- keys.each { |k| opts.separator(" - '#{k}' (#{ACTIONS[k.intern]})") }
496
-
497
- opts.separator("")
498
- opts.separator(" options are :")
499
- opts.separator("")
500
-
501
- opts.on("-H", "--host", "AWS queue host") do |host|
502
- queue_host = host
503
- end
504
-
505
- opts.on("-h", "--help", "displays this help / usage") do
506
- STDERR.puts "\n#{opts.to_s}\n"
507
- exit 0
508
- end
509
-
510
- opts.on("-b", "--base64", "encode/decode messages with base64") do
511
- b64 = true
512
- end
513
-
514
- argv = opts.parse(ARGV)
515
-
516
- if argv.length < 1
517
- STDERR.puts "\n#{opts.to_s}\n"
518
- exit 0
519
- end
520
-
521
- a = argv[0]
522
- queue_name = argv[1]
523
- message_id = argv[2]
524
-
525
- action = ACTIONS[a.intern]
526
-
527
- unless action
528
- STDERR.puts "unknown action '#{a}'"
529
- exit 1
530
- end
531
-
532
- qs = SQS::QueueService.new
533
-
534
- STDERR.puts "#{action.to_s}..."
535
-
536
- #
537
- # just do it
538
-
539
- case action
540
- when :list_queues, :create_queue, :delete_queue, :flush_queue
541
-
542
- pp qs.send(action, queue_name)
543
-
544
- when :get_message
545
-
546
- if message_id
547
- m = qs.get_message(queue_name, message_id)
548
- body = m.message_body
549
- body = Base64.decode64(body) if b64
550
- puts body
551
- else
552
- pp qs.get_messages(queue_name, :timeout => 0, :count => 255)
553
- end
554
-
555
- when :delete_message
556
-
557
- raise "argument 'message_id' is missing" unless message_id
558
- pp qs.delete_message(queue_name, message_id)
559
-
560
- when :put_message
561
-
562
- message = argv[2]
563
-
564
- unless message
565
- message = ""
566
- while true
567
- s = STDIN.gets()
568
- break if s == nil
569
- message += s[0..-2]
570
- end
571
- end
572
-
573
- message = Base64.encode64(message).strip if b64
574
-
575
- pp qs.put_message(queue_name, message)
576
- else
577
-
578
- STDERR.puts "not yet implemented..."
579
- end
580
-
581
- end