instrumental_agent 0.13.1 → 0.13.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Instrumental
2
- VERSION = "0.13.1"
2
+ VERSION = "0.13.2"
3
3
  end
data/spec/agent_spec.rb CHANGED
@@ -1,460 +1,583 @@
1
1
  require 'spec_helper'
2
2
 
3
- def wait
4
- sleep 0.2 # FIXME: hack
3
+ def wait(n=0.2)
4
+ sleep n # FIXME: hack
5
5
  end
6
6
 
7
- describe Instrumental::Agent, "disabled" do
8
- before do
9
- Instrumental::Agent.logger.level = Logger::UNKNOWN
10
- @server = TestServer.new
11
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :enabled => false)
12
- end
7
+ FORK_SUPPORTED = begin
8
+ Process.wait(fork { true })
9
+ true
10
+ rescue Exception => e
11
+ false
12
+ end
13
13
 
14
- after do
15
- @agent.stop
16
- @agent = nil
17
- @server.stop
18
- end
19
14
 
20
- it "should not connect to the server" do
21
- wait
22
- @server.connect_count.should == 0
23
- end
24
15
 
25
- it "should not connect to the server after receiving a metric" do
26
- wait
27
- @agent.gauge('disabled_test', 1)
28
- wait
29
- @server.connect_count.should == 0
30
- end
16
+ shared_examples "Instrumental Agent" do
17
+ context do
31
18
 
32
- it "should no op on flush without reconnect" do
33
- 1.upto(100) { @agent.gauge('disabled_test', 1) }
34
- @agent.flush(false)
35
- wait
36
- @server.commands.should be_empty
37
- end
19
+ # Inferred:
20
+ # secure? and verify_cert? are set
38
21
 
39
- it "should no op on flush with reconnect" do
40
- 1.upto(100) { @agent.gauge('disabled_test', 1) }
41
- @agent.flush(true)
42
- wait
43
- @server.commands.should be_empty
44
- end
22
+ # Agent options
23
+ let(:enabled) { true }
24
+ let(:synchronous) { false }
25
+ let(:token) { 'test_token' }
26
+ let(:address) { server.host_and_port }
27
+ let(:agent) { Instrumental::Agent.new(token, :collector => address, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?) }
45
28
 
46
- it "should no op on an empty flush" do
47
- @agent.flush(true)
48
- wait
49
- @server.commands.should be_empty
50
- end
29
+ # Server options
30
+ let(:listen) { true }
31
+ let(:response) { true }
32
+ let(:authenticate) { true }
33
+ let(:server) { TestServer.new(:listen => listen, :authenticate => authenticate, :response => response, :secure => secure?) }
51
34
 
52
- it "should send metrics to logger" do
53
- now = Time.now
54
- @agent.logger.should_receive(:debug).with("gauge metric 1 #{now.to_i} 1")
55
- @agent.gauge("metric", 1)
56
- end
57
- end
35
+ before do
36
+ Instrumental::Agent.logger.level = Logger::UNKNOWN
37
+ @server = server
38
+ wait
39
+ end
58
40
 
59
- describe Instrumental::Agent, "enabled" do
60
- before do
61
- @server = TestServer.new
62
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
63
- end
41
+ after do
42
+ agent.stop
43
+ server.stop
44
+ end
64
45
 
65
- after do
66
- @agent.stop
67
- @agent = nil
68
- @server.stop
69
- end
46
+ describe Instrumental::Agent, "disabled" do
47
+ let(:enabled) { false }
70
48
 
71
- it "should not connect to the server" do
72
- wait
73
- @server.connect_count.should == 0
74
- end
49
+ it "should not connect to the server" do
50
+ server.connect_count.should == 0
51
+ end
75
52
 
76
- it "should connect to the server after sending a metric" do
77
- @agent.increment("test.foo")
78
- wait
79
- @server.connect_count.should == 1
80
- end
53
+ it "should not connect to the server after receiving a metric" do
54
+ agent.gauge('disabled_test', 1)
55
+ wait
56
+ server.connect_count.should == 0
57
+ end
81
58
 
82
- it "should announce itself, and include version" do
83
- @agent.increment("test.foo")
84
- wait
85
- @server.commands[0].should =~ /hello .*/
86
- @server.commands[0].should =~ / version /
87
- @server.commands[0].should =~ / hostname /
88
- @server.commands[0].should =~ / pid /
89
- @server.commands[0].should =~ / runtime /
90
- @server.commands[0].should =~ / platform /
91
- end
59
+ it "should no op on flush without reconnect" do
60
+ 1.upto(100) { agent.gauge('disabled_test', 1) }
61
+ agent.flush(false)
62
+ wait
63
+ server.commands.should be_empty
64
+ end
92
65
 
