right_agent 2.2.1 → 2.3.0
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.
- checksums.yaml +7 -0
- data/lib/right_agent.rb +1 -0
- data/lib/right_agent/actor.rb +0 -28
- data/lib/right_agent/actors/agent_manager.rb +20 -18
- data/lib/right_agent/agent.rb +69 -87
- data/lib/right_agent/agent_tag_manager.rb +1 -1
- data/lib/right_agent/clients/api_client.rb +0 -1
- data/lib/right_agent/clients/auth_client.rb +2 -6
- data/lib/right_agent/clients/balanced_http_client.rb +2 -2
- data/lib/right_agent/clients/base_retry_client.rb +12 -19
- data/lib/right_agent/clients/right_http_client.rb +1 -5
- data/lib/right_agent/clients/router_client.rb +8 -15
- data/lib/right_agent/command/command_parser.rb +3 -3
- data/lib/right_agent/command/command_runner.rb +1 -1
- data/lib/right_agent/connectivity_checker.rb +7 -11
- data/lib/right_agent/dispatcher.rb +7 -42
- data/lib/right_agent/enrollment_result.rb +2 -2
- data/lib/right_agent/error_tracker.rb +202 -0
- data/lib/right_agent/log.rb +0 -2
- data/lib/right_agent/packets.rb +1 -1
- data/lib/right_agent/pending_requests.rb +10 -4
- data/lib/right_agent/pid_file.rb +3 -3
- data/lib/right_agent/protocol_version_mixin.rb +3 -3
- data/lib/right_agent/scripts/agent_deployer.rb +13 -1
- data/lib/right_agent/sender.rb +14 -30
- data/lib/right_agent/serialize/secure_serializer.rb +4 -4
- data/right_agent.gemspec +2 -2
- data/spec/agent_spec.rb +5 -5
- data/spec/clients/auth_client_spec.rb +1 -1
- data/spec/clients/balanced_http_client_spec.rb +4 -2
- data/spec/clients/base_retry_client_spec.rb +5 -6
- data/spec/clients/router_client_spec.rb +1 -4
- data/spec/dispatcher_spec.rb +6 -55
- data/spec/error_tracker_spec.rb +293 -0
- data/spec/pending_requests_spec.rb +2 -2
- data/spec/sender_spec.rb +3 -3
- data/spec/spec_helper.rb +4 -2
- metadata +33 -66
@@ -28,9 +28,9 @@ module RightScale
|
|
28
28
|
|
29
29
|
include ProtocolVersionMixin
|
30
30
|
|
31
|
-
class MissingPrivateKey <
|
32
|
-
class MissingCertificate <
|
33
|
-
class InvalidSignature <
|
31
|
+
class MissingPrivateKey < StandardError; end
|
32
|
+
class MissingCertificate < StandardError; end
|
33
|
+
class InvalidSignature < StandardError; end
|
34
34
|
|
35
35
|
# Create the one and only SecureSerializer
|
36
36
|
def self.init(serializer, identity, store, encrypt = true)
|
@@ -105,7 +105,7 @@ module RightScale
|
|
105
105
|
msg = EncryptedDocument.new(msg, certs).encrypted_data(encode_format)
|
106
106
|
else
|
107
107
|
target = obj.target_for_encryption if obj.respond_to?(:target_for_encryption)
|
108
|
-
|
108
|
+
ErrorTracker.log(self, "No certs available for object #{obj.class} being sent to #{target.inspect}") if target
|
109
109
|
end
|
110
110
|
end
|
111
111
|
sig = Signature.new(msg, @cert, @key).data(encode_format)
|
data/right_agent.gemspec
CHANGED
@@ -25,8 +25,8 @@ require 'rbconfig'
|
|
25
25
|
|
26
26
|
Gem::Specification.new do |spec|
|
27
27
|
spec.name = 'right_agent'
|
28
|
-
spec.version = '2.
|
29
|
-
spec.date = '2014-05-
|
28
|
+
spec.version = '2.3.0'
|
29
|
+
spec.date = '2014-05-27'
|
30
30
|
spec.authors = ['Lee Kirchhoff', 'Raphael Simon', 'Tony Spataro', 'Scott Messier']
|
31
31
|
spec.email = 'lee@rightscale.com'
|
32
32
|
spec.homepage = 'https://github.com/rightscale/right_agent'
|
data/spec/agent_spec.rb
CHANGED
@@ -493,10 +493,10 @@ describe RightScale::Agent do
|
|
493
493
|
it "should ack if request dispatch fails" do
|
494
494
|
run_in_em do
|
495
495
|
request = RightScale::Request.new("/foo/bar", "payload")
|
496
|
-
@log.should_receive(:error).with(/Failed to dispatch request/,
|
496
|
+
@log.should_receive(:error).with(/Failed to dispatch request/, StandardError, :trace).once
|
497
497
|
@broker.should_receive(:subscribe).with(hsh(:name => @identity), nil, Hash, Proc).
|
498
498
|
and_return(@broker_ids).and_yield(@broker_id, request, @header).once
|
499
|
-
@dispatcher.should_receive(:dispatch).and_raise(
|
499
|
+
@dispatcher.should_receive(:dispatch).and_raise(StandardError)
|
500
500
|
@header.should_receive(:ack).once
|
501
501
|
@agent.run
|
502
502
|
end
|
@@ -505,10 +505,10 @@ describe RightScale::Agent do
|
|
505
505
|
it "should ack if response delivery fails" do
|
506
506
|
run_in_em do
|
507
507
|
result = RightScale::Result.new("token", "to", "results", "from")
|
508
|
-
@log.should_receive(:error).with(/Failed to deliver response/,
|
508
|
+
@log.should_receive(:error).with(/Failed to deliver response/, StandardError, :trace).once
|
509
509
|
@broker.should_receive(:subscribe).with(hsh(:name => @identity), nil, Hash, Proc).
|
510
510
|
and_return(@broker_ids).and_yield(@broker_id, result, @header).once
|
511
|
-
@sender.should_receive(:handle_response).and_raise(
|
511
|
+
@sender.should_receive(:handle_response).and_raise(StandardError)
|
512
512
|
@header.should_receive(:ack).once
|
513
513
|
@agent.run
|
514
514
|
end
|
@@ -528,7 +528,7 @@ describe RightScale::Agent do
|
|
528
528
|
run_in_em do
|
529
529
|
@agent = RightScale::Agent.new(:user => "me", :identity => @identity)
|
530
530
|
@broker.should_receive(:nil?).and_return(true)
|
531
|
-
@log.should_receive(:error).with("[stop] Terminating because just because"
|
531
|
+
@log.should_receive(:error).with("[stop] Terminating because just because").once
|
532
532
|
@agent.terminate("just because")
|
533
533
|
end
|
534
534
|
end
|
@@ -269,7 +269,7 @@ describe RightScale::AuthClient do
|
|
269
269
|
end
|
270
270
|
|
271
271
|
it "log error if callback fails" do
|
272
|
-
@log.should_receive(:error).with("Failed status callback", StandardError).once
|
272
|
+
@log.should_receive(:error).with("Failed status callback", StandardError, :caller).once
|
273
273
|
@client.status { |t, s| raise StandardError, "test" }
|
274
274
|
@client.send(:state=, state).should == state
|
275
275
|
end
|
@@ -412,7 +412,8 @@ describe RightScale::BalancedHttpClient do
|
|
412
412
|
gateway_timeout = RightScale::HttpExceptions.create(504, "server timeout")
|
413
413
|
bad_request = RightScale::HttpExceptions.create(400, "bad data")
|
414
414
|
@no_result = RightSupport::Net::NoResult.new("no result", {@url => gateway_timeout, @url => bad_request})
|
415
|
-
lambda { @client.send(:handle_no_result, @no_result, @url, &@proc) }.
|
415
|
+
lambda { @client.send(:handle_no_result, @no_result, @url, &@proc) }.
|
416
|
+
should raise_error(RightScale::HttpExceptions::BadRequest)
|
416
417
|
@yielded.should == bad_request
|
417
418
|
end
|
418
419
|
|
@@ -458,7 +459,8 @@ describe RightScale::BalancedHttpClient do
|
|
458
459
|
it "raises last exception in details if not retryable" do
|
459
460
|
bad_request = RightScale::HttpExceptions.create(400, "bad data")
|
460
461
|
@no_result = RightSupport::Net::NoResult.new("no result", {@url => bad_request})
|
461
|
-
lambda { @client.send(:handle_no_result, @no_result, @url, &@proc) }.
|
462
|
+
lambda { @client.send(:handle_no_result, @no_result, @url, &@proc) }.
|
463
|
+
should raise_error(RightScale::HttpExceptions::BadRequest)
|
462
464
|
@yielded.should == bad_request
|
463
465
|
end
|
464
466
|
end
|
@@ -89,7 +89,6 @@ describe RightScale::BaseRetryClient do
|
|
89
89
|
options = @client.instance_variable_get(:@options)
|
90
90
|
options[:retry_enabled].should be_nil
|
91
91
|
options[:filter_params].should be_nil
|
92
|
-
options[:exception_callback].should be_nil
|
93
92
|
end
|
94
93
|
|
95
94
|
it "initiates connection and enables use if connected" do
|
@@ -214,7 +213,7 @@ describe RightScale::BaseRetryClient do
|
|
214
213
|
end
|
215
214
|
|
216
215
|
it "log error if callback fails" do
|
217
|
-
@log.should_receive(:error).with("Failed status callback", StandardError).once
|
216
|
+
@log.should_receive(:error).with("Failed status callback", StandardError, :caller).once
|
218
217
|
@client.status { |t, s| raise StandardError, "test" }
|
219
218
|
@client.send(:state=, state).should == state
|
220
219
|
end
|
@@ -321,13 +320,13 @@ describe RightScale::BaseRetryClient do
|
|
321
320
|
it "sets state to :disconnected and logs if server not responding" do
|
322
321
|
e = RightScale::BalancedHttpClient::NotResponding.new("not responding", RightScale::HttpExceptions.create(503))
|
323
322
|
@http_client.should_receive(:check_health).and_raise(e).once
|
324
|
-
@log.should_receive(:error).with("Failed test health check", RightScale::HttpException).once
|
323
|
+
@log.should_receive(:error).with("Failed test health check", RightScale::HttpException, :trace).once
|
325
324
|
@client.send(:check_health).should == :disconnected
|
326
325
|
@client.state.should == :disconnected
|
327
326
|
end
|
328
327
|
|
329
328
|
it "sets state to :disconnected and logs if exception unexpected" do
|
330
|
-
@log.should_receive(:error).with("Failed test health check", StandardError).once
|
329
|
+
@log.should_receive(:error).with("Failed test health check", StandardError, :caller).once
|
331
330
|
@http_client.should_receive(:check_health).and_raise(StandardError).once
|
332
331
|
@client.send(:check_health).should == :disconnected
|
333
332
|
@client.state.should == :disconnected
|
@@ -387,14 +386,14 @@ describe RightScale::BaseRetryClient do
|
|
387
386
|
it "logs error if exception is raised" do
|
388
387
|
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
389
388
|
flexmock(@client).should_receive(:enable_use).and_raise(StandardError).once
|
390
|
-
@log.should_receive(:error).with("Failed test reconnect", StandardError).once
|
389
|
+
@log.should_receive(:error).with("Failed test reconnect", StandardError, :caller).once
|
391
390
|
@client.send(:reconnect).should be_true
|
392
391
|
@client.state.should == :disconnected
|
393
392
|
end
|
394
393
|
|
395
394
|
it "resets the timer interval to the configured value" do
|
396
395
|
@client.send(:reconnect); @client.instance_variable_set(:@reconnecting, nil) # to get @reconnect_timer initialized
|
397
|
-
@log.should_receive(:error).with("Failed test health check", StandardError).once
|
396
|
+
@log.should_receive(:error).with("Failed test health check", StandardError, :caller).once
|
398
397
|
@http_client.should_receive(:check_health).and_raise(StandardError).once
|
399
398
|
@timer.should_receive(:interval=).with(15).once
|
400
399
|
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
@@ -470,7 +470,7 @@ describe RightScale::RouterClient do
|
|
470
470
|
end
|
471
471
|
|
472
472
|
it "adjusts connect interval if websocket creation fails and sets state to :long_poll" do
|
473
|
-
@log.should_receive(:error).with("Failed creating WebSocket", RuntimeError).once
|
473
|
+
@log.should_receive(:error).with("Failed creating WebSocket", RuntimeError, :caller).once
|
474
474
|
flexmock(Faye::WebSocket::Client).should_receive(:new).and_raise(RuntimeError).once
|
475
475
|
@client.send(:try_connect, @routing_keys, &@handler)
|
476
476
|
@client.instance_variable_get(:@connect_interval).should == 60
|
@@ -661,7 +661,6 @@ describe RightScale::RouterClient do
|
|
661
661
|
end
|
662
662
|
|
663
663
|
it "handles exception if there is a long-polling failure" do
|
664
|
-
@log.should_receive(:error).with("Failed long-polling", RuntimeError, :trace).once
|
665
664
|
@defer_callback_proc.call(RuntimeError.new).should be nil
|
666
665
|
@client.instance_variable_get(:@listen_state).should == :choose
|
667
666
|
@client.instance_variable_get(:@listen_interval).should == 4
|
@@ -745,7 +744,6 @@ describe RightScale::RouterClient do
|
|
745
744
|
RightScale::Exceptions::RetryableError.new("error"),
|
746
745
|
RightScale::Exceptions::InternalServerError.new("error", "server")].each do |e|
|
747
746
|
it "does not trace #{e} exceptions but sets state to :choose" do
|
748
|
-
@log.should_receive(:error).with("Failed long-polling", e, :no_trace).once
|
749
747
|
@client.send(:process_long_poll, e).should be nil
|
750
748
|
@client.instance_variable_get(:@listen_state).should == :choose
|
751
749
|
@client.instance_variable_get(:@listen_interval).should == 4
|
@@ -754,7 +752,6 @@ describe RightScale::RouterClient do
|
|
754
752
|
|
755
753
|
it "traces unexpected exceptions and sets state to :choose" do
|
756
754
|
e = RuntimeError.new
|
757
|
-
@log.should_receive(:error).with("Failed long-polling", e, :trace).once
|
758
755
|
@client.send(:process_long_poll, e).should be nil
|
759
756
|
@client.instance_variable_get(:@listen_state).should == :choose
|
760
757
|
@client.instance_variable_get(:@listen_interval).should == 4
|
data/spec/dispatcher_spec.rb
CHANGED
@@ -26,7 +26,6 @@ class Foo
|
|
26
26
|
include RightScale::Actor
|
27
27
|
expose_idempotent :bar, :index, :i_kill_you
|
28
28
|
expose_non_idempotent :bar_non
|
29
|
-
on_exception :handle_exception
|
30
29
|
|
31
30
|
def index(payload)
|
32
31
|
bar(payload)
|
@@ -47,34 +46,17 @@ class Foo
|
|
47
46
|
def i_kill_you(payload)
|
48
47
|
raise RuntimeError.new('I kill you!')
|
49
48
|
end
|
50
|
-
|
51
|
-
def handle_exception(method, deliverable, error)
|
52
|
-
end
|
53
49
|
end
|
54
50
|
|
55
51
|
class Bar
|
56
52
|
include RightScale::Actor
|
57
53
|
expose :i_kill_you
|
58
|
-
on_exception do |method, deliverable, error|
|
59
|
-
@scope = self
|
60
|
-
@called_with = [method, deliverable, error]
|
61
|
-
end
|
62
54
|
|
63
55
|
def i_kill_you(payload)
|
64
56
|
raise RuntimeError.new('I kill you!')
|
65
57
|
end
|
66
58
|
end
|
67
59
|
|
68
|
-
# No specs, simply ensures multiple methods for assigning on_exception callback,
|
69
|
-
# on_exception raises exception when called with an invalid argument.
|
70
|
-
class Doomed
|
71
|
-
include RightScale::Actor
|
72
|
-
on_exception do
|
73
|
-
end
|
74
|
-
on_exception lambda {}
|
75
|
-
on_exception :doh
|
76
|
-
end
|
77
|
-
|
78
60
|
describe "RightScale::Dispatcher" do
|
79
61
|
|
80
62
|
include FlexMock::ArgumentTypes
|
@@ -89,7 +71,7 @@ describe "RightScale::Dispatcher" do
|
|
89
71
|
@registry = RightScale::ActorRegistry.new
|
90
72
|
@registry.register(@actor, nil)
|
91
73
|
@agent_id = "rs-agent-1-1"
|
92
|
-
@agent = flexmock("Agent", :identity => @agent_id, :registry => @registry
|
74
|
+
@agent = flexmock("Agent", :identity => @agent_id, :registry => @registry).by_default
|
93
75
|
@cache = RightScale::DispatchedCache.new(@agent_id)
|
94
76
|
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
95
77
|
end
|
@@ -157,41 +139,17 @@ describe "RightScale::Dispatcher" do
|
|
157
139
|
lambda { @dispatcher.dispatch(req) }.should raise_error(RightScale::Dispatcher::InvalidRequestType)
|
158
140
|
end
|
159
141
|
|
160
|
-
it "should
|
161
|
-
@log.should_receive(:error).once
|
142
|
+
it "should log exception if dispatch fails" do
|
143
|
+
@log.should_receive(:error).with(/Failed dispatching/, RuntimeError, :trace).once
|
162
144
|
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
163
|
-
flexmock(@actor).should_receive(:handle_exception).with(:i_kill_you, req, Exception).once
|
164
|
-
res = @dispatcher.dispatch(req)
|
165
|
-
res.results.error?.should be_true
|
166
|
-
(res.results.content =~ /Could not handle \/foo\/i_kill_you request/).should be_true
|
167
|
-
end
|
168
|
-
|
169
|
-
it "should call on_exception Procs defined in a subclass with the correct arguments" do
|
170
|
-
@log.should_receive(:error).once
|
171
|
-
actor = Bar.new
|
172
|
-
@registry.register(actor, nil)
|
173
|
-
req = RightScale::Request.new('/bar/i_kill_you', nil)
|
174
145
|
@dispatcher.dispatch(req)
|
175
|
-
called_with = actor.instance_variable_get("@called_with")
|
176
|
-
called_with[0].should == :i_kill_you
|
177
|
-
called_with[1].should == req
|
178
|
-
called_with[2].should be_kind_of(RuntimeError)
|
179
|
-
called_with[2].message.should == 'I kill you!'
|
180
146
|
end
|
181
147
|
|
182
|
-
it "should
|
148
|
+
it "should return error result if dispatch fails" do
|
183
149
|
@log.should_receive(:error).once
|
184
|
-
actor = Bar.new
|
185
|
-
@registry.register(actor, nil)
|
186
|
-
req = RightScale::Request.new('/bar/i_kill_you', nil)
|
187
|
-
@dispatcher.dispatch(req)
|
188
|
-
actor.instance_variable_get("@scope").should == actor
|
189
|
-
end
|
190
|
-
|
191
|
-
it "should log error if dispatch fails" do
|
192
|
-
RightScale::Log.should_receive(:error).once
|
193
150
|
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
194
|
-
@dispatcher.dispatch(req)
|
151
|
+
res = @dispatcher.dispatch(req)
|
152
|
+
res.results.error?.should be_true
|
195
153
|
end
|
196
154
|
|
197
155
|
it "should reject requests whose time-to-live has expired" do
|
@@ -312,13 +270,6 @@ describe "RightScale::Dispatcher" do
|
|
312
270
|
end
|
313
271
|
end
|
314
272
|
|
315
|
-
it "should return error result if dispatch fails" do
|
316
|
-
@log.should_receive(:error).with(/Could not handle/, Exception, :trace).once
|
317
|
-
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
318
|
-
res = @dispatcher.dispatch(req)
|
319
|
-
res.results.error?.should be_true
|
320
|
-
end
|
321
|
-
|
322
273
|
end
|
323
274
|
|
324
275
|
end # RightScale::Dispatcher
|
@@ -0,0 +1,293 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'hydraulic_brake'
|
24
|
+
|
25
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
26
|
+
|
27
|
+
describe RightScale::ErrorTracker do
|
28
|
+
|
29
|
+
include FlexMock::ArgumentTypes
|
30
|
+
|
31
|
+
class AgentMock; end
|
32
|
+
|
33
|
+
before(:each) do
|
34
|
+
@agent = AgentMock.new
|
35
|
+
@agent_name = "test_agent"
|
36
|
+
@shard_id = 9
|
37
|
+
@endpoint = "https://airbrake.com"
|
38
|
+
@api_key = "secret"
|
39
|
+
@trace_level = RightScale::Agent::TRACE_LEVEL
|
40
|
+
@tracker = RightScale::ErrorTracker.instance
|
41
|
+
@log = flexmock(RightScale::Log)
|
42
|
+
@brake = flexmock(HydraulicBrake)
|
43
|
+
end
|
44
|
+
|
45
|
+
context :init do
|
46
|
+
it "initializes the tracker" do
|
47
|
+
flexmock(@tracker).should_receive(:notify_init).with(@agent_name, @shard_id, @endpoint, @api_key).once
|
48
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id,
|
49
|
+
:airbrake_endpoint => @endpoint, :airbrake_api_key => @api_key).should be true
|
50
|
+
@tracker.instance_variable_get(:@agent).should == @agent
|
51
|
+
@tracker.instance_variable_get(:@trace_level).should == @trace_level
|
52
|
+
@tracker.exception_stats.should be_a RightSupport::Stats::Exceptions
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context :log do
|
57
|
+
before(:each) do
|
58
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id)
|
59
|
+
end
|
60
|
+
|
61
|
+
context "exception nil" do
|
62
|
+
it "logs description" do
|
63
|
+
@log.should_receive(:error).with("failed").once
|
64
|
+
@tracker.log(self, "failed").should be true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "exception string" do
|
69
|
+
it "logs error string" do
|
70
|
+
@log.should_receive(:error).with("failed", "error").once
|
71
|
+
@tracker.log(self, "failed", "error").should be true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "exception" do
|
76
|
+
it "logs exception" do
|
77
|
+
@log.should_receive(:error).with("failed", RuntimeError, :trace).once
|
78
|
+
@tracker.log(self, "failed", RuntimeError.new("error")).should be true
|
79
|
+
end
|
80
|
+
|
81
|
+
it "applies trace level configured for selected exceptions" do
|
82
|
+
@log.should_receive(:error).with("failed", RightScale::BalancedHttpClient::NotResponding, :no_trace).once
|
83
|
+
@tracker.log(self, "failed", RightScale::BalancedHttpClient::NotResponding.new("error")).should be true
|
84
|
+
end
|
85
|
+
|
86
|
+
it "applies specified trace level" do
|
87
|
+
@log.should_receive(:error).with("failed", RuntimeError, :caller).once
|
88
|
+
@tracker.log(self, "failed", RuntimeError.new("error"), nil, :caller).should be true
|
89
|
+
end
|
90
|
+
|
91
|
+
it "tracks exception statistics when component is not a string" do
|
92
|
+
request = RightScale::Request.new("/foo/bar", "payload")
|
93
|
+
@log.should_receive(:error).with("failed", RuntimeError, :trace).once
|
94
|
+
flexmock(@tracker).should_receive(:track).with(self, RuntimeError, request).once
|
95
|
+
@tracker.log(self, "failed", RuntimeError.new("error"), request).should be true
|
96
|
+
end
|
97
|
+
|
98
|
+
it "tracks exception statistics when component is a string" do
|
99
|
+
request = RightScale::Request.new("/foo/bar", "payload")
|
100
|
+
@log.should_receive(:error).once
|
101
|
+
flexmock(@tracker).should_receive(:track).with("test", RuntimeError, request).once
|
102
|
+
@tracker.log("test", "failed", RuntimeError.new("error"), request).should be true
|
103
|
+
end
|
104
|
+
|
105
|
+
it "does not track exception statistics for :no_trace" do
|
106
|
+
@log.should_receive(:error).once
|
107
|
+
flexmock(@tracker).should_receive(:track).never
|
108
|
+
@tracker.log(self, "failed", RuntimeError.new("error"), nil, :no_trace).should be true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it "logs error if error logging fails" do
|
113
|
+
@log.should_receive(:error).with("failed", RuntimeError, :trace).once.ordered
|
114
|
+
@log.should_receive(:error).with("Failed to log error", RuntimeError, :trace).once.ordered
|
115
|
+
flexmock(@tracker).should_receive(:track).and_raise(RuntimeError).once
|
116
|
+
@tracker.log(self, "failed", RuntimeError.new("error")).should be false
|
117
|
+
end
|
118
|
+
|
119
|
+
it "does not raise exception even if exception logging fails" do
|
120
|
+
@log.should_receive(:error).and_raise(RuntimeError).twice
|
121
|
+
@tracker.log(self, "failed").should be false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context :track do
|
126
|
+
before(:each) do
|
127
|
+
@now = Time.at(1000000)
|
128
|
+
flexmock(Time).should_receive(:now).and_return(@now)
|
129
|
+
@exception = RuntimeError.new("error")
|
130
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "records exception in stats when component is an object" do
|
134
|
+
@tracker.track(@agent, @exception).should be true
|
135
|
+
@tracker.exception_stats.all.should == {"agent_mock" => {"total" => 1, "recent" => [{"count" => 1, "when" => 1000000,
|
136
|
+
"type" => "RuntimeError", "message" => "error", "where" => nil}]}}
|
137
|
+
end
|
138
|
+
|
139
|
+
it "records exception in stats when component is a string" do
|
140
|
+
request = RightScale::Request.new("/foo/bar", "payload")
|
141
|
+
@tracker.track("test", @exception, request).should be true
|
142
|
+
@tracker.exception_stats.all.should == {"test" => {"total" => 1, "recent" => [{"count" => 1, "when" => 1000000,
|
143
|
+
"type" => "RuntimeError", "message" => "error", "where" => nil}]}}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context :notify do
|
148
|
+
before(:each) do
|
149
|
+
@exception = RuntimeError.new("error")
|
150
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id,
|
151
|
+
:airbrake_endpoint => @endpoint, :airbrake_api_key => @api_key)
|
152
|
+
@cgi_data = @tracker.instance_variable_get(:@cgi_data)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "sends notification using HydraulicBrake" do
|
156
|
+
@brake.should_receive(:notify).with(on { |a| a[:error_message] == "error" &&
|
157
|
+
a[:error_class] == "RuntimeError" &&
|
158
|
+
a[:backtrace].nil? &&
|
159
|
+
["test", "development"].include?(a[:environment_name]) &&
|
160
|
+
a[:cgi_data].should == @cgi_data &&
|
161
|
+
a.keys & [:component, :action, :parameters, :session_data] == [] }).once
|
162
|
+
@tracker.notify(@exception).should be true
|
163
|
+
end
|
164
|
+
|
165
|
+
it "includes packet data in notification" do
|
166
|
+
request = RightScale::Request.new("/foo/bar", {:pay => "load"}, :token => "token")
|
167
|
+
@brake.should_receive(:notify).with(on { |a| a[:error_message] == "error" &&
|
168
|
+
a[:error_class] == "RuntimeError" &&
|
169
|
+
a[:backtrace].nil? &&
|
170
|
+
a[:action] == "bar" &&
|
171
|
+
a[:parameters] == {:pay => "load"} &&
|
172
|
+
["test", "development"].include?(a[:environment_name]) &&
|
173
|
+
a[:cgi_data] == @cgi_data &&
|
174
|
+
a[:session_data] == {:uuid => "token"} }).once
|
175
|
+
@tracker.notify(@exception, request).should be true
|
176
|
+
end
|
177
|
+
|
178
|
+
it "includes event data in notification" do
|
179
|
+
@brake.should_receive(:notify).with(on { |a| a[:error_message] == "error" &&
|
180
|
+
a[:error_class] == "RuntimeError" &&
|
181
|
+
a[:backtrace].nil? &&
|
182
|
+
a[:action] == "bar" &&
|
183
|
+
a[:parameters] == {:pay => "load"} &&
|
184
|
+
["test", "development"].include?(a[:environment_name]) &&
|
185
|
+
a[:cgi_data] == @cgi_data &&
|
186
|
+
a[:session_data] == {:uuid => "token"} }).once
|
187
|
+
@tracker.notify(@exception, {"uuid" => "token", "path" => "/foo/bar", "data" => {:pay => "load"}}).should be true
|
188
|
+
end
|
189
|
+
|
190
|
+
it "adds agent class to :cgi_data in notification" do
|
191
|
+
@brake.should_receive(:notify).with(on { |a| a[:cgi_data] == @cgi_data.merge(:agent_class => "AgentMock") }).once
|
192
|
+
@tracker.notify(@exception, packet = nil, @agent).should be true
|
193
|
+
end
|
194
|
+
|
195
|
+
it "adds component to notification" do
|
196
|
+
@brake.should_receive(:notify).with(on { |a| a[:component] == "component" }).once
|
197
|
+
@tracker.notify(@exception, packet = nil, agent = nil, "component").should be true
|
198
|
+
end
|
199
|
+
|
200
|
+
it "functions even if cgi_data has not been initialized by notify_init" do
|
201
|
+
@tracker.instance_variable_set(:@cgi_data, nil)
|
202
|
+
@brake.should_receive(:notify).with(on { |a| a[:error_message] == "error" &&
|
203
|
+
a[:error_class] == "RuntimeError" &&
|
204
|
+
a[:backtrace].nil? &&
|
205
|
+
["test", "development"].include?(a[:environment_name]) &&
|
206
|
+
a.keys & [:cgi_data, :component, :action, :parameters, :session_data] == [] }).once
|
207
|
+
@tracker.notify(@exception).should be true
|
208
|
+
end
|
209
|
+
|
210
|
+
it "does nothing if notify is disabled" do
|
211
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id)
|
212
|
+
@brake.should_receive(:notify).never
|
213
|
+
@tracker.notify(@exception).should be true
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context :notify_callback do
|
218
|
+
it "returns proc that calls notify" do
|
219
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id,
|
220
|
+
:airbrake_endpoint => @endpoint, :airbrake_api_key => @api_key)
|
221
|
+
flexmock(@tracker).should_receive(:notify).with("exception", "packet", "agent", "component").once
|
222
|
+
@tracker.notify_callback.call("exception", "packet", "agent", "component")
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context :stats do
|
227
|
+
before(:each) do
|
228
|
+
@now = Time.at(1000000)
|
229
|
+
flexmock(Time).should_receive(:now).and_return(@now)
|
230
|
+
@exception = RuntimeError.new("error")
|
231
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "returns exception stats" do
|
235
|
+
@tracker.track(@agent, @exception).should be true
|
236
|
+
@tracker.stats.should == {"exceptions" => {"agent_mock" => {"total" => 1, "recent" => [{"count" => 1,
|
237
|
+
"when" => 1000000, "type" => "RuntimeError", "message" => "error", "where" => nil}]}}}
|
238
|
+
end
|
239
|
+
|
240
|
+
it "resets stats after collecting current stats" do
|
241
|
+
@tracker.track(@agent, @exception).should be true
|
242
|
+
@tracker.stats(true).should == {"exceptions" => {"agent_mock" => {"total" => 1, "recent" => [{"count" => 1,
|
243
|
+
"when" => 1000000, "type" => "RuntimeError", "message" => "error", "where" => nil}]}}}
|
244
|
+
@tracker.stats.should == {"exceptions" => nil}
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context :notify_init do
|
249
|
+
class ConfigMock
|
250
|
+
attr_accessor :secure, :host, :port, :api_key, :project_root
|
251
|
+
end
|
252
|
+
|
253
|
+
it "does not initialize if Airbrake endpoint or API key is undefined" do
|
254
|
+
@brake.should_receive(:configure).never
|
255
|
+
@tracker.send(:notify_init, @agent_name, @shard_id, @endpoint, nil)
|
256
|
+
@tracker.instance_variable_get(:@notify_enabled).should be false
|
257
|
+
@tracker.send(:notify_init, @agent_name, @shard_id, nil, @api_key)
|
258
|
+
@tracker.instance_variable_get(:@notify_enabled).should be false
|
259
|
+
@tracker.instance_variable_get(:@cgi_data).should be_empty
|
260
|
+
end
|
261
|
+
|
262
|
+
it "initializes cgi data and configures HydraulicBrake" do
|
263
|
+
config = ConfigMock.new
|
264
|
+
@brake.should_receive(:configure).and_yield(config).once
|
265
|
+
@tracker.send(:notify_init, @agent_name, @shard_id, @endpoint, @api_key).should be true
|
266
|
+
cgi_data = @tracker.instance_variable_get(:@cgi_data)
|
267
|
+
cgi_data[:agent_name].should == @agent_name
|
268
|
+
cgi_data[:pid].should be_a Integer
|
269
|
+
cgi_data[:process].should be_a String
|
270
|
+
cgi_data[:shard_id].should == @shard_id
|
271
|
+
config.secure.should be true
|
272
|
+
config.host.should == "airbrake.com"
|
273
|
+
config.port.should == 443
|
274
|
+
config.api_key.should == @api_key
|
275
|
+
config.project_root.should be_a String
|
276
|
+
@tracker.instance_variable_get(:@notify_enabled).should be true
|
277
|
+
end
|
278
|
+
|
279
|
+
it "raises exception if hydraulic_gem is not available" do
|
280
|
+
flexmock(@tracker).should_receive(:require_succeeds?).with("hydraulic_brake").and_return(false)
|
281
|
+
lambda do
|
282
|
+
@tracker.send(:notify_init, @agent_name, @shard_id, @endpoint, @api_key)
|
283
|
+
end.should raise_error(RuntimeError, /hydraulic_brake gem missing/)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "notify_init" do
|
288
|
+
before(:each) do
|
289
|
+
@tracker.init(@agent, @agent_name, :trace_level => @trace_level, :shard_id => @shard_id,
|
290
|
+
:airbrake_endpoint => @endpoint, :airbrake_api_key => @api_key)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|