right_agent 0.5.1
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 +78 -0
- data/Rakefile +86 -0
- data/lib/right_agent.rb +66 -0
- data/lib/right_agent/actor.rb +163 -0
- data/lib/right_agent/actor_registry.rb +76 -0
- data/lib/right_agent/actors/agent_manager.rb +189 -0
- data/lib/right_agent/agent.rb +735 -0
- data/lib/right_agent/agent_config.rb +403 -0
- data/lib/right_agent/agent_identity.rb +209 -0
- data/lib/right_agent/agent_tags_manager.rb +213 -0
- data/lib/right_agent/audit_formatter.rb +107 -0
- data/lib/right_agent/broker_client.rb +683 -0
- data/lib/right_agent/command.rb +30 -0
- data/lib/right_agent/command/agent_manager_commands.rb +134 -0
- data/lib/right_agent/command/command_client.rb +136 -0
- data/lib/right_agent/command/command_constants.rb +42 -0
- data/lib/right_agent/command/command_io.rb +128 -0
- data/lib/right_agent/command/command_parser.rb +87 -0
- data/lib/right_agent/command/command_runner.rb +105 -0
- data/lib/right_agent/command/command_serializer.rb +63 -0
- data/lib/right_agent/console.rb +65 -0
- data/lib/right_agent/core_payload_types.rb +42 -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 +90 -0
- data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
- data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
- data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
- data/lib/right_agent/core_payload_types/login_user.rb +62 -0
- data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
- data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -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 +73 -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/dispatcher.rb +348 -0
- data/lib/right_agent/enrollment_result.rb +217 -0
- data/lib/right_agent/exceptions.rb +30 -0
- data/lib/right_agent/ha_broker_client.rb +1278 -0
- data/lib/right_agent/idempotent_request.rb +140 -0
- data/lib/right_agent/log.rb +418 -0
- data/lib/right_agent/monkey_patches.rb +29 -0
- data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
- data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -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/singleton_patch.rb +46 -0
- data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -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 +90 -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 +91 -0
- data/lib/right_agent/operation_result.rb +270 -0
- data/lib/right_agent/packets.rb +637 -0
- data/lib/right_agent/payload_formatter.rb +104 -0
- data/lib/right_agent/pid_file.rb +159 -0
- data/lib/right_agent/platform.rb +319 -0
- data/lib/right_agent/platform/darwin.rb +227 -0
- data/lib/right_agent/platform/linux.rb +268 -0
- data/lib/right_agent/platform/windows.rb +1204 -0
- data/lib/right_agent/scripts/agent_controller.rb +522 -0
- data/lib/right_agent/scripts/agent_deployer.rb +379 -0
- data/lib/right_agent/scripts/common_parser.rb +153 -0
- data/lib/right_agent/scripts/log_level_manager.rb +193 -0
- data/lib/right_agent/scripts/stats_manager.rb +256 -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 +63 -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 +84 -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 +69 -0
- data/lib/right_agent/sender.rb +937 -0
- data/lib/right_agent/serialize.rb +29 -0
- data/lib/right_agent/serialize/message_pack.rb +102 -0
- data/lib/right_agent/serialize/secure_serializer.rb +131 -0
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
- data/lib/right_agent/serialize/serializable.rb +135 -0
- data/lib/right_agent/serialize/serializer.rb +149 -0
- data/lib/right_agent/stats_helper.rb +731 -0
- data/lib/right_agent/subprocess.rb +38 -0
- data/lib/right_agent/tracer.rb +124 -0
- data/right_agent.gemspec +60 -0
- data/spec/actor_registry_spec.rb +81 -0
- data/spec/actor_spec.rb +99 -0
- data/spec/agent_config_spec.rb +226 -0
- data/spec/agent_identity_spec.rb +75 -0
- data/spec/agent_spec.rb +571 -0
- data/spec/broker_client_spec.rb +961 -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 +72 -0
- data/spec/command/command_serializer_spec.rb +51 -0
- data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
- data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
- data/spec/core_payload_types/login_user_spec.rb +98 -0
- data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
- data/spec/core_payload_types/spec_helper.rb +23 -0
- data/spec/dispatcher_spec.rb +372 -0
- data/spec/enrollment_result_spec.rb +53 -0
- data/spec/ha_broker_client_spec.rb +1673 -0
- data/spec/idempotent_request_spec.rb +136 -0
- data/spec/log_spec.rb +177 -0
- data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
- data/spec/monkey_patches/eventmachine_spec.rb +62 -0
- data/spec/monkey_patches/string_patch_spec.rb +99 -0
- data/spec/multiplexer_spec.rb +48 -0
- data/spec/operation_result_spec.rb +171 -0
- data/spec/packets_spec.rb +418 -0
- data/spec/platform/platform_spec.rb +60 -0
- data/spec/results_mock.rb +45 -0
- data/spec/secure_identity_spec.rb +50 -0
- data/spec/security/cached_certificate_store_proxy_spec.rb +56 -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 +52 -0
- data/spec/sender_spec.rb +887 -0
- data/spec/serialize/message_pack_spec.rb +131 -0
- data/spec/serialize/secure_serializer_spec.rb +102 -0
- data/spec/serialize/serializable_spec.rb +90 -0
- data/spec/serialize/serializer_spec.rb +174 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/stats_helper_spec.rb +681 -0
- data/spec/tracer_spec.rb +114 -0
- metadata +320 -0
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'right_agent', 'core_payload_types'))
|
|
25
|
+
|
|
26
|
+
# Copy of the old model for RightScale::LoginUser before the public_keys member was added
|
|
27
|
+
module LoginUserSpec
|
|
28
|
+
class LoginUserBeforePublicKeys
|
|
29
|
+
include RightScale::Serializable
|
|
30
|
+
|
|
31
|
+
attr_accessor :uuid, :username, :public_key, :common_name, :superuser, :expires_at
|
|
32
|
+
|
|
33
|
+
def initialize(*args)
|
|
34
|
+
@uuid = args[0]
|
|
35
|
+
@username = args[1]
|
|
36
|
+
@public_key = args[2]
|
|
37
|
+
@common_name = args[3] || ''
|
|
38
|
+
@superuser = args[4] || false
|
|
39
|
+
@expires_at = Time.at(args[5]) if args[5] && (args[5] != 0)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def serialized_members
|
|
43
|
+
[ @uuid, @username, @public_key, @common_name, @superuser, @expires_at.to_i ]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe RightScale::LoginUser do
|
|
49
|
+
|
|
50
|
+
# ensures that the serialization downgrade case works.
|
|
51
|
+
def test_serialization_downgrade(user, public_key)
|
|
52
|
+
json = user.to_json
|
|
53
|
+
old_json = json.gsub("RightScale::LoginUser", "LoginUserSpec::LoginUserBeforePublicKeys")
|
|
54
|
+
old_user = JSON.parse(old_json)
|
|
55
|
+
old_user.class.should == LoginUserSpec::LoginUserBeforePublicKeys
|
|
56
|
+
old_user.public_key.should == public_key
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'should serialize old version without public_keys member' do
|
|
60
|
+
num = rand(2**32).to_s(32)
|
|
61
|
+
pub = rand(2**32).to_s(32)
|
|
62
|
+
public_key = "ssh-rsa #{pub} #{num}@rightscale.com"
|
|
63
|
+
user = LoginUserSpec::LoginUserBeforePublicKeys.new("v0-#{num}", "rs-#{num}", public_key, "#{num}@rightscale.old", true, nil)
|
|
64
|
+
json = user.to_json
|
|
65
|
+
json = json.gsub("LoginUserSpec::LoginUserBeforePublicKeys", "RightScale::LoginUser")
|
|
66
|
+
user = JSON.parse(json)
|
|
67
|
+
user.public_key.should == public_key
|
|
68
|
+
user.public_keys.should == [public_key]
|
|
69
|
+
test_serialization_downgrade(user, public_key)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'should serialize current version with single public_key' do
|
|
73
|
+
num = rand(2**32).to_s(32)
|
|
74
|
+
pub = rand(2**32).to_s(32)
|
|
75
|
+
public_key = "ssh-rsa #{pub} #{num}@rightscale.com"
|
|
76
|
+
user = RightScale::LoginUser.new("v0-#{num}", "rs-#{num}", public_key, "#{num}@rightscale.old", true, nil, nil)
|
|
77
|
+
json = user.to_json
|
|
78
|
+
user = JSON.parse(json)
|
|
79
|
+
user.class.should == RightScale::LoginUser
|
|
80
|
+
user.public_key.should == public_key
|
|
81
|
+
user.public_keys.should == [public_key]
|
|
82
|
+
test_serialization_downgrade(user, public_key)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'should serialize current version with multiple public_keys' do
|
|
86
|
+
num = rand(2**32).to_s(32)
|
|
87
|
+
public_keys = []
|
|
88
|
+
3.times do
|
|
89
|
+
pub = rand(2**32).to_s(32)
|
|
90
|
+
public_keys << "ssh-rsa #{pub} #{num}@rightscale.com"
|
|
91
|
+
end
|
|
92
|
+
new_user = RightScale::LoginUser.new("v0-#{num}", "rs-#{num}", nil, "#{num}@rightscale.old", true, nil, public_keys)
|
|
93
|
+
new_user.public_key.should == public_keys.first
|
|
94
|
+
new_user.public_keys.should == public_keys
|
|
95
|
+
test_serialization_downgrade(new_user, public_keys.first)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#-- -*- mode: ruby; encoding: utf-8 -*-
|
|
2
|
+
# Copyright: Copyright (c) 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 NONINFRINGEMENT.
|
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
21
|
+
# 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', 'core_payload_types'))
|
|
26
|
+
|
|
27
|
+
module RightScale
|
|
28
|
+
describe RightScriptAttachment do
|
|
29
|
+
context 'as a class' do
|
|
30
|
+
it 'should compute a simple hash correctly' do
|
|
31
|
+
RightScriptAttachment.hash_for('http://foo.bar.baz/', 'index.html', 'bsthl').should ==
|
|
32
|
+
Digest::SHA1.hexdigest("http://foo.bar.baz/\000bsthl")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should compute a hash with a query term correctly' do
|
|
36
|
+
RightScriptAttachment.hash_for('http://foo.bar.baz/?q=bar;baz=foo', 'index.html', 'bsthl').should ==
|
|
37
|
+
Digest::SHA1.hexdigest("http://foo.bar.baz/\000bsthl")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'as an instance' do
|
|
42
|
+
it 'should compute a simple hash correctly' do
|
|
43
|
+
RightScriptAttachment.new('http://foo.bar.baz/', 'index.html', 'bsthl').to_hash.should ==
|
|
44
|
+
Digest::SHA1.hexdigest("http://foo.bar.baz/\000bsthl")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should compute a hash with a query term correctly' do
|
|
48
|
+
RightScriptAttachment.new('http://foo.bar.baz/?q=bar;baz=foo', 'index.html', 'bsthl').to_hash.should ==
|
|
49
|
+
Digest::SHA1.hexdigest("http://foo.bar.baz/\000bsthl")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'should know how to fill out a session' do
|
|
53
|
+
attachment = RightScriptAttachment.new('http://foo.bar.baz/', 'index.html', 'bsthl')
|
|
54
|
+
session = flexmock('session')
|
|
55
|
+
session.should_receive(:[]=).with('scope', 'attachments').once
|
|
56
|
+
session.should_receive(:[]=).with('resource', attachment.to_hash).once
|
|
57
|
+
session.should_receive(:[]=).with('url', 'http://foo.bar.baz/').once
|
|
58
|
+
session.should_receive(:[]=).with('etag', 'bsthl').once
|
|
59
|
+
session.should_receive(:to_s).and_return("blah").once
|
|
60
|
+
attachment.fill_out(session)
|
|
61
|
+
attachment.token.should == "blah"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
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', 'spec_helper'))
|
|
@@ -0,0 +1,372 @@
|
|
|
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
|
+
class Foo
|
|
26
|
+
include RightScale::Actor
|
|
27
|
+
expose :bar, :index, :i_kill_you
|
|
28
|
+
on_exception :handle_exception
|
|
29
|
+
|
|
30
|
+
def index(payload)
|
|
31
|
+
bar(payload)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def bar(payload)
|
|
35
|
+
['hello', payload]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def i_kill_you(payload)
|
|
39
|
+
raise RuntimeError.new('I kill you!')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def handle_exception(method, deliverable, error)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Bar
|
|
47
|
+
include RightScale::Actor
|
|
48
|
+
expose :i_kill_you
|
|
49
|
+
on_exception do |method, deliverable, error|
|
|
50
|
+
@scope = self
|
|
51
|
+
@called_with = [method, deliverable, error]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def i_kill_you(payload)
|
|
55
|
+
raise RuntimeError.new('I kill you!')
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# No specs, simply ensures multiple methods for assigning on_exception callback,
|
|
60
|
+
# on_exception raises exception when called with an invalid argument.
|
|
61
|
+
class Doomed
|
|
62
|
+
include RightScale::Actor
|
|
63
|
+
on_exception do
|
|
64
|
+
end
|
|
65
|
+
on_exception lambda {}
|
|
66
|
+
on_exception :doh
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Mock the EventMachine deferrer.
|
|
70
|
+
class EMMock
|
|
71
|
+
def self.defer(op = nil, callback = nil)
|
|
72
|
+
callback.call(op.call)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Mock the EventMachine deferrer but do not do callback.
|
|
77
|
+
class EMMockNoCallback
|
|
78
|
+
def self.defer(op = nil, callback = nil)
|
|
79
|
+
op.call
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe "RightScale::Dispatcher" do
|
|
84
|
+
|
|
85
|
+
include FlexMock::ArgumentTypes
|
|
86
|
+
|
|
87
|
+
before(:each) do
|
|
88
|
+
flexmock(RightScale::Log).should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
|
89
|
+
flexmock(RightScale::Log).should_receive(:info).by_default
|
|
90
|
+
@now = Time.at(1000000)
|
|
91
|
+
flexmock(Time).should_receive(:now).and_return(@now).by_default
|
|
92
|
+
@broker = flexmock("Broker", :subscribe => true, :publish => true).by_default
|
|
93
|
+
@actor = Foo.new
|
|
94
|
+
@registry = RightScale::ActorRegistry.new
|
|
95
|
+
@registry.register(@actor, nil)
|
|
96
|
+
@agent = flexmock("Agent", :identity => "agent", :broker => @broker, :registry => @registry, :options => {}).by_default
|
|
97
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
98
|
+
@dispatcher.em = EMMock
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe "Dispatched cache" do
|
|
102
|
+
|
|
103
|
+
before(:each) do
|
|
104
|
+
@dispatched = RightScale::Dispatcher::Dispatched.new
|
|
105
|
+
@token1 = "token1"
|
|
106
|
+
@token2 = "token2"
|
|
107
|
+
@token3 = "token3"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context "when storing" do
|
|
111
|
+
|
|
112
|
+
it "should store request token" do
|
|
113
|
+
@dispatched.store(@token1)
|
|
114
|
+
@dispatched.instance_variable_get(:@cache)[@token1].should == @now.to_i
|
|
115
|
+
@dispatched.instance_variable_get(:@lru).should == [@token1]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "should update lru list when store to existing entry" do
|
|
119
|
+
@dispatched.store(@token1)
|
|
120
|
+
@dispatched.instance_variable_get(:@cache)[@token1].should == @now.to_i
|
|
121
|
+
@dispatched.instance_variable_get(:@lru).should == [@token1]
|
|
122
|
+
@dispatched.store(@token2)
|
|
123
|
+
@dispatched.instance_variable_get(:@cache)[@token2].should == @now.to_i
|
|
124
|
+
@dispatched.instance_variable_get(:@lru).should == [@token1, @token2]
|
|
125
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
|
126
|
+
@dispatched.store(@token1)
|
|
127
|
+
@dispatched.instance_variable_get(:@cache)[@token1].should == @now.to_i
|
|
128
|
+
@dispatched.instance_variable_get(:@lru).should == [@token2, @token1]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "should remove old cache entries when store new one" do
|
|
132
|
+
@dispatched.store(@token1)
|
|
133
|
+
@dispatched.store(@token2)
|
|
134
|
+
@dispatched.instance_variable_get(:@cache).keys.should == [@token1, @token2]
|
|
135
|
+
@dispatched.instance_variable_get(:@lru).should == [@token1, @token2]
|
|
136
|
+
flexmock(Time).should_receive(:now).and_return(@now += RightScale::Dispatcher::Dispatched::MAX_AGE + 1)
|
|
137
|
+
@dispatched.store(@token3)
|
|
138
|
+
@dispatched.instance_variable_get(:@cache).keys.should == [@token3]
|
|
139
|
+
@dispatched.instance_variable_get(:@lru).should == [@token3]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
context "when fetching" do
|
|
145
|
+
|
|
146
|
+
it "should fetch request and make it the most recently used" do
|
|
147
|
+
@dispatched.store(@token1)
|
|
148
|
+
@dispatched.store(@token2)
|
|
149
|
+
@dispatched.instance_variable_get(:@lru).should == [@token1, @token2]
|
|
150
|
+
@dispatched.fetch(@token1).should be_true
|
|
151
|
+
@dispatched.instance_variable_get(:@lru).should == [@token2, @token1]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "should return false if fetch non-existent request" do
|
|
155
|
+
@dispatched.fetch(@token1).should be_false
|
|
156
|
+
@dispatched.store(@token1)
|
|
157
|
+
@dispatched.fetch(@token1).should be_true
|
|
158
|
+
@dispatched.fetch(@token2).should be_false
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
context "when retrieving stats" do
|
|
164
|
+
|
|
165
|
+
it "should return nil if cache empty" do
|
|
166
|
+
@dispatched.stats.should be_nil
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "should return total, youngest age, and oldest age" do
|
|
170
|
+
@dispatched.store(@token1)
|
|
171
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
|
172
|
+
@dispatched.store(@token2)
|
|
173
|
+
stats = @dispatched.stats
|
|
174
|
+
stats["total"].should == 2
|
|
175
|
+
stats["youngest age"].should == 0
|
|
176
|
+
stats["oldest age"].should == 10
|
|
177
|
+
@dispatched.fetch(@token1)
|
|
178
|
+
stats = @dispatched.stats
|
|
179
|
+
stats["oldest age"].should == 0
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
end # Dispatched
|
|
185
|
+
|
|
186
|
+
it "should dispatch a request" do
|
|
187
|
+
req = RightScale::Request.new('/foo/bar', 'you', :token => 'token')
|
|
188
|
+
res = @dispatcher.dispatch(req)
|
|
189
|
+
res.should(be_kind_of(RightScale::Result))
|
|
190
|
+
res.token.should == 'token'
|
|
191
|
+
res.results.should == ['hello', 'you']
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should dispatch a request to the default action" do
|
|
195
|
+
req = RightScale::Request.new('/foo', 'you', :token => 'token')
|
|
196
|
+
res = @dispatcher.dispatch(req)
|
|
197
|
+
res.should(be_kind_of(RightScale::Result))
|
|
198
|
+
res.token.should == req.token
|
|
199
|
+
res.results.should == ['hello', 'you']
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "should handle custom prefixes" do
|
|
203
|
+
@registry.register(Foo.new, 'umbongo')
|
|
204
|
+
req = RightScale::Request.new('/umbongo/bar', 'you')
|
|
205
|
+
res = @dispatcher.dispatch(req)
|
|
206
|
+
res.should(be_kind_of(RightScale::Result))
|
|
207
|
+
res.token.should == req.token
|
|
208
|
+
res.results.should == ['hello', 'you']
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "should call the on_exception callback if something goes wrong" do
|
|
212
|
+
flexmock(RightScale::Log).should_receive(:error).once
|
|
213
|
+
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
|
214
|
+
flexmock(@actor).should_receive(:handle_exception).with(:i_kill_you, req, Exception).once
|
|
215
|
+
@dispatcher.dispatch(req)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it "should call on_exception Procs defined in a subclass with the correct arguments" do
|
|
219
|
+
flexmock(RightScale::Log).should_receive(:error).once
|
|
220
|
+
actor = Bar.new
|
|
221
|
+
@registry.register(actor, nil)
|
|
222
|
+
req = RightScale::Request.new('/bar/i_kill_you', nil)
|
|
223
|
+
@dispatcher.dispatch(req)
|
|
224
|
+
called_with = actor.instance_variable_get("@called_with")
|
|
225
|
+
called_with[0].should == :i_kill_you
|
|
226
|
+
called_with[1].should == req
|
|
227
|
+
called_with[2].should be_kind_of(RuntimeError)
|
|
228
|
+
called_with[2].message.should == 'I kill you!'
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
it "should call on_exception Procs defined in a subclass in the scope of the actor" do
|
|
232
|
+
flexmock(RightScale::Log).should_receive(:error).once
|
|
233
|
+
actor = Bar.new
|
|
234
|
+
@registry.register(actor, nil)
|
|
235
|
+
req = RightScale::Request.new('/bar/i_kill_you', nil)
|
|
236
|
+
@dispatcher.dispatch(req)
|
|
237
|
+
actor.instance_variable_get("@scope").should == actor
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "should log error if something goes wrong" do
|
|
241
|
+
RightScale::Log.should_receive(:error).once
|
|
242
|
+
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
|
243
|
+
@dispatcher.dispatch(req)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it "should reject requests whose time-to-live has expired" do
|
|
247
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
|
248
|
+
flexmock(RightScale::Log).should_receive(:info).once.with(on {|arg| arg =~ /REJECT EXPIRED.*TTL 2 sec ago/})
|
|
249
|
+
@broker.should_receive(:publish).never
|
|
250
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
251
|
+
@dispatcher.em = EMMock
|
|
252
|
+
req = RightScale::Push.new('/foo/bar', 'you', :expires_at => @now.to_i + 8)
|
|
253
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
|
254
|
+
@dispatcher.dispatch(req).should be_nil
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it "should send non-delivery result if Request is rejected because its time-to-live has expired" do
|
|
258
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
|
259
|
+
flexmock(RightScale::Log).should_receive(:info).once.with(on {|arg| arg =~ /REJECT EXPIRED/})
|
|
260
|
+
@broker.should_receive(:publish).with(Hash, on {|arg| arg.class == RightScale::Result &&
|
|
261
|
+
arg.results.non_delivery? &&
|
|
262
|
+
arg.results.content == RightScale::OperationResult::TTL_EXPIRATION},
|
|
263
|
+
hsh(:persistent => true, :mandatory => true)).once
|
|
264
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
265
|
+
@dispatcher.em = EMMock
|
|
266
|
+
req = RightScale::Request.new('/foo/bar', 'you', :expires_at => @now.to_i + 8)
|
|
267
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
|
268
|
+
@dispatcher.dispatch(req).should be_nil
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "should send error result instead of non-delivery if agent does not know about non-delivery" do
|
|
272
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
|
273
|
+
flexmock(RightScale::Log).should_receive(:info).once.with(on {|arg| arg =~ /REJECT EXPIRED/})
|
|
274
|
+
@broker.should_receive(:publish).with(Hash, on {|arg| arg.class == RightScale::Result &&
|
|
275
|
+
arg.results.error? &&
|
|
276
|
+
arg.results.content =~ /Could not deliver/},
|
|
277
|
+
hsh(:persistent => true, :mandatory => true)).once
|
|
278
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
279
|
+
@dispatcher.em = EMMock
|
|
280
|
+
req = RightScale::Request.new('/foo/bar', 'you', {:expires_at => @now.to_i + 8}, [12, 13])
|
|
281
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
|
282
|
+
@dispatcher.dispatch(req).should be_nil
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
it "should not reject requests whose time-to-live has not expired" do
|
|
286
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
|
287
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
288
|
+
@dispatcher.em = EMMock
|
|
289
|
+
req = RightScale::Request.new('/foo/bar', 'you', :expires_at => @now.to_i + 11)
|
|
290
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
|
291
|
+
res = @dispatcher.dispatch(req)
|
|
292
|
+
res.should(be_kind_of(RightScale::Result))
|
|
293
|
+
res.token.should == req.token
|
|
294
|
+
res.results.should == ['hello', 'you']
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "should not check age of requests with time-to-live check disabled" do
|
|
298
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
299
|
+
@dispatcher.em = EMMock
|
|
300
|
+
req = RightScale::Request.new('/foo/bar', 'you', :expires_at => 0)
|
|
301
|
+
res = @dispatcher.dispatch(req)
|
|
302
|
+
res.should(be_kind_of(RightScale::Result))
|
|
303
|
+
res.token.should == req.token
|
|
304
|
+
res.results.should == ['hello', 'you']
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "should reject duplicate requests" do
|
|
308
|
+
flexmock(RightScale::Log).should_receive(:info).once.with(on {|arg| arg =~ /REJECT DUP/})
|
|
309
|
+
EM.run do
|
|
310
|
+
@agent.should_receive(:options).and_return(:dup_check => true)
|
|
311
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
312
|
+
req = RightScale::Request.new('/foo/bar', 'you', :token => "try")
|
|
313
|
+
@dispatcher.instance_variable_get(:@dispatched).store(req.token)
|
|
314
|
+
@dispatcher.dispatch(req).should be_nil
|
|
315
|
+
EM.stop
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
it "should reject duplicate retry requests" do
|
|
320
|
+
flexmock(RightScale::Log).should_receive(:info).once.with(on {|arg| arg =~ /REJECT RETRY DUP/})
|
|
321
|
+
EM.run do
|
|
322
|
+
@agent.should_receive(:options).and_return(:dup_check => true)
|
|
323
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
324
|
+
req = RightScale::Request.new('/foo/bar', 'you', :token => "try")
|
|
325
|
+
req.tries.concat(["try1", "try2"])
|
|
326
|
+
@dispatcher.instance_variable_get(:@dispatched).store("try2")
|
|
327
|
+
@dispatcher.dispatch(req).should be_nil
|
|
328
|
+
EM.stop
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
it "should not reject non-duplicate requests" do
|
|
333
|
+
EM.run do
|
|
334
|
+
@agent.should_receive(:options).and_return(:dup_check => true)
|
|
335
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
336
|
+
req = RightScale::Request.new('/foo/bar', 'you', :token => "try")
|
|
337
|
+
req.tries.concat(["try1", "try2"])
|
|
338
|
+
@dispatcher.instance_variable_get(:@dispatched).store("try3")
|
|
339
|
+
@dispatcher.dispatch(req).should_not be_nil
|
|
340
|
+
EM.stop
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
it "should not check for duplicates if dup_check disabled" do
|
|
345
|
+
EM.run do
|
|
346
|
+
@dispatcher = RightScale::Dispatcher.new(@agent)
|
|
347
|
+
req = RightScale::Request.new('/foo/bar', 'you', :token => "try")
|
|
348
|
+
req.tries.concat(["try1", "try2"])
|
|
349
|
+
@dispatcher.instance_variable_get(:@dispatched).should be_nil
|
|
350
|
+
@dispatcher.dispatch(req).should_not be_nil
|
|
351
|
+
EM.stop
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "should return dispatch age of youngest unfinished request" do
|
|
356
|
+
@dispatcher.em = EMMockNoCallback
|
|
357
|
+
@dispatcher.dispatch_age.should be_nil
|
|
358
|
+
@dispatcher.dispatch(RightScale::Push.new('/foo/bar', 'you'))
|
|
359
|
+
@dispatcher.dispatch_age.should == 0
|
|
360
|
+
@dispatcher.dispatch(RightScale::Request.new('/foo/bar', 'you'))
|
|
361
|
+
flexmock(Time).should_receive(:now).and_return(@now += 100)
|
|
362
|
+
@dispatcher.dispatch_age.should == 100
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
it "should return dispatch age of nil if all requests finished" do
|
|
366
|
+
@dispatcher.dispatch_age.should be_nil
|
|
367
|
+
@dispatcher.dispatch(RightScale::Request.new('/foo/bar', 'you'))
|
|
368
|
+
flexmock(Time).should_receive(:now).and_return(@now += 100)
|
|
369
|
+
@dispatcher.dispatch_age.should be_nil
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
end # RightScale::Dispatcher
|