instrumental_agent 0.13.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68d5c4451d15ba2eeb83f189d14f4a34c6ec77fd
4
- data.tar.gz: 6e7e2fd6ec37a5eb210563deec7c04ee91794a84
3
+ metadata.gz: d22dc8e7673c5701cf1cdc654ebd8b953eff28c7
4
+ data.tar.gz: 53fa9eb53ace63abe3a243b3ce8397bf3d53f01f
5
5
  SHA512:
6
- metadata.gz: 7973eec6253fe0e536480351318746c2ab6561a272485d39297b13e4cc50fdda98dd32fd538baf22c7eac9128ba61cb64982b0c221002155bf14a77862d04ede
7
- data.tar.gz: c1979ec7dcc1f04c3c5d4aaad1debf8d6ffecaa87ee2e527332e3fda2d96904db701f2f5c922897f09ed841775c3c84233682edb18c101b963553aeaba5c42e7
6
+ metadata.gz: bc8ec3269967f078690443c315c0dfe1d450603855b2f9cb31a01f3625cb00642cb1e5975cfdc7b3ee25c04c9f4b5249f4c1d5b1aa8fc7e0fed5086fb5f9fc49
7
+ data.tar.gz: 3c846c8a723c0c5e9b5c1998a78d330caad7a7b67e663ebb8a5abd21e8866348af0538ef14149b7bd897190930703e9e2c990cb5168caa9fa929941354f762ea
data/.travis.yml CHANGED
@@ -1,4 +1,3 @@
1
- sudo: false
2
1
  language: ruby
3
2
  rvm:
4
3
  - 1.8.7
@@ -7,3 +6,5 @@ rvm:
7
6
  - 2.0.0
8
7
  - jruby-18mode
9
8
  - jruby-19mode
9
+ - rbx-18mode
10
+ - rbx-19mode
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ### 0.13.1 [August 3, 2015]
2
+ * Revert encrypted support
3
+
1
4
  ### 0.13.0 [August 3, 2015]
2
5
  * Add support for encrypted transport, tests for same
3
6
 
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
- ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
5
- if RUBY_VERSION < "1.9" && !%w{jruby rbx}.include?(ruby_engine)
4
+
5
+ if RUBY_VERSION < "1.9"
6
6
  # Built and installed via ext/mkrf_conf.rb
7
7
  gem 'system_timer', '~> 1.2'
8
8
  end
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- require 'bundler/setup'
2
1
  require 'bundler/gem_tasks'
3
2
  require 'rspec/core/rake_task'
4
3
 
@@ -19,4 +19,10 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency(%q<rake>, [">= 0"])
20
20
  s.add_development_dependency(%q<rspec>, ["~> 2.0"])
21
21
  s.add_development_dependency(%q<fuubar>, [">= 0"])
22
+ if RUBY_VERSION >= "1.9.2"
23
+ s.add_development_dependency(%q<guard>, [">= 0"])
24
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
25
+ s.add_development_dependency(%q<growl>, [">= 0"])
26
+ s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
27
+ end
22
28
  end
@@ -1,24 +1,22 @@
1
1
  require 'instrumental/version'
2
2
  require 'instrumental/system_timer'
3
3
  require 'logger'
4
- require 'openssl' rescue nil
5
4
  require 'thread'
6
5
  require 'socket'
7
6
 
8
7
 
9
8
  module Instrumental
10
9
  class Agent
11
- BACKOFF = 2.0
12
- CONNECT_TIMEOUT = 20
13
- EXIT_FLUSH_TIMEOUT = 5
14
- HOSTNAME = Socket.gethostbyname(Socket.gethostname).first rescue Socket.gethostname
15
- MAX_BUFFER = 5000
10
+ BACKOFF = 2.0
16
11
  MAX_RECONNECT_DELAY = 15
17
- REPLY_TIMEOUT = 10
18
-
12
+ MAX_BUFFER = 5000
13
+ REPLY_TIMEOUT = 10
14
+ CONNECT_TIMEOUT = 20
15
+ EXIT_FLUSH_TIMEOUT = 5
16
+ HOSTNAME = Socket.gethostbyname(Socket.gethostname).first rescue Socket.gethostname
19
17
 
