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,49 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 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 File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::Certificate do
|
26
|
+
|
27
|
+
include RightScale::SpecHelper
|
28
|
+
|
29
|
+
before(:all) do
|
30
|
+
@certificate, key = issue_cert
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should save' do
|
34
|
+
filename = File.join(File.dirname(__FILE__), "cert.pem")
|
35
|
+
@certificate.save(filename)
|
36
|
+
File.size(filename).should be > 0
|
37
|
+
File.delete(filename)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should load' do
|
41
|
+
filename = File.join(File.dirname(__FILE__), "cert.pem")
|
42
|
+
@certificate.save(filename)
|
43
|
+
cert = RightScale::Certificate.load(filename)
|
44
|
+
File.delete(filename)
|
45
|
+
cert.should_not be_nil
|
46
|
+
cert.data.should == @certificate.data
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 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 File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::DistinguishedName do
|
26
|
+
|
27
|
+
before(:all) do
|
28
|
+
test_dn = { 'C' => 'US',
|
29
|
+
'ST' => 'California',
|
30
|
+
'L' => 'Santa Barbara',
|
31
|
+
'O' => 'RightScale',
|
32
|
+
'OU' => 'Certification Services',
|
33
|
+
'CN' => 'rightscale.com/emailAddress=cert@rightscale.com' }
|
34
|
+
@dn = RightScale::DistinguishedName.new(test_dn)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should convert to string and X509 DN' do
|
38
|
+
@dn.to_s.should_not be_nil
|
39
|
+
@dn.to_x509.should_not be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should correctly encode' do
|
43
|
+
@dn.to_s.should == @dn.to_x509.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 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 File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::EncryptedDocument do
|
26
|
+
|
27
|
+
include RightScale::SpecHelper
|
28
|
+
|
29
|
+
before(:all) do
|
30
|
+
@test_data = "Test Data to Sign"
|
31
|
+
@cert, @key = issue_cert
|
32
|
+
@doc = RightScale::EncryptedDocument.new(@test_data, @cert)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should create encrypted data' do
|
36
|
+
@doc.encrypted_data.should_not be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should create encrypted data using either PEM or DER format' do
|
40
|
+
@doc.encrypted_data(:pem).should_not be_nil
|
41
|
+
@doc.encrypted_data(:der).should_not be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should decrypt correctly' do
|
45
|
+
@doc.decrypted_data(@key, @cert).should == @test_data
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should load correctly with data in either PEM or DER format' do
|
49
|
+
@doc = RightScale::EncryptedDocument.from_data(@doc.encrypted_data(:pem))
|
50
|
+
@doc.decrypted_data(@key, @cert).should == @test_data
|
51
|
+
@doc = RightScale::EncryptedDocument.from_data(@doc.encrypted_data(:der))
|
52
|
+
@doc.decrypted_data(@key, @cert).should == @test_data
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 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 File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::RsaKeyPair do
|
26
|
+
|
27
|
+
before(:all) do
|
28
|
+
@pair = RightScale::RsaKeyPair.new
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should create a private and a public keys' do
|
32
|
+
@pair.has_private?.should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should strip out private key in to_public' do
|
36
|
+
@pair.to_public.has_private?.should be_false
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should save' do
|
40
|
+
filename = File.join(File.dirname(__FILE__), "key.pem")
|
41
|
+
@pair.save(filename)
|
42
|
+
File.size(filename).should be > 0
|
43
|
+
File.delete(filename)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should load' do
|
47
|
+
filename = File.join(File.dirname(__FILE__), "key.pem")
|
48
|
+
@pair.save(filename)
|
49
|
+
key = RightScale::RsaKeyPair.load(filename)
|
50
|
+
File.delete(filename)
|
51
|
+
key.should_not be_nil
|
52
|
+
key.data.should == @pair.data
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 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 File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::Signature do
|
26
|
+
|
27
|
+
include RightScale::SpecHelper
|
28
|
+
|
29
|
+
before(:all) do
|
30
|
+
@test_data = "Test Data"
|
31
|
+
@cert, @key = issue_cert
|
32
|
+
@sig = RightScale::Signature.new(@test_data, @cert, @key)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should create signed data' do
|
36
|
+
@sig.to_s.should_not be_empty
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should create signed data using either PEM or DER format' do
|
40
|
+
@sig.data(:pem).should_not be_empty
|
41
|
+
@sig.data(:der).should_not be_empty
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should verify the signature' do
|
45
|
+
cert2, key2 = issue_cert
|
46
|
+
|
47
|
+
@sig.should be_a_match(@cert)
|
48
|
+
@sig.should_not be_a_match(cert2)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should load from serialized signature' do
|
52
|
+
sig2 = RightScale::Signature.from_data(@sig.data)
|
53
|
+
sig2.should_not be_nil
|
54
|
+
sig2.should be_a_match(@cert)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should load from serialized signature using either PEM or DER format' do
|
58
|
+
sig2 = RightScale::Signature.from_data(@sig.data(:pem))
|
59
|
+
sig2.should_not be_nil
|
60
|
+
sig2.should be_a_match(@cert)
|
61
|
+
sig2 = RightScale::Signature.from_data(@sig.data(:der))
|
62
|
+
sig2.should_not be_nil
|
63
|
+
sig2.should be_a_match(@cert)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-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
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::StaticCertificateStore do
|
26
|
+
|
27
|
+
include RightScale::SpecHelper
|
28
|
+
|
29
|
+
before(:all) do
|
30
|
+
@signer, key = issue_cert
|
31
|
+
@target, key = issue_cert
|
32
|
+
@cert, @key = issue_cert
|
33
|
+
@store = RightScale::StaticCertificateStore.new(@cert, @key, @signer, @target)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should not raise when passed nil objects' do
|
37
|
+
res = nil
|
38
|
+
lambda { res = @store.get_signer(nil) }.should_not raise_error
|
39
|
+
res.should == [ @signer ]
|
40
|
+
lambda { res = @store.get_target(nil) }.should_not raise_error
|
41
|
+
res.should == [ @target ]
|
42
|
+
lambda { res = @store.get_receiver(nil) }.should_not raise_error
|
43
|
+
res.should == [ @cert, @key ]
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return signer certificates' do
|
47
|
+
@store.get_signer('anything').should == [ @signer ]
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return target certificates' do
|
51
|
+
@store.get_target('anything').should == [ @target ]
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return certificate and key for decrypting' do
|
55
|
+
@store.get_receiver('anything').should == [ @cert, @key ]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/spec/sender_spec.rb
ADDED
@@ -0,0 +1,1045 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-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
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::Sender do
|
26
|
+
|
27
|
+
include FlexMock::ArgumentTypes
|
28
|
+
|
29
|
+
context "access instance" do
|
30
|
+
before do
|
31
|
+
RightScale::Sender.class_eval do
|
32
|
+
remove_class_variable(:@@instance) if class_variable_defined?(:@@instance)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns nil when the instance is undefined" do
|
37
|
+
RightScale::Sender.instance.should == nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns the instance if defined" do
|
41
|
+
RightScale::Sender.class_eval { @@instance = "instance" }
|
42
|
+
RightScale::Sender.instance.should_not == nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "use instance" do
|
47
|
+
# Create new sender with specified mode and options
|
48
|
+
# Also create mock agent and client for it with basic support for both modes
|
49
|
+
def create_sender(mode, options = {})
|
50
|
+
@broker_id = "rs-broker-1-1"
|
51
|
+
@broker_ids = [@broker_id]
|
52
|
+
@client = flexmock("client", :push => true, :request => true, :all => @broker_ids, :publish => @broker_ids).by_default
|
53
|
+
@agent_id = "rs-agent-1-1"
|
54
|
+
@agent = flexmock("agent", :identity => @agent_id, :client => @client, :mode => mode, :request_queue => "request",
|
55
|
+
:options => options).by_default
|
56
|
+
RightScale::Sender.new(@agent)
|
57
|
+
RightScale::Sender.instance
|
58
|
+
end
|
59
|
+
|
60
|
+
before(:each) do
|
61
|
+
@log = flexmock(RightScale::Log)
|
62
|
+
@log.should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
63
|
+
@log.should_receive(:warning).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
64
|
+
@sender = create_sender(:http)
|
65
|
+
@token = "token"
|
66
|
+
@type = "/foo/bar"
|
67
|
+
@action = "bar"
|
68
|
+
@payload = {:pay => "load"}
|
69
|
+
@target = "target"
|
70
|
+
@callback = lambda { |_| }
|
71
|
+
end
|
72
|
+
|
73
|
+
context :initialize do
|
74
|
+
it "creates connectivity checker if mode is amqp" do
|
75
|
+
@sender = create_sender(:amqp)
|
76
|
+
@sender.connectivity_checker.should be_a(RightScale::ConnectivityChecker)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "offline handling" do
|
81
|
+
|
82
|
+
context :initialize_offline_queue do
|
83
|
+
it "initializes offline handler" do
|
84
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
85
|
+
flexmock(@sender.offline_handler).should_receive(:init).once
|
86
|
+
@sender.initialize_offline_queue
|
87
|
+
end
|
88
|
+
|
89
|
+
it "does nothing if offline queueing is disabled" do
|
90
|
+
flexmock(@sender.offline_handler).should_receive(:init).never
|
91
|
+
@sender.initialize_offline_queue
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context :start_offline_queue do
|
96
|
+
it "starts offline handler" do
|
97
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
98
|
+
flexmock(@sender.offline_handler).should_receive(:start).once
|
99
|
+
@sender.start_offline_queue
|
100
|
+
end
|
101
|
+
|
102
|
+
it "does nothing if offline queueing is disabled" do
|
103
|
+
flexmock(@sender.offline_handler).should_receive(:start).never
|
104
|
+
@sender.start_offline_queue
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context :enable_offline_mode do
|
109
|
+
it "enables offline handler" do
|
110
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
111
|
+
flexmock(@sender.offline_handler).should_receive(:enable).once
|
112
|
+
@sender.enable_offline_mode
|
113
|
+
end
|
114
|
+
|
115
|
+
it "does nothing if offline queueing is disabled" do
|
116
|
+
flexmock(@sender.offline_handler).should_receive(:enable).never
|
117
|
+
@sender.enable_offline_mode
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context :disable_offline_mode do
|
122
|
+
it "initializes offline handler" do
|
123
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
124
|
+
flexmock(@sender.offline_handler).should_receive(:disable).once
|
125
|
+
@sender.disable_offline_mode
|
126
|
+
end
|
127
|
+
|
128
|
+
it "does nothing if offline queueing is disabled" do
|
129
|
+
flexmock(@sender.offline_handler).should_receive(:disable).never
|
130
|
+
@sender.disable_offline_mode
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context :send_push do
|
136
|
+
it "creates Push object" do
|
137
|
+
flexmock(RightSupport::Data::UUID, :generate => "random token")
|
138
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, nil, on { |a| a.class == RightScale::Push &&
|
139
|
+
a.type == @type && a.from == @agent_id && a.target.nil? && a.persistent == true && a.confirm.nil? &&
|
140
|
+
a.token == "random token" && a.expires_at == 0 }, Time, nil).once
|
141
|
+
@sender.send_push(@type).should be_true
|
142
|
+
end
|
143
|
+
|
144
|
+
it "stores payload" do
|
145
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, nil, on { |a| a.payload == @payload }, Time, nil).once
|
146
|
+
@sender.send_push(@type, @payload).should be_true
|
147
|
+
end
|
148
|
+
|
149
|
+
it "sets specific target using string" do
|
150
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, @target, on { |a| a.target == @target &&
|
151
|
+
a.selector == :any }, Time, nil).once
|
152
|
+
@sender.send_push(@type, nil, @target).should be_true
|
153
|
+
end
|
154
|
+
|
155
|
+
it "sets specific target using :agent_id" do
|
156
|
+
target = {:agent_id => @agent_id}
|
157
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.target == @agent_id }, Time, nil).once
|
158
|
+
@sender.send_push(@type, nil, target).should be_true
|
159
|
+
end
|
160
|
+
|
161
|
+
it "sets target tags" do
|
162
|
+
tags = ["a:b=c"]
|
163
|
+
target = {:tags => tags}
|
164
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.tags == tags &&
|
165
|
+
a.selector == :any }, Time, nil).once
|
166
|
+
@sender.send_push(@type, nil, target).should be_true
|
167
|
+
end
|
168
|
+
|
169
|
+
it "sets target scope" do
|
170
|
+
scope = {:shard => 1, :account => 123}
|
171
|
+
target = {:scope => scope}
|
172
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.scope == scope &&
|
173
|
+
a.selector == :any }, Time, nil).once
|
174
|
+
@sender.send_push(@type, nil, target).should be_true
|
175
|
+
end
|
176
|
+
|
177
|
+
it "sets target selector" do
|
178
|
+
tags = ["a:b=c"]
|
179
|
+
target = {:tags => tags, :selector => :all}
|
180
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.selector == :all }, Time, nil).once
|
181
|
+
@sender.send_push(@type, nil, target).should be_true
|
182
|
+
end
|
183
|
+
|
184
|
+
it "sets token" do
|
185
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, @target, on { |a| a.token == "token2" }, Time, nil).once
|
186
|
+
@sender.send_push(@type, nil, @target, "token2").should be_true
|
187
|
+
end
|
188
|
+
|
189
|
+
it "sets applies callback for returning response" do
|
190
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, nil, on { |a| a.confirm == true }, Time, Proc).once
|
191
|
+
@sender.send_push(@type) { |_| }.should be_true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context :send_request do
|
196
|
+
it "creates Request object" do
|
197
|
+
flexmock(RightSupport::Data::UUID, :generate => "random token")
|
198
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, nil, on { |a| a.class == RightScale::Request &&
|
199
|
+
a.type == @type && a.from == @agent_id && a.target.nil? && a.persistent.nil? && a.selector == :any &&
|
200
|
+
a.token == "random token" && a.expires_at == 0 }, Time, Proc).once
|
201
|
+
@sender.send_request(@type) { |_| }.should be_true
|
202
|
+
end
|
203
|
+
|
204
|
+
it "stores payload" do
|
205
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, nil, on { |a| a.payload == @payload }, Time, Proc)
|
206
|
+
@sender.send_request(@type, @payload) { |_| }.should be_true
|
207
|
+
end
|
208
|
+
|
209
|
+
it "sets specific target using string" do
|
210
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, @agent_id, on { |a| a.target == @agent_id &&
|
211
|
+
a.selector == :any }, Time, Proc).once
|
212
|
+
@sender.send_request(@type, nil, @agent_id) { |_| }.should be_true
|
213
|
+
end
|
214
|
+
|
215
|
+
it "sets specific target using :agent_id" do
|
216
|
+
target = {:agent_id => @agent_id}
|
217
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, target, on { |a| a.target == @agent_id }, Time, Proc).once
|
218
|
+
@sender.send_request(@type, nil, target) { |_| }.should be_true
|
219
|
+
end
|
220
|
+
|
221
|
+
it "sets target tags" do
|
222
|
+
tags = ["a:b=c"]
|
223
|
+
target = {:tags => tags}
|
224
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, target, on { |a| a.tags == tags &&
|
225
|
+
a.selector == :any }, Time, Proc).once
|
226
|
+
@sender.send_request(@type, nil, target) { |_| }.should be_true
|
227
|
+
end
|
228
|
+
|
229
|
+
it "sets target scope" do
|
230
|
+
scope = {:shard => 1, :account => 123}
|
231
|
+
target = {:scope => scope}
|
232
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, target, on { |a| a.scope == scope &&
|
233
|
+
a.selector == :any }, Time, Proc).once
|
234
|
+
@sender.send_request(@type, nil, target) { |_| }.should be_true
|
235
|
+
end
|
236
|
+
|
237
|
+
it "sets token" do
|
238
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, @target, on { |a| a.token == "token2" }, Time, Proc).once
|
239
|
+
@sender.send_request(@type, nil, @target, "token2") { |_| }.should be_true
|
240
|
+
end
|
241
|
+
|
242
|
+
it "sets expiration time if TTL enabled" do
|
243
|
+
@sender = create_sender(:http, :time_to_live => 60)
|
244
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, nil, on { |a| a.expires_at != 0 }, Time, Proc).once
|
245
|
+
@sender.send_request(@type) { |_| }.should be_true
|
246
|
+
end
|
247
|
+
|
248
|
+
it "does not allow fanout" do
|
249
|
+
lambda { @sender.send_request(@type, nil, :selector => :all) }.should raise_error(ArgumentError)
|
250
|
+
end
|
251
|
+
|
252
|
+
it "requires callback block" do
|
253
|
+
lambda { @sender.send_request(@type) }.should raise_error(ArgumentError)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context :build_and_send_packet do
|
258
|
+
[:http, :amqp].each do |mode|
|
259
|
+
|
260
|
+
context "when #{mode}" do
|
261
|
+
before(:each) do
|
262
|
+
@sender = create_sender(mode)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "builds packet" do
|
266
|
+
packet = flexmock("packet", :type => @type, :token => @token, :selector => :any)
|
267
|
+
flexmock(@sender).should_receive(:build_packet).with(:send_push, @type, @payload, @target, @token, nil).and_return(packet).once
|
268
|
+
flexmock(@sender).should_receive("#{mode}_send".to_sym)
|
269
|
+
@sender.build_and_send_packet(:send_push, @type, @payload, @target, @token, callback = nil).should be_true
|
270
|
+
end
|
271
|
+
|
272
|
+
it "sends packet" do
|
273
|
+
flexmock(@sender).should_receive("#{mode}_send".to_sym).with(:send_push, @target,
|
274
|
+
on { |a| a.class == RightScale::Push }, Time, nil).once
|
275
|
+
@sender.build_and_send_packet(:send_push, @type, @payload, @target, @token, callback = nil).should be_true
|
276
|
+
end
|
277
|
+
|
278
|
+
it "ignores nil packet result when queueing" do
|
279
|
+
flexmock(@sender).should_receive(:build_packet).with(:send_push, @type, @payload, @target, @token, nil).and_return(nil).once
|
280
|
+
flexmock(@sender).should_receive("#{mode}_send".to_sym).never
|
281
|
+
@sender.build_and_send_packet(:send_push, @type, @payload, @target, @token, callback = nil).should be_true
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context :build_packet do
|
288
|
+
[:send_push, :send_request].each do |kind|
|
289
|
+
|
290
|
+
context "when #{kind}" do
|
291
|
+
it "validates target" do
|
292
|
+
flexmock(@sender).should_receive(:validate_target).with(@target, kind == :send_push).once
|
293
|
+
@sender.build_packet(kind, @type, nil, @target, @token).should_not be_nil
|
294
|
+
end
|
295
|
+
|
296
|
+
it "submits request to offline handler and returns nil if queueing" do
|
297
|
+
flexmock(@sender).should_receive(:queueing?).and_return(true).once
|
298
|
+
@sender.build_packet(kind, @type, nil, @target, @token).should be_nil
|
299
|
+
end
|
300
|
+
|
301
|
+
it "generates a request token if none provided" do
|
302
|
+
flexmock(RightSupport::Data::UUID, :generate => "random token")
|
303
|
+
packet = @sender.build_packet(kind, @type, nil, @target, token = nil)
|
304
|
+
packet.token.should == "random token"
|
305
|
+
end
|
306
|
+
|
307
|
+
it "sets payload" do
|
308
|
+
packet = @sender.build_packet(kind, @type, @payload, @target, @token)
|
309
|
+
packet.payload.should == @payload
|
310
|
+
end
|
311
|
+
|
312
|
+
it "sets the packet from this agent" do
|
313
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
314
|
+
packet.from.should == @agent_id
|
315
|
+
end
|
316
|
+
|
317
|
+
it "sets target if target is not a hash" do
|
318
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
319
|
+
packet.target.should == @target
|
320
|
+
end
|
321
|
+
|
322
|
+
context "when target is a hash" do
|
323
|
+
it "sets agent ID" do
|
324
|
+
target = {:agent_id => @agent_id}
|
325
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
326
|
+
packet.target.should == @agent_id
|
327
|
+
end
|
328
|
+
|
329
|
+
it "sets tags" do
|
330
|
+
tags = ["a:b=c"]
|
331
|
+
target = {:tags => tags}
|
332
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
333
|
+
packet.tags.should == tags
|
334
|
+
packet.scope.should be_nil
|
335
|
+
end
|
336
|
+
|
337
|
+
it "sets scope" do
|
338
|
+
scope = {:shard => 1, :account => 123}
|
339
|
+
target = {:scope => scope}
|
340
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
341
|
+
packet.tags.should == []
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
if kind == :send_push
|
346
|
+
it "defaults selector to :any" do
|
347
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
348
|
+
packet.selector.should == :any
|
349
|
+
end
|
350
|
+
|
351
|
+
it "sets selector" do
|
352
|
+
target = {:selector => :all}
|
353
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
354
|
+
packet.selector.should == :all
|
355
|
+
end
|
356
|
+
|
357
|
+
it "sets persistent flag" do
|
358
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
359
|
+
packet.persistent.should be_true
|
360
|
+
end
|
361
|
+
|
362
|
+
it "enables confirm if callback provided" do
|
363
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token, @callback)
|
364
|
+
packet.confirm.should be_true
|
365
|
+
end
|
366
|
+
|
367
|
+
it "does not enable confirm if not callback provided" do
|
368
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
369
|
+
packet.confirm.should be_false
|
370
|
+
end
|
371
|
+
|
372
|
+
it "does not set expiration time" do
|
373
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
374
|
+
packet.expires_at.should == 0
|
375
|
+
end
|
376
|
+
else
|
377
|
+
it "always sets selector to :any" do
|
378
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
379
|
+
packet.selector.should == :any
|
380
|
+
end
|
381
|
+
|
382
|
+
it "sets expiration time to specified TTL" do
|
383
|
+
@sender = create_sender(:http, :time_to_live => 99)
|
384
|
+
now = Time.now
|
385
|
+
flexmock(Time).should_receive(:now).and_return(now)
|
386
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
387
|
+
packet.expires_at.should == (now + 99).to_i
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
context :handle_response do
|
395
|
+
before(:each) do
|
396
|
+
flexmock(RightSupport::Data::UUID, :generate => @token)
|
397
|
+
@received_at = Time.now
|
398
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
399
|
+
@callback = lambda { |_| }
|
400
|
+
@pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
401
|
+
@sender.pending_requests[@token] = @pending_request
|
402
|
+
end
|
403
|
+
|
404
|
+
[:send_push, :send_request].each do |kind|
|
405
|
+
it "delivers the response for a #{kind}" do
|
406
|
+
@pending_request = RightScale::PendingRequest.new(kind, @received_at, @callback)
|
407
|
+
@sender.pending_requests[@token] = @pending_request
|
408
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
409
|
+
flexmock(@sender).should_receive(:deliver_response).with(response, @pending_request).once
|
410
|
+
@sender.handle_response(response).should be_true
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
it "logs a debug message if request no longer pending" do
|
415
|
+
@sender.pending_requests.delete(@token)
|
416
|
+
@log.should_receive(:debug).with(/No pending request for response/).once
|
417
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
418
|
+
@sender.handle_response(response)
|
419
|
+
end
|
420
|
+
|
421
|
+
it "ignores responses that are not a Result" do
|
422
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
423
|
+
@sender.handle_response("response").should be_true
|
424
|
+
end
|
425
|
+
|
426
|
+
context "when non-delivery" do
|
427
|
+
before(:each) do
|
428
|
+
@reason = RightScale::OperationResult::TARGET_NOT_CONNECTED
|
429
|
+
non_delivery = RightScale::OperationResult.non_delivery(@reason)
|
430
|
+
@response = RightScale::Result.new(@token, "to", non_delivery, @target)
|
431
|
+
end
|
432
|
+
|
433
|
+
it "records non-delivery regardless of whether there is a pending request" do
|
434
|
+
@sender.pending_requests.delete(@token)
|
435
|
+
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::NO_ROUTE_TO_TARGET)
|
436
|
+
response = RightScale::Result.new(@token, "to", non_delivery, @target)
|
437
|
+
@sender.handle_response(response).should be_true
|
438
|
+
@sender.instance_variable_get(:@non_delivery_stats).total.should == 1
|
439
|
+
end
|
440
|
+
|
441
|
+
it "logs non-delivery if there is no pending request" do
|
442
|
+
@sender.pending_requests.delete(@token)
|
443
|
+
@log.should_receive(:info).with(/Non-delivery of/).once
|
444
|
+
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::NO_ROUTE_TO_TARGET)
|
445
|
+
response = RightScale::Result.new(@token, "to", non_delivery, @target)
|
446
|
+
@sender.handle_response(response).should be_true
|
447
|
+
end
|
448
|
+
|
449
|
+
context "for a Request" do
|
450
|
+
|
451
|
+
context "with target not connected" do
|
452
|
+
it "logs non-delivery but does not deliver it" do
|
453
|
+
@log.should_receive(:info).with(/Non-delivery of/).once
|
454
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
455
|
+
@sender.handle_response(@response).should be_true
|
456
|
+
end
|
457
|
+
|
458
|
+
it "records non-delivery reason in pending request if for parent request" do
|
459
|
+
@log.should_receive(:info).with(/Non-delivery of/).once
|
460
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
461
|
+
parent_token = "parent token"
|
462
|
+
parent_pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
463
|
+
@sender.pending_requests[parent_token] = parent_pending_request
|
464
|
+
@pending_request.retry_parent_token = parent_token
|
465
|
+
@sender.handle_response(@response).should be_true
|
466
|
+
parent_pending_request.non_delivery.should == @reason
|
467
|
+
end
|
468
|
+
|
469
|
+
it "updates non-delivery reason if for retry request" do
|
470
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
471
|
+
@sender.handle_response(@response).should be_true
|
472
|
+
@pending_request.non_delivery.should == @reason
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
context "with retry timeout and previous non-delivery" do
|
477
|
+
it "delivers response using stored non-delivery reason" do
|
478
|
+
@reason = RightScale::OperationResult::RETRY_TIMEOUT
|
479
|
+
@response.results = RightScale::OperationResult.non_delivery(@reason)
|
480
|
+
@pending_request.non_delivery = "other reason"
|
481
|
+
flexmock(@sender).should_receive(:deliver_response).with(on { |a| a.results.content == "other reason"},
|
482
|
+
@pending_request).once
|
483
|
+
@sender.handle_response(@response).should be_true
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
context "otherwise" do
|
488
|
+
it "delivers non-delivery response as is" do
|
489
|
+
@response.results = RightScale::OperationResult.non_delivery("other")
|
490
|
+
flexmock(@sender).should_receive(:deliver_response).with(on { |a| a.results.content == "other"},
|
491
|
+
RightScale::PendingRequest).once
|
492
|
+
@sender.handle_response(@response).should be_true
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
context :terminate do
|
500
|
+
it "terminates offline handler" do
|
501
|
+
flexmock(@sender.offline_handler).should_receive(:terminate).once
|
502
|
+
@sender.terminate
|
503
|
+
end
|
504
|
+
|
505
|
+
it "terminates connectivity checker if configured" do
|
506
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
507
|
+
flexmock(@sender.connectivity_checker).should_receive(:terminate).once
|
508
|
+
@sender.terminate
|
509
|
+
end
|
510
|
+
|
511
|
+
it "returns number of pending requests and age of youngest request" do
|
512
|
+
receive_time = Time.now - 10
|
513
|
+
@sender.pending_requests[@token] = RightScale::PendingRequest.new(:send_request, receive_time, @callback)
|
514
|
+
@sender.terminate.should == [1, 10]
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
context :dump_requests do
|
519
|
+
it "returns array of unfinished non-push requests" do
|
520
|
+
time1 = Time.now
|
521
|
+
@sender.pending_requests["token1"] = RightScale::PendingRequest.new(:send_push, time1, @callback)
|
522
|
+
time2 = time1 + 10
|
523
|
+
@sender.pending_requests["token2"] = RightScale::PendingRequest.new(:send_request, time2, @callback)
|
524
|
+
@sender.dump_requests.should == ["#{time2.localtime} <token2>"]
|
525
|
+
end
|
526
|
+
|
527
|
+
it "returns requests in descending time order" do
|
528
|
+
time1 = Time.now
|
529
|
+
@sender.pending_requests["token1"] = RightScale::PendingRequest.new(:send_request, time1, @callback)
|
530
|
+
time2 = time1 + 10
|
531
|
+
@sender.pending_requests["token2"] = RightScale::PendingRequest.new(:send_request, time2, @callback)
|
532
|
+
@sender.dump_requests.should == ["#{time2.localtime} <token2>", "#{time1.localtime} <token1>"]
|
533
|
+
end
|
534
|
+
|
535
|
+
it "limits the number returned to 50" do
|
536
|
+
pending_request = RightScale::PendingRequest.new(:send_request, Time.now, @callback)
|
537
|
+
55.times.each { |i| @sender.pending_requests["token#{i}"] = pending_request }
|
538
|
+
result = @sender.dump_requests
|
539
|
+
result.size.should == 51
|
540
|
+
result.last.should == "..."
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
context :validate_target do
|
545
|
+
it "should accept nil target" do
|
546
|
+
@sender.send(:validate_target, nil, true).should be_true
|
547
|
+
end
|
548
|
+
|
549
|
+
it "should accept named target" do
|
550
|
+
@sender.send(:validate_target, "name", true).should be_true
|
551
|
+
end
|
552
|
+
|
553
|
+
context "when target is a hash" do
|
554
|
+
|
555
|
+
context "and agent ID is specified" do
|
556
|
+
it "should not allow other keys" do
|
557
|
+
@sender.send(:validate_target, {:agent_id => @agent_id}, true).should be_true
|
558
|
+
lambda { @sender.send(:validate_target, {:agent_id => @agent_id, :tags => ["a:b=c"]}, true) }.should \
|
559
|
+
raise_error(ArgumentError, /Invalid target/)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context "and selector is allowed" do
|
564
|
+
it "should accept :all or :any selector" do
|
565
|
+
@sender.send(:validate_target, {:selector => :all}, true).should be_true
|
566
|
+
@sender.send(:validate_target, {"selector" => "any"}, true).should be_true
|
567
|
+
end
|
568
|
+
|
569
|
+
it "should reject values other than :all or :any" do
|
570
|
+
lambda { @sender.send(:validate_target, {:selector => :other}, true) }.
|
571
|
+
should raise_error(ArgumentError, /Invalid target selector/)
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
context "and selector is not allowed" do
|
576
|
+
it "should reject selector" do
|
577
|
+
lambda { @sender.send(:validate_target, {:selector => :all}, false) }.
|
578
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
context "and tags is specified" do
|
583
|
+
it "should accept tags" do
|
584
|
+
@sender.send(:validate_target, {:tags => []}, true).should be_true
|
585
|
+
@sender.send(:validate_target, {"tags" => ["tag"]}, true).should be_true
|
586
|
+
end
|
587
|
+
|
588
|
+
it "should reject non-array" do
|
589
|
+
lambda { @sender.send(:validate_target, {:tags => {}}, true) }.
|
590
|
+
should raise_error(ArgumentError, /Invalid target tags/)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
context "and scope is specified" do
|
595
|
+
it "should accept account" do
|
596
|
+
@sender.send(:validate_target, {:scope => {:account => 1}}, true).should be_true
|
597
|
+
@sender.send(:validate_target, {"scope" => {"account" => 1}}, true).should be_true
|
598
|
+
end
|
599
|
+
|
600
|
+
it "should accept shard" do
|
601
|
+
@sender.send(:validate_target, {:scope => {:shard => 1}}, true).should be_true
|
602
|
+
@sender.send(:validate_target, {"scope" => {"shard" => 1}}, true).should be_true
|
603
|
+
end
|
604
|
+
|
605
|
+
it "should accept account and shard" do
|
606
|
+
@sender.send(:validate_target, {"scope" => {:shard => 1, "account" => 1}}, true).should be_true
|
607
|
+
end
|
608
|
+
|
609
|
+
it "should reject keys other than account and shard" do
|
610
|
+
target = {"scope" => {:shard => 1, "account" => 1, :other => 2}}
|
611
|
+
lambda { @sender.send(:validate_target, target, true) }.
|
612
|
+
should raise_error(ArgumentError, /Invalid target scope/)
|
613
|
+
end
|
614
|
+
|
615
|
+
it "should reject empty hash" do
|
616
|
+
lambda { @sender.send(:validate_target, {:scope => {}}, true) }.
|
617
|
+
should raise_error(ArgumentError, /Invalid target scope/)
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
context "and multiple are specified" do
|
622
|
+
it "should accept scope and tags" do
|
623
|
+
@sender.send(:validate_target, {:scope => {:shard => 1}, :tags => []}, true).should be_true
|
624
|
+
end
|
625
|
+
|
626
|
+
it "should accept scope, tags, and selector" do
|
627
|
+
target = {:scope => {:shard => 1}, :tags => ["tag"], :selector => :all}
|
628
|
+
@sender.send(:validate_target, target, true).should be_true
|
629
|
+
end
|
630
|
+
|
631
|
+
it "should reject selector if not allowed" do
|
632
|
+
target = {:scope => {:shard => 1}, :tags => ["tag"], :selector => :all}
|
633
|
+
lambda { @sender.send(:validate_target, target, false) }.
|
634
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
it "should reject keys other than selector, scope, and tags" do
|
639
|
+
target = {:scope => {:shard => 1}, :tags => [], :selector => :all, :other => 2}
|
640
|
+
lambda { @sender.send(:validate_target, target, true) }.
|
641
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
642
|
+
end
|
643
|
+
|
644
|
+
it "should reject empty hash" do
|
645
|
+
lambda { @sender.send(:validate_target, {}, true) }.
|
646
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
647
|
+
end
|
648
|
+
|
649
|
+
it "should reject value that is not nil, string, or hash" do
|
650
|
+
lambda { @sender.send(:validate_target, [], true) }.
|
651
|
+
should raise_error(ArgumentError, /Invalid target/)
|
652
|
+
end
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
context :http do
|
657
|
+
|
658
|
+
context :http_send do
|
659
|
+
before(:each) do
|
660
|
+
@received_at = Time.now
|
661
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
662
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@token).by_default
|
663
|
+
@packet = @sender.build_packet(:send_request, @type, @payload, @target, @token, @callback)
|
664
|
+
@response = nil
|
665
|
+
@callback = lambda { |response| @response = response }
|
666
|
+
end
|
667
|
+
|
668
|
+
it "sends request using configured client" do
|
669
|
+
@packet = @sender.build_packet(:send_push, @type, @payload, @target, @token, @callback)
|
670
|
+
@client.should_receive(:push).with(@type, @payload, @target, @token).and_return(nil).once
|
671
|
+
@sender.send(:http_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
672
|
+
end
|
673
|
+
|
674
|
+
it "responds with success result containing response" do
|
675
|
+
@client.should_receive(:request).with(@type, @payload, @target, @token).and_return("result").once
|
676
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
677
|
+
@response.token.should == @token
|
678
|
+
@response.results.success?.should be_true
|
679
|
+
@response.results.content.should == "result"
|
680
|
+
@response.from.should == @target
|
681
|
+
@response.received_at.should == @received_at.to_f
|
682
|
+
end
|
683
|
+
|
684
|
+
it "responds with success result when response is nil" do
|
685
|
+
@client.should_receive(:request).with(@type, @payload, @target, @token).and_return(nil).once
|
686
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
687
|
+
@response.token.should == @token
|
688
|
+
@response.results.success?.should be_true
|
689
|
+
@response.results.content.should be_nil
|
690
|
+
@response.from.should == @target
|
691
|
+
@response.received_at.should == @received_at.to_f
|
692
|
+
end
|
693
|
+
|
694
|
+
it "responds asynchronously if requested" do
|
695
|
+
@sender = create_sender(:http, :async_response => true)
|
696
|
+
flexmock(EM).should_receive(:next_tick).and_yield.once
|
697
|
+
@client.should_receive(:request).with(@type, @payload, @target, @token).and_return(nil).once
|
698
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
699
|
+
@response.token.should == @token
|
700
|
+
@response.results.success?.should be_true
|
701
|
+
@response.results.content.should be_nil
|
702
|
+
@response.from.should == @target
|
703
|
+
@response.received_at.should == @received_at.to_f
|
704
|
+
end
|
705
|
+
|
706
|
+
context "when fails" do
|
707
|
+
context "with connectivity error" do
|
708
|
+
it "queues request if queueing and does not respond" do
|
709
|
+
@sender = create_sender(:http, :offline_queueing => true)
|
710
|
+
@sender.initialize_offline_queue
|
711
|
+
@sender.enable_offline_mode
|
712
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::ConnectivityFailure, "disconnected").once
|
713
|
+
flexmock(@sender.offline_handler).should_receive(:queue_request).with(:send_request, @type,
|
714
|
+
@payload, @target, @callback).once
|
715
|
+
flexmock(@sender).should_receive(:handle_response).never
|
716
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
717
|
+
end
|
718
|
+
|
719
|
+
it "responds with retry result if not queueing" do
|
720
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::ConnectivityFailure, "Server not responding").once
|
721
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
722
|
+
@response.results.retry?.should be_true
|
723
|
+
@response.results.content.should == "Server not responding"
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
it "responds with retry result if retryable error" do
|
728
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::RetryableError, "try again").once
|
729
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
730
|
+
@response.results.retry?.should be_true
|
731
|
+
@response.results.content.should == "try again"
|
732
|
+
end
|
733
|
+
|
734
|
+
it "responds with error result if internal error" do
|
735
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::InternalServerError.new("unprocessable", "Router")).once
|
736
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
737
|
+
@response.results.error?.should be_true
|
738
|
+
@response.results.content.should == "Router internal error"
|
739
|
+
end
|
740
|
+
|
741
|
+
it "does not respond to request if terminating" do
|
742
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::Terminating, "going down").once
|
743
|
+
flexmock(@sender).should_receive(:handle_response).never
|
744
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
745
|
+
end
|
746
|
+
|
747
|
+
it "responds with error result if HTTP error" do
|
748
|
+
@client.should_receive(:request).and_raise(RestExceptionMock.new(400, "bad data")).once
|
749
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
750
|
+
@response.results.error?.should be_true
|
751
|
+
@response.results.content.should == "400 Bad Request: bad data"
|
752
|
+
end
|
753
|
+
|
754
|
+
it "responds with error result if unexpected error" do
|
755
|
+
@log.should_receive(:error).with(/Failed to send/, StandardError, :trace).once
|
756
|
+
@client.should_receive(:request).and_raise(StandardError, "unexpected").once
|
757
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
758
|
+
@response.results.error?.should be_true
|
759
|
+
@response.results.content.should == "Agent agent internal error"
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
context :amqp do
|
766
|
+
before(:each) do
|
767
|
+
@sender = create_sender(:amqp)
|
768
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@token).by_default
|
769
|
+
@packet = @sender.build_packet(:send_push, @type, @payload, @target, @token, nil)
|
770
|
+
@received_at = Time.now
|
771
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
772
|
+
@response = nil
|
773
|
+
@callback = lambda { |response| @response = response }
|
774
|
+
end
|
775
|
+
|
776
|
+
context :amqp_send do
|
777
|
+
it "stores pending request for use in responding if callback specified" do
|
778
|
+
@packet = @sender.build_packet(:send_push, @type, @payload, @target, @token, @callback)
|
779
|
+
flexmock(@sender).should_receive(:amqp_send_once)
|
780
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
781
|
+
@sender.pending_requests[@token].should_not be_nil
|
782
|
+
end
|
783
|
+
|
784
|
+
it "does not store pending request if callback not specified" do
|
785
|
+
flexmock(@sender).should_receive(:amqp_send_once)
|
786
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, nil).should be_true
|
787
|
+
@sender.pending_requests[@token].should be_nil
|
788
|
+
end
|
789
|
+
|
790
|
+
it "publishes without retry capability if send_push" do
|
791
|
+
flexmock(@sender).should_receive(:amqp_send_once).with(@packet).once
|
792
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, nil).should be_true
|
793
|
+
end
|
794
|
+
|
795
|
+
it "publishes with retry capability if send_request" do
|
796
|
+
@packet = @sender.build_packet(:send_request, @type, @payload, @target, @token, @callback)
|
797
|
+
flexmock(@sender).should_receive(:amqp_send_retry).with(@packet, @token).once
|
798
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
799
|
+
end
|
800
|
+
|
801
|
+
context "when fails" do
|
802
|
+
|
803
|
+
context "with offline error" do
|
804
|
+
it "submits request to offline handler if queuing" do
|
805
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
806
|
+
@sender.initialize_offline_queue
|
807
|
+
@sender.enable_offline_mode
|
808
|
+
flexmock(@sender).should_receive(:amqp_send_once).and_raise(RightScale::Sender::TemporarilyOffline).once
|
809
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, nil).should be_true
|
810
|
+
@sender.offline_handler.queue.size.should == 1
|
811
|
+
@sender.pending_requests[@token].should be_nil
|
812
|
+
end
|
813
|
+
|
814
|
+
it "responds with retry result if not queueing" do
|
815
|
+
flexmock(@sender).should_receive(:amqp_send_once).and_raise(RightScale::Sender::TemporarilyOffline).once
|
816
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
817
|
+
@sender.offline_handler.queue.size.should == 0
|
818
|
+
@sender.pending_requests[@token].should_not be_nil # because send_push does not get deleted when deliver
|
819
|
+
@response.results.retry?.should be_true
|
820
|
+
@response.results.content.should == "lost RightNet connectivity"
|
821
|
+
end
|
822
|
+
end
|
823
|
+
|
824
|
+
it "responds with non-delivery result if send failure" do
|
825
|
+
flexmock(@sender).should_receive(:amqp_send_once).and_raise(RightScale::Sender::SendFailure).once
|
826
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
827
|
+
@sender.pending_requests[@token].should_not be_nil # because send_push does not get deleted when deliver
|
828
|
+
@response.results.non_delivery?.should be_true
|
829
|
+
@response.results.content.should == "send failed unexpectedly"
|
830
|
+
end
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
context :amqp_send_once do
|
835
|
+
it "publishes request to request queue" do
|
836
|
+
@client.should_receive(:publish).with(hsh(:name => "request"), @packet, hsh(:persistent => true,
|
837
|
+
:mandatory => true, :broker_ids => nil)).and_return(@broker_ids).once
|
838
|
+
@sender.send(:amqp_send_once, @packet, @broker_ids).should == @broker_ids
|
839
|
+
end
|
840
|
+
|
841
|
+
context "when fails" do
|
842
|
+
it "raises TemporarilyOffline if no connected brokers" do
|
843
|
+
@log.should_receive(:error).with(/Failed to publish/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
844
|
+
@client.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
845
|
+
lambda { @sender.send(:amqp_send_once, @packet) }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
846
|
+
end
|
847
|
+
|
848
|
+
it "raises SendFailure if unexpected exception" do
|
849
|
+
@log.should_receive(:error).with(/Failed to publish/, StandardError, :trace).once
|
850
|
+
@client.should_receive(:publish).and_raise(StandardError, "unexpected").once
|
851
|
+
lambda { @sender.send(:amqp_send_once, @packet) }.should raise_error(RightScale::Sender::SendFailure)
|
852
|
+
end
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
context :amqp_send_retry do
|
857
|
+
before(:each) do
|
858
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return("retry token")
|
859
|
+
@packet = @sender.build_packet(:send_request, @type, @payload, @target, @token, @callback)
|
860
|
+
end
|
861
|
+
|
862
|
+
it "publishes request to request queue" do
|
863
|
+
@client.should_receive(:publish).with(hsh(:name => "request"), @packet, hsh(:persistent => nil,
|
864
|
+
:mandatory => true, :broker_ids => nil)).and_return(@broker_ids).once
|
865
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
866
|
+
end
|
867
|
+
|
868
|
+
it "does not rescue if publish fails" do
|
869
|
+
@log.should_receive(:error).with(/Failed to publish request/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
870
|
+
@client.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
871
|
+
lambda { @sender.send(:amqp_send_retry, @packet, @token) }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
872
|
+
end
|
873
|
+
|
874
|
+
it "does not retry if retry timeout not set" do
|
875
|
+
@sender = create_sender(:amqp, :retry_interval => 10)
|
876
|
+
@client.should_receive(:publish).once
|
877
|
+
flexmock(EM).should_receive(:add_timer).never
|
878
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
879
|
+
end
|
880
|
+
|
881
|
+
it "does not retry if retry interval not set" do
|
882
|
+
@sender = create_sender(:amqp, :retry_timeout => 60)
|
883
|
+
@client.should_receive(:publish).once
|
884
|
+
flexmock(EM).should_receive(:add_timer).never
|
885
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
886
|
+
end
|
887
|
+
|
888
|
+
context "when retry enabled" do
|
889
|
+
it "uses timer to wait for a response" do
|
890
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
891
|
+
@client.should_receive(:publish).once
|
892
|
+
flexmock(EM).should_receive(:add_timer).once
|
893
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
894
|
+
end
|
895
|
+
|
896
|
+
it "stops retrying if response was received" do
|
897
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
898
|
+
@client.should_receive(:publish).once
|
899
|
+
flexmock(EM).should_receive(:add_timer).and_yield.once
|
900
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
901
|
+
@sender.pending_requests[@token].should be_nil
|
902
|
+
end
|
903
|
+
|
904
|
+
it "keeps retrying if has not exceeded retry timeout" do
|
905
|
+
EM.run do
|
906
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
907
|
+
@client.should_receive(:publish).and_return(@broker_ids).twice
|
908
|
+
flexmock(@sender.connectivity_checker).should_receive(:check).once
|
909
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
910
|
+
|
911
|
+
EM.add_timer(0.15) do
|
912
|
+
@sender.pending_requests.empty?.should be_false
|
913
|
+
result = RightScale::Result.new(@token, nil, RightScale::OperationResult.success, nil)
|
914
|
+
@sender.handle_response(result)
|
915
|
+
end
|
916
|
+
|
917
|
+
EM.add_timer(0.3) do
|
918
|
+
EM.stop
|
919
|
+
@response.results.success?.should be_true
|
920
|
+
@sender.pending_requests.empty?.should be_true
|
921
|
+
end
|
922
|
+
end
|
923
|
+
end
|
924
|
+
|
925
|
+
it "stops retrying and responds with non-delivery result if exceeds retry timeout" do
|
926
|
+
EM.run do
|
927
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.1, :retry_interval => 0.1)
|
928
|
+
@client.should_receive(:publish).and_return(@broker_ids).once
|
929
|
+
@log.should_receive(:warning).once
|
930
|
+
flexmock(@sender.connectivity_checker).should_receive(:check).once
|
931
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
932
|
+
|
933
|
+
EM.add_timer(0.3) do
|
934
|
+
EM.stop
|
935
|
+
@response.results.non_delivery?.should be_true
|
936
|
+
@response.results.content.should == RightScale::OperationResult::RETRY_TIMEOUT
|
937
|
+
@sender.pending_requests.empty?.should be_true
|
938
|
+
end
|
939
|
+
end
|
940
|
+
end
|
941
|
+
|
942
|
+
it "stops retrying if temporarily offline" do
|
943
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
944
|
+
@client.should_receive(:publish).and_return(@broker_ids).once.ordered
|
945
|
+
@client.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers).once.ordered
|
946
|
+
flexmock(EM).should_receive(:add_timer).and_yield.once
|
947
|
+
@log.should_receive(:error).with(/Failed to publish request/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
948
|
+
@log.should_receive(:error).with(/Failed retry.*temporarily offline/).once
|
949
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
950
|
+
@sender.pending_requests[@token].should_not be_nil
|
951
|
+
@sender.pending_requests["retry token"].should_not be_nil
|
952
|
+
end
|
953
|
+
|
954
|
+
it "stops retrying if there is a send failure" do
|
955
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
956
|
+
@client.should_receive(:publish).and_return(@broker_ids).once.ordered
|
957
|
+
@client.should_receive(:publish).and_raise(StandardError, "failed").once.ordered
|
958
|
+
flexmock(EM).should_receive(:add_timer).and_yield.once
|
959
|
+
@log.should_receive(:error).with(/Failed to publish request/, StandardError, :trace).once
|
960
|
+
@log.should_receive(:error).with(/Failed retry.*send failure/).once
|
961
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
962
|
+
@sender.pending_requests[@token].should_not be_nil
|
963
|
+
@sender.pending_requests["retry token"].should_not be_nil
|
964
|
+
end
|
965
|
+
|
966
|
+
it "stops retrying if there is an unexpected exception" do
|
967
|
+
# This will actually call amqp_send_retry 3 times recursively because add_timer always yields immediately
|
968
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
969
|
+
@client.should_receive(:publish).and_return(@broker_ids)
|
970
|
+
@log.should_receive(:error).with(/Failed retry.*without responding/, StandardError, :trace).twice
|
971
|
+
flexmock(EM).should_receive(:add_timer).and_yield
|
972
|
+
flexmock(@sender.connectivity_checker).should_receive(:check).and_raise(StandardError).once
|
973
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
974
|
+
@sender.pending_requests[@token].should_not be_nil
|
975
|
+
@sender.pending_requests["retry token"].should_not be_nil
|
976
|
+
end
|
977
|
+
end
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
981
|
+
context :deliver_response do
|
982
|
+
before(:each) do
|
983
|
+
@received_at = Time.now
|
984
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
985
|
+
@response = nil
|
986
|
+
@callback = lambda { |response| @response = response }
|
987
|
+
end
|
988
|
+
|
989
|
+
it "calls the response handler" do
|
990
|
+
pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
991
|
+
@sender.pending_requests[@token] = pending_request
|
992
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
993
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
994
|
+
@response.should == response
|
995
|
+
end
|
996
|
+
|
997
|
+
it "deletes associated pending request if is it a Request" do
|
998
|
+
pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
999
|
+
@sender.pending_requests[@token] = pending_request
|
1000
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
1001
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
1002
|
+
@sender.pending_requests[@token].should be_nil
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
it "does not delete pending request if it is a Push" do
|
1006
|
+
pending_request = RightScale::PendingRequest.new(:send_push, @received_at, @callback)
|
1007
|
+
@sender.pending_requests[@token] = pending_request
|
1008
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
1009
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
1010
|
+
@sender.pending_requests[@token].should_not be_nil
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
it "deletes any associated retry requests" do
|
1014
|
+
@parent_token = RightSupport::Data::UUID.generate
|
1015
|
+
pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
1016
|
+
@sender.pending_requests[@token] = pending_request
|
1017
|
+
@sender.pending_requests[@token].retry_parent_token = @parent_token
|
1018
|
+
@sender.pending_requests[@parent_token] = @sender.pending_requests[@token].dup
|
1019
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
1020
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
1021
|
+
@sender.pending_requests[@token].should be_nil
|
1022
|
+
@sender.pending_requests[@parent_token].should be_nil
|
1023
|
+
end
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
context :queueing? do
|
1027
|
+
it "returns true if offline handling enabled and currently in queueing mode" do
|
1028
|
+
@sender = create_sender(:http, :offline_queueing => true)
|
1029
|
+
flexmock(@sender.offline_handler).should_receive(:queueing?).and_return(true)
|
1030
|
+
@sender.send(:queueing?).should be_true
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
it "returns false if offline handling disabled" do
|
1034
|
+
flexmock(@sender.offline_handler).should_receive(:queueing?).and_return(true)
|
1035
|
+
@sender.send(:queueing?).should be_false
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
it "returns false if offline handling enabled but not in queueing mode" do
|
1039
|
+
@sender = create_sender(:http, :offline_queueing => true)
|
1040
|
+
flexmock(@sender.offline_handler).should_receive(:queueing?).and_return(false)
|
1041
|
+
@sender.send(:queueing?).should be_false
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
end
|
1045
|
+
end
|