right_agent 2.0.7-x86-mingw32
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.
- data/LICENSE +20 -0
- data/README.rdoc +82 -0
- data/Rakefile +113 -0
- data/lib/right_agent.rb +59 -0
- data/lib/right_agent/actor.rb +182 -0
- data/lib/right_agent/actor_registry.rb +76 -0
- data/lib/right_agent/actors/agent_manager.rb +232 -0
- data/lib/right_agent/agent.rb +1149 -0
- data/lib/right_agent/agent_config.rb +480 -0
- data/lib/right_agent/agent_identity.rb +210 -0
- data/lib/right_agent/agent_tag_manager.rb +237 -0
- data/lib/right_agent/audit_formatter.rb +107 -0
- data/lib/right_agent/clients.rb +31 -0
- data/lib/right_agent/clients/api_client.rb +383 -0
- data/lib/right_agent/clients/auth_client.rb +247 -0
- data/lib/right_agent/clients/balanced_http_client.rb +369 -0
- data/lib/right_agent/clients/base_retry_client.rb +495 -0
- data/lib/right_agent/clients/right_http_client.rb +279 -0
- data/lib/right_agent/clients/router_client.rb +493 -0
- data/lib/right_agent/command.rb +30 -0
- data/lib/right_agent/command/agent_manager_commands.rb +150 -0
- data/lib/right_agent/command/command_client.rb +136 -0
- data/lib/right_agent/command/command_constants.rb +33 -0
- data/lib/right_agent/command/command_io.rb +126 -0
- data/lib/right_agent/command/command_parser.rb +87 -0
- data/lib/right_agent/command/command_runner.rb +118 -0
- data/lib/right_agent/command/command_serializer.rb +63 -0
- data/lib/right_agent/connectivity_checker.rb +179 -0
- data/lib/right_agent/console.rb +65 -0
- data/lib/right_agent/core_payload_types.rb +44 -0
- data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
- data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
- data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
- data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
- data/lib/right_agent/core_payload_types/dev_repositories.rb +100 -0
- data/lib/right_agent/core_payload_types/dev_repository.rb +76 -0
- data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
- data/lib/right_agent/core_payload_types/executable_bundle.rb +130 -0
- data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
- data/lib/right_agent/core_payload_types/login_user.rb +79 -0
- data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
- data/lib/right_agent/core_payload_types/recipe_instantiation.rb +73 -0
- data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
- data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
- data/lib/right_agent/core_payload_types/right_script_instantiation.rb +94 -0
- data/lib/right_agent/core_payload_types/runlist_policy.rb +44 -0
- data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
- data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
- data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
- data/lib/right_agent/daemonize.rb +35 -0
- data/lib/right_agent/dispatched_cache.rb +109 -0
- data/lib/right_agent/dispatcher.rb +272 -0
- data/lib/right_agent/enrollment_result.rb +221 -0
- data/lib/right_agent/exceptions.rb +87 -0
- data/lib/right_agent/history.rb +145 -0
- data/lib/right_agent/log.rb +460 -0
- data/lib/right_agent/minimal.rb +46 -0
- data/lib/right_agent/monkey_patches.rb +30 -0
- data/lib/right_agent/monkey_patches/ruby_patch.rb +55 -0
- data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
- data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
- data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +60 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
- data/lib/right_agent/multiplexer.rb +102 -0
- data/lib/right_agent/offline_handler.rb +270 -0
- data/lib/right_agent/operation_result.rb +300 -0
- data/lib/right_agent/packets.rb +673 -0
- data/lib/right_agent/payload_formatter.rb +104 -0
- data/lib/right_agent/pending_requests.rb +128 -0
- data/lib/right_agent/pid_file.rb +159 -0
- data/lib/right_agent/platform.rb +770 -0
- data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
- data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
- data/lib/right_agent/platform/unix/platform.rb +226 -0
- data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
- data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
- data/lib/right_agent/platform/windows/platform.rb +1808 -0
- data/lib/right_agent/protocol_version_mixin.rb +69 -0
- data/lib/right_agent/retryable_request.rb +195 -0
- data/lib/right_agent/scripts/agent_controller.rb +543 -0
- data/lib/right_agent/scripts/agent_deployer.rb +400 -0
- data/lib/right_agent/scripts/common_parser.rb +160 -0
- data/lib/right_agent/scripts/log_level_manager.rb +192 -0
- data/lib/right_agent/scripts/stats_manager.rb +268 -0
- data/lib/right_agent/scripts/usage.rb +58 -0
- data/lib/right_agent/secure_identity.rb +92 -0
- data/lib/right_agent/security.rb +32 -0
- data/lib/right_agent/security/cached_certificate_store_proxy.rb +77 -0
- data/lib/right_agent/security/certificate.rb +102 -0
- data/lib/right_agent/security/certificate_cache.rb +89 -0
- data/lib/right_agent/security/distinguished_name.rb +56 -0
- data/lib/right_agent/security/encrypted_document.rb +83 -0
- data/lib/right_agent/security/rsa_key_pair.rb +76 -0
- data/lib/right_agent/security/signature.rb +86 -0
- data/lib/right_agent/security/static_certificate_store.rb +85 -0
- data/lib/right_agent/sender.rb +792 -0
- data/lib/right_agent/serialize.rb +29 -0
- data/lib/right_agent/serialize/message_pack.rb +107 -0
- data/lib/right_agent/serialize/secure_serializer.rb +151 -0
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
- data/lib/right_agent/serialize/serializable.rb +151 -0
- data/lib/right_agent/serialize/serializer.rb +159 -0
- data/lib/right_agent/subprocess.rb +38 -0
- data/lib/right_agent/tracer.rb +124 -0
- data/right_agent.gemspec +101 -0
- data/spec/actor_registry_spec.rb +80 -0
- data/spec/actor_spec.rb +162 -0
- data/spec/agent_config_spec.rb +235 -0
- data/spec/agent_identity_spec.rb +78 -0
- data/spec/agent_spec.rb +734 -0
- data/spec/agent_tag_manager_spec.rb +319 -0
- data/spec/clients/api_client_spec.rb +423 -0
- data/spec/clients/auth_client_spec.rb +272 -0
- data/spec/clients/balanced_http_client_spec.rb +576 -0
- data/spec/clients/base_retry_client_spec.rb +635 -0
- data/spec/clients/router_client_spec.rb +594 -0
- data/spec/clients/spec_helper.rb +111 -0
- data/spec/command/agent_manager_commands_spec.rb +51 -0
- data/spec/command/command_io_spec.rb +93 -0
- data/spec/command/command_parser_spec.rb +79 -0
- data/spec/command/command_runner_spec.rb +107 -0
- data/spec/command/command_serializer_spec.rb +51 -0
- data/spec/connectivity_checker_spec.rb +83 -0
- data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
- data/spec/core_payload_types/dev_repository_spec.rb +33 -0
- data/spec/core_payload_types/executable_bundle_spec.rb +67 -0
- data/spec/core_payload_types/login_user_spec.rb +102 -0
- data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
- data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
- data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
- data/spec/core_payload_types/spec_helper.rb +23 -0
- data/spec/dispatched_cache_spec.rb +136 -0
- data/spec/dispatcher_spec.rb +324 -0
- data/spec/enrollment_result_spec.rb +53 -0
- data/spec/history_spec.rb +246 -0
- data/spec/log_spec.rb +192 -0
- data/spec/monkey_patches/eventmachine_spec.rb +62 -0
- data/spec/multiplexer_spec.rb +48 -0
- data/spec/offline_handler_spec.rb +340 -0
- data/spec/operation_result_spec.rb +208 -0
- data/spec/packets_spec.rb +461 -0
- data/spec/pending_requests_spec.rb +136 -0
- data/spec/platform/spec_helper.rb +216 -0
- data/spec/platform/unix/darwin/platform_spec.rb +181 -0
- data/spec/platform/unix/linux/platform_spec.rb +540 -0
- data/spec/platform/unix/spec_helper.rb +149 -0
- data/spec/platform/windows/mingw/platform_spec.rb +222 -0
- data/spec/platform/windows/mswin/platform_spec.rb +259 -0
- data/spec/platform/windows/spec_helper.rb +720 -0
- data/spec/retryable_request_spec.rb +306 -0
- data/spec/secure_identity_spec.rb +50 -0
- data/spec/security/cached_certificate_store_proxy_spec.rb +62 -0
- data/spec/security/certificate_cache_spec.rb +71 -0
- data/spec/security/certificate_spec.rb +49 -0
- data/spec/security/distinguished_name_spec.rb +46 -0
- data/spec/security/encrypted_document_spec.rb +55 -0
- data/spec/security/rsa_key_pair_spec.rb +55 -0
- data/spec/security/signature_spec.rb +66 -0
- data/spec/security/static_certificate_store_spec.rb +58 -0
- data/spec/sender_spec.rb +1045 -0
- data/spec/serialize/message_pack_spec.rb +131 -0
- data/spec/serialize/secure_serializer_spec.rb +132 -0
- data/spec/serialize/serializable_spec.rb +90 -0
- data/spec/serialize/serializer_spec.rb +197 -0
- data/spec/spec.opts +2 -0
- data/spec/spec.win32.opts +1 -0
- data/spec/spec_helper.rb +130 -0
- data/spec/tracer_spec.rb +114 -0
- metadata +447 -0
@@ -0,0 +1,635 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2013 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
|
+
|
24
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
25
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'right_agent', 'clients', 'base_retry_client'))
|
26
|
+
|
27
|
+
describe RightScale::BaseRetryClient do
|
28
|
+
|
29
|
+
include FlexMock::ArgumentTypes
|
30
|
+
|
31
|
+
before(:each) do
|
32
|
+
@log = flexmock(RightScale::Log)
|
33
|
+
@log.should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
34
|
+
@log.should_receive(:warning).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
35
|
+
@url = "http://test.com"
|
36
|
+
@timer = flexmock("timer", :cancel => true, :interval= => 0).by_default
|
37
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).by_default
|
38
|
+
@http_client = flexmock("http client", :get => true, :check_health => true).by_default
|
39
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).by_default
|
40
|
+
@auth_header = {"Authorization" => "Bearer <session>"}
|
41
|
+
@auth_client = AuthClientMock.new(@url, @auth_header)
|
42
|
+
@client = RightScale::BaseRetryClient.new
|
43
|
+
@options = {:api_version => "2.0"}
|
44
|
+
@client.init(:test, @auth_client, @options)
|
45
|
+
end
|
46
|
+
|
47
|
+
context :init do
|
48
|
+
it "requires auth client to produce URL for specified type of client" do
|
49
|
+
lambda { @client.init(:bogus, @auth_client, @options) }.should \
|
50
|
+
raise_error(ArgumentError, "Auth client does not support server type :bogus")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "requires :api_version option" do
|
54
|
+
lambda { @client.init(:test, @auth_client, {}) }.should \
|
55
|
+
raise_error(ArgumentError, ":api_version option missing")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "initializes options" do
|
59
|
+
@options = {
|
60
|
+
:server_name => "Test",
|
61
|
+
:api_version => "2.0",
|
62
|
+
:open_timeout => 1,
|
63
|
+
:request_timeout => 2,
|
64
|
+
:retry_timeout => 3,
|
65
|
+
:retry_intervals => [1, 2, 3],
|
66
|
+
:reconnect_interval => 4 }
|
67
|
+
@client.init(:test, @auth_client, @options).should be_true
|
68
|
+
options = @client.instance_variable_get(:@options)
|
69
|
+
options[:server_name].should == "Test"
|
70
|
+
options[:api_version].should == "2.0"
|
71
|
+
options[:open_timeout].should == 1
|
72
|
+
options[:request_timeout].should == 2
|
73
|
+
options[:retry_timeout].should == 3
|
74
|
+
options[:retry_intervals].should == [1, 2, 3]
|
75
|
+
options[:reconnect_interval].should == 4
|
76
|
+
end
|
77
|
+
|
78
|
+
it "initializes options to defaults if no value specified" do
|
79
|
+
options = @client.instance_variable_get(:@options)
|
80
|
+
options[:server_name].should == "test"
|
81
|
+
options[:open_timeout].should == 2
|
82
|
+
options[:request_timeout].should == 35
|
83
|
+
options[:retry_timeout].should == 25
|
84
|
+
options[:retry_intervals].should == [4, 12, 36]
|
85
|
+
options[:reconnect_interval].should == 15
|
86
|
+
end
|
87
|
+
|
88
|
+
it "does not default some options" do
|
89
|
+
options = @client.instance_variable_get(:@options)
|
90
|
+
options[:retry_enabled].should be_nil
|
91
|
+
options[:filter_params].should be_nil
|
92
|
+
options[:exception_callback].should be_nil
|
93
|
+
end
|
94
|
+
|
95
|
+
it "initiates connection and enables use if connected" do
|
96
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).once
|
97
|
+
@http_client.should_receive(:check_health).once
|
98
|
+
flexmock(@client).should_receive(:enable_use).once
|
99
|
+
flexmock(@client).should_receive(:reconnect).never
|
100
|
+
@client.init(:test, @auth_client, @options).should be_true
|
101
|
+
@client.state.should == :connected
|
102
|
+
end
|
103
|
+
|
104
|
+
it "sets up for reconnect if not connected" do
|
105
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).once
|
106
|
+
e = RightScale::BalancedHttpClient::NotResponding.new(nil, RestExceptionMock.new(503))
|
107
|
+
@http_client.should_receive(:check_health).and_raise(e)
|
108
|
+
@log.should_receive(:error)
|
109
|
+
flexmock(@client).should_receive(:enable_use).never
|
110
|
+
flexmock(@client).should_receive(:reconnect).once
|
111
|
+
@client.init(:test, @auth_client, @options).should be_false
|
112
|
+
@client.state.should == :disconnected
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context :status do
|
117
|
+
it "stores callback" do
|
118
|
+
callback = lambda { |_, _| }
|
119
|
+
@client.instance_variable_get(:@status_callbacks).size.should == 0
|
120
|
+
@client.status(&callback)
|
121
|
+
@client.instance_variable_get(:@status_callbacks).size.should == 1
|
122
|
+
@client.instance_variable_get(:@status_callbacks)[0].should == callback
|
123
|
+
end
|
124
|
+
|
125
|
+
it "treats callback as optional" do
|
126
|
+
@client.instance_variable_get(:@status_callbacks).size.should == 0
|
127
|
+
@client.status
|
128
|
+
@client.instance_variable_get(:@status_callbacks).size.should == 0
|
129
|
+
end
|
130
|
+
|
131
|
+
it "returns current state" do
|
132
|
+
@client.status.should == :connected
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context :communicated do
|
137
|
+
it "stores callback" do
|
138
|
+
callback = lambda { |_, _| }
|
139
|
+
@client.instance_variable_get(:@communicated_callbacks).size.should == 0
|
140
|
+
@client.communicated(&callback)
|
141
|
+
@client.instance_variable_get(:@communicated_callbacks).size.should == 1
|
142
|
+
@client.instance_variable_get(:@communicated_callbacks)[0].should == callback
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context :close do
|
147
|
+
it "sets state to :closed by default" do
|
148
|
+
@client.close
|
149
|
+
@client.state.should == :closed
|
150
|
+
end
|
151
|
+
|
152
|
+
it "cancels reconnect timer" do
|
153
|
+
@client.send(:reconnect)
|
154
|
+
@timer.should_receive(:cancel).once
|
155
|
+
@client.close
|
156
|
+
end
|
157
|
+
|
158
|
+
it "sets state to :closing if only closing for receiving" do
|
159
|
+
@client.close(:receive)
|
160
|
+
@client.state.should == :closing
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context :state= do
|
165
|
+
before(:each) do
|
166
|
+
@client.instance_variable_set(:@state, :pending)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "raises exception if state transition is invalid" do
|
170
|
+
@client.send(:state=, :connected)
|
171
|
+
lambda { @client.send(:state=, :pending) }.should raise_error(ArgumentError, "Invalid state transition: :connected -> :pending")
|
172
|
+
end
|
173
|
+
|
174
|
+
[:pending, :closed].each do |state|
|
175
|
+
context state do
|
176
|
+
it "stores new state" do
|
177
|
+
@client.send(:state=, state)
|
178
|
+
@client.state.should == state
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
[:connected, :disconnected, :failed].each do |state|
|
184
|
+
context state do
|
185
|
+
before(:each) do
|
186
|
+
flexmock(@client).should_receive(:reconnect).by_default
|
187
|
+
end
|
188
|
+
|
189
|
+
it "stores new state" do
|
190
|
+
@client.send(:state=, state)
|
191
|
+
@client.state.should == state
|
192
|
+
end
|
193
|
+
|
194
|
+
it "stores new state" do
|
195
|
+
@client.send(:state=, state).should == state
|
196
|
+
end
|
197
|
+
|
198
|
+
context "when callbacks" do
|
199
|
+
it "makes callbacks with new state" do
|
200
|
+
callback_type = callback_state = nil
|
201
|
+
@client.status { |t, s| callback_type = t; callback_state = s }
|
202
|
+
@client.send(:state=, state)
|
203
|
+
callback_type.should == :test
|
204
|
+
callback_state.should == state
|
205
|
+
end
|
206
|
+
|
207
|
+
it "log error if callback fails" do
|
208
|
+
@log.should_receive(:error).with("Failed status callback", StandardError).once
|
209
|
+
@client.status { |t, s| raise StandardError, "test" }
|
210
|
+
@client.send(:state=, state).should == state
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it "reconnects only if transitioning to :disconnected" do
|
215
|
+
flexmock(@client).should_receive(:reconnect).times(state == :disconnected ? 1 : 0)
|
216
|
+
@client.send(:state=, state)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "does nothing if current state is :closed" do
|
220
|
+
flexmock(@client.instance_variable_get(:@stats)["state"]).should_receive(:update).once
|
221
|
+
@client.send(:state=, :closed)
|
222
|
+
@client.send(:state=, state).should == :closed
|
223
|
+
end
|
224
|
+
|
225
|
+
it "does nothing if current state is the same" do
|
226
|
+
flexmock(@client.instance_variable_get(:@stats)["state"]).should_receive(:update).once
|
227
|
+
@client.send(:state=, state)
|
228
|
+
@client.send(:state=, state).should == state
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context :create_http_client do
|
235
|
+
it "obtains URL from auth client" do
|
236
|
+
@log.should_receive(:info).with("Connecting to test via \"http://test.com\"")
|
237
|
+
@client.send(:create_http_client)
|
238
|
+
end
|
239
|
+
|
240
|
+
it "creates HTTP client" do
|
241
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).once
|
242
|
+
@client.send(:create_http_client).should == @http_client
|
243
|
+
end
|
244
|
+
|
245
|
+
it "uses specified options" do
|
246
|
+
@options = {
|
247
|
+
:server_name => "Test",
|
248
|
+
:api_version => "2.0",
|
249
|
+
:open_timeout => 1,
|
250
|
+
:request_timeout => 2,
|
251
|
+
:filter_params => ["secret"] }
|
252
|
+
@client.init(:test, @auth_client, @options)
|
253
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).with(@url,
|
254
|
+
on { |a| a[:server_name] == "Test" &&
|
255
|
+
a[:api_version] == "2.0" &&
|
256
|
+
a[:open_timeout] == 1 &&
|
257
|
+
a[:request_timeout] == 2 &&
|
258
|
+
a[:filter_params] == ["secret"] }).and_return(@http_client).once
|
259
|
+
@client.send(:create_http_client).should == @http_client
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context :enable_use do
|
264
|
+
it "should return true" do
|
265
|
+
@client.send(:enable_use).should be_true
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context :check_health do
|
270
|
+
before(:each) do
|
271
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).by_default
|
272
|
+
@client.send(:create_http_client)
|
273
|
+
end
|
274
|
+
|
275
|
+
it "sends health check request using existing HTTP client" do
|
276
|
+
@http_client.should_receive(:check_health).once
|
277
|
+
@client.send(:check_health)
|
278
|
+
end
|
279
|
+
|
280
|
+
it "sets state to :connected" do
|
281
|
+
@client.send(:check_health)
|
282
|
+
@client.state.should == :connected
|
283
|
+
end
|
284
|
+
|
285
|
+
it "returns current state" do
|
286
|
+
@client.send(:check_health).should == :connected
|
287
|
+
end
|
288
|
+
|
289
|
+
it "sets state to :disconnected and logs if server not responding" do
|
290
|
+
e = RightScale::BalancedHttpClient::NotResponding.new("not responding", RestExceptionMock.new(503))
|
291
|
+
@http_client.should_receive(:check_health).and_raise(e).once
|
292
|
+
@log.should_receive(:error).with("Failed test health check", RestExceptionMock).once
|
293
|
+
@client.send(:check_health).should == :disconnected
|
294
|
+
@client.state.should == :disconnected
|
295
|
+
end
|
296
|
+
|
297
|
+
it "sets state to :disconnected and logs if exception unexpected" do
|
298
|
+
@log.should_receive(:error).with("Failed test health check", StandardError).once
|
299
|
+
@http_client.should_receive(:check_health).and_raise(StandardError).once
|
300
|
+
@client.send(:check_health).should == :disconnected
|
301
|
+
@client.state.should == :disconnected
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context :reconnect do
|
306
|
+
before(:each) do
|
307
|
+
@client.instance_variable_set(:@reconnecting, nil)
|
308
|
+
end
|
309
|
+
|
310
|
+
it "waits random interval for initial reconnect attempt" do
|
311
|
+
flexmock(@client).should_receive(:rand).with(15).and_return(10).once
|
312
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).with(10, Proc).and_return(@timer).once
|
313
|
+
@client.send(:reconnect).should be_true
|
314
|
+
end
|
315
|
+
|
316
|
+
it "attempts to connect even if currently connected" do
|
317
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
318
|
+
@client.send(:create_http_client)
|
319
|
+
@client.send(:check_health).should == :connected
|
320
|
+
flexmock(@client).should_receive(:check_health).once
|
321
|
+
@client.send(:reconnect).should be_true
|
322
|
+
end
|
323
|
+
|
324
|
+
it "recreates HTTP client and checks health" do
|
325
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
326
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).once
|
327
|
+
@http_client.should_receive(:check_health).once
|
328
|
+
@client.send(:reconnect).should be_true
|
329
|
+
end
|
330
|
+
|
331
|
+
context "when health check successful" do
|
332
|
+
it "enables use of client" do
|
333
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
334
|
+
flexmock(@client).should_receive(:enable_use).once
|
335
|
+
@client.send(:reconnect).should be_true
|
336
|
+
end
|
337
|
+
|
338
|
+
it "disables timer" do
|
339
|
+
@client.send(:reconnect); @client.instance_variable_set(:@reconnecting, nil) # to get @reconnect_timer initialized
|
340
|
+
@client.instance_variable_set(:@reconnecting, nil)
|
341
|
+
@timer.should_receive(:cancel).once
|
342
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
343
|
+
@client.send(:reconnect).should be_true
|
344
|
+
@client.instance_variable_get(:@reconnecting).should be_nil
|
345
|
+
end
|
346
|
+
|
347
|
+
it "does not reset timer interval" do
|
348
|
+
@timer.should_receive(:interval=).never
|
349
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
350
|
+
@client.send(:reconnect).should be_true
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context "when reconnect fails" do
|
355
|
+
it "logs error if exception is raised" do
|
356
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
357
|
+
flexmock(@client).should_receive(:enable_use).and_raise(StandardError).once
|
358
|
+
@log.should_receive(:error).with("Failed test reconnect", StandardError).once
|
359
|
+
@client.send(:reconnect).should be_true
|
360
|
+
@client.state.should == :disconnected
|
361
|
+
end
|
362
|
+
|
363
|
+
it "resets the timer interval to the configured value" do
|
364
|
+
@client.send(:reconnect); @client.instance_variable_set(:@reconnecting, nil) # to get @reconnect_timer initialized
|
365
|
+
@log.should_receive(:error).with("Failed test health check", StandardError).once
|
366
|
+
@http_client.should_receive(:check_health).and_raise(StandardError).once
|
367
|
+
@timer.should_receive(:interval=).with(15).once
|
368
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
369
|
+
@client.send(:reconnect).should be_true
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
it "does nothing if already reconnecting" do
|
374
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).once
|
375
|
+
@client.send(:reconnect).should be_true
|
376
|
+
@client.instance_variable_get(:@reconnecting).should be_true
|
377
|
+
@client.send(:reconnect).should be_true
|
378
|
+
@client.instance_variable_get(:@reconnecting).should be_true
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
context :make_request do
|
383
|
+
before(:each) do
|
384
|
+
@path = "/foo/bar"
|
385
|
+
@params = {:some => "data"}
|
386
|
+
@request_uuid = "random uuid"
|
387
|
+
@now = Time.now
|
388
|
+
flexmock(Time).should_receive(:now).and_return(@now).by_default
|
389
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@request_uuid)
|
390
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
391
|
+
@client.instance_variable_set(:@reconnecting, nil)
|
392
|
+
@client.init(:test, @auth_client, @options)
|
393
|
+
end
|
394
|
+
|
395
|
+
it "raises exception if terminating" do
|
396
|
+
@client.close
|
397
|
+
lambda { @client.send(:make_request, :get, @path) }.should raise_error(RightScale::Exceptions::Terminating)
|
398
|
+
end
|
399
|
+
|
400
|
+
it "generates a request UUID if none specified" do
|
401
|
+
@http_client.should_receive(:get).with(@path, @params, hsh(:request_uuid => @request_uuid)).once
|
402
|
+
@client.send(:make_request, :get, @path, @params)
|
403
|
+
end
|
404
|
+
|
405
|
+
it "raises exception if not connected" do
|
406
|
+
@client.send(:state=, :failed)
|
407
|
+
lambda { @client.send(:make_request, :get, @path) }.
|
408
|
+
should raise_error(RightScale::Exceptions::ConnectivityFailure, "test client not connected")
|
409
|
+
end
|
410
|
+
|
411
|
+
it "sets HTTP options for request" do
|
412
|
+
@http_client.should_receive(:get).with(@path, @params,
|
413
|
+
on { |a| a[:open_timeout] == 2 &&
|
414
|
+
a[:request_timeout] == 35 &&
|
415
|
+
a[:request_uuid] == "uuid" &&
|
416
|
+
a[:headers] == @auth_header }).once
|
417
|
+
@client.send(:make_request, :get, @path, @params, nil, "uuid")
|
418
|
+
end
|
419
|
+
|
420
|
+
it "overrides HTTP options with those supplied on request" do
|
421
|
+
@http_client.should_receive(:get).with(@path, @params,
|
422
|
+
on { |a| a[:open_timeout] == 2 &&
|
423
|
+
a[:request_timeout] == 20 &&
|
424
|
+
a[:request_uuid] == "uuid" &&
|
425
|
+
a[:headers] == @auth_header }).once
|
426
|
+
@client.send(:make_request, :get, @path, @params, nil, "uuid", {:request_timeout => 20})
|
427
|
+
end
|
428
|
+
|
429
|
+
it "makes request using HTTP client" do
|
430
|
+
@http_client.should_receive(:get).with(@path, @params, Hash).once
|
431
|
+
@client.send(:make_request, :get, @path, @params)
|
432
|
+
end
|
433
|
+
|
434
|
+
it "makes communicated callbacks" do
|
435
|
+
@http_client.should_receive(:get).once
|
436
|
+
called = 0
|
437
|
+
@client.communicated { called += 1 }
|
438
|
+
@client.send(:make_request, :get, @path, @params)
|
439
|
+
called.should == 1
|
440
|
+
end
|
441
|
+
|
442
|
+
context "when exception" do
|
443
|
+
it "handles any exceptions" do
|
444
|
+
@http_client.should_receive(:get).and_raise(StandardError, "test").once
|
445
|
+
flexmock(@client).should_receive(:handle_exception).with(StandardError, "type", @request_uuid, @now, 1).
|
446
|
+
and_raise(StandardError, "failed").once
|
447
|
+
lambda { @client.send(:make_request, :get, @path, @params, "type") }.should raise_error(StandardError, "failed")
|
448
|
+
end
|
449
|
+
|
450
|
+
it "uses path for request type if no request type specified" do
|
451
|
+
@http_client.should_receive(:get).and_raise(StandardError, "test").once
|
452
|
+
flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @now, 1).
|
453
|
+
and_raise(StandardError, "failed").once
|
454
|
+
lambda { @client.send(:make_request, :get, @path, @params) }.should raise_error(StandardError, "failed")
|
455
|
+
end
|
456
|
+
|
457
|
+
it "retries if exception handling does not result in raise" do
|
458
|
+
@http_client.should_receive(:get).and_raise(StandardError, "test").twice
|
459
|
+
flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @now, 1).
|
460
|
+
and_return("updated uuid").once.ordered
|
461
|
+
flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, "updated uuid", @now, 2).
|
462
|
+
and_raise(StandardError, "failed").once.ordered
|
463
|
+
lambda { @client.send(:make_request, :get, @path, @params) }.should raise_error(StandardError, "failed")
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
it "returns result of request" do
|
468
|
+
@http_client.should_receive(:get).with(@path, @params, Hash).and_return("result").once
|
469
|
+
@client.send(:make_request, :get, @path, @params).should == "result"
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
context "make_request failures" do
|
474
|
+
before(:each) do
|
475
|
+
@type = "type"
|
476
|
+
@request_uuid = "random uuid"
|
477
|
+
@now = Time.now
|
478
|
+
flexmock(Time).should_receive(:now).and_return(@now, @now + 10, @now + 20)
|
479
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@request_uuid)
|
480
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
|
481
|
+
@client.instance_variable_set(:@reconnecting, nil)
|
482
|
+
@client.init(:test, @auth_client, @options.merge(:retry_enabled => true))
|
483
|
+
end
|
484
|
+
|
485
|
+
context :handle_exception do
|
486
|
+
context "when redirect" do
|
487
|
+
[301, 302].each do |http_code|
|
488
|
+
it "handles #{http_code} redirect" do
|
489
|
+
e = RestExceptionMock.new(http_code, "redirect")
|
490
|
+
flexmock(@client).should_receive(:handle_redirect).with(e, @type, @request_uuid).once
|
491
|
+
@client.send(:handle_exception, e, @type, @request_uuid, @now, 1)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
it "raises if unauthorized" do
|
497
|
+
e = RestExceptionMock.new(401, "unauthorized")
|
498
|
+
lambda { @client.send(:handle_exception, e, @type, @request_uuid, @now, 1) }.should \
|
499
|
+
raise_error(RightScale::Exceptions::Unauthorized, "unauthorized")
|
500
|
+
end
|
501
|
+
|
502
|
+
it "notifies auth client and raises retryable if session expired" do
|
503
|
+
e = RestExceptionMock.new(403, "forbidden")
|
504
|
+
lambda { @client.send(:handle_exception, e, @type, @request_uuid, @now, 1) }.should \
|
505
|
+
raise_error(RightScale::Exceptions::RetryableError, "Authorization expired")
|
506
|
+
@auth_client.expired_called.should be_true
|
507
|
+
end
|
508
|
+
|
509
|
+
it "handles retry with and updates request_uuid to distinguish for retry" do
|
510
|
+
e = RestExceptionMock.new(449, "retry with")
|
511
|
+
flexmock(@client).should_receive(:handle_retry_with).with(e, @type, @request_uuid, @now, 1).
|
512
|
+
and_return("modified uuid").once
|
513
|
+
@client.send(:handle_exception, e, @type, @request_uuid, @now, 1).should == "modified uuid"
|
514
|
+
end
|
515
|
+
|
516
|
+
it "handles internal server error" do
|
517
|
+
e = RestExceptionMock.new(500, "test internal error")
|
518
|
+
lambda { @client.send(:handle_exception, e, @type, @request_uuid, @now, 1) }.should \
|
519
|
+
raise_error(RightScale::Exceptions::InternalServerError, "test internal error")
|
520
|
+
end
|
521
|
+
|
522
|
+
it "handles not responding" do
|
523
|
+
e = RightScale::BalancedHttpClient::NotResponding.new("not responding")
|
524
|
+
flexmock(@client).should_receive(:handle_not_responding).with(e, @type, @request_uuid, @now, 1).once
|
525
|
+
@client.send(:handle_exception, e, @type, @request_uuid, @now, 1)
|
526
|
+
end
|
527
|
+
|
528
|
+
it "causes other HTTP exceptions to be re-raised by returning nil" do
|
529
|
+
e = RestExceptionMock.new(400, "bad request")
|
530
|
+
@client.send(:handle_exception, e, @type, @request_uuid, @now, 1).should be_nil
|
531
|
+
end
|
532
|
+
|
533
|
+
it "causes other non-HTTP exceptions to be re-raised by returning nil" do
|
534
|
+
@client.send(:handle_exception, StandardError, @type, @request_uuid, @now, 1).should be_nil
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
context :handle_redirect do
|
539
|
+
it "initiates redirect by notifying auth client and raising retryable error" do
|
540
|
+
location = "http://somewhere.com"
|
541
|
+
e = RestExceptionMock.new(301, "moved permanently", {:location => location})
|
542
|
+
@log.should_receive(:info).with(/Received REDIRECT/).once.ordered
|
543
|
+
@log.should_receive(:info).with("Requesting auth client to handle redirect to #{location.inspect}").once.ordered
|
544
|
+
lambda { @client.send(:handle_redirect, e, @type, @request_uuid) }.should \
|
545
|
+
raise_error(RightScale::Exceptions::RetryableError, "moved permanently")
|
546
|
+
@auth_client.redirect_location.should == location
|
547
|
+
end
|
548
|
+
|
549
|
+
it "raises internal error if no redirect location is provided" do
|
550
|
+
e = RestExceptionMock.new(301, "moved permanently")
|
551
|
+
@log.should_receive(:info).with(/Received REDIRECT/).once
|
552
|
+
lambda { @client.send(:handle_redirect, e, @type, @request_uuid) }.should \
|
553
|
+
raise_error(RightScale::Exceptions::InternalServerError, "No redirect location provided")
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
context :handle_retry_with do
|
558
|
+
before(:each) do
|
559
|
+
@exception = RestExceptionMock.new(449, "retry with")
|
560
|
+
end
|
561
|
+
|
562
|
+
it "sleeps for configured interval and does not raise if retry still viable" do
|
563
|
+
@log.should_receive(:error).with(/Retrying type request/).once
|
564
|
+
flexmock(@client).should_receive(:sleep).with(4).once
|
565
|
+
@client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1)
|
566
|
+
end
|
567
|
+
|
568
|
+
it "returns modified request_uuid" do
|
569
|
+
@log.should_receive(:error)
|
570
|
+
flexmock(@client).should_receive(:sleep)
|
571
|
+
@client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1).should == "#{@request_uuid}:retry"
|
572
|
+
end
|
573
|
+
|
574
|
+
it "does not retry more than once" do
|
575
|
+
lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 2) }.should \
|
576
|
+
raise_error(RightScale::Exceptions::RetryableError)
|
577
|
+
end
|
578
|
+
|
579
|
+
it "raises retryable error if retry timed out" do
|
580
|
+
@client.init(:test, @auth_client, @options.merge(:retry_enabled => true, :retry_timeout => 10))
|
581
|
+
lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1) }.should \
|
582
|
+
raise_error(RightScale::Exceptions::RetryableError)
|
583
|
+
end
|
584
|
+
|
585
|
+
it "raises retryable error if retry disabled" do
|
586
|
+
@client.init(:test, @auth_client, @options.merge(:retry_enabled => false))
|
587
|
+
lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1) }.should \
|
588
|
+
raise_error(RightScale::Exceptions::RetryableError)
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
context :handle_not_responding do
|
593
|
+
before(:each) do
|
594
|
+
@exception = RightScale::BalancedHttpClient::NotResponding.new("Server not responding")
|
595
|
+
end
|
596
|
+
|
597
|
+
it "sleeps for configured interval and does not raise if retry still viable" do
|
598
|
+
@log.should_receive(:error).with(/Retrying type request/).once
|
599
|
+
flexmock(@client).should_receive(:sleep).with(4).once
|
600
|
+
@client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 1)
|
601
|
+
end
|
602
|
+
|
603
|
+
it "changes sleep interval for successive retries" do
|
604
|
+
@log.should_receive(:error).with(/Retrying type request/).once
|
605
|
+
flexmock(@client).should_receive(:sleep).with(12).once
|
606
|
+
@client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 2)
|
607
|
+
end
|
608
|
+
|
609
|
+
it "does not retry more than configured number of retry intervals" do
|
610
|
+
lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 4) }.should \
|
611
|
+
raise_error(RightScale::Exceptions::ConnectivityFailure, "Server not responding after 4 attempts")
|
612
|
+
end
|
613
|
+
|
614
|
+
it "sets state to :disconnected and raises connectivity error if retry timed out" do
|
615
|
+
@client.init(:test, @auth_client, @options.merge(:retry_enabled => true, :retry_timeout => 10))
|
616
|
+
# Need to shut off reconnect, otherwise since timers are always yielding,
|
617
|
+
# setting state to :disconnected sets it to :connected
|
618
|
+
flexmock(@client).should_receive(:reconnect).once
|
619
|
+
lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 3) }.should \
|
620
|
+
raise_error(RightScale::Exceptions::ConnectivityFailure, "Server not responding after 3 attempts")
|
621
|
+
@client.state.should == :disconnected
|
622
|
+
end
|
623
|
+
|
624
|
+
it "sets state to :disconnected and raises connectivity error if retry disabled" do
|
625
|
+
@client.init(:test, @auth_client, @options.merge(:retry_enabled => false))
|
626
|
+
# Need to shut off reconnect, otherwise since timers are always yielding,
|
627
|
+
# setting state to :disconnected sets it to :connected
|
628
|
+
flexmock(@client).should_receive(:reconnect).once
|
629
|
+
lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 1) }.should \
|
630
|
+
raise_error(RightScale::Exceptions::ConnectivityFailure, "Server not responding")
|
631
|
+
@client.state.should == :disconnected
|
632
|
+
end
|
633
|
+
end
|
634
|
+
end
|
635
|
+
end
|