logstash-integration-rabbitmq 0.0.1

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 (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