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 +1 -1
- data/bin/amqp-listen +143 -0
- data/bin/emissary-send-message +154 -0
- metadata +9 -5
data/VERSION.yml
CHANGED
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:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 1.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-
|
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
|