20
18
  attr_accessor :host, :port, :synchronous, :queue
21
- attr_reader :connection, :enabled, :secure
19
+ attr_reader :connection, :enabled
22
20
 
23
21
  def self.logger=(l)
24
22
  @logger = l
@@ -44,33 +42,18 @@ module Instrumental
44
42
 
45
43
  # defaults
46
44
  # host: collector.instrumentalapp.com
47
- # port: 8001
45
+ # port: 8000
48
46
  # enabled: true
49
47
  # synchronous: false
50
- # secure: true
51
- # verify: true
52
48
  @api_key = api_key
53
49
  @host, @port = options[:collector].to_s.split(':')
54
50
  @host ||= 'collector.instrumentalapp.com'
55
- requested_secure = options[:secure] == true
56
- desired_secure = options[:secure].nil? ? allows_secure? : !!options[:secure]
57
- if !allows_secure? && desired_secure
58
- logger.warn "Cannot connect to Instrumental via encrypted transport, SSL not available"
59
- if requested_secure
60
- options[:enabled] = false
61
- logger.error "You requested secure protocol to connect to Instrumental, but it is not available on this system (OpenSSL is not defined). Connecting to Instrumental has been disabled."
62
- end
63
- desired_secure = false
64
- end
65
- @secure = desired_secure
66
- @verify_cert = options[:verify_cert].nil? ? true : !!options[:verify_cert]
67
- default_port = @secure ? 8001 : 8000
68
- @port = (@port || default_port).to_i
51
+ @port = (@port || 8000).to_i
69
52
  @enabled = options.has_key?(:enabled) ? !!options[:enabled] : true
70
53
  @synchronous = !!options[:synchronous]
71
54
  @pid = Process.pid
72
55
  @allow_reconnect = true
73
- @certs = certificates
56
+
74
57
  setup_cleanup_at_exit if @enabled
75
58
  end
76
59
 
@@ -308,41 +291,10 @@ module Instrumental
308
291
  message
309
292
  end
310
293
 
311
- def wait_exceptions
312
- classes = [Errno::EAGAIN]
313
- if defined?(IO::EAGAINWaitReadable)
314
- classes << IO::EAGAINWaitReadable
315
- end
316
- if defined?(IO::EWOULDBLOCKWaitReadable)
317
- classes << IO::EWOULDBLOCKWaitReadable
318
- end
319
- if defined?(IO::WaitReadable)
320
- classes << IO::WaitReadable
321
- end
322
- classes
323
- end
324
-
325
-
326
294
  def test_connection
327
295
  begin
328
- # In the case where the socket is an OpenSSL::SSL::SSLSocket,
329
- # on Ruby 1.8.6, 1.8.7 or 1.9.1, read_nonblock does not exist,
330
- # and so the case of testing socket liveliness via a nonblocking
331
- # read that catches a wait condition won't work.
332
- #
333
- # We grab the SSL socket's underlying IO object and perform the
334
- # non blocking read there in order to ensure the socket is still
335
- # valid
336
- if @socket.respond_to?(:read_nonblock)
337
- @socket.read_nonblock(1)
338
- elsif @socket.respond_to?(:io)
339
- # The SSL Socket may send down additional data at close time,
340
- # so we perform two nonblocking reads, one to pull any pending
341
- # data on the socket, and the second to actually perform the connection
342
- # liveliness test
343
- @socket.io.read_nonblock(1024) && @socket.io.read_nonblock(1024)
344
- end
345
- rescue *wait_exceptions
296
+ @socket.read_nonblock(1)
297
+ rescue Errno::EAGAIN
346
298
  # noop
347
299
  end
348
300
  end
@@ -371,29 +323,15 @@ module Instrumental
371
323
  end
372
324
  end
373
325
 