93
- it "should authenticate using the token" do
94
- @agent.increment("test.foo")
95
- wait
96
- @server.commands[1].should == "authenticate test_token"
97
- end
66
+ it "should no op on flush with reconnect" do
67
+ 1.upto(100) { agent.gauge('disabled_test', 1) }
68
+ agent.flush(true)
69
+ wait
70
+ server.commands.should be_empty
71
+ end
98
72
 
99
- it "should report a gauge" do
100
- now = Time.now
101
- @agent.gauge('gauge_test', 123)
102
- wait
103
- @server.commands.last.should == "gauge gauge_test 123 #{now.to_i} 1"
104
- end
73
+ it "should no op on an empty flush" do
74
+ agent.flush(true)
75
+ wait
76
+ server.commands.should be_empty
77
+ end
105
78
 
106
- it "should report a time as gauge and return the block result" do
107
- now = Time.now
108
- @agent.time("time_value_test") do
109
- 1 + 1
110
- end.should == 2
111
- wait
112
- @server.commands.last.should =~ /gauge time_value_test .* #{now.to_i}/
113
- end
79
+ it "should send metrics to logger" do
80
+ now = Time.now
81
+ agent.logger.should_receive(:debug).with("gauge metric 1 #{now.to_i} 1")
82
+ agent.gauge("metric", 1)
83
+ end
84
+ end
114
85
 
115
- it "should return the value gauged" do
116
- now = Time.now
117
- @agent.gauge('gauge_test', 123).should == 123
118
- @agent.gauge('gauge_test', 989).should == 989
119
- wait
120
- end
86
+ describe Instrumental::Agent, "enabled" do
121
87
 
122
- it "should report a gauge with a set time" do
123
- @agent.gauge('gauge_test', 123, 555)
124
- wait
125
- @server.commands.last.should == "gauge gauge_test 123 555 1"
126
- end
88
+ it "should not connect to the server" do
89
+ server.connect_count.should == 0
90
+ end
127
91
 
128
- it "should report a gauge with a set time and count" do
129
- @agent.gauge('gauge_test', 123, 555, 111)
130
- wait
131
- @server.commands.last.should == "gauge gauge_test 123 555 111"
132
- end
92
+ it "should connect to the server after sending a metric" do
93
+ agent.increment("test.foo")
94
+ wait
95
+ server.connect_count.should == 1
96
+ end
133
97
 
134
- it "should report an increment" do
135
- now = Time.now
136
- @agent.increment("increment_test")
137
- wait
138
- @server.commands.last.should == "increment increment_test 1 #{now.to_i} 1"
139
- end
98
+ it "should announce itself, and include version" do
99
+ agent.increment("test.foo")
100
+ wait
101
+ server.commands[0].should =~ /hello .*/
102
+ server.commands[0].should =~ / version /
103
+ server.commands[0].should =~ / hostname /
104
+ server.commands[0].should =~ / pid /
105
+ server.commands[0].should =~ / runtime /
106
+ server.commands[0].should =~ / platform /
107
+ end
140
108
 
141
- it "should return the value incremented by" do
142
- now = Time.now
143
- @agent.increment("increment_test").should == 1
144
- @agent.increment("increment_test", 5).should == 5
145
- wait
146
- end
109
+ it "should authenticate using the token" do
110
+ agent.increment("test.foo")
111
+ wait
112
+ server.commands[1].should == "authenticate test_token"
113
+ end
147
114
 
148
- it "should report an increment a value" do
149
- now = Time.now
150
- @agent.increment("increment_test", 2)
151
- wait
152
- @server.commands.last.should == "increment increment_test 2 #{now.to_i} 1"
153
- end
115
+ it "should report a gauge" do
116
+ now = Time.now
117
+ agent.gauge('gauge_test', 123)
118
+ wait
119
+ server.commands.last.should == "gauge gauge_test 123 #{now.to_i} 1"
120
+ end
154
121
 
155
- it "should report an increment with a set time" do
156
- @agent.increment('increment_test', 1, 555)
157
- wait
158
- @server.commands.last.should == "increment increment_test 1 555 1"
159
- end
122
+ it "should report a time as gauge and return the block result" do
123
+ now = Time.now
124
+ agent.time("time_value_test") do
125
+ 1 + 1
126
+ end.should == 2
127
+ wait
128
+ server.commands.last.should =~ /gauge time_value_test .* #{now.to_i}/
129
+ end
160
130
 
161
- it "should report an increment with a set time and count" do
162
- @agent.increment('increment_test', 1, 555, 111)
163
- wait
164
- @server.commands.last.should == "increment increment_test 1 555 111"
165
- end
131
+ it "should return the value gauged" do
132
+ now = Time.now
133
+ agent.gauge('gauge_test', 123).should == 123
134
+ agent.gauge('gauge_test', 989).should == 989
135
+ wait
136
+ end
166
137
 
