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,594 @@
|
|
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', 'router_client'))
|
26
|
+
|
27
|
+
describe RightScale::RouterClient 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
|
+
@timer = flexmock("timer", :cancel => true, :interval= => 0).by_default
|
36
|
+
flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).by_default
|
37
|
+
@http_client = flexmock("http client", :get => true, :check_health => true).by_default
|
38
|
+
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).by_default
|
39
|
+
@websocket = WebSocketClientMock.new
|
40
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket).by_default
|
41
|
+
@auth_header = {"Authorization" => "Bearer <session>"}
|
42
|
+
@url = "http://test.com"
|
43
|
+
@ws_url = "ws://test.com"
|
44
|
+
@auth_client = AuthClientMock.new(@url, @auth_header, :authorized)
|
45
|
+
@routing_keys = nil
|
46
|
+
@options = {}
|
47
|
+
@client = RightScale::RouterClient.new(@auth_client, @options)
|
48
|
+
@version = RightScale::AgentConfig.protocol_version
|
49
|
+
@event = {:uuid => "uuid", :type => "Push", :path => "/foo/bar", :from => "rs-agent-1-1", :data => {}, :version => @version}
|
50
|
+
end
|
51
|
+
|
52
|
+
context :initialize do
|
53
|
+
it "initializes options" do
|
54
|
+
@options = {
|
55
|
+
:open_timeout => 1,
|
56
|
+
:request_timeout => 2,
|
57
|
+
:listen_timeout => 3,
|
58
|
+
:retry_timeout => 4,
|
59
|
+
:retry_intervals => [1, 2, 3],
|
60
|
+
:reconnect_interval => 5 }
|
61
|
+
@client = RightScale::RouterClient.new(@auth_client, @options)
|
62
|
+
options = @client.instance_variable_get(:@options)
|
63
|
+
options[:server_name] = "RightNet"
|
64
|
+
options[:api_version] = "2.0"
|
65
|
+
options[:open_timeout] = 1
|
66
|
+
options[:request_timeout] = 2
|
67
|
+
options[:listen_timeout] = 3
|
68
|
+
options[:retry_timeout] = 4
|
69
|
+
options[:retry_intervals] = [1, 2, 3]
|
70
|
+
options[:reconnect_interval] = 5
|
71
|
+
end
|
72
|
+
|
73
|
+
it "initializes options to defaults if no value specified" do
|
74
|
+
options = @client.instance_variable_get(:@options)
|
75
|
+
options[:listen_timeout].should == 60
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "requests" do
|
80
|
+
|
81
|
+
before(:each) do
|
82
|
+
@type = "/foo/bar"
|
83
|
+
@action = "bar"
|
84
|
+
@payload = {:some => "data"}
|
85
|
+
@target = "rs-agent-2-2"
|
86
|
+
@token = "random token"
|
87
|
+
@params = {
|
88
|
+
:type => @type,
|
89
|
+
:payload => @payload,
|
90
|
+
:target => @target }
|
91
|
+
end
|
92
|
+
|
93
|
+
context :push do
|
94
|
+
it "makes post request to router" do
|
95
|
+
flexmock(@client).should_receive(:make_request).with(:post, "/push", @params, @action, @token).
|
96
|
+
and_return(nil).once
|
97
|
+
@client.push(@type, @payload, @target, @token).should be_nil
|
98
|
+
end
|
99
|
+
|
100
|
+
it "does not require token" do
|
101
|
+
flexmock(@client).should_receive(:make_request).with(:post, "/push", @params, @action, nil).
|
102
|
+
and_return(nil).once
|
103
|
+
@client.push(@type, @payload, @target).should be_nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context :request do
|
108
|
+
it "makes post request to router" do
|
109
|
+
flexmock(@client).should_receive(:make_request).with(:post, "/request", @params, @action, @token).
|
110
|
+
and_return(nil).once
|
111
|
+
@client.request(@type, @payload, @target, @token)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "does not require token" do
|
115
|
+
flexmock(@client).should_receive(:make_request).with(:post, "/request", @params, @action, nil).
|
116
|
+
and_return(nil).once
|
117
|
+
@client.request(@type, @payload, @target).should be_nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "events" do
|
123
|
+
|
124
|
+
before(:each) do
|
125
|
+
@later = Time.at(@now = Time.now)
|
126
|
+
@tick = 30
|
127
|
+
flexmock(Time).should_receive(:now).and_return { @later += @tick }
|
128
|
+
end
|
129
|
+
|
130
|
+
context :notify do
|
131
|
+
before(:each) do
|
132
|
+
@routing_keys = ["key"]
|
133
|
+
@params = {
|
134
|
+
:event => @event,
|
135
|
+
:routing_keys => @routing_keys }
|
136
|
+
end
|
137
|
+
|
138
|
+
it "sends using websocket if available" do
|
139
|
+
@client.send(:connect, @routing_keys) { |_| }
|
140
|
+
@client.notify(@event, @routing_keys).should be_true
|
141
|
+
@websocket.sent.should == JSON.dump(@params)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "makes post request by default" do
|
145
|
+
flexmock(@client).should_receive(:make_request).with(:post, "/notify", @params, "notify", "uuid",
|
146
|
+
{:filter_params => ["event"]}).once
|
147
|
+
@client.notify(@event, @routing_keys).should be_true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context :listen do
|
152
|
+
it "raises if block missing" do
|
153
|
+
lambda { @client.listen(@routing_keys) }.should raise_error(ArgumentError, "Block missing")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "loops forever until closed" do
|
157
|
+
@client.close
|
158
|
+
@client.listen(@routing_keys) { |_| }.should be_true
|
159
|
+
end
|
160
|
+
|
161
|
+
it "loops forever until closing" do
|
162
|
+
@client.close(:receive)
|
163
|
+
@client.listen(@routing_keys) { |_| }.should be_true
|
164
|
+
end
|
165
|
+
|
166
|
+
it "sleeps if websocket already exists" do
|
167
|
+
@client.send(:connect, @routing_keys) { |_| }
|
168
|
+
flexmock(@client).should_receive(:sleep).with(5).and_return { @client.close }.once
|
169
|
+
@client.listen(@routing_keys) { |_| }
|
170
|
+
@client.instance_variable_get(:@connect_interval).should == 30
|
171
|
+
end
|
172
|
+
|
173
|
+
it "tries to create websocket if time to" do
|
174
|
+
@client.instance_variable_get(:@websocket).should be_nil
|
175
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket).once
|
176
|
+
flexmock(@client).should_receive(:sleep).and_return { @client.close }
|
177
|
+
@client.listen(@routing_keys) { |_| }
|
178
|
+
end
|
179
|
+
|
180
|
+
it "does not try long-polling if websocket connect attempt indicates not to" do
|
181
|
+
@client.instance_variable_get(:@websocket).should be_nil
|
182
|
+
flexmock(@client).should_receive(:try_connect).and_return { @client.close; true }
|
183
|
+
flexmock(@client).should_receive(:try_long_poll).never
|
184
|
+
@client.listen(@routing_keys) { |_| }
|
185
|
+
end
|
186
|
+
|
187
|
+
it "tries long-polling if could not create websocket" do
|
188
|
+
@client.instance_variable_get(:@websocket).should be_nil
|
189
|
+
flexmock(@client).should_receive(:retry_connect?).and_return(false)
|
190
|
+
flexmock(@client).should_receive(:long_poll).and_return { @client.close; ["uuid"] }.once
|
191
|
+
@client.listen(@routing_keys) { |_| }.should be_true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context :close do
|
196
|
+
it "closes websocket" do
|
197
|
+
@client.send(:connect, nil) { |_| }
|
198
|
+
@client.close
|
199
|
+
@websocket.closed.should be_true
|
200
|
+
@websocket.code.should == 1001
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context :retry_connect? do
|
205
|
+
before(:each) do
|
206
|
+
@client.instance_variable_set(:@last_connect_time, @now)
|
207
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "requires websocket to be enabled" do
|
211
|
+
@client = RightScale::RouterClient.new(@auth_client, :long_polling_only => true)
|
212
|
+
@client.send(:retry_connect?).should be_false
|
213
|
+
end
|
214
|
+
|
215
|
+
it "requires there be no existing websocket connection" do
|
216
|
+
@client.instance_variable_set(:@websocket, @websocket)
|
217
|
+
@client.send(:retry_connect?).should be_false
|
218
|
+
end
|
219
|
+
|
220
|
+
context "when no existing websocket" do
|
221
|
+
before(:each) do
|
222
|
+
@client.instance_variable_get(:@websocket).should be_nil
|
223
|
+
end
|
224
|
+
|
225
|
+
it "allows retry if enough time has elapsed" do
|
226
|
+
@tick = 1
|
227
|
+
@client.instance_variable_set(:@last_connect_time, @now - 29)
|
228
|
+
@client.send(:retry_connect?).should be_false
|
229
|
+
@client.instance_variable_set(:@last_connect_time, @now - 30)
|
230
|
+
@client.send(:retry_connect?).should be_true
|
231
|
+
end
|
232
|
+
|
233
|
+
[RightScale::RouterClient::NORMAL_CLOSE, RightScale::RouterClient::SHUTDOWN_CLOSE].each do |code|
|
234
|
+
it "allows retry if previous close code is #{code}" do
|
235
|
+
@client.instance_variable_set(:@close_code, code)
|
236
|
+
@client.instance_variable_set(:@connect_interval, 300)
|
237
|
+
@client.send(:retry_connect?).should be_true
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
[502, 503].each do |code|
|
242
|
+
it "allows retry if previous close has reason with code #{code} indicating router inaccessible" do
|
243
|
+
@client.instance_variable_set(:@close_code, RightScale::RouterClient::PROTOCOL_ERROR_CLOSE)
|
244
|
+
@client.instance_variable_set(:@close_reason, "Unexpected response code: #{code}")
|
245
|
+
@client.instance_variable_set(:@connect_interval, 300)
|
246
|
+
@client.send(:retry_connect?).should be_true
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
context :try_connect do
|
253
|
+
before(:each) do
|
254
|
+
@handler = lambda { |_| }
|
255
|
+
@client.instance_variable_get(:@websocket).should be_nil
|
256
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
257
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
258
|
+
end
|
259
|
+
|
260
|
+
it "makes websocket connect request" do
|
261
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket).once
|
262
|
+
flexmock(@client).should_receive(:sleep).and_return { @client.close }
|
263
|
+
@client.send(:try_connect, @routing_keys, &@handler)
|
264
|
+
end
|
265
|
+
|
266
|
+
it "periodically checks whether websocket creation was really successful" do
|
267
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket)
|
268
|
+
flexmock(@client).should_receive(:sleep).with(1).times(3).ordered
|
269
|
+
flexmock(@client).should_receive(:sleep).and_return { @client.instance_variable_set(:@websocket, nil) }.once.ordered
|
270
|
+
@client.send(:try_connect, @routing_keys, &@handler)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "sleeps if websocket creation failed because router not responding" do
|
274
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket)
|
275
|
+
flexmock(@client).should_receive(:sleep).with(1).and_return do
|
276
|
+
@client.instance_variable_set(:@websocket, nil)
|
277
|
+
@client.instance_variable_set(:@close_code, RightScale::RouterClient::PROTOCOL_ERROR_CLOSE)
|
278
|
+
@client.instance_variable_set(:@close_reason, "Unexpected response code: 502")
|
279
|
+
end.once
|
280
|
+
flexmock(@client).should_receive(:sleep).with(4).and_return { @client.close }.once
|
281
|
+
@client.send(:try_connect, @routing_keys, &@handler)
|
282
|
+
@client.instance_variable_get(:@reconnect_interval).should == 4
|
283
|
+
@client.instance_variable_get(:@connect_interval).should == 30
|
284
|
+
end
|
285
|
+
|
286
|
+
it "adjusts connect interval if websocket creation was unsuccessful" do
|
287
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket)
|
288
|
+
flexmock(@client).should_receive(:sleep).and_return { @client.instance_variable_set(:@websocket, nil); @client.close }.once
|
289
|
+
@client.send(:try_connect, @routing_keys, &@handler)
|
290
|
+
@client.instance_variable_get(:@connect_interval).should == 60
|
291
|
+
@client.instance_variable_get(:@reconnect_interval).should == 2
|
292
|
+
end
|
293
|
+
|
294
|
+
it "loops instead of long-polling if websocket creation was unsuccessful" do
|
295
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket)
|
296
|
+
flexmock(@client).should_receive(:sleep).and_return { @client.instance_variable_set(:@websocket, nil); @client.close }
|
297
|
+
flexmock(@client).should_receive(:long_poll).never
|
298
|
+
@client.send(:try_connect, @routing_keys, &@handler)
|
299
|
+
end
|
300
|
+
|
301
|
+
it "sleeps after successfully creating websocket" do
|
302
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket)
|
303
|
+
flexmock(@client).should_receive(:sleep).with(1).times(4).ordered
|
304
|
+
flexmock(@client).should_receive(:sleep).with(1).and_return { @client.close }.once.ordered
|
305
|
+
@client.send(:try_connect, @routing_keys, &@handler)
|
306
|
+
end
|
307
|
+
|
308
|
+
it "adjusts connect interval if websocket creation fails" do
|
309
|
+
@log.should_receive(:error).with("Failed creating WebSocket", StandardError).once
|
310
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_raise(StandardError).once
|
311
|
+
flexmock(@client).should_receive(:sleep).never
|
312
|
+
@client.send(:try_connect, @routing_keys, &@handler)
|
313
|
+
@client.instance_variable_get(:@connect_interval).should == 60
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context :connect do
|
318
|
+
it "raises if block missing" do
|
319
|
+
lambda { @client.send(:connect, @routing_keys) }.should raise_error(ArgumentError, "Block missing")
|
320
|
+
end
|
321
|
+
|
322
|
+
context "when creating connection" do
|
323
|
+
it "connects to router" do
|
324
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).with(@ws_url + "/connect", nil, Hash).and_return(@websocket).once
|
325
|
+
@client.send(:connect, @routing_keys) { |_| }
|
326
|
+
end
|
327
|
+
|
328
|
+
it "chooses scheme based on scheme in router URL" do
|
329
|
+
@url = "https://test.com"
|
330
|
+
@ws_url = "wss://test.com"
|
331
|
+
@auth_client = AuthClientMock.new(@url, @auth_header, :authorized)
|
332
|
+
@client = RightScale::RouterClient.new(@auth_client, @options)
|
333
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).with(@ws_url + "/connect", nil, Hash).and_return(@websocket).once
|
334
|
+
@client.send(:connect, @routing_keys) { |_| }
|
335
|
+
end
|
336
|
+
|
337
|
+
it "uses headers containing only API version and authorization" do
|
338
|
+
headers = @auth_header.merge("X-API-Version" => "2.0")
|
339
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).with(String, nil, hsh(:headers => headers)).and_return(@websocket).once
|
340
|
+
@client.send(:connect, @routing_keys) { |_| }
|
341
|
+
end
|
342
|
+
|
343
|
+
it "enables ping" do
|
344
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).with(String, nil, hsh(:ping => 60)).and_return(@websocket).once
|
345
|
+
@client.send(:connect, @routing_keys) { |_| }
|
346
|
+
end
|
347
|
+
|
348
|
+
it "adds routing keys as query parameters" do
|
349
|
+
url = @ws_url + "/connect" + "?routing_keys[]=a%3Ab%3Dc"
|
350
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).with(url, nil, Hash).and_return(@websocket).once
|
351
|
+
@client.send(:connect, ["a:b=c"]) { |_| }
|
352
|
+
end
|
353
|
+
|
354
|
+
it "returns websocket" do
|
355
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket).once
|
356
|
+
@client.send(:connect, @routing_keys) { |_| }.should == @websocket
|
357
|
+
@client.instance_variable_get(:@websocket).should == @websocket
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
context "when message received" do
|
362
|
+
before(:each) do
|
363
|
+
@json_event = JSON.dump(@event)
|
364
|
+
@json_ack = JSON.dump({:ack => "uuid"})
|
365
|
+
end
|
366
|
+
|
367
|
+
it "presents JSON-decoded event to the specified handler" do
|
368
|
+
event = nil
|
369
|
+
@client.send(:connect, @routing_keys) { |e| event = e }
|
370
|
+
@websocket.onmessage(@json_event)
|
371
|
+
event.should == @event
|
372
|
+
end
|
373
|
+
|
374
|
+
it "logs event" do
|
375
|
+
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
376
|
+
@log.should_receive(:info).with("Received EVENT <uuid> Push /foo/bar from rs-agent-1-1").once.ordered
|
377
|
+
@log.should_receive(:info).with("Sending EVENT <uuid> Push /foo/bar to rs-agent-1-1").once.ordered
|
378
|
+
event = nil
|
379
|
+
@client.send(:connect, @routing_keys) { |e| event = e }
|
380
|
+
@websocket.onmessage(@json_event)
|
381
|
+
event.should == @event
|
382
|
+
end
|
383
|
+
|
384
|
+
it "acknowledges event" do
|
385
|
+
@client.send(:connect, @routing_keys) { |_| nil }
|
386
|
+
@websocket.onmessage(@json_event)
|
387
|
+
@websocket.sent.should == @json_ack
|
388
|
+
end
|
389
|
+
|
390
|
+
it "sends event response using websocket" do
|
391
|
+
result = {:uuid => "uuid2", :type => "Result", :from => "rs-agent-2-2", :data => {}, :version => @version}
|
392
|
+
@client.send(:connect, @routing_keys) { |_| result }
|
393
|
+
@websocket.onmessage(@json_event)
|
394
|
+
@websocket.sent.should == [@json_ack, JSON.dump({:event => result, :routing_keys => ["rs-agent-1-1"]})]
|
395
|
+
end
|
396
|
+
|
397
|
+
it "only sends non-nil responses" do
|
398
|
+
@client.send(:connect, @routing_keys) { |_| nil }
|
399
|
+
@websocket.onmessage(@json_event)
|
400
|
+
@websocket.sent.should == @json_ack
|
401
|
+
end
|
402
|
+
|
403
|
+
it "logs failures" do
|
404
|
+
@log.should_receive(:error).with("Failed handling WebSocket event", StandardError, :trace).once
|
405
|
+
request = RightScale::Request.new("/foo/bar", "payload")
|
406
|
+
@client.send(:connect, @routing_keys) { |_| raise StandardError, "bad event" }
|
407
|
+
@websocket.onmessage(JSON.dump(request))
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
context "on close" do
|
412
|
+
it "logs info" do
|
413
|
+
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
414
|
+
@log.should_receive(:info).with("WebSocket closed (1000)").once.ordered
|
415
|
+
@client.send(:connect, @routing_keys) { |_| }
|
416
|
+
@websocket.onclose(1000)
|
417
|
+
@client.instance_variable_get(:@websocket).should be_nil
|
418
|
+
end
|
419
|
+
|
420
|
+
it "logged info includes reason if available" do
|
421
|
+
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
422
|
+
@log.should_receive(:info).with("WebSocket closed (1001: Going Away)").once.ordered
|
423
|
+
@client.send(:connect, @routing_keys) { |_| }
|
424
|
+
@websocket.onclose(1001, "Going Away")
|
425
|
+
end
|
426
|
+
|
427
|
+
it "logs unexpected exceptions" do
|
428
|
+
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
429
|
+
@log.should_receive(:info).and_raise(RuntimeError).once.ordered
|
430
|
+
@log.should_receive(:error).with("Failed closing WebSocket", RuntimeError, :trace).once
|
431
|
+
@client.send(:connect, @routing_keys) { |_| }
|
432
|
+
@websocket.onclose(1000)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
context "on error" do
|
437
|
+
it "logs error" do
|
438
|
+
@log.should_receive(:error).with("WebSocket error (Protocol Error)")
|
439
|
+
@client.send(:connect, @routing_keys) { |_| }
|
440
|
+
@websocket.onerror("Protocol Error")
|
441
|
+
end
|
442
|
+
|
443
|
+
it "does not log if there is no error data" do
|
444
|
+
@log.should_receive(:error).never
|
445
|
+
@client.send(:connect, @routing_keys) { |_| }
|
446
|
+
@websocket.onerror(nil)
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
context :try_long_poll do
|
452
|
+
before(:each) do
|
453
|
+
@uuids = ["uuid"]
|
454
|
+
@handler = lambda { |_| }
|
455
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
456
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
457
|
+
end
|
458
|
+
|
459
|
+
it "makes long-polling request" do
|
460
|
+
flexmock(@client).should_receive(:long_poll).with(@routing_keys, @uuids, @handler).and_return([]).once
|
461
|
+
@client.send(:try_long_poll, @routing_keys, @uuids, &@handler).should == []
|
462
|
+
end
|
463
|
+
|
464
|
+
it "returns UUIDs of events received" do
|
465
|
+
flexmock(@client).should_receive(:long_poll).with(@routing_keys, [], @handler).and_return { @uuids }.once
|
466
|
+
@client.send(:try_long_poll, @routing_keys, [], &@handler).should == @uuids
|
467
|
+
end
|
468
|
+
|
469
|
+
it "sleeps if there is a long-polling failure" do
|
470
|
+
@log.should_receive(:error).with("Failed long-polling", StandardError, :trace).once
|
471
|
+
flexmock(@client).should_receive(:long_poll).and_raise(StandardError).once
|
472
|
+
flexmock(@client).should_receive(:sleep).with(4).once
|
473
|
+
@client.send(:try_long_poll, @routing_keys, @uuids, &@handler).should be_nil
|
474
|
+
end
|
475
|
+
|
476
|
+
[RightScale::Exceptions::Unauthorized,
|
477
|
+
RightScale::Exceptions::ConnectivityFailure,
|
478
|
+
RightScale::Exceptions::RetryableError].each do |e|
|
479
|
+
it "does not trace #{e} exceptions" do
|
480
|
+
@log.should_receive(:error).with("Failed long-polling", e, :no_trace).once
|
481
|
+
flexmock(@client).should_receive(:long_poll).and_raise(e, "failed").once
|
482
|
+
flexmock(@client).should_receive(:sleep).with(4).once
|
483
|
+
@client.send(:try_long_poll, @routing_keys, @uuids, &@handler).should be_nil
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
context :long_poll do
|
489
|
+
before(:each) do
|
490
|
+
@ack = []
|
491
|
+
end
|
492
|
+
|
493
|
+
it "raises if block missing" do
|
494
|
+
lambda { @client.send(:long_poll, @routing_keys, @ack) }.should raise_error(ArgumentError, "Block missing")
|
495
|
+
end
|
496
|
+
|
497
|
+
it "makes listen request to router" do
|
498
|
+
flexmock(@client).should_receive(:make_request).with(:get, "/listen",
|
499
|
+
on { |a| a[:wait_time].should == 55 && !a.key?(:routing_keys) &&
|
500
|
+
a[:timestamp] == @later.to_f }, "listen", nil, Hash).and_return([@event]).once
|
501
|
+
@client.send(:long_poll, @routing_keys, @ack) { |_| }
|
502
|
+
end
|
503
|
+
|
504
|
+
it "uses listen timeout for request" do
|
505
|
+
flexmock(@client).should_receive(:make_request).with(:get, "/listen", Hash, "listen", nil,
|
506
|
+
{:request_timeout => 60, :log_level => :debug}).and_return([@event]).once
|
507
|
+
@client.send(:long_poll, @routing_keys, @ack) { |_| }
|
508
|
+
end
|
509
|
+
|
510
|
+
it "logs event" do
|
511
|
+
@log.should_receive(:info).with("Received EVENT <uuid> Push /foo/bar from rs-agent-1-1").once
|
512
|
+
flexmock(@client).should_receive(:make_request).and_return([@event])
|
513
|
+
@client.send(:long_poll, @routing_keys, @ack) { |_| }
|
514
|
+
end
|
515
|
+
|
516
|
+
it "presents event to handler" do
|
517
|
+
flexmock(@client).should_receive(:make_request).and_return([@event])
|
518
|
+
event = nil
|
519
|
+
@client.send(:long_poll, @routing_keys, @ack) { |e| event = e }
|
520
|
+
event.should == @event
|
521
|
+
end
|
522
|
+
|
523
|
+
it "handles event keys that are strings" do
|
524
|
+
event = {"uuid" => "uuid", "type" => "Push", "path" => "/foo/bar", "from" => "rs-agent-1-1", "data" => {}, "version" => @version}
|
525
|
+
@log.should_receive(:info).with("Received EVENT <uuid> Push /foo/bar from rs-agent-1-1").once
|
526
|
+
flexmock(@client).should_receive(:make_request).and_return([event])
|
527
|
+
event = nil
|
528
|
+
@client.send(:long_poll, @routing_keys, @ack) { |e| event = e }
|
529
|
+
event.should == @event
|
530
|
+
end
|
531
|
+
|
532
|
+
it "does nothing if no events are returned" do
|
533
|
+
flexmock(@client).should_receive(:make_request).and_return(nil)
|
534
|
+
event = nil
|
535
|
+
@client.send(:long_poll, @routing_keys, @ack) { |e| event = e }
|
536
|
+
event.should be_nil
|
537
|
+
end
|
538
|
+
|
539
|
+
it "returns event UUIDs" do
|
540
|
+
flexmock(@client).should_receive(:make_request).and_return([@event])
|
541
|
+
@client.send(:long_poll, @routing_keys, @ack) { |_| }.should == ["uuid"]
|
542
|
+
end
|
543
|
+
|
544
|
+
it "returns nil if no events are received" do
|
545
|
+
flexmock(@client).should_receive(:make_request).and_return(nil)
|
546
|
+
@client.send(:long_poll, @routing_keys, @ack) { |_| }.should be_nil
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
context :backoff_connect_interval do
|
551
|
+
it "backs off exponentially" do
|
552
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
553
|
+
@client.send(:backoff_connect_interval).should == 60
|
554
|
+
@client.send(:backoff_connect_interval).should == 120
|
555
|
+
end
|
556
|
+
|
557
|
+
it "limits backoff" do
|
558
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
559
|
+
12.times { @client.send(:backoff_connect_interval) }
|
560
|
+
@client.instance_variable_get(:@connect_interval).should == RightScale::RouterClient::MAX_CONNECT_INTERVAL
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
context :backoff_reconnect_interval do
|
565
|
+
it "backs off exponentially" do
|
566
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
567
|
+
@client.send(:backoff_reconnect_interval).should == 4
|
568
|
+
@client.send(:backoff_reconnect_interval).should == 8
|
569
|
+
end
|
570
|
+
|
571
|
+
it "limits backoff" do
|
572
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
573
|
+
6.times { @client.send(:backoff_reconnect_interval) }
|
574
|
+
@client.instance_variable_get(:@reconnect_interval).should == RightScale::RouterClient::MAX_RECONNECT_INTERVAL
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
context :router_not_responding? do
|
579
|
+
[502, 503].each do |code|
|
580
|
+
it "declares not responding if have close reason code #{code} indicating router inaccessible" do
|
581
|
+
@client.instance_variable_set(:@close_code, RightScale::RouterClient::PROTOCOL_ERROR_CLOSE)
|
582
|
+
@client.instance_variable_set(:@close_reason, "Unexpected response code: #{code}")
|
583
|
+
@client.send(:router_not_responding?).should be_true
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
it "does not declare not responding for other close codes" do
|
588
|
+
@client.instance_variable_set(:@close_code, RightScale::RouterClient::UNEXPECTED_ERROR_CLOSE)
|
589
|
+
@client.instance_variable_set(:@close_reason, "Unexpected response code: 502")
|
590
|
+
@client.send(:router_not_responding?).should be_false
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|
594
|
+
end
|