374
- def open_socket(remote_host, remote_port, secure, verify_cert)
375
- sock = TCPSocket.open(remote_host, remote_port)
376
- if secure
377
- context = OpenSSL::SSL::SSLContext.new()
378
- if verify_cert
379
- context.set_params(:verify_mode => OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT)
380
- else
381
- context.set_params(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
382
- end
383
- ssl_socket = OpenSSL::SSL::SSLSocket.new(sock, context)
384
- ssl_socket.sync_close = true
385
- ssl_socket.connect
386
- sock = ssl_socket
387
- end
388
- sock
389
- end
326
+
390
327
 
391
328
  def run_worker_loop
392
329
  command_and_args = nil
393
330
  command_options = nil
394
331
  logger.info "connecting to collector"
332
+ @socket = Socket.new(Socket::PF_INET, Socket::SOCK_STREAM, 0)
395
333
  with_timeout(CONNECT_TIMEOUT) do
396
- @socket = open_socket(host, port, @secure, @verify_cert)
334
+ @socket.connect Socket.pack_sockaddr_in(port, ipv4_address_for_host(host, port))
397
335
  end
398
336
  logger.info "connected to collector at #{host}:#{port}"
399
337
  hello_options = {
@@ -466,17 +404,11 @@ module Instrumental
466
404
  !@thread.nil? && @pid == Process.pid
467
405
  end
468
406
 
469
- def flush_socket(socket)
470
- socket.flush
471
- end
472
-
473
407
  def disconnect
474
408
  if connected?
475
409
  logger.info "Disconnecting..."
476
410
  begin
477
- with_timeout(EXIT_FLUSH_TIMEOUT) do
478
- flush_socket(@socket)
479
- end
411
+ with_timeout(EXIT_FLUSH_TIMEOUT) { @socket.flush }
480
412
  rescue Timeout::Error
481
413
  logger.info "Timed out flushing socket..."
482
414
  end
@@ -485,21 +417,6 @@ module Instrumental
485
417
  @socket = nil
486
418
  end
487
419
 
488
- def allows_secure?
489
- defined?(OpenSSL)
490
- end
491
-
492
- def certificates
493
- if allows_secure?
494
- base_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
495
- %w{equifax geotrust rapidssl}.map do |name|
496
- OpenSSL::X509::Certificate.new(File.open(File.join(base_dir, "certs", "#{name}.ca.pem")))
497
- end
498
- else
499
- []
500
- end
501
- end
502
-
503
420
  end
504
421
 
505
422
  end
@@ -1,3 +1,3 @@
1
1
  module Instrumental
2
- VERSION = "0.13.0"
2
+ VERSION = "0.13.1"
3
3
  end
data/spec/agent_spec.rb CHANGED
@@ -1,485 +1,460 @@
1
1
  require 'spec_helper'
2
2
 
3
- def wait(n=0.2)
4
- sleep n # FIXME: hack
3
+ def wait
4
+ sleep 0.2 # FIXME: hack
5
5
  end
6
6
 
7
- FORK_SUPPORTED = begin
8
- Process.wait(fork { true })
9
- true
10
- rescue Exception => e
11
- false
12
- end
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
13
13
 
14
+ after do
15
+ @agent.stop
16
+ @agent = nil
17
+ @server.stop
18
+ end
14
19
 
20
+ it "should not connect to the server" do
21
+ wait
22
+ @server.connect_count.should == 0
23
+ end
15
24
 
16
- shared_examples "Instrumental Agent" do
17
- context do
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
18
31
 
19
- # Inferred:
20
- # secure? and verify_cert? are set
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
21
38
 
22
- # Agent options
23
- let(:enabled) { true }
24
- let(:synchronous) { false }
25
- let(:token) { 'test_token' }
26
- let(:agent) { Instrumental::Agent.new(token, :collector => server.host_and_port, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?) }
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
27
45
 
28
- # Server options
29
- let(:listen) { true }
30
- let(:response) { true }
31
- let(:authenticate) { true }
32
- let(:server) { TestServer.new(:listen => listen, :authenticate => authenticate, :response => response, :secure => secure?) }
46
+ it "should no op on an empty flush" do
47
+ @agent.flush(true)
48
+ wait
49
+ @server.commands.should be_empty
50
+ end
33
51
 
34
- before do
35
- Instrumental::Agent.logger.level = Logger::UNKNOWN
36
- @server = server
37
- wait
38
- end
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
39
58
 
40
- after do
41
- agent.stop
42
- server.stop
43
- end
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
44
64
 
45
- describe Instrumental::Agent, "disabled" do
46
- let(:enabled) { false }
65
+ after do
66
+ @agent.stop
67
+ @agent = nil
68
+ @server.stop
69
+ end
47
70
 
48
- it "should not connect to the server" do
49
- server.connect_count.should == 0
50
- end
71
+ it "should not connect to the server" do
72
+ wait
73
+ @server.connect_count.should == 0
74
+ end
51
75
 
52
- it "should not connect to the server after receiving a metric" do
53
- agent.gauge('disabled_test', 1)
54
- wait
55
- server.connect_count.should == 0
56
- end
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
57
81
 
58
- it "should no op on flush without reconnect" do
59
- 1.upto(100) { agent.gauge('disabled_test', 1) }
60
- agent.flush(false)
61
- wait
62
- server.commands.should be_empty
63
- end
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
64
92
 
65
- it "should no op on flush with reconnect" do
66
- 1.upto(100) { agent.gauge('disabled_test', 1) }
67
- agent.flush(true)
68
- wait
69
- server.commands.should be_empty
70
- end
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
71
98
 
72
- it "should no op on an empty flush" do
73
- agent.flush(true)
74
- wait
75
- server.commands.should be_empty
76
- end
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
77
105
 
78
- it "should send metrics to logger" do
79
- now = Time.now
80
- agent.logger.should_receive(:debug).with("gauge metric 1 #{now.to_i} 1")
81
- agent.gauge("metric", 1)
82
- end
83
- end
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
84
114
 
85
- describe Instrumental::Agent, "enabled" do
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
121
 
87
- it "should not connect to the server" do
88
- server.connect_count.should == 0
89
- end
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
90
127
 
91
- it "should connect to the server after sending a metric" do
92
- agent.increment("test.foo")
93
- wait
94
- server.connect_count.should == 1
95
- end
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
96
133
 
97
- it "should announce itself, and include version" do
98
- agent.increment("test.foo")
99
- wait
100
- server.commands[0].should =~ /hello .*/
101
- server.commands[0].should =~ / version /
102
- server.commands[0].should =~ / hostname /
103
- server.commands[0].should =~ / pid /
104
- server.commands[0].should =~ / runtime /
105
- server.commands[0].should =~ / platform /
106
- end
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
107
140
 
108
- it "should authenticate using the token" do
109
- agent.increment("test.foo")
110
- wait
111
- server.commands[1].should == "authenticate test_token"
112
- end
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
113
147
 
114
- it "should report a gauge" do
115
- now = Time.now
116
- agent.gauge('gauge_test', 123)
117
- wait
118
- server.commands.last.should == "gauge gauge_test 123 #{now.to_i} 1"
119
- end
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
120
154
 
121
- it "should report a time as gauge and return the block result" do
122
- now = Time.now
123
- agent.time("time_value_test") do
124
- 1 + 1
125
- end.should == 2
126
- wait
127
- server.commands.last.should =~ /gauge time_value_test .* #{now.to_i}/
128
- end
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
129
160
 
130
- it "should return the value gauged" do
131
- now = Time.now
132
- agent.gauge('gauge_test', 123).should == 123
133
- agent.gauge('gauge_test', 989).should == 989
134
- wait
135
- end
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
136
166
 
137
- it "should report a gauge with a set time" do
138
- agent.gauge('gauge_test', 123, 555)
139
- wait
140
- server.commands.last.should == "gauge gauge_test 123 555 1"
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)
141
171
  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
142
180
 
143
- it "should report a gauge with a set time and count" do
144
- agent.gauge('gauge_test', 123, 555, 111)
145
- wait
146
- server.commands.last.should == "gauge gauge_test 123 555 111"
147
- end
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
148
196
 
149
- it "should report an increment" do
150
- now = Time.now
151
- agent.increment("increment_test")
152
- wait
153
- server.commands.last.should == "increment increment_test 1 #{now.to_i} 1"
154
- end
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
155
211
 
156
- it "should return the value incremented by" do
157
- now = Time.now
158
- agent.increment("increment_test").should == 1
159
- agent.increment("increment_test", 5).should == 5
160
- wait
161
- end
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
162
219
 
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
220
+ it "should let exceptions in time bubble up" do
221
+ expect { @agent.time('za') { raise "fail" } }.to raise_error
222
+ end
169
223
 
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
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
175
232
 
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
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
181
239
 
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
- server.commands.should_not include("increment overflow_test 4 300 1")
192
- server.commands.should_not include("increment overflow_test 5 300 1")
193
- end
194
- end
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
195
248
 
196
- it "should send all data in synchronous mode" do
197
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
198
- agent.synchronous = true
199
- 5.times do |i|
200
- agent.increment('overflow_test', i + 1, 300)
201
- end
202
- agent.instance_variable_get(:@queue).size.should == 0
203
- wait # let the server receive the commands
204
- server.commands.should include("increment overflow_test 1 300 1")
205
- server.commands.should include("increment overflow_test 2 300 1")
206
- server.commands.should include("increment overflow_test 3 300 1")
207
- server.commands.should include("increment overflow_test 4 300 1")
208
- server.commands.should include("increment overflow_test 5 300 1")
209
- end
210
- end
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
211
255
 
212
- if FORK_SUPPORTED
213
- it "should automatically reconnect when forked" do
214
- agent.increment('fork_reconnect_test', 1, 2)
215
- fork do
216
- agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
217
- end
218
- wait(1)
219
- agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
220
- wait(1)
221
- server.connect_count.should == 2
222
- server.commands.should include("increment fork_reconnect_test 1 2 1")
223
- server.commands.should include("increment fork_reconnect_test 1 3 1")
224
- server.commands.should include("increment fork_reconnect_test 1 4 1")
225
- end
226
- end
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
227
268
 
228
- it "should never let an exception reach the user" do
229
- agent.stub(:send_command).and_raise(Exception.new("Test Exception"))
230
- agent.increment('throws_exception', 2).should be_nil
231
- wait
232
- agent.gauge('throws_exception', 234).should be_nil
233
- wait
234
- end
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
235
275
 
236
- it "should let exceptions in time bubble up" do
237
- expect { agent.time('za') { raise "fail" } }.to raise_error
238
- end
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
239
281
 
240
- it "should return nil if the user overflows the MAX_BUFFER" do
241
- 1.upto(Instrumental::Agent::MAX_BUFFER) do
242
- agent.increment("test").should == 1
243
- thread = agent.instance_variable_get(:@thread)
244
- thread.kill
245
- end
246
- agent.increment("test").should be_nil
247
- end
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
248
292
 
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
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
255
301
 
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
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
264
308
 
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
309
+ describe Instrumental::Agent, "connection problems" do
310
+ after do
311
+ @agent.stop
312
+ @server.stop
313
+ end
271
314
 
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
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
284
327
 
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
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
291
336
 
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
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
297
345
 
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
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
308
354
 
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
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
317
361
 
318
- it "should no op on an empty flush" do
319
- agent.flush(true)
320
- wait
321
- server.commands.should be_empty
322
- end
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)
323
367
  end
