logstash-integration-rabbitmq 7.0.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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