167
- it "should discard data that overflows the buffer" do
168
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
169
- 5.times do |i|
170
- @agent.increment('overflow_test', i + 1, 300)
138
+ it "should report a gauge with a set time" do
139
+ agent.gauge('gauge_test', 123, 555)
140
+ wait
141
+ server.commands.last.should == "gauge gauge_test 123 555 1"
171
142
  end
172
- wait
173
- @server.commands.should include("increment overflow_test 1 300 1")
174
- @server.commands.should include("increment overflow_test 2 300 1")
175
- @server.commands.should include("increment overflow_test 3 300 1")
176
- @server.commands.should_not include("increment overflow_test 4 300 1")
177
- @server.commands.should_not include("increment overflow_test 5 300 1")
178
- end
179
- end
180
143
 
181
- it "should send all data in synchronous mode" do
182
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
183
- @agent.synchronous = true
184
- 5.times do |i|
185
- @agent.increment('overflow_test', i + 1, 300)
186
- end
187
- @agent.instance_variable_get(:@queue).size.should == 0
188
- wait # let the server receive the commands
189
- @server.commands.should include("increment overflow_test 1 300 1")
190
- @server.commands.should include("increment overflow_test 2 300 1")
191
- @server.commands.should include("increment overflow_test 3 300 1")
192
- @server.commands.should include("increment overflow_test 4 300 1")
193
- @server.commands.should include("increment overflow_test 5 300 1")
194
- end
195
- end
144
+ it "should report a gauge with a set time and count" do
145
+ agent.gauge('gauge_test', 123, 555, 111)
146
+ wait
147
+ server.commands.last.should == "gauge gauge_test 123 555 111"
148
+ end
196
149
 
197
- it "should automatically reconnect when forked" do
198
- wait
199
- @agent.increment('fork_reconnect_test', 1, 2)
200
- fork do
201
- @agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
202
- end
203
- wait
204
- @agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
205
- wait
206
- @server.connect_count.should == 2
207
- @server.commands.should include("increment fork_reconnect_test 1 2 1")
208
- @server.commands.should include("increment fork_reconnect_test 1 3 1")
209
- @server.commands.should include("increment fork_reconnect_test 1 4 1")
210
- end
150
+ it "should report an increment" do
151
+ now = Time.now
152
+ agent.increment("increment_test")
153
+ wait
154
+ server.commands.last.should == "increment increment_test 1 #{now.to_i} 1"
155
+ end
211
156
 
212
- it "should never let an exception reach the user" do
213
- @agent.stub!(:send_command).and_raise(Exception.new("Test Exception"))
214
- @agent.increment('throws_exception', 2).should be_nil
215
- wait
216
- @agent.gauge('throws_exception', 234).should be_nil
217
- wait
218
- end
157
+ it "should return the value incremented by" do
158
+ now = Time.now
159
+ agent.increment("increment_test").should == 1
160
+ agent.increment("increment_test", 5).should == 5
161
+ end
219
162
 
220
- it "should let exceptions in time bubble up" do
221
- expect { @agent.time('za') { raise "fail" } }.to raise_error
222
- end
163
+ it "should report an increment a value" do
164
+ now = Time.now
165
+ agent.increment("increment_test", 2)
166
+ wait
167
+ server.commands.last.should == "increment increment_test 2 #{now.to_i} 1"
168
+ end
223
169
 
224
- it "should return nil if the user overflows the MAX_BUFFER" do
225
- 1.upto(Instrumental::Agent::MAX_BUFFER) do
226
- @agent.increment("test").should == 1
227
- thread = @agent.instance_variable_get(:@thread)
228
- thread.kill
229
- end
230
- @agent.increment("test").should be_nil
231
- end
170
+ it "should report an increment with a set time" do
171
+ agent.increment('increment_test', 1, 555)
172
+ wait
173
+ server.commands.last.should == "increment increment_test 1 555 1"
174
+ end
232
175
 
233
- it "should track invalid metrics" do
234
- @agent.logger.should_receive(:warn).with(/%%/)
235
- @agent.increment(' %% .!#@$%^&*', 1, 1)
236
- wait
237
- @server.commands.join("\n").should include("increment agent.invalid_metric")
238
- end
176
+ it "should report an increment with a set time and count" do
177
+ agent.increment('increment_test', 1, 555, 111)
178
+ wait
179
+ server.commands.last.should == "increment increment_test 1 555 111"
180
+ end
239
181
 
