logstash-integration-rabbitmq 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2 -0
  3. data/CONTRIBUTORS +26 -0
  4. data/Gemfile +11 -0
  5. data/LICENSE +13 -0
  6. data/NOTICE.TXT +5 -0
  7. data/README.md +98 -0
  8. data/docs/index-input.asciidoc +395 -0
  9. data/docs/index-output.asciidoc +266 -0
  10. data/lib/logstash/inputs/rabbitmq.rb +317 -0
  11. data/lib/logstash/outputs/rabbitmq.rb +100 -0
  12. data/lib/logstash/plugin_mixins/rabbitmq_connection.rb +235 -0
  13. data/lib/logstash_registry.rb +7 -0
  14. data/logstash-integration-rabbitmq.gemspec +37 -0
  15. data/spec/fixtures/README.md +150 -0
  16. data/spec/fixtures/client/cert.pem +69 -0
  17. data/spec/fixtures/client/key.pem +27 -0
  18. data/spec/fixtures/client/keycert.p12 +0 -0
  19. data/spec/fixtures/client/req.pem +15 -0
  20. data/spec/fixtures/client_untrusted/cert.pem +19 -0
  21. data/spec/fixtures/client_untrusted/key.pem +28 -0
  22. data/spec/fixtures/client_untrusted/keycert.p12 +0 -0
  23. data/spec/fixtures/server/cert.pem +69 -0
  24. data/spec/fixtures/server/key.pem +27 -0
  25. data/spec/fixtures/server/key_password +1 -0
  26. data/spec/fixtures/server/keycert.p12 +0 -0
  27. data/spec/fixtures/server/rabbitmq.config +10 -0
  28. data/spec/fixtures/server/req.pem +15 -0
  29. data/spec/fixtures/testca/cacert.cer +0 -0
  30. data/spec/fixtures/testca/cacert.pem +17 -0
  31. data/spec/fixtures/testca/certs/01.pem +18 -0
  32. data/spec/fixtures/testca/certs/02.pem +18 -0
  33. data/spec/fixtures/testca/certs/05.pem +69 -0
  34. data/spec/fixtures/testca/certs/06.pem +69 -0
  35. data/spec/fixtures/testca/index.txt +4 -0
  36. data/spec/fixtures/testca/index.txt.attr +1 -0
  37. data/spec/fixtures/testca/index.txt.attr.old +1 -0
  38. data/spec/fixtures/testca/index.txt.old +3 -0
  39. data/spec/fixtures/testca/openssl.cnf +53 -0
  40. data/spec/fixtures/testca/private/cakey.pem +27 -0
  41. data/spec/fixtures/testca/serial +1 -0
  42. data/spec/fixtures/testca/serial.old +1 -0
  43. data/spec/inputs/rabbitmq_spec.rb +278 -0
  44. data/spec/outputs/rabbitmq_spec.rb +228 -0
  45. data/spec/plugin_mixins/rabbitmq_connection_spec.rb +175 -0
  46. metadata +241 -0