368
+ end
324
369
 
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
- server.commands.last.should == "increment reconnect_test 1 5678 1"
335
- end
336
-
337
- context 'not listening' do
338
- let(:listen) { false }
339
-
340
- it "should buffer commands when server is down" do
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
345
-
346
- it "should warn once when buffer is full" do
347
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
348
- wait
349
- agent.logger.should_receive(:warn).with(/Queue full/).once
350
-
351
- agent.increment('buffer_full_warn_test', 1, 1234)
352
- agent.increment('buffer_full_warn_test', 1, 1234)
353
- agent.increment('buffer_full_warn_test', 1, 1234)
354
- agent.increment('buffer_full_warn_test', 1, 1234)
355
- agent.increment('buffer_full_warn_test', 1, 1234)
356
- end
357
- end
358
- end
359
-
360
- context 'not responding' do
361
- let(:response) { false }
362
-
363
- it "should buffer commands when server is not responsive" do
364
- agent.increment('reconnect_test', 1, 1234)
365
- wait
366
- agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
367
- end
368
- end
369
-
370
-
371
- context 'not authenticating' do
372
- let(:authenticate) { false }
373
-
374
- it "should buffer commands when authentication fails" do
375
- agent.increment('reconnect_test', 1, 1234)
376
- wait
377
- agent.queue.pop(true).should include("increment reconnect_test 1 1234 1\n")
378
- end
379
- end
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
380
378
 