240
- it "should allow reasonable metric names" do
241
- @agent.increment('a')
242
- @agent.increment('a.b')
243
- @agent.increment('hello.world')
244
- @agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
245
- wait
246
- @server.commands.join("\n").should_not include("increment agent.invalid_metric")
247
- end
182
+ it "should discard data that overflows the buffer" do
183
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
184
+ 5.times do |i|
185
+ agent.increment('overflow_test', i + 1, 300)
186
+ end
187
+ wait
188
+ server.commands.should include("increment overflow_test 1 300 1")
189
+ server.commands.should include("increment overflow_test 2 300 1")
190
+ server.commands.should include("increment overflow_test 3 300 1")
191
+
192
+ server.commands.should_not include("increment overflow_test 4 300 1")
193
+ server.commands.should_not include("increment overflow_test 5 300 1")
194
+ end
195
+ end
248
196
 
249
- it "should track invalid values" do
250
- @agent.logger.should_receive(:warn).with(/hello.*testington/)
251
- @agent.increment('testington', 'hello')
252
- wait
253
- @server.commands.join("\n").should include("increment agent.invalid_value")
254
- end
197
+ it "should send all data in synchronous mode" do
198
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
199
+ agent.synchronous = true
200
+ 5.times do |i|
201
+ agent.increment('overflow_test', i + 1, 300)
202
+ end
203
+ agent.instance_variable_get(:@queue).size.should == 0
204
+ wait # let the server receive the commands
205
+ server.commands.should include("increment overflow_test 1 300 1")
206
+ server.commands.should include("increment overflow_test 2 300 1")
207
+ server.commands.should include("increment overflow_test 3 300 1")
208
+ server.commands.should include("increment overflow_test 4 300 1")
209
+ server.commands.should include("increment overflow_test 5 300 1")
210
+ end
211
+ end
255
212
 
256
- it "should allow reasonable values" do
257
- @agent.increment('a', -333.333)
258
- @agent.increment('a', -2.2)
259
- @agent.increment('a', -1)
260
- @agent.increment('a', 0)
261
- @agent.increment('a', 1)
262
- @agent.increment('a', 2.2)
263
- @agent.increment('a', 333.333)
264
- @agent.increment('a', Float::EPSILON)
265
- wait
266
- @server.commands.join("\n").should_not include("increment agent.invalid_value")
267
- end
213
+ if FORK_SUPPORTED
214
+ it "should automatically reconnect when forked" do
215
+ agent.increment('fork_reconnect_test', 1, 2)
216
+ fork do
217
+ agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
218
+ end
219
+ wait(1)
220
+ agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
221
+ wait(1)
222
+ server.connect_count.should == 2
223
+ server.commands.should include("increment fork_reconnect_test 1 2 1")
224
+ server.commands.should include("increment fork_reconnect_test 1 3 1")
225
+ server.commands.should include("increment fork_reconnect_test 1 4 1")
226
+ end
227
+ end
268
228
 
269
- it "should send notices to the server" do
270
- tm = Time.now
271
- @agent.notice("Test note", tm)
272
- wait
273
- @server.commands.join("\n").should include("notice #{tm.to_i} 0 Test note")
274
- end
229
+ it "should never let an exception reach the user" do
230
+ agent.stub(:send_command).and_raise(Exception.new("Test Exception"))
231
+ agent.increment('throws_exception', 2).should be_nil
232
+ wait
233
+ agent.gauge('throws_exception', 234).should be_nil
234
+ wait
235
+ end
275
236
 
276
- it "should prevent a note w/ newline characters from being sent to the server" do
277
- @agent.notice("Test note\n").should be_nil
278
- wait
279
- @server.commands.join("\n").should_not include("notice Test note")
280
- end
237
+ it "should let exceptions in time bubble up" do
238
+ expect { agent.time('za') { raise "fail" } }.to raise_error
239
+ end
281
240
 
282
- it "should allow outgoing metrics to be stopped" do
283
- tm = Time.now
284
- @agent.increment("foo.bar", 1, tm)
285
- @agent.stop
286
- wait
287
- @agent.increment("foo.baz", 1, tm)
288
- wait
289
- @server.commands.join("\n").should include("increment foo.baz 1 #{tm.to_i}")
290
- @server.commands.join("\n").should_not include("increment foo.bar 1 #{tm.to_i}")
291
- end
241
+ it "should return nil if the user overflows the MAX_BUFFER" do
242
+ Queue.any_instance.stub(:pop) { nil }
243
+ 1.upto(Instrumental::Agent::MAX_BUFFER) do
244
+ agent.increment("test").should == 1
245
+ end
246
+ agent.increment("test").should be_nil
247
+ end
292
248
 
