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,61 @@
|
|
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
|
+
|
24
|
+
module RightScale
|
25
|
+
|
26
|
+
# Software repository
|
27
|
+
# May or may not be frozen depending on whether frozen_date is set
|
28
|
+
class SoftwareRepositoryInstantiation
|
29
|
+
|
30
|
+
include Serializable
|
31
|
+
|
32
|
+
# (String) Software repository name
|
33
|
+
attr_accessor :name
|
34
|
+
|
35
|
+
# (Array) Software repository base URL
|
36
|
+
attr_accessor :base_urls
|
37
|
+
|
38
|
+
# (Date) Frozen date if any
|
39
|
+
attr_accessor :frozen_date
|
40
|
+
|
41
|
+
def initialize(*args)
|
42
|
+
@name = args[0] if args.size > 0
|
43
|
+
@base_urls = args[1] if args.size > 1
|
44
|
+
@frozen_date = args[2] if args.size > 2
|
45
|
+
end
|
46
|
+
|
47
|
+
# Human readable representation
|
48
|
+
#
|
49
|
+
# === Return
|
50
|
+
# Text representing repository instantiation that can be audited
|
51
|
+
def to_s
|
52
|
+
res = "#{name} #{base_urls.inspect}"
|
53
|
+
frozen_date ? res + " @ #{frozen_date.to_s}" : res
|
54
|
+
end
|
55
|
+
|
56
|
+
# Array of serialized fields given to constructor
|
57
|
+
def serialized_members
|
58
|
+
[ @name, @base_urls, @frozen_date ]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,35 @@
|
|
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
|
+
module RightScale
|
24
|
+
module DaemonizeHelper
|
25
|
+
def daemonize(identity, options = {})
|
26
|
+
exit if fork
|
27
|
+
Process.setsid
|
28
|
+
File.umask 0022
|
29
|
+
exit if fork
|
30
|
+
STDIN.reopen "/dev/null"
|
31
|
+
STDOUT.reopen "#{options[:log_path]}/#{identity}.out", "a"
|
32
|
+
STDERR.reopen "#{options[:log_path]}/#{identity}.err", "a"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2012 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
|
+
module RightScale
|
24
|
+
|
25
|
+
# Cache for requests that have been dispatched recently
|
26
|
+
# This cache is intended for use in checking for duplicate requests
|
27
|
+
# when there is only one server servicing a queue
|
28
|
+
class DispatchedCache
|
29
|
+
|
30
|
+
# Maximum number of seconds to retain a dispatched request in cache
|
31
|
+
# This must be greater than the maximum possible retry timeout to avoid
|
32
|
+
# duplicate execution of a request
|
33
|
+
MAX_AGE = 12 * 60 * 60
|
34
|
+
|
35
|
+
# Initialize cache
|
36
|
+
#
|
37
|
+
# === Parameters
|
38
|
+
# identity(String):: Serialized identity of agent
|
39
|
+
def initialize(identity)
|
40
|
+
@identity = identity
|
41
|
+
@cache = {}
|
42
|
+
@lru = []
|
43
|
+
@max_age = MAX_AGE
|
44
|
+
end
|
45
|
+
|
46
|
+
# Store dispatched request token in cache
|
47
|
+
#
|
48
|
+
# === Parameters
|
49
|
+
# token(String):: Generated message identifier
|
50
|
+
#
|
51
|
+
# === Return
|
52
|
+
# true:: Always return true
|
53
|
+
def store(token)
|
54
|
+
if token
|
55
|
+
now = Time.now.to_i
|
56
|
+
if @cache.has_key?(token)
|
57
|
+
@cache[token] = now
|
58
|
+
@lru.push(@lru.delete(token))
|
59
|
+
else
|
60
|
+
@cache[token] = now
|
61
|
+
@lru.push(token)
|
62
|
+
@cache.delete(@lru.shift) while (now - @cache[@lru.first]) > @max_age
|
63
|
+
end
|
64
|
+
end
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
# Determine whether request has already been serviced
|
69
|
+
#
|
70
|
+
# === Parameters
|
71
|
+
# token(String):: Generated message identifier
|
72
|
+
#
|
73
|
+
# === Return
|
74
|
+
# (String|nil):: Identity of agent that already serviced request, or nil if none
|
75
|
+
def serviced_by(token)
|
76
|
+
if @cache[token]
|
77
|
+
@cache[token] = Time.now.to_i
|
78
|
+
@lru.push(@lru.delete(token))
|
79
|
+
@identity
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get local cache statistics
|
84
|
+
#
|
85
|
+
# === Return
|
86
|
+
# stats(Hash|nil):: Current statistics, or nil if cache empty
|
87
|
+
# "local total"(Integer):: Total number in local cache, or nil if none
|
88
|
+
# "local max age"(String):: Time since oldest local cache entry created or updated
|
89
|
+
def stats
|
90
|
+
if (s = size) > 0
|
91
|
+
now = Time.now.to_i
|
92
|
+
{
|
93
|
+
"local total" => s,
|
94
|
+
"local max age" => RightSupport::Stats.elapsed(now - @cache[@lru.first])
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Get local cache size
|
100
|
+
#
|
101
|
+
# === Return
|
102
|
+
# (Integer):: Number of cache entries
|
103
|
+
def size
|
104
|
+
@cache.size
|
105
|
+
end
|
106
|
+
|
107
|
+
end # DispatchedCache
|
108
|
+
|
109
|
+
end # RightScale
|
@@ -0,0 +1,272 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2012 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
|
+
module RightScale
|
24
|
+
|
25
|
+
# Dispatching of payload to specified actor
|
26
|
+
class Dispatcher
|
27
|
+
|
28
|
+
include ProtocolVersionMixin
|
29
|
+
|
30
|
+
class InvalidRequestType < Exception; end
|
31
|
+
class DuplicateRequest < Exception; end
|
32
|
+
|
33
|
+
# (ActorRegistry) Registry for actors
|
34
|
+
attr_reader :registry
|
35
|
+
|
36
|
+
# (String) Identity of associated agent
|
37
|
+
attr_reader :identity
|
38
|
+
|
39
|
+
# For direct access to current dispatcher
|
40
|
+
#
|
41
|
+
# === Return
|
42
|
+
# (Dispatcher):: This dispatcher instance if defined, otherwise nil
|
43
|
+
def self.instance
|
44
|
+
@@instance if defined?(@@instance)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Initialize dispatcher
|
48
|
+
#
|
49
|
+
# === Parameters
|
50
|
+
# agent(Agent):: Agent using this dispatcher; uses its identity and registry
|
51
|
+
# dispatched_cache(DispatchedCache|nil):: Cache for dispatched requests that is used for detecting
|
52
|
+
# duplicate requests, or nil if duplicate checking is disabled
|
53
|
+
def initialize(agent, dispatched_cache = nil)
|
54
|
+
@agent = agent
|
55
|
+
@registry = @agent.registry
|
56
|
+
@identity = @agent.identity
|
57
|
+
@dispatched_cache = dispatched_cache
|
58
|
+
reset_stats
|
59
|
+
@@instance = self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Determine whether able to route requests to specified actor
|
63
|
+
#
|
64
|
+
# === Parameters
|
65
|
+
# actor(String):: Actor name
|
66
|
+
#
|
67
|
+
# === Return
|
68
|
+
# (Boolean):: true if can route to actor, otherwise false
|
69
|
+
def routable?(actor)
|
70
|
+
!!@registry.actor_for(actor)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Route request to appropriate actor for servicing
|
74
|
+
# Reject requests whose TTL has expired or that are duplicates of work already dispatched
|
75
|
+
#
|
76
|
+
# === Parameters
|
77
|
+
# request(Request|Push):: Packet containing request
|
78
|
+
# header(AMQP::Frame::Header|nil):: Request header containing ack control
|
79
|
+
#
|
80
|
+
# === Return
|
81
|
+
# (Result|nil):: Result of request, or nil if there is no result because request is a Push
|
82
|
+
#
|
83
|
+
# === Raise
|
84
|
+
# InvalidRequestType:: If the request cannot be routed to an actor
|
85
|
+
# DuplicateRequest:: If request rejected because it has already been processed
|
86
|
+
def dispatch(request)
|
87
|
+
token = request.token
|
88
|
+
actor, method, idempotent = route(request)
|
89
|
+
received_at = @request_stats.update(method, (token if request.is_a?(Request)))
|
90
|
+
if (dup = duplicate?(request, method, idempotent))
|
91
|
+
raise DuplicateRequest.new(dup)
|
92
|
+
end
|
93
|
+
unless (result = expired?(request, method))
|
94
|
+
result = perform(request, actor, method, idempotent)
|
95
|
+
end
|
96
|
+
if request.is_a?(Request)
|
97
|
+
duration = @request_stats.finish(received_at, token)
|
98
|
+
Result.new(token, request.reply_to, result, @identity, request.from, request.tries, request.persistent, duration)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get dispatcher statistics
|
103
|
+
#
|
104
|
+
# === Parameters
|
105
|
+
# reset(Boolean):: Whether to reset the statistics after getting the current ones
|
106
|
+
#
|
107
|
+
# === Return
|
108
|
+
# stats(Hash):: Current statistics:
|
109
|
+
# "dispatched cache"(Hash|nil):: Number of dispatched requests cached and age of youngest and oldest,
|
110
|
+
# or nil if empty
|
111
|
+
# "dispatch failures"(Hash|nil):: Dispatch failure activity stats with keys "total", "percent", "last", and "rate"
|
112
|
+
# with percentage breakdown per failure type, or nil if none
|
113
|
+
# "exceptions"(Hash|nil):: Exceptions raised per category, or nil if none
|
114
|
+
# "total"(Integer):: Total for category
|
115
|
+
# "recent"(Array):: Most recent as a hash of "count", "type", "message", "when", and "where"
|
116
|
+
# "rejects"(Hash|nil):: Request reject activity stats with keys "total", "percent", "last", and "rate"
|
117
|
+
# with percentage breakdown per reason ("duplicate (<method>)", "retry duplicate (<method>)", or
|
118
|
+
# "stale (<method>)"), or nil if none
|
119
|
+
# "requests"(Hash|nil):: Request activity stats with keys "total", "percent", "last", and "rate"
|
120
|
+
# with percentage breakdown per request type, or nil if none
|
121
|
+
# "response time"(Float):: Average number of seconds to respond to a request recently
|
122
|
+
def stats(reset = false)
|
123
|
+
stats = {
|
124
|
+
"dispatched cache" => (@dispatched_cache.stats if @dispatched_cache),
|
125
|
+
"dispatch failures" => @dispatch_failure_stats.all,
|
126
|
+
"exceptions" => @exception_stats.stats,
|
127
|
+
"rejects" => @reject_stats.all,
|
128
|
+
"requests" => @request_stats.all,
|
129
|
+
"response time" => @request_stats.avg_duration
|
130
|
+
}
|
131
|
+
reset_stats if reset
|
132
|
+
stats
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# Reset dispatch statistics
|
138
|
+
#
|
139
|
+
# === Return
|
140
|
+
# true:: Always return true
|
141
|
+
def reset_stats
|
142
|
+
@reject_stats = RightSupport::Stats::Activity.new
|
143
|
+
@request_stats = RightSupport::Stats::Activity.new
|
144
|
+
@dispatch_failure_stats = RightSupport::Stats::Activity.new
|
145
|
+
@exception_stats = RightSupport::Stats::Exceptions.new(@agent, @agent.exception_callback)
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
# Determine if request TTL has expired
|
150
|
+
#
|
151
|
+
# === Parameters
|
152
|
+
# request(Push|Request):: Request to be checked
|
153
|
+
# method(String):: Actor method requested to be performed
|
154
|
+
#
|
155
|
+
# === Return
|
156
|
+
# (OperationResult|nil):: Error result if expired, otherwise nil
|
157
|
+
def expired?(request, method)
|
158
|
+
if (expires_at = request.expires_at) && expires_at > 0 && (now = Time.now.to_i) >= expires_at
|
159
|
+
@reject_stats.update("expired (#{method})")
|
160
|
+
Log.info("REJECT EXPIRED <#{request.token}> from #{request.from} TTL #{RightSupport::Stats.elapsed(now - expires_at)} ago")
|
161
|
+
# For agents that do not know about non-delivery, use error result
|
162
|
+
if can_handle_non_delivery_result?(request.recv_version)
|
163
|
+
OperationResult.non_delivery(OperationResult::TTL_EXPIRATION)
|
164
|
+
else
|
165
|
+
OperationResult.error("Could not deliver request (#{OperationResult::TTL_EXPIRATION})")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Determine whether this request is a duplicate
|
171
|
+
#
|
172
|
+
# === Parameters
|
173
|
+
# request(Request|Push):: Packet containing request
|
174
|
+
# method(String):: Actor method requested to be performed
|
175
|
+
# idempotent(Boolean):: Whether this method is idempotent
|
176
|
+
#
|
177
|
+
# === Return
|
178
|
+
# (String|nil):: Messaging describing who already serviced request if it is a duplicate, otherwise nil
|
179
|
+
def duplicate?(request, method, idempotent)
|
180
|
+
if !idempotent && @dispatched_cache
|
181
|
+
if (serviced_by = @dispatched_cache.serviced_by(request.token))
|
182
|
+
from_retry = ""
|
183
|
+
else
|
184
|
+
from_retry = "retry "
|
185
|
+
request.tries.each { |t| break if (serviced_by = @dispatched_cache.serviced_by(t)) }
|
186
|
+
end
|
187
|
+
if serviced_by
|
188
|
+
@reject_stats.update("#{from_retry}duplicate (#{method})")
|
189
|
+
msg = "<#{request.token}> already serviced by #{serviced_by == @identity ? 'self' : serviced_by}"
|
190
|
+
Log.info("REJECT #{from_retry.upcase}DUP #{msg}")
|
191
|
+
msg
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Use request type to route request to actor and an associated method
|
197
|
+
#
|
198
|
+
# === Parameters
|
199
|
+
# request(Push|Request):: Packet containing request
|
200
|
+
#
|
201
|
+
# === Return
|
202
|
+
# (Array):: Actor name, method name, and whether method is idempotent
|
203
|
+
#
|
204
|
+
# === Raise
|
205
|
+
# InvalidRequestType:: If the request cannot be routed to an actor
|
206
|
+
def route(request)
|
207
|
+
prefix, method = request.type.split('/')[1..-1]
|
208
|
+
method ||= :index
|
209
|
+
method = method.to_sym
|
210
|
+
actor = @registry.actor_for(prefix)
|
211
|
+
if actor.nil? || !actor.respond_to?(method)
|
212
|
+
raise InvalidRequestType.new("Unknown actor or method for dispatching request <#{request.token}> of type #{request.type}")
|
213
|
+
end
|
214
|
+
[actor, method, actor.class.idempotent?(method)]
|
215
|
+
end
|
216
|
+
|
217
|
+
# Perform requested action
|
218
|
+
#
|
219
|
+
# === Parameters
|
220
|
+
# request(Push|Request):: Packet containing request
|
221
|
+
# token(String):: Unique identity token for request
|
222
|
+
# method(String):: Actor method requested to be performed
|
223
|
+
# idempotent(Boolean):: Whether this method is idempotent
|
224
|
+
#
|
225
|
+
# === Return
|
226
|
+
# (OperationResult):: Result from performing a request
|
227
|
+
def perform(request, actor, method, idempotent)
|
228
|
+
@dispatched_cache.store(request.token) if @dispatched_cache && !idempotent
|
229
|
+
if actor.method(method).arity.abs == 1
|
230
|
+
actor.send(method, request.payload)
|
231
|
+
else
|
232
|
+
actor.send(method, request.payload, request)
|
233
|
+
end
|
234
|
+
rescue Exception => e
|
235
|
+
@dispatch_failure_stats.update("#{request.type}->#{e.class.name}")
|
236
|
+
OperationResult.error(handle_exception(actor, method, request, e))
|
237
|
+
end
|
238
|
+
|
239
|
+
# Handle exception by logging it, calling the actors exception callback method,
|
240
|
+
# and gathering exception statistics
|
241
|
+
#
|
242
|
+
# === Parameters
|
243
|
+
# actor(Actor):: Actor that failed to process request
|
244
|
+
# method(Symbol):: Name of actor method being dispatched to
|
245
|
+
# request(Packet):: Packet that dispatcher is acting upon
|
246
|
+
# exception(Exception):: Exception that was raised
|
247
|
+
#
|
248
|
+
# === Return
|
249
|
+
# (String):: Error description for this exception
|
250
|
+
def handle_exception(actor, method, request, exception)
|
251
|
+
error = "Could not handle #{request.type} request"
|
252
|
+
Log.error(error, exception, :trace)
|
253
|
+
begin
|
254
|
+
if actor && actor.class.exception_callback
|
255
|
+
case actor.class.exception_callback
|
256
|
+
when Symbol, String
|
257
|
+
actor.send(actor.class.exception_callback, method, request, exception)
|
258
|
+
when Proc
|
259
|
+
actor.instance_exec(method, request, exception, &actor.class.exception_callback)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
@exception_stats.track(request.type, exception)
|
263
|
+
rescue Exception => e
|
264
|
+
Log.error("Failed handling error for #{request.type}", e, :trace)
|
265
|
+
@exception_stats.track(request.type, e) rescue nil
|
266
|
+
end
|
267
|
+
Log.format(error, exception)
|
268
|
+
end
|
269
|
+
|
270
|
+
end # Dispatcher
|
271
|
+
|
272
|
+
end # RightScale
|