381
- if FORK_SUPPORTED
382
- it "should send commands in a short-lived process" do
383
- if pid = fork { agent.increment('foo', 1, 1234) }
384
- Process.wait(pid)
385
- server.commands.last.should == "increment foo 1 1234 1"
386
- end
387
- end
388
-
389
- it "should send commands in a process that bypasses at_exit when using #cleanup" do
390
- if pid = fork { agent.increment('foo', 1, 1234); agent.cleanup; exit! }
391
- Process.wait(pid)
392
- server.commands.last.should == "increment foo 1 1234 1"
393
- end
394
- end
395
-
396
- it "should not wait longer than EXIT_FLUSH_TIMEOUT seconds to exit a process" do
397
- agent.stub(:open_socket) { |*args, &block| sleep(5) && block.call }
398
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
399
- if (pid = fork { agent.increment('foo', 1) })
400
- tm = Time.now.to_f
401
- Process.wait(pid)
402
- diff = Time.now.to_f - tm
403
- diff.abs.should >= 3
404
- diff.abs.should < 5
405
- end
406
- end
407
- end
408
-
409
- it "should not wait to exit a process if there are no commands queued" do
410
- agent.stub(:open_socket) { |*args, &block| sleep(5) && block.call }
411
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
412
- if (pid = fork { agent.increment('foo', 1); agent.queue.clear })
413
- tm = Time.now.to_f
414
- Process.wait(pid)
415
- diff = Time.now.to_f - tm
416
- diff.should < 1
417
- end
418
- end
419
- end
420
- end
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
421
387
 