293
- it "should allow flushing pending values to the server" do
294
- 1.upto(100) { @agent.gauge('a', rand(50)) }
295
- @agent.instance_variable_get(:@queue).size.should >= 100
296
- @agent.flush
297
- @agent.instance_variable_get(:@queue).size.should == 0
298
- wait
299
- @server.commands.grep(/^gauge a /).size.should == 100
300
- end
249
+ it "should track invalid metrics" do
250
+ agent.logger.should_receive(:warn).with(/%%/)
251
+ agent.increment(' %% .!#@$%^&*', 1, 1)
252
+ wait
253
+ server.commands.join("\n").should include("increment agent.invalid_metric")
254
+ end
301
255
 
302
- it "should no op on an empty flush" do
303
- @agent.flush(true)
304
- wait
305
- @server.commands.should be_empty
306
- end
307
- end
256
+ it "should allow reasonable metric names" do
257
+ agent.increment('a')
258
+ agent.increment('a.b')
259
+ agent.increment('hello.world')
260
+ agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
261
+ wait
262
+ server.commands.join("\n").should_not include("increment agent.invalid_metric")
263
+ end
308
264
 
309
- describe Instrumental::Agent, "connection problems" do
310
- after do
311
- @agent.stop
312
- @server.stop
313
- end
265
+ it "should track invalid values" do
266
+ agent.logger.should_receive(:warn).with(/hello.*testington/)
267
+ agent.increment('testington', 'hello')
268
+ wait
269
+ server.commands.join("\n").should include("increment agent.invalid_value")
270
+ end
314
271
 
315
- it "should automatically reconnect on disconnect" do
316
- @server = TestServer.new
317
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
318
- @agent.increment("reconnect_test", 1, 1234)
319
- wait
320
- @server.disconnect_all
321
- wait
322
- @agent.increment('reconnect_test', 1, 5678) # triggers reconnect
323
- wait
324
- @server.connect_count.should == 2
325
- @server.commands.last.should == "increment reconnect_test 1 5678 1"
326
- end
272
+ it "should allow reasonable values" do
273
+ agent.increment('a', -333.333)
274
+ agent.increment('a', -2.2)
275
+ agent.increment('a', -1)
276
+ agent.increment('a', 0)
277
+ agent.increment('a', 1)
278
+ agent.increment('a', 2.2)
279
+ agent.increment('a', 333.333)
280
+ agent.increment('a', Float::EPSILON)
281
+ wait
282
+ server.commands.join("\n").should_not include("increment agent.invalid_value")
283
+ end
327
284
 
328
- it "should buffer commands when server is down" do
329
- @server = TestServer.new(:listen => false)
330
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
331
- wait
332
- @agent.increment('reconnect_test', 1, 1234)
333
- wait
334
- @agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
335
- end
285
+ it "should send notices to the server" do
286
+ tm = Time.now
287
+ agent.notice("Test note", tm)
288
+ wait
289
+ server.commands.join("\n").should include("notice #{tm.to_i} 0 Test note")
290
+ end
336
291
 
337
- it "should buffer commands when server is not responsive" do
338
- @server = TestServer.new(:response => false)
339
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
340
- wait
341
- @agent.increment('reconnect_test', 1, 1234)
342
- wait
343
- @agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
344
- end
292
+ it "should prevent a note w/ newline characters from being sent to the server" do
293
+ agent.notice("Test note\n").should be_nil
294
+ wait
295
+ server.commands.join("\n").should_not include("notice Test note")
296
+ end
345
297
 
346
- it "should buffer commands when authentication fails" do
347
- @server = TestServer.new(:authenticate => false)
348
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
349
- wait
350
- @agent.increment('reconnect_test', 1, 1234)
351
- wait
352
- @agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
353
- end
298
+ it "should allow outgoing metrics to be stopped" do
299
+ tm = Time.now
300
+ agent.increment("foo.bar", 1, tm)
301
+ agent.stop
302
+ wait
303
+ agent.increment("foo.baz", 1, tm)
304
+ wait
305
+ server.commands.join("\n").should include("increment foo.baz 1 #{tm.to_i}")
306
+ server.commands.join("\n").should_not include("increment foo.bar 1 #{tm.to_i}")
307
+ end
354
308
 
355
- it "should warn once when buffer is full" do
356
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
357
- @server = TestServer.new(:listen => false)
358
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
359
- wait
360
- @agent.logger.should_receive(:warn).with(/Queue full/).once
309
+ it "should allow flushing pending values to the server" do
310
+ 1.upto(100) { agent.gauge('a', rand(50)) }
311
+ agent.instance_variable_get(:@queue).size.should > 0
312
+ agent.flush
313
+ agent.instance_variable_get(:@queue).size.should == 0
314
+ wait
315
+ server.commands.grep(/^gauge a /).size.should == 100
316
+ end
361
317
 