@@ -0,0 +1,4 @@
1
+ V 170510165759Z 01 unknown /CN=localhost/O=server
2
+ V 170510170001Z 02 unknown /CN=localhost/O=client
3
+ V 450505233839Z 05 unknown /CN=localhost, O=client
4
+ V 450505234253Z 06 unknown /CN=localhost, O=server
@@ -0,0 +1 @@
1
+ unique_subject = yes
@@ -0,0 +1 @@
1
+ unique_subject = yes
@@ -0,0 +1,3 @@
1
+ V 170510165759Z 01 unknown /CN=localhost/O=server
2
+ V 170510170001Z 02 unknown /CN=localhost/O=client
3
+ V 450505233839Z 05 unknown /CN=localhost, O=client
@@ -0,0 +1,53 @@
1
+ [ ca ]
2
+ default_ca = testca
3
+
4
+ [ testca ]
5
+ dir = .
6
+ certificate = $dir/cacert.pem
7
+ database = $dir/index.txt
8
+ new_certs_dir = $dir/certs
9
+ private_key = $dir/private/cakey.pem
10
+ serial = $dir/serial
11
+
12
+ default_crl_days = 7
13
+ default_days = 10000
14
+ default_md = sha256
15
+
16
+ policy = testca_policy
17
+ x509_extensions = certificate_extensions
18
+
19
+ [ testca_policy ]
20
+ commonName = supplied
21
+ stateOrProvinceName = optional
22
+ countryName = optional
23
+ emailAddress = optional
24
+ organizationName = optional
25
+ organizationalUnitName = optional
26
+
27
+ [ certificate_extensions ]
28
+ basicConstraints = CA:false
29
+
30
+ [ req ]
31
+ default_bits = 2048
32
+ default_keyfile = ./private/cakey.pem
33
+ default_md = sha256
34
+ prompt = yes
35
+ distinguished_name = root_ca_distinguished_name
36
+ x509_extensions = root_ca_extensions
37
+
38
+ [ root_ca_distinguished_name ]
39
+ commonName = hostname
40
+
41
+ [ root_ca_extensions ]
42
+ basicConstraints = CA:true
43
+ keyUsage = keyCertSign, cRLSign
44
+
45
+ [ client_ca_extensions ]
46
+ basicConstraints = CA:false
47
+ keyUsage = digitalSignature
48
+ extendedKeyUsage = 1.3.6.1.5.5.7.3.2
49
+
50
+ [ server_ca_extensions ]
51
+ basicConstraints = CA:false
52
+ keyUsage = keyEncipherment
53
+ extendedKeyUsage = 1.3.6.1.5.5.7.3.1
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEpQIBAAKCAQEAuP3x9a2NoWisFrddKseOJir4xvvLPP65bWUH5l8KkzvG5c+N
3
+ PKfcxkzlHme9B3WWdGB3lx1buwyunbAejn9Uuwwc1QQA1kzBEGZgx7tgvmU0BRsg
4
+ EL1pwd3v9Rc2cZxmNrQCqymGWNUqo4BfaNVlxoCHS23FckjwUJRfck54c3WqsunT
5
+ jE6CoVtbYiKztp4fA38eJRyLgw1bZLOtNIySj1fRKywrCuxrGjdB7zd+c/ScsB8Y
6
+ EFXwYEx2a9cL3qtnBrbIEh6V09E2cnIvsrdILzzk6OtNs9NnSA0XGcMMrBWHHs+i
7
+ POdPX851inr3QFTnrPVtn4blGOGdZdR6QwNo2wIDAQABAoIBADoXZLflfChHmmK3
8
+ ygX5DGZn8B9sSnIo+0mjBEwPZF6/0sGv34ZAoE+VLg3SPcXt4wVAlc1aZsfiP6M8
9
+ /xt4WL80Gom57BlfmPDxdUrDSKoBVciUsAkRsfgzHXs1gt9CYcaj2IKvU4Tpy630
10
+ TgG5oXAoRFQncG1nAjALp71ZbvGyZDOK2+/Vjs0NITyC+4srnafiF8pwLXmgm0Zj
11
+ FVyCy8H/dwt7/pJKKkDX8Mq6kdvvROg8QDqTyCsTn3qAfDQUjO1BJje7x31E+lXW
12
+ /FgUqs43ATU2HVczP0fbBZeGeFFTRycypmmSXYb9eZ05mDf8cm93V8KMPkTVh4Pu
13
+ 6/kF8AECgYEA8VpoTggar66f/0rfxX263q+L5YD8fwfuqcjLx/Bnuzpp1POhsTLD
14
+ Gv0W5FcAf4i1jfnBevrk9q9WFnnUpUvX7Y+e1cvK9M5BuVRs77C4mE5IQ7hcHN6I
15
+ vNgYihIK/PTVXYrT2j1EFLBwD2QWwO7BcV5aAiShlce7qC8fJeAhRtsCgYEAxDft
16
+ QIUHm2MIzZD25q6fkki1Rx0ifOjO7AlK0NIyxQ+gsxZQrrUsSqcgC/W3XJSONeCY
17
+ UjbHRQmhzkexA6ZFjgrL39qHL9x0kzle1dZ+oFGVQKiIMWn1WfEL0baDxMjFKDQt
18
+ xF0SWRHZsIDPUjZzPTyfaPf8I148pIoSILB7BgECgYEA3679ioxiRz5dlNqM59ku
19
+ DuK9klfoK8drPzoU+1nomZJ6sV2XFsZIIsQ7qiakFI7cTRgTZGoRODuqWqxRE11m
20
+ Ywq/l8AHermKGjyPtdmgS7AJs5Gy9SKdsf/JRnWQb35uHQLkc5hid5ZKVUla+TaO
21
+ XAao/uF6THnPhwEdKho+XQ8CgYEAiyQBiJQM/eIvVt4qRxCjNS975M7DKwJH4VcB
22
+ h6zWtajMUtJLKmhs3Q9ACVsXyH3LjmcSfJI9ojYfWFC8NJNOlVgQlE+5N3ZD8DZp
23
+ ioeMyZCwnuYjla7Gfh4RPIgJTpz0OfsuTSWWojSnQqNE4M6dz1nSzLO1RztHE4KZ
24
+ MjcTNgECgYEAmeEA8KJzfsSgjjbhOmzxEJwRxzq0HTknTmWyEIamQBgFhaQ3AqZ+
25
+ cZWkqS34foKxjLLXp3AMukpS50yRB98Wzaa7eoA/H0zXlRjA6id9qFuCXt+E9e/5
26
+ sjPhQphj8473SsviSndT29JadTHgY8nSmXtxx+whO29D9Ods0laGb8A=
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1 @@
1
+ 07
@@ -0,0 +1 @@
1
+ 06
@@ -0,0 +1,278 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/inputs/rabbitmq"
4
+ require "thread"
5
+ require 'logstash/event'
6
+
7
+ Thread.abort_on_exception = true
8
+
9
+ describe LogStash::Inputs::RabbitMQ do
10
+ let(:klass) { LogStash::Inputs::RabbitMQ }
11
+ let(:host) { "localhost" }
12
+ let(:port) { 5672 }
13
+ let(:exchange_type) { "topic" }
14
+ let(:exchange) { "myexchange" }
15
+ let(:queue) { "myqueue" }
16
+ let(:rabbitmq_settings) {
17
+ {
18
+ "host" => host,
19
+ "port" => port,
20
+ "queue" => queue,
21
+ "prefetch_count" => 123
22
+ }
23
+ }
24
+ let(:instance) { klass.new(rabbitmq_settings) }
25
+ let(:hare_info) { instance.instance_variable_get(:@hare_info) }
26
+
27
+ context "when connected" do
28
+ let(:connection) { double("MarchHare Connection") }
29
+ let(:channel) { double("Channel") }
30
+ let(:exchange) { double("Exchange") }
31
+ let(:channel) { double("Channel") }
32
+ let(:queue) { double("queue") }
33
+
34
+ # Doing this in a before block doesn't give us enough control over scope
35
+ before do
36
+ allow(instance).to receive(:connect!).and_call_original
37
+ allow(::MarchHare).to receive(:connect).and_return(connection)
38
+ allow(connection).to receive(:create_channel).and_return(channel)
39
+ allow(connection).to receive(:on_shutdown)
40
+ allow(connection).to receive(:on_blocked)
41
+ allow(connection).to receive(:on_unblocked)
42
+ allow(channel).to receive(:exchange).and_return(exchange)
43
+ allow(channel).to receive(:queue).and_return(queue)
44
+ allow(channel).to receive(:prefetch=)
45
+
46
+ allow(queue).to receive(:build_consumer).with(:on_cancellation => anything)
47
+ allow(queue).to receive(:subscribe_with).with(any_args)
48
+ allow(queue).to receive(:bind).with(any_args)
49
+ end
50
+
51
+ it "should default the codec to JSON" do
52
+ expect(instance.codec).to be_a(LogStash::Codecs::JSON)
53
+ end
54
+
55
+ describe "#connect!" do
56
+ subject { hare_info }
57
+
58
+ context "without an exchange declared" do
59
+ before do
60
+ instance.register
61
+ instance.setup!
62
+ end
63
+
64
+ it "should set the queue correctly" do
65
+ expect(subject.queue).to eql(queue)
66
+ end
67
+
68
+ it "should set the prefetch value correctly" do
69
+ expect(channel).to have_received(:prefetch=).with(123)
70
+ end
71
+ end
72
+
73
+ context "with an exchange declared" do
74
+ let(:exchange) { "exchange" }
75
+ let(:key) { "routing key" }
76
+ let(:rabbitmq_settings) { super.merge("exchange" => exchange, "key" => key, "exchange_type" => "fanout") }
77
+
78
+ before do
79
+ allow(instance).to receive(:declare_exchange!)
80
+ end
81
+
82
+ context "on run" do
83
+ before do
84
+ instance.register
85
+ instance.setup!
86
+ end
87
+
88
+ it "should bind to the exchange" do
89
+ expect(queue).to have_received(:bind).with(exchange, :routing_key => key)
90
+ end
91
+
92
+ it "should declare the exchange" do
93
+ expect(instance).to have_received(:declare_exchange!)
94
+ end
95
+ end
96
+
97
+ context "but not immediately available" do
98
+ before do
99
+ i = 0
100
+ allow(queue).to receive(:bind).with(any_args) do
101
+ i += 1
102
+ raise "foo" if i == 1
103
+ end
104
+ end
105
+
106
+ it "should reconnect" do
107
+ instance.register
108
+ instance.setup!
109
+ expect(queue).to have_received(:bind).with(any_args).twice()
110
+ end
111
+ end
112
+
113
+ context "initially unable to subscribe" do
114
+ before do
115
+ i = 0
116
+ allow(queue).to receive(:subscribe_with).with(any_args) do
117
+ i += 1
118
+ raise "sub error" if i == 1
119
+ end
120
+
121
+ it "should retry the subscribe" do
122
+ expect(queue).to have_receive(:subscribe_with).twice()
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ describe "with a live server", :integration => true do
131
+ let(:klass) { LogStash::Inputs::RabbitMQ }
132
+ let(:config) { {"host" => "127.0.0.1", "auto_delete" => true, "codec" => "plain", "add_field" => {"[@metadata][foo]" => "bar"} } }
133
+ let(:instance) { klass.new(config) }
134
+ let(:hare_info) { instance.instance_variable_get(:@hare_info) }
135
+ let(:output_queue) { Queue.new }
136
+
137
+ # Spawn a connection in the bg and wait up (n) seconds
138
+ def spawn_and_wait(instance)
139
+ instance.register
140
+
141
+ output_queue # materialize in this thread
142
+
143
+ Thread.new {
144
+ instance.run(output_queue)
145
+ }
146
+
147
+ 20.times do
148
+ instance.connected? ? break : sleep(0.1)
149
+ end
150
+
151
+ # Extra time to make sure the consumer can attach
152
+ # Without this there's a chance the shutdown code will execute
153
+ # before consumption begins. This is tricky to do more elegantly
154
+ sleep 4
155
+ end
156
+
157
+ let(:test_connection) { MarchHare.connect(instance.send(:rabbitmq_settings)) }
158
+ let(:test_channel) { test_connection.create_channel }
159
+
160
+ before do
161
+ # Materialize the instance in the current thread to prevent dupes
162
+ # If you use multiple threads with lazy evaluation weird stuff happens
163
+ instance
164
+ spawn_and_wait(instance)
165
+
166
+ test_channel # Start up the test client as well
167
+ end
168
+
169
+ after do
170
+ instance.stop()
171
+ test_channel.close
172
+ test_connection.close
173
+ end
174
+
175
+ context "using defaults" do
176
+ it "should start, connect, and stop cleanly" do
177
+ expect(instance.connected?).to be_truthy
178
+ end
179
+ end
180
+
181
+ it "should have the correct prefetch value" do
182
+ expect(instance.instance_variable_get(:@hare_info).channel.prefetch).to eql(256)
183
+ end
184
+
185
+ describe "receiving a message with a queue + exchange specified" do
186
+ let(:config) { super.merge("queue" => queue_name, "exchange" => exchange_name, "exchange_type" => "fanout", "metadata_enabled" => true) }
187
+ let(:event) { output_queue.pop }
188
+ let(:exchange) { test_channel.exchange(exchange_name, :type => "fanout") }
189
+ let(:exchange_name) { "logstash-input-rabbitmq-#{rand(0xFFFFFFFF)}" }
190
+ #let(:queue) { test_channel.queue(queue_name, :auto_delete => true) }
191
+ let(:queue_name) { "logstash-input-rabbitmq-#{rand(0xFFFFFFFF)}" }
192
+
193
+ after do
194
+ exchange.delete
195
+ end
196
+
197
+ context "when the message has a payload but no message headers" do
198
+ before do
199
+ exchange.publish(message)
200
+ end
201
+
202
+ let(:message) { "Foo Message" }
203
+
204
+ it "should process the message and store the payload" do
205
+ expect(event.get("message")).to eql(message)
206
+ end
207
+
208
+ it "should save an empty message header hash" do
209
+ expect(event).to include("@metadata")
210
+ expect(event.get("@metadata")).to include("rabbitmq_headers")
211
+ expect(event.get("[@metadata][rabbitmq_headers]")).to eq({})
212
+ end
213
+ end
214
+
215
+ context "when message properties are available" do
216
+ before do
217
+ # Don't test every single property but select a few with
218
+ # different characteristics to get sufficient coverage.
219
+ exchange.publish("",
220
+ :properties => {
221
+ :app_id => app_id,
222
+ :timestamp => Java::JavaUtil::Date.new(epoch * 1000),
223
+ :priority => priority,
224
+ })
225
+ end
226
+
227
+ let(:app_id) { "myapplication" }
228
+ # Randomize the epoch we test with but limit its range to signed
229
+ # ints to not assume all protocols and libraries involved use
230
+ # unsigned ints for epoch values.
231
+ let(:epoch) { rand(0x7FFFFFFF) }
232
+ let(:priority) { 5 }
233
+
234
+ it "should save message properties into a @metadata field" do
235
+ expect(event).to include("@metadata")
236
+ expect(event.get("@metadata")).to include("rabbitmq_properties")
237
+
238
+ props = event.get("[@metadata][rabbitmq_properties")
239
+ expect(props["app-id"]).to eq(app_id)
240
+ expect(props["delivery-mode"]).to eq(1)
241
+ expect(props["exchange"]).to eq(exchange_name)
242
+ expect(props["priority"]).to eq(priority)
243
+ expect(props["routing-key"]).to eq("")
244
+ expect(props["timestamp"]).to eq(epoch)
245
+ end
246
+ end
247
+
248
+ context "when message headers are available" do
249
+ before do
250
+ exchange.publish("", :properties => { :headers => headers })
251
+ end
252
+
253
+ let (:headers) {
254
+ {
255
+ "arrayvalue" => [true, 123, "foo"],
256
+ "boolvalue" => true,
257
+ "intvalue" => 123,
258
+ "stringvalue" => "foo",
259
+ }
260
+ }
261
+
262
+ it "should save message headers into a @metadata field" do
263
+ expect(event).to include("@metadata")
264
+ expect(event.get("@metadata")).to include("rabbitmq_headers")
265
+ expect(event.get("[@metadata][rabbitmq_headers]")).to include(headers)
266
+ end
267
+
268
+ it "should properly decorate the event" do
269
+ expect(event.get("[@metadata][foo]")).to eq("bar")
270
+ end
271
+ end
272
+ end
273
+
274
+ describe LogStash::Inputs::RabbitMQ do
275
+ it_behaves_like "an interruptible input plugin"
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,228 @@
1
+ # encoding: UTF-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/pipeline"
4
+ require "logstash/outputs/rabbitmq"
5
+ java_import java.util.concurrent.TimeoutException
6
+
7
+ describe LogStash::Outputs::RabbitMQ do
8
+ let(:klass) { LogStash::Outputs::RabbitMQ }
9
+ let(:host) { "localhost" }
10
+ let(:port) { 5672 }
11
+ let(:exchange_type) { "topic" }
12
+ let(:exchange) { "myexchange" }
13
+ let(:key) { "mykey" }
14
+ let(:persistent) { true }
15
+ let(:rabbitmq_settings) {
16
+ {
17
+ "host" => host,
18
+ "port" => port,
19
+ "exchange_type" => exchange_type,
20
+ "exchange" => exchange,
21
+ "key" => key,
22
+ "persistent" => persistent
23
+ }
24
+ }
25
+ let(:instance) { klass.new(rabbitmq_settings) }
26
+ let(:hare_info) { instance.instance_variable_get(:@hare_info) }
27
+
28
+ shared_examples 'recovers from exception gracefully' do
29
+ it 'should execute publish twice due to a retry' do
30
+ expect(exchange).to have_received(:publish).twice
31
+ end
32
+
33
+ it 'should sleep for the retry' do
34
+ expect(instance).to have_received(:sleep_for_retry).once
35
+ end
36
+
37
+ it 'should send the correct message (twice)' do
38
+ expect(exchange).to have_received(:publish).with(encoded_event, anything).twice
39
+ end
40
+
41
+ it 'should send the correct metadata (twice)' do
42
+ expected_metadata = {:routing_key => event.sprintf(key), :properties => {:persistent => persistent }}
43
+ expect(exchange).to have_received(:publish).with(anything, expected_metadata).twice
44
+ end
45
+ end
46
+
47
+ it "should register as an output plugin" do
48
+ expect(LogStash::Plugin.lookup("output", "rabbitmq")).to eql(LogStash::Outputs::RabbitMQ)
49
+ end
50
+
51
+ context "when connected" do
52
+ let(:connection) { double("MarchHare Connection") }
53
+ let(:channel) { double("Channel") }
54
+ let(:exchange) { double("Exchange") }
55
+
56
+ before do
57
+ allow(instance).to receive(:connect!).and_call_original
58
+ allow(::MarchHare).to receive(:connect).and_return(connection)
59
+ allow(connection).to receive(:create_channel).and_return(channel)
60
+ allow(connection).to receive(:on_blocked)
61
+ allow(connection).to receive(:on_unblocked)
62
+ allow(connection).to receive(:on_shutdown)
63
+ allow(channel).to receive(:exchange).and_return(exchange)
64
+
65
+ instance.register
66
+ end
67
+
68
+ describe "#connect!" do
69
+ subject { hare_info }
70
+
71
+ it "should set the exchange correctly" do
72
+ expect(subject.exchange).to eql(exchange)
73
+ end
74
+ end
75
+
76
+ describe "#publish_encoded" do
77
+ let(:event) { LogStash::Event.new("foo" => "bar") }
78
+ let(:sprinted_key) { double("sprinted key") }
79
+ let(:encoded_event) { LogStash::Json.dump(event) }
80
+
81
+ describe "issuing the publish" do
82
+ before do
83
+ allow(exchange).to receive(:publish).with(any_args)
84
+ allow(event).to receive(:sprintf).with(key).and_return(sprinted_key)
85
+ instance.send(:publish, event, encoded_event)
86
+ end
87
+
88
+ it "should send the correct message" do
89
+ expect(exchange).to have_received(:publish).with(encoded_event, anything)
90
+ end
91
+
92
+ it "should send the correct metadata" do
93
+ expected_metadata = {:routing_key => sprinted_key, :properties => {:persistent => persistent }}
94
+
95
+ expect(exchange).to have_received(:publish).with(anything, expected_metadata)
96
+ end
97
+ end
98
+
99
+ context 'when an exception is encountered' do
100
+ let(:exception) { nil }
101
+
102
+ before do
103
+ i = 0
104
+ allow(instance).to receive(:connect!)
105
+ allow(instance).to receive(:sleep_for_retry)
106
+ allow(exchange).to receive(:publish).with(any_args) do
107
+ i += 1
108
+ raise exception if i == 1
109
+ end
110
+
111
+ instance.send(:publish, event, encoded_event)
112
+ end
113
+
114
+ context 'when it is a MarchHare exception' do
115
+ let(:exception) { MarchHare::Exception }
116
+ it_behaves_like 'recovers from exception gracefully'
117
+ end
118
+
119
+ context 'when it is a MarchHare::ChannelAlreadyClosed' do
120
+ let(:exception) { MarchHare::ChannelAlreadyClosed }
121
+ it_behaves_like 'recovers from exception gracefully'
122
+ end
123
+
124
+ context 'when it is a TimeoutException' do
125
+ let(:exception) { TimeoutException.new }
126
+ it_behaves_like 'recovers from exception gracefully'
127
+ end
128
+ end
129
+ end
130
+ end
131
+ describe "with a live server", :integration => true do
132
+ let(:klass) { LogStash::Outputs::RabbitMQ }
133
+ let(:exchange) { "myexchange" }
134
+ let(:exchange_type) { "topic" }
135
+ let(:priority) { 34 }
136
+ let(:default_plugin_config) {
137
+ {
138
+ "host" => "127.0.0.1",
139
+ "exchange" => exchange,
140
+ "exchange_type" => exchange_type,
141
+ "key" => "foo",
142
+ "message_properties" => {
143
+ "priority" => priority
144
+ }
145
+ }
146
+ }
147
+ let(:config) { default_plugin_config }
148
+ let(:instance) { klass.new(config) }
149
+ let(:hare_info) { instance.instance_variable_get(:@hare_info) }
150
+
151
+ # Spawn a connection in the bg and wait up (n) seconds
152
+ def spawn_and_wait(instance)
153
+ instance.register
154
+
155
+ 20.times do
156
+ instance.connected? ? break : sleep(0.1)
157
+ end
158
+
159
+ # Extra time to make sure the output can attach
160
+ sleep 1
161
+ end
162
+ let(:message) { LogStash::Event.new("message" => "Foo Message", "extra_field" => "Blah") }
163
+ let(:encoded) { message.to_json }
164
+ let(:test_connection) { MarchHare.connect(instance.send(:rabbitmq_settings)) }
165
+ let(:test_channel) { test_connection.create_channel }
166
+ let(:test_queue) {
167
+ test_channel.queue("testq", :auto_delete => true).bind(exchange, :key => config["key"])
168
+ }
169
+
170
+ before do
171
+ # Materialize the instance in the current thread to prevent dupes
172
+ # If you use multiple threads with lazy evaluation weird stuff happens
173
+ instance
174
+ spawn_and_wait(instance)
175
+
176
+ test_channel # Start up the test client as well
177
+ test_queue
178
+ end
179
+
180
+ after do
181
+ instance.close()
182
+ test_channel.close
183
+ test_connection.close
184
+ end
185
+
186
+ context "using defaults" do
187
+ it "should start, connect, and stop cleanly" do
188
+ expect(instance.connected?).to be_truthy
189
+ end
190
+
191
+ it "should close cleanly" do
192
+ instance.close
193
+ expect(instance.connected?).to be_falsey
194
+ end
195
+
196
+ it 'applies per message settings' do
197
+ instance.multi_receive_encoded([[message, encoded]])
198
+ sleep 1.0
199
+
200
+ message, payload = test_queue.pop
201
+ expect(message.properties.to_s).to include("priority=#{priority}")
202
+ end
203
+ end
204
+
205
+ describe "sending a message with an exchange specified" do
206
+ let(:message) { LogStash::Event.new("message" => "Foo Message", "extra_field" => "Blah") }
207
+
208
+ before do
209
+ @received = nil
210
+ test_queue.subscribe do |metadata,payload|
211
+ @received = payload
212
+ end
213
+
214
+ instance.multi_receive_encoded([[message, encoded]])
215
+
216
+ until @received
217
+ sleep 1
218
+ end
219
+
220
+ @decoded = LogStash::Json.load(@received)
221
+ end
222
+
223
+ it "should process the message fully using the default (JSON) codec" do
224
+ expect(@decoded).to eql(LogStash::Json.load(LogStash::Json.dump(message)))
225
+ end
226
+ end
227
+ end
228
+ end