logstash-integration-rabbitmq 7.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +9 -0
  3. data/CONTRIBUTORS +27 -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.asciidoc +29 -0
  9. data/docs/input-rabbitmq.asciidoc +398 -0
  10. data/docs/output-rabbitmq.asciidoc +272 -0
  11. data/lib/logstash/inputs/rabbitmq.rb +317 -0
  12. data/lib/logstash/outputs/rabbitmq.rb +131 -0
  13. data/lib/logstash/plugin_mixins/rabbitmq_connection.rb +234 -0
  14. data/lib/logstash_registry.rb +7 -0
  15. data/logstash-integration-rabbitmq.gemspec +53 -0
  16. data/spec/fixtures/README.md +150 -0
  17. data/spec/fixtures/client/cert.pem +69 -0
  18. data/spec/fixtures/client/key.pem +27 -0
  19. data/spec/fixtures/client/keycert.p12 +0 -0
  20. data/spec/fixtures/client/req.pem +15 -0
  21. data/spec/fixtures/client_untrusted/cert.pem +19 -0
  22. data/spec/fixtures/client_untrusted/key.pem +28 -0
  23. data/spec/fixtures/client_untrusted/keycert.p12 +0 -0
  24. data/spec/fixtures/server/cert.pem +69 -0
  25. data/spec/fixtures/server/key.pem +27 -0
  26. data/spec/fixtures/server/key_password +1 -0
  27. data/spec/fixtures/server/keycert.p12 +0 -0
  28. data/spec/fixtures/server/rabbitmq.config +10 -0
  29. data/spec/fixtures/server/req.pem +15 -0
  30. data/spec/fixtures/testca/cacert.cer +0 -0
  31. data/spec/fixtures/testca/cacert.pem +17 -0
  32. data/spec/fixtures/testca/certs/01.pem +18 -0
  33. data/spec/fixtures/testca/certs/02.pem +18 -0
  34. data/spec/fixtures/testca/certs/05.pem +69 -0
  35. data/spec/fixtures/testca/certs/06.pem +69 -0
  36. data/spec/fixtures/testca/index.txt +4 -0
  37. data/spec/fixtures/testca/index.txt.attr +1 -0
  38. data/spec/fixtures/testca/index.txt.attr.old +1 -0
  39. data/spec/fixtures/testca/index.txt.old +3 -0
  40. data/spec/fixtures/testca/openssl.cnf +53 -0
  41. data/spec/fixtures/testca/private/cakey.pem +27 -0
  42. data/spec/fixtures/testca/serial +1 -0
  43. data/spec/fixtures/testca/serial.old +1 -0
  44. data/spec/inputs/rabbitmq_spec.rb +279 -0
  45. data/spec/outputs/rabbitmq_spec.rb +232 -0
  46. data/spec/plugin_mixins/rabbitmq_connection_spec.rb +175 -0
  47. metadata +256 -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,279 @@
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_not be_nil
53
+ expect(instance.codec.config_name).to eq "json"
54
+ end
55
+
56
+ describe "#connect!" do
57
+ subject { hare_info }
58
+
59
+ context "without an exchange declared" do
60
+ before do
61
+ instance.register
62
+ instance.setup!
63
+ end
64
+
65
+ it "should set the queue correctly" do
66
+ expect(subject.queue).to eql(queue)
67
+ end
68
+
69
+ it "should set the prefetch value correctly" do
70
+ expect(channel).to have_received(:prefetch=).with(123)
71
+ end
72
+ end
73
+
74
+ context "with an exchange declared" do
75
+ let(:exchange) { "exchange" }
76
+ let(:key) { "routing key" }
77
+ let(:rabbitmq_settings) { super.merge("exchange" => exchange, "key" => key, "exchange_type" => "fanout") }
78
+
79
+ before do
80
+ allow(instance).to receive(:declare_exchange!)
81
+ end
82
+
83
+ context "on run" do
84
+ before do
85
+ instance.register
86
+ instance.setup!
87
+ end
88
+
89
+ it "should bind to the exchange" do
90
+ expect(queue).to have_received(:bind).with(exchange, :routing_key => key)
91
+ end
92
+
93
+ it "should declare the exchange" do
94
+ expect(instance).to have_received(:declare_exchange!)
95
+ end
96
+ end
97
+
98
+ context "but not immediately available" do
99
+ before do
100
+ i = 0
101
+ allow(queue).to receive(:bind).with(any_args) do
102
+ i += 1
103
+ raise "foo" if i == 1
104
+ end
105
+ end
106
+
107
+ it "should reconnect" do
108
+ instance.register
109
+ instance.setup!
110
+ expect(queue).to have_received(:bind).with(any_args).twice()
111
+ end
112
+ end
113
+
114
+ context "initially unable to subscribe" do
115
+ before do
116
+ i = 0
117
+ allow(queue).to receive(:subscribe_with).with(any_args) do
118
+ i += 1
119
+ raise "sub error" if i == 1
120
+ end
121
+
122
+ it "should retry the subscribe" do
123
+ expect(queue).to have_receive(:subscribe_with).twice()
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "with a live server", :integration => true do
133
+ let(:klass) { LogStash::Inputs::RabbitMQ }
134
+ let(:config) { {"host" => "127.0.0.1", "auto_delete" => true, "codec" => "plain", "add_field" => {"[@metadata][foo]" => "bar"} } }
135
+ let(:instance) { klass.new(config) }
136
+ let(:hare_info) { instance.instance_variable_get(:@hare_info) }
137
+ let(:output_queue) { Queue.new }
138
+
139
+ # Spawn a connection in the bg and wait up (n) seconds
140
+ def spawn_and_wait(instance)
141
+ instance.register
142
+
143
+ output_queue # materialize in this thread
144
+
145
+ Thread.new {
146
+ instance.run(output_queue)
147
+ }
148
+
149
+ 20.times do
150
+ instance.connected? ? break : sleep(0.1)
151
+ end
152
+
153
+ # Extra time to make sure the consumer can attach
154
+ # Without this there's a chance the shutdown code will execute
155
+ # before consumption begins. This is tricky to do more elegantly
156
+ sleep 4
157
+ end
158
+
159
+ let(:test_connection) { MarchHare.connect(instance.send(:rabbitmq_settings)) }
160
+ let(:test_channel) { test_connection.create_channel }
161
+
162
+ before do
163
+ # Materialize the instance in the current thread to prevent dupes
164
+ # If you use multiple threads with lazy evaluation weird stuff happens
165
+ instance
166
+ spawn_and_wait(instance)
167
+
168
+ test_channel # Start up the test client as well
169
+ end
170
+
171
+ after do
172
+ instance.stop()
173
+ test_channel.close
174
+ test_connection.close
175
+ end
176
+
177
+ context "using defaults" do
178
+ it "should start, connect, and stop cleanly" do
179
+ expect(instance.connected?).to be_truthy
180
+ end
181
+ end
182
+
183
+ it "should have the correct prefetch value" do
184
+ expect(instance.instance_variable_get(:@hare_info).channel.prefetch).to eql(256)
185
+ end
186
+
187
+ describe "receiving a message with a queue + exchange specified" do
188
+ let(:config) { super.merge("queue" => queue_name, "exchange" => exchange_name, "exchange_type" => "fanout", "metadata_enabled" => true) }
189
+ let(:event) { output_queue.pop }
190
+ let(:exchange) { test_channel.exchange(exchange_name, :type => "fanout") }
191
+ let(:exchange_name) { "logstash-input-rabbitmq-#{rand(0xFFFFFFFF)}" }
192
+ #let(:queue) { test_channel.queue(queue_name, :auto_delete => true) }
193
+ let(:queue_name) { "logstash-input-rabbitmq-#{rand(0xFFFFFFFF)}" }
194
+
195
+ after do
196
+ exchange.delete
197
+ end
198
+
199
+ context "when the message has a payload but no message headers" do
200
+ before do
201
+ exchange.publish(message)
202
+ end
203
+
204
+ let(:message) { "Foo Message" }
205
+
206
+ it "should process the message and store the payload" do
207
+ expect(event.get("message")).to eql(message)
208
+ end
209
+
210
+ it "should save an empty message header hash" do
211
+ expect(event).to include("@metadata")
212
+ expect(event.get("@metadata")).to include("rabbitmq_headers")
213
+ expect(event.get("[@metadata][rabbitmq_headers]")).to eq({})
214
+ end
215
+ end
216
+
217
+ context "when message properties are available" do
218
+ before do
219
+ # Don't test every single property but select a few with
220
+ # different characteristics to get sufficient coverage.
221
+ exchange.publish("",
222
+ :properties => {
223
+ :app_id => app_id,
224
+ :timestamp => Java::JavaUtil::Date.new(epoch * 1000),
225
+ :priority => priority,
226
+ })
227
+ end
228
+
229
+ let(:app_id) { "myapplication" }
230
+ # Randomize the epoch we test with but limit its range to signed
231
+ # ints to not assume all protocols and libraries involved use
232
+ # unsigned ints for epoch values.
233
+ let(:epoch) { rand(0x7FFFFFFF) }
234
+ let(:priority) { 5 }
235
+
236
+ it "should save message properties into a @metadata field" do
237
+ expect(event).to include("@metadata")
238
+ expect(event.get("@metadata")).to include("rabbitmq_properties")
239
+
240
+ props = event.get("[@metadata][rabbitmq_properties")
241
+ expect(props["app-id"]).to eq(app_id)
242
+ expect(props["delivery-mode"]).to eq(1)
243
+ expect(props["exchange"]).to eq(exchange_name)
244
+ expect(props["priority"]).to eq(priority)
245
+ expect(props["routing-key"]).to eq("")
246
+ expect(props["timestamp"]).to eq(epoch)
247
+ end
248
+ end
249
+
250
+ context "when message headers are available" do
251
+ before do
252
+ exchange.publish("", :properties => { :headers => headers })
253
+ end
254
+
255
+ let (:headers) {
256
+ {
257
+ "arrayvalue" => [true, 123, "foo"],
258
+ "boolvalue" => true,
259
+ "intvalue" => 123,
260
+ "stringvalue" => "foo",
261
+ }
262
+ }
263
+
264
+ it "should save message headers into a @metadata field" do
265
+ expect(event).to include("@metadata")
266
+ expect(event.get("@metadata")).to include("rabbitmq_headers")
267
+ expect(event.get("[@metadata][rabbitmq_headers]")).to include(headers)
268
+ end
269
+
270
+ it "should properly decorate the event" do
271
+ expect(event.get("[@metadata][foo]")).to eq("bar")
272
+ end
273
+ end
274
+ end
275
+
276
+ describe LogStash::Inputs::RabbitMQ do
277
+ it_behaves_like "an interruptible input plugin"
278
+ end
279
+ end
@@ -0,0 +1,232 @@
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(connection).to receive(:on_recovery_started)
64
+ allow(connection).to receive(:on_recovery)
65
+ allow(channel).to receive(:exchange).and_return(exchange)
66
+
67
+ instance.register
68
+ end
69
+
70
+ describe "#connect!" do
71
+ subject { hare_info }
72
+
73
+ it "should set the exchange correctly" do
74
+ expect(subject.exchange).to eql(exchange)
75
+ end
76
+ end
77
+
78
+ describe "#publish_encoded" do
79
+ let(:event) { LogStash::Event.new("foo" => "bar") }
80
+ let(:sprinted_key) { double("sprinted key") }
81
+ let(:encoded_event) { LogStash::Json.dump(event) }
82
+
83
+ describe "issuing the publish" do
84
+ before do
85
+ allow(exchange).to receive(:publish).with(any_args)
86
+ allow(event).to receive(:sprintf).with(key).and_return(sprinted_key)
87
+ instance.send(:publish, event, encoded_event)
88
+ end
89
+
90
+ it "should send the correct message" do
91
+ expect(exchange).to have_received(:publish).with(encoded_event, anything)
92
+ end
93
+
94
+ it "should send the correct metadata" do
95
+ expected_metadata = {:routing_key => sprinted_key, :properties => {:persistent => persistent }}
96
+
97
+ expect(exchange).to have_received(:publish).with(anything, expected_metadata)
98
+ end
99
+ end
100
+
101
+ context 'when an exception is encountered' do
102
+ let(:exception) { nil }
103
+
104
+ before do
105
+ i = 0
106
+ allow(instance).to receive(:connect!)
107
+ allow(instance).to receive(:sleep_for_retry)
108
+ allow(exchange).to receive(:publish).with(any_args) do
109
+ i += 1
110
+ raise exception if i == 1
111
+ end
112
+
113
+ instance.send(:publish, event, encoded_event)
114
+ end
115
+
116
+ context 'when it is a MarchHare exception' do
117
+ let(:exception) { MarchHare::Exception }
118
+ it_behaves_like 'recovers from exception gracefully'
119
+ end
120
+
121
+ context 'when it is a MarchHare::ChannelAlreadyClosed' do
122
+ let(:exception) { MarchHare::ChannelAlreadyClosed }
123
+ it_behaves_like 'recovers from exception gracefully'
124
+ end
125
+
126
+ context 'when it is a TimeoutException' do
127
+ let(:exception) { TimeoutException.new }
128
+ it_behaves_like 'recovers from exception gracefully'
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+
136
+ describe "with a live server", :integration => true do
137
+ let(:klass) { LogStash::Outputs::RabbitMQ }
138
+ let(:exchange) { "myexchange" }
139
+ let(:exchange_type) { "topic" }
140
+ let(:priority) { 34 }
141
+ let(:default_plugin_config) {
142
+ {
143
+ "host" => "127.0.0.1",
144
+ "exchange" => exchange,
145
+ "exchange_type" => exchange_type,
146
+ "key" => "foo",
147
+ "message_properties" => {
148
+ "priority" => priority
149
+ }
150
+ }
151
+ }
152
+ let(:config) { default_plugin_config }
153
+ let(:instance) { klass.new(config) }
154
+ let(:hare_info) { instance.instance_variable_get(:@hare_info) }
155
+
156
+ # Spawn a connection in the bg and wait up (n) seconds
157
+ def spawn_and_wait(instance)
158
+ instance.register
159
+
160
+ 20.times do
161
+ instance.connected? ? break : sleep(0.1)
162
+ end
163
+
164
+ # Extra time to make sure the output can attach
165
+ sleep 1
166
+ end
167
+ let(:message) { LogStash::Event.new("message" => "Foo Message", "extra_field" => "Blah") }
168
+ let(:encoded) { message.to_json }
169
+ let(:test_connection) { MarchHare.connect(instance.send(:rabbitmq_settings)) }
170
+ let(:test_channel) { test_connection.create_channel }
171
+ let(:test_queue) {
172
+ test_channel.queue("testq", :auto_delete => true).bind(exchange, :key => config["key"])
173
+ }
174
+
175
+ before do
176
+ # Materialize the instance in the current thread to prevent dupes
177
+ # If you use multiple threads with lazy evaluation weird stuff happens
178
+ instance
179
+ spawn_and_wait(instance)
180
+
181
+ test_channel # Start up the test client as well
182
+ test_queue
183
+ end
184
+
185
+ after do
186
+ instance.close()
187
+ test_channel.close
188
+ test_connection.close
189
+ end
190
+
191
+ context "using defaults" do
192
+ it "should start, connect, and stop cleanly" do
193
+ expect(instance.connected?).to be_truthy
194
+ end
195
+
196
+ it "should close cleanly" do
197
+ instance.close
198
+ expect(instance.connected?).to be_falsey
199
+ end
200
+
201
+ it 'applies per message settings' do
202
+ instance.multi_receive_encoded([[message, encoded]])
203
+ sleep 1.0
204
+
205
+ message, payload = test_queue.pop
206
+ expect(message.properties.to_s).to include("priority=#{priority}")
207
+ end
208
+ end
209
+
210
+ describe "sending a message with an exchange specified" do
211
+ let(:message) { LogStash::Event.new("message" => "Foo Message", "extra_field" => "Blah") }
212
+
213
+ before do
214
+ @received = nil
215
+ test_queue.subscribe do |metadata,payload|
216
+ @received = payload
217
+ end
218
+
219
+ instance.multi_receive_encoded([[message, encoded]])
220
+
221
+ until @received
222
+ sleep 1
223
+ end
224
+
225
+ @decoded = LogStash::Json.load(@received)
226
+ end
227
+
228
+ it "should process the message fully using the default (JSON) codec" do
229
+ expect(@decoded).to eql(LogStash::Json.load(LogStash::Json.dump(message)))
230
+ end
231
+ end
232
+ end