362
- @agent.increment('buffer_full_warn_test', 1, 1234)
363
- @agent.increment('buffer_full_warn_test', 1, 1234)
364
- @agent.increment('buffer_full_warn_test', 1, 1234)
365
- @agent.increment('buffer_full_warn_test', 1, 1234)
366
- @agent.increment('buffer_full_warn_test', 1, 1234)
318
+ it "should no op on an empty flush" do
319
+ agent.flush(true)
320
+ wait
321
+ server.commands.should be_empty
322
+ end
367
323
  end
368
- end
369
324
 
370
- it "should send commands in a short-lived process" do
371
- @server = TestServer.new
372
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
373
- if pid = fork { @agent.increment('foo', 1, 1234) }
374
- Process.wait(pid)
375
- @server.commands.last.should == "increment foo 1 1234 1"
376
- end
377
- end
325
+ describe Instrumental::Agent, "connection problems" do
326
+ it "should automatically reconnect on disconnect" do
327
+ agent.increment("reconnect_test", 1, 1234)
328
+ wait
329
+ server.disconnect_all
330
+ wait(1)
331
+ agent.increment('reconnect_test', 1, 5678) # triggers reconnect
332
+ wait(1)
333
+ server.connect_count.should == 2
334
+ # Ensure the last command sent has been received after the reconnect attempt
335
+ server.commands.last.should == "increment reconnect_test 1 5678 1"
336
+ end
378
337
 
379
- it "should send commands in a process that bypasses at_exit when using #cleanup" do
380
- @server = TestServer.new
381
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
382
- if pid = fork { @agent.increment('foo', 1, 1234); @agent.cleanup; exit! }
383
- Process.wait(pid)
384
- @server.commands.last.should == "increment foo 1 1234 1"
385
- end
386
- end
338
+ context 'not listening' do
339
+ # Mark server as down
340
+ let(:listen) { false }
341
+
342
+ it "should buffer commands when server is down" do
343
+ agent.increment('reconnect_test', 1, 1234)
344
+ wait
345
+ # The agent should not have sent the metric yet, the server is not responding
346
+ agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
347
+ end
348
+
349
+ it "should warn once when buffer is full" do
350
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
351
+ wait
352
+ agent.logger.should_receive(:warn).with(/Queue full/).once
353
+
354
+ agent.increment('buffer_full_warn_test', 1, 1234)
355
+ agent.increment('buffer_full_warn_test', 1, 1234)
356
+ agent.increment('buffer_full_warn_test', 1, 1234)
357
+ agent.increment('buffer_full_warn_test', 1, 1234)
358
+ agent.increment('buffer_full_warn_test', 1, 1234)
359
+ end
360
+ end
361
+ end
387
362
 
388
- it "should not wait longer than EXIT_FLUSH_TIMEOUT seconds to exit a process" do
389
- @server = TestServer.new
390
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
391
- Socket.stub!(:new) { |*args| sleep(5) && StringIO.new }
392
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
393
- if (pid = fork { @agent.increment('foo', 1) })
394
- tm = Time.now.to_f
395
- Process.wait(pid)
396
- diff = Time.now.to_f - tm
397
- diff.should >= 3
398
- diff.should < 5
363
+ context 'bad address' do
364
+ let(:address) { "nope:9999" }
365
+
366
+ it "should not be running if it cannot connect" do
367
+ agent.gauge('connection_test', 1, 1234)
368
+ # nope:9999 does not resolve to anything, the agent will not resolve
369
+ # the address and refuse to start a worker thread
370
+ agent.should_not be_running
371
+ end
399
372
  end
400
- end
401
- end
402
373
 
