emissary 1.3.3 → 1.3.5

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 1
3
3
  :minor: 3
4
- :patch: 3
4
+ :patch: 5
data/bin/amqp-listen ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'ostruct'
6
+ require 'pp'
7
+
8
+ def require_lib library
9
+ begin
10
+ require library
11
+ rescue LoadError
12
+ real_name = library[/([^\/]+)/,1]
13
+ puts %Q(Missing required library '#{real_name}' - please install to continue: sudo gem install #{real_name})
14
+ exit! 1
15
+ end
16
+ end
17
+
18
+ require_lib 'mq'
19
+ require_lib 'highline/import'
20
+ require_lib 'emissary'
21
+
22
+ $options = OpenStruct.new(
23
+ :user => 'nimbul',
24
+ :pass => nil,
25
+ :host => ENV['AMQP_HOST'] || 'mq.example.tld',
26
+ :port => ENV['AMQP_PORT'] || 5672,
27
+ :vhost => ENV['AMQP_VHOST'] || '/nimbul',
28
+ :ssl => (ENV['AMQP_USE_SSL'].nil? ? true : !!ENV['AMQP_USE_SSL']),
29
+ :bindings => { :direct => [], :topic => [], :fanout => [] }
30
+ )
31
+
32
+ def password
33
+ $options.pass = ask('Enter AMQP Service password: ') { |q| q.echo = '*' } unless not $options.pass.blank?
34
+ $options.pass
35
+ end
36
+
37
+ OptionParser.new("Usage: #{File.basename($0)} [options] -- 'exchange_type:route_key1' .. 'exchange_type:route_keyN' ") do |parser|
38
+ parser.separator ''
39
+ parser.separator "Connection Options:"
40
+ parser.on('-u', '--user [USER]', %Q(User to connect to the AMQP service as. Default: #{$options.user})) {|v| $options.user = v }
41
+ parser.on('-p', '--password [PASS]', %Q(Password to use when connecting to AMQP service. Default: <empty>)) { |v| $options.pass = escape_password(v)}
42
+ parser.on('-H', '--host [HOST]', String, %Q(Host to connect to for AMQP service. Default: #{$options.host})) {|v| $options.host= v }
43
+ parser.on('-P', '--port [PORT]', Integer, %Q(Port to connect to for AMQP service. Default: #{$options.port})) {|v| $options.port = v }
44
+ parser.on('-V', '--vhost [VHOST]', %Q(VHOST to use when connecting to AMQP service. Default: #{$options.vhost})) { |v| $options.vhost = v }
45
+ parser.on('--[no-]ssl', 'Use ssl (or not) when connecting to the AMQP service. Default: true') { |v| $options.ssl = !!v }
46
+
47
+ parser.separator ''
48
+ parser.separator 'General'
49
+ parser.on_tail('-h', '--help', 'This message') { puts parser.help; exit! 0 }
50
+
51
+ result = parser.parse!
52
+
53
+ unless ARGV.size > 0
54
+ puts "Missing required exchange bindings (format: <exchange>:<route_key>)"
55
+ exit! 1
56
+ else
57
+ ARGV.each do |binding|
58
+ binding = "topic:#{binding}" unless binding.include? ':'
59
+ type, route = binding.downcase.split(/:/)
60
+
61
+ unless $options.bindings.include? type.to_sym
62
+ puts %Q(Unsupported exchange type '#{type}' - valid exchanges are: #{$options.bindings.keys.join ', '})
63
+ exit! 1
64
+ end
65
+
66
+ $options.bindings[type.to_sym] << route
67
+ end
68
+ end
69
+ result
70
+ end
71
+
72
+ HOSTNAME = `hostname -f`.strip
73
+
74
+ def connect
75
+ @connect_details = {
76
+ :host => $options.host,
77
+ :ssl => $options.ssl,
78
+ :port => $options.port,
79
+ :user => $options.user,
80
+ :pass => password,
81
+ :vhost => $options.vhost
82
+ }
83
+
84
+ @connection = ::AMQP.connect(@connect_details)
85
+ @channel = ::MQ.new(@connection)
86
+
87
+ @queue_config = {
88
+ :durable => false,
89
+ :auto_delete => true,
90
+ :exclusive => true
91
+ }
92
+
93
+ puts "QUEUE: #{HOSTNAME}.#{$$}"
94
+ @queue = ::MQ::Queue.new(@channel, "#{HOSTNAME}.#{$$}", @queue_config)
95
+
96
+ @exchanges = {}
97
+ @exchanges[:topic] = ::MQ::Exchange.new(@channel, :topic, 'amq.topic')
98
+ @exchanges[:fanout] = ::MQ::Exchange.new(@channel, :fanout, 'amq.fanout')
99
+ @exchanges[:direct] = ::MQ::Exchange.new(@channel, :direct, 'amq.direct')
100
+
101
+ end
102
+
103
+ def subscribe
104
+ $options.bindings.each do |type, bindings|
105
+ bindings.each { |routing_key|
106
+ puts "Binding to #{type.to_s.capitalize} exchange with routing key '#{routing_key}'"
107
+ @queue.bind(@exchanges[type], :key => routing_key )
108
+ }
109
+ end
110
+
111
+ puts; puts
112
+ puts "Waiting for Messages..."
113
+ puts "------"
114
+
115
+ @queue.subscribe do |header,message|
116
+ puts "Received Message:"
117
+ puts "------- START -------"
118
+ begin
119
+ m = Emissary::Message.decode(message)
120
+ puts
121
+ puts "AMQP Headers: #{header.inspect}"
122
+ puts "Emissary Headers: "; pp m.headers
123
+ puts "Emissary Data: "; pp m.data
124
+ puts "Emissary Errors: "; pp m.errors
125
+ rescue ::Emissary::Error::InvalidMessageFormat => e
126
+ puts
127
+ puts "AMQP Headers: #{header.inspect}"
128
+ puts "Message: #{message}"
129
+ end
130
+ puts "------- END -------"
131
+ end
132
+ end
133
+
134
+ EM.run do
135
+ trap("INT") { EM.stop }
136
+ trap("TERM") { EM.stop }
137
+ connect
138
+ subscribe
139
+ EM.add_periodic_timer(300) {
140
+ # reset every five minutes in case of connection issues
141
+ @queue.reset
142
+ }
143
+ end
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'ostruct'
5
+
6
+ def require_lib library
7
+ begin
8
+ require library
9
+ rescue LoadError
10
+ puts %q(Missing required library '#{library[/([^\/]+)/,1}' - please install to continue: sudo gem install eventmachine)
11
+ exit! 1
12
+ end
13
+ end
14
+
15
+ require_lib 'eventmachine'
16
+ require_lib 'highline/import'
17
+ require_lib 'emissary'
18
+
19
+ $options = OpenStruct.new(
20
+ :user => 'nimbul',
21
+ :pass => nil,
22
+ :host => ENV['AMQP_HOST'] || 'mq.example.tld',
23
+ :port => ENV['AMQP_PORT'] || 5672,
24
+ :vhost => ENV['AMQP_VHOST'] || '/nimbul',
25
+ :ssl => (ENV['AMQP_USE_SSL'].nil? ? true : !!ENV['AMQP_USE_SSL']),
26
+ :uri => '',
27
+ :route => 'request.#',
28
+ :exchange => 'topic',
29
+ :agent => :ping,
30
+ :func => :ping,
31
+ :account => -1,
32
+ :timeout => 0.25,
33
+ :show_message => false,
34
+ :raise_errors => true
35
+ )
36
+
37
+ $options.uri = "amqps://#{$options.host}:#{$options.port}/nimbul"
38
+
39
+ def escape_password password
40
+ URI.escape(password, '~`!@#$%^&*()_-+=[]{}|\:;<,>.?/')
41
+ end
42
+
43
+ OptionParser.new("Usage: #{File.basename($0)} [options] -- [ARGS]") do |parser|
44
+ parser.separator ''
45
+ parser.separator "Connection Options:"
46
+
47
+ parser.on('-u', '--user [USER]', %Q(User to connect to the AMQP service as. Default: #{$options.user})) {|v| $options.user = v }
48
+ parser.on('-p', '--password [PASS]', %Q(Password to use when connecting to AMQP service. Default: <empty>)) { |v| $options.pass = escape_password(v)}
49
+ parser.on('-H', '--host [HOST]', String, %Q(Host to connect to for AMQP service. Default: #{$options.host})) {|v| $options.host= v }
50
+ parser.on('-P', '--port [PORT]', Integer, %Q(Port to connect to for AMQP service. Default: #{$options.port})) {|v| $options.port = v }
51
+ parser.on('-V', '--vhost [VHOST]', %Q(VHOST to use when connecting to AMQP service. Default: #{$options.vhost})) { |v| $options.vhost = v }
52
+ parser.on('--[no-]ssl', 'Use ssl (or not) when connecting to the AMQP service. Default: true') { |v| $options.ssl = !!v }
53
+ parser.on('-U', '--uri [URI]', "URI to use when connecting. Default: #{$options.uri}") { |v| $options.uri = v }
54
+
55
+ parser.separator ''
56
+ parser.separator 'Routing Options:'
57
+ parser.on('-r', '--route [ROUTE]', String, 'Routing key to use when sending message') { |v| $options.route = v }
58
+ parser.on('-x', '--exchange [EXCHANGE]', [ 'topic', 'direct' ], 'Exchange type (topic/direct) used when routing message [DEFAULT: topic]') \
59
+ { |v| $options.exchange = v.downcase }
60
+
61
+ parser.separator ''
62
+ parser.separator 'Package Data:'
63
+ parser.on('-a', '--agent [NAME]', String, 'Agent to request execution of') { |v| $options.agent = v.to_sym }
64
+ parser.on('-A', '--account [ID]', Integer, 'Account to request execution of') { |v| $options.account = v.to_i }
65
+ parser.on('-m', '--method [NAME]', String, 'Method to request execution of') { |v| $options.func = v.to_sym }
66
+ parser.on('-t', '--timeout [SECONDS]', Float, 'Time to wait from last message before quitting [DEFAULT: 5 seconds]') \
67
+ { |v| v > 0 ? $options.timeout = v : nil }
68
+
69
+ parser.separator ''
70
+ parser.separator 'General'
71
+ parser.on_tail('--show-message', 'Display the message that would be sent and exit') { |v| $options.show_message = v }
72
+ parser.on_tail('--[no-]raise-errors', 'Raise errors received in respose messages.') { |v| $options.raise_errors = v }
73
+ parser.on_tail('-h', '--help', 'This message') { puts parser.help; exit! 0 }
74
+
75
+ result = parser.parse!
76
+ result
77
+ end
78
+
79
+ def get_message
80
+ message = Emissary::Message.new(
81
+ :headers => {
82
+ :recipient => "#{$options.route}:#{$options.exchange}",
83
+ :replyto => 'nimbul:direct',
84
+ :sender => 'nimbul:direct',
85
+ :originator => 'nimbul:direct'
86
+ },
87
+ :data => {
88
+ :agent => $options.agent,
89
+ :method => $options.func,
90
+ :account => $options.account,
91
+ :args => ARGV
92
+ }
93
+ )
94
+
95
+ unless not $options.show_message
96
+ puts "Sending the following message:"; pp message
97
+ exit! 0
98
+ end
99
+
100
+ message
101
+ end
102
+
103
+ def get_operator
104
+ unless not $options.pass.blank?
105
+ $options.pass = escape_password(ask('Enter AMQP Service password: ') { |q| q.echo = '*' })
106
+ end
107
+
108
+ connect_uri = URI.parse($options.uri)
109
+ connect_uri.user = $options.user || 'nimbul'
110
+ connect_uri.password = $options.pass
111
+ connect_uri.host = $options.host
112
+ connect_uri.port = $options.port || ($options.ssl ? 5671 : 5672)
113
+ connect_uri.scheme = $options.ssl ? 'amqps' : 'amqp'
114
+ connect_uri.path = $options.vhost || '/'
115
+
116
+ puts "URI: #{connect_uri.to_s}"
117
+ operator = Emissary.call(:amqp, { :uri => connect_uri.to_s, :subscriptions => [] })
118
+
119
+ class << operator
120
+ alias :original_receive :receive
121
+ def last_received=(value) @last_received = value; end
122
+ def last_received() @last_received ||= Time.now; end
123
+
124
+ def receive message
125
+ last_received = Time.now
126
+ raise message.errors.first unless message.errors.empty? or not $options.raise_errors
127
+ $stdout.puts sprintf("Instance Response Recieved: %s - response time: %.2fms\n", message.replyto, (1000 * (last_received.to_f - @send_time.to_f)))
128
+ end
129
+
130
+ def send message
131
+ @send_time = Time.now
132
+ send_data message
133
+ end
134
+ end
135
+
136
+ operator
137
+ end
138
+
139
+
140
+ EM.run {
141
+ message = get_message
142
+ operator = get_operator
143
+
144
+ operator.connect
145
+ operator.subscribe
146
+ operator.send message
147
+
148
+ EM.add_periodic_timer(0.05) {
149
+ if Time.now.to_f - operator.last_received.to_f > $options.timeout.to_f
150
+ EM.stop
151
+ exit! 0
152
+ end
153
+ }
154
+ }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: emissary
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 3
9
- - 3
10
- version: 1.3.3
9
+ - 5
10
+ version: 1.3.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Carl P. Corliss
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-19 00:00:00 -04:00
18
+ date: 2010-09-09 00:00:00 -04:00
19
19
  default_executable: bin/emissary-setup
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -181,8 +181,10 @@ dependencies:
181
181
  description:
182
182
  email: carl.corliss@nytimes.com
183
183
  executables:
184
- - emissary
185
184
  - emissary-setup
185
+ - emissary-send-message
186
+ - emissary
187
+ - amqp-listen
186
188
  extensions: []
187
189
 
188
190
  extra_rdoc_files:
@@ -220,7 +222,9 @@ files:
220
222
  - etc/init.d/emissary
221
223
  - etc/emissary/config.ini
222
224
  - bin/emissary-setup
225
+ - bin/emissary-send-message
223
226
  - bin/emissary
227
+ - bin/amqp-listen
224
228
  - VERSION.yml
225
229
  - LICENSE
226
230
  - README.txt