422
- it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
423
- agent.increment('foo', 1)
424
- wait
425
- agent.should_receive(:flush_socket) do
426
- r, w = IO.pipe
427
- Thread.new do
428
- IO.select([r]) # mimic an endless blocking select poll
429
- end.join
430
- end
431
- with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
432
- tm = Time.now.to_f
433
- agent.cleanup
434
- diff = Time.now.to_f - tm
435
- diff.should <= 3
436
- end
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
437
399
  end
438
400
  end
401
+ end
439
402
 
440
- describe Instrumental::Agent, "enabled with sync option" do
441
- let(:synchronous) { true }
442
-
443
- it "should send all data in synchronous mode" do
444
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
445
- 5.times do |i|
446
- agent.increment('overflow_test', i + 1, 300)
447
- end
448
- wait # let the server receive the commands
449
- server.commands.should include("increment overflow_test 1 300 1")
450
- server.commands.should include("increment overflow_test 2 300 1")
451
- server.commands.should include("increment overflow_test 3 300 1")
452
- server.commands.should include("increment overflow_test 4 300 1")
453
- server.commands.should include("increment overflow_test 5 300 1")
454
- end
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
455
413
  end
456
-
457
414
  end
458
415
  end
459
- end
460
416
 
461
- describe "Insecure" do
462
- let(:secure?) { false }
463
- let(:verify_cert?) { false }
464
- it_behaves_like "Instrumental Agent"
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
431
+ end
432
+ end
465
433
  end
466
434
 
467
- describe "Secure without cert verify" do
468
- let(:secure?) { true }
469
- let(:verify_cert?) { false }
470
- it_behaves_like "Instrumental Agent"
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
471
440
 
472
- it "should be disabled if the system does not allow secure connections but the user specifically requested secure" do
473
- Instrumental::Agent.any_instance.stub(:allows_secure?) { false }
474
- agent = Instrumental::Agent.new('test-token', :enabled => true, :secure => true)
475
- agent.secure.should == false
476
- agent.enabled.should == false
441
+ after do
442
+ @agent.stop
443
+ @server.stop
477
444
  end
478
445
 
479
- it "should be fallback to insecure if the system does not allow secure connections but the user did not specifically request secure" do
480
- Instrumental::Agent.any_instance.stub(:allows_secure?) { false }
481
- agent = Instrumental::Agent.new('test-token', :enabled => true)
482
- agent.secure.should == false
483
- agent.enabled.should == true
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
484
458
  end
459
+
485
460
  end