403
- it "should not wait to exit a process if there are no commands queued" do
404
- @server = TestServer.new
405
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
406
- Socket.stub!(:new) { |*args| sleep(5) && StringIO.new }
407
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
408
- if (pid = fork { @agent.increment('foo', 1); @agent.queue.clear })
409
- tm = Time.now.to_f
410
- Process.wait(pid)
411
- diff = Time.now.to_f - tm
412
- diff.should < 1
374
+ context 'not responding' do
375
+ # Server will not acknowledge hello or authenticate commands
376
+ let(:response) { false }
377
+
378
+ it "should buffer commands when server is not responsive" do
379
+ agent.increment('reconnect_test', 1, 1234)
380
+ wait
381
+ # Since server hasn't responded to hello or authenticate, worker thread will not send data
382
+ agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
383
+ end
384
+ end
385
+
386
+ context 'server hangup' do
387
+ it "should cancel the worker thread when the host has hung up" do
388
+ # Start the background agent thread and let it send one metric successfully
389
+ agent.gauge('connection_failure', 1, 1234)
390
+ wait
391
+ # Stop the server
392
+ server.stop
393
+ wait
394
+ # Send one metric to the stopped server
395
+ agent.gauge('connection_failure', 1, 1234)
396
+ wait
397
+ # The agent thread should have stopped running since the network write would
398
+ # have failed. The queue will still contain the metric that has yet to be sent
399
+ agent.should_not be_running
400
+ agent.queue.size.should == 1
401
+
402
+ end
403
+
404
+ it "should restart the worker thread after hanging it up during an unreachable host event" do
405
+ # Start the background agent thread and let it send one metric successfully
406
+ agent.gauge('connection_failure', 1, 1234)
407
+ wait
408
+ # Stop the server
409
+ server.stop
410
+ wait
411
+ # Send one metric to the stopped server
412
+ agent.gauge('connection_failure', 1, 1234)
413
+ wait
414
+ # The agent thread should have stopped running since the network write would
415
+ # have failed. The queue will still contain the metric that has yet to be sent
416
+ agent.should_not be_running
417
+ agent.queue.size.should == 1
418
+ wait
419
+ # Start the server back up again
420
+ server.listen
421
+ wait
422
+ # Sending another metric should kickstart the background worker thread
423
+ agent.gauge('connection_failure', 1, 1234)
424
+ wait
425
+ # The agent should now be running the background thread, and the queue should be empty
426
+ agent.should be_running
427
+ agent.queue.size.should == 0
428
+ end
429
+
430
+ end
431
+
432
+
433
+ context 'not authenticating' do
434
+ # Server will fail all authentication attempts
435
+ let(:authenticate) { false }
436
+
437
+ it "should buffer commands when authentication fails" do
438
+ agent.increment('reconnect_test', 1, 1234)
439
+ wait
440
+ # Metrics should not have been sent since all authentication failed
441
+ agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
442
+ end
443
+ end
444
+
445
+ if FORK_SUPPORTED
446
+ it "should send commands in a short-lived process" do
447
+ if pid = fork { agent.increment('foo', 1, 1234) }
448
+ Process.wait(pid)
449
+ # The forked process should have flushed and waited on at_exit
450
+ server.commands.last.should == "increment foo 1 1234 1"
451
+ end
452
+ end
453
+
454
+ it "should send commands in a process that bypasses at_exit when using #cleanup" do
455
+ if pid = fork { agent.increment('foo', 1, 1234); agent.cleanup; exit! }
456
+ Process.wait(pid)
457
+ # The forked process should have flushed and waited on at_exit since cleanup was called explicitly
458
+ server.commands.last.should == "increment foo 1 1234 1"
459
+ end
460
+ end
461
+
462
+ it "should not wait longer than EXIT_FLUSH_TIMEOUT seconds to exit a process" do
463
+ agent.stub(:open_socket) { |*args, &block| sleep(5) && block.call }
464
+ with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
465
+ if (pid = fork { agent.increment('foo', 1) })
466
+ tm = Time.now.to_f
467
+ Process.wait(pid)
468
+ diff = Time.now.to_f - tm
469
+ diff.abs.should >= 3
470
+ diff.abs.should < 5
471
+ end
472
+ end
473
+ end
474
+
475
+ it "should not wait to exit a process if there are no commands queued" do
476
+ agent.stub(:open_socket) { |*args, &block| sleep(5) && block.call }
477
+ with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
478
+ if (pid = fork { agent.increment('foo', 1); agent.queue.clear })
479
+ tm = Time.now.to_f
480
+ Process.wait(pid)
481
+ diff = Time.now.to_f - tm
482
+ diff.should < 1
483
+ end
484
+ end
485
+ end
486
+ end
487
+
488
+ it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
489
+ agent.increment('foo', 1)
490
+ wait
491
+ agent.should_receive(:flush_socket) do
492
+ r, w = IO.pipe
493
+ Thread.new do
494
+ IO.select([r]) # mimic an endless blocking select poll
495
+ end.join
496
+ end
497
+ with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
498
+ tm = Time.now.to_f
499
+ agent.cleanup
500
+ diff = Time.now.to_f - tm
501
+ diff.should <= 3
502
+ end
503
+ end
504
+
505
+ it "should not attempt to resolve DNS more than RESOLUTION_FAILURES_BEFORE_WAITING before introducing an inactive period" do
506
+ with_constants('Instrumental::Agent::RESOLUTION_FAILURES_BEFORE_WAITING' => 1,
507
+ 'Instrumental::Agent::RESOLUTION_WAIT' => 2,
508
+ 'Instrumental::Agent::RESOLVE_TIMEOUT' => 0.1) do
509
+ attempted_resolutions = 0
510
+ Resolv.stub(:getaddresses) { attempted_resolutions +=1 ; sleep 1 }
511
+ agent.gauge('test', 1)
512
+ attempted_resolutions.should == 1
513
+ agent.should_not be_running
514
+ agent.gauge('test', 1)
515
+ attempted_resolutions.should == 1
516
+ agent.should_not be_running
517
+ end
518
+ end
519
+
520
+ it "should attempt to resolve DNS after the RESOLUTION_WAIT inactive period has been exceeded" do
521
+ with_constants('Instrumental::Agent::RESOLUTION_FAILURES_BEFORE_WAITING' => 1,
522
+ 'Instrumental::Agent::RESOLUTION_WAIT' => 2,
523
+ 'Instrumental::Agent::RESOLVE_TIMEOUT' => 0.1) do
524
+ attempted_resolutions = 0
525
+ Resolv.stub(:getaddresses) { attempted_resolutions +=1 ; sleep 1 }
526
+ agent.gauge('test', 1)
527
+ attempted_resolutions.should == 1
528
+ agent.should_not be_running
529
+ agent.gauge('test', 1)
530
+ attempted_resolutions.should == 1
531
+ agent.should_not be_running
532
+ sleep 2
533
+ agent.gauge('test', 1)
534
+ attempted_resolutions.should == 2
535
+ end
413
536
  end
414
537
  end
415
- end
416
538
 
417
- it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
418
- @server = TestServer.new
419
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => false)
420
- @agent.increment('foo', 1)
421
- wait
422
- @agent.instance_variable_get(:@socket).should_receive(:flush).and_return {
423
- r, w = IO.pipe
424
- IO.select([r]) # mimic an endless blocking select poll
425
- }
426
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
427
- tm = Time.now.to_f
428
- @agent.cleanup
429
- diff = Time.now.to_f - tm
430
- diff.should <= 3
539
+ describe Instrumental::Agent, "enabled with sync option" do
540
+ let(:synchronous) { true }
541
+
542
+ it "should send all data in synchronous mode" do
543
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
544
+ 5.times do |i|
545
+ agent.increment('overflow_test', i + 1, 300)
546
+ end
547
+ wait # let the server receive the commands
548
+ server.commands.should include("increment overflow_test 1 300 1")
549
+ server.commands.should include("increment overflow_test 2 300 1")
550
+ server.commands.should include("increment overflow_test 3 300 1")
551
+ server.commands.should include("increment overflow_test 4 300 1")
552
+ server.commands.should include("increment overflow_test 5 300 1")
553
+ end
554
+ end
431
555
  end
432
556
  end
433
557
  end
434
558
 
435
- describe Instrumental::Agent, "enabled with sync option" do
436
- before do
437
- @server = TestServer.new
438
- @agent = Instrumental::Agent.new('test_token', :collector => @server.host_and_port, :synchronous => true)
439
- end
559
+ describe "Insecure" do
560
+ let(:secure?) { false }
561
+ let(:verify_cert?) { false }
562
+ it_behaves_like "Instrumental Agent"
563
+ end
440
564
 
441
- after do
442
- @agent.stop
443
- @server.stop
444
- end
565
+ describe "Secure without cert verify" do
566
+ let(:secure?) { true }
567
+ let(:verify_cert?) { false }
568
+ it_behaves_like "Instrumental Agent"
445
569
 
446
- it "should send all data in synchronous mode" do
447
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
448
- 5.times do |i|
449
- @agent.increment('overflow_test', i + 1, 300)
450
- end
451
- wait # let the server receive the commands
452
- @server.commands.should include("increment overflow_test 1 300 1")
453
- @server.commands.should include("increment overflow_test 2 300 1")
454
- @server.commands.should include("increment overflow_test 3 300 1")
455
- @server.commands.should include("increment overflow_test 4 300 1")
456
- @server.commands.should include("increment overflow_test 5 300 1")
457
- end
570
+ it "should be disabled if the system does not allow secure connections but the user specifically requested secure" do
571
+ Instrumental::Agent.any_instance.stub(:allows_secure?) { false }
572
+ agent = Instrumental::Agent.new('test-token', :enabled => true, :secure => true)
573
+ agent.secure.should == false
574
+ agent.enabled.should == false
458
575
  end
459
576
 
577
+ it "should be fallback to insecure if the system does not allow secure connections but the user did not specifically request secure" do
578
+ Instrumental::Agent.any_instance.stub(:allows_secure?) { false }
579
+ agent = Instrumental::Agent.new('test-token', :enabled => true)
580
+ agent.secure.should == false
581
+ agent.enabled.should == true
582
+ end
460
583
  end