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.
Files changed (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +82 -0
  3. data/Rakefile +113 -0
  4. data/lib/right_agent.rb +59 -0
  5. data/lib/right_agent/actor.rb +182 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +232 -0
  8. data/lib/right_agent/agent.rb +1149 -0
  9. data/lib/right_agent/agent_config.rb +480 -0
  10. data/lib/right_agent/agent_identity.rb +210 -0
  11. data/lib/right_agent/agent_tag_manager.rb +237 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/clients.rb +31 -0
  14. data/lib/right_agent/clients/api_client.rb +383 -0
  15. data/lib/right_agent/clients/auth_client.rb +247 -0
  16. data/lib/right_agent/clients/balanced_http_client.rb +369 -0
  17. data/lib/right_agent/clients/base_retry_client.rb +495 -0
  18. data/lib/right_agent/clients/right_http_client.rb +279 -0
  19. data/lib/right_agent/clients/router_client.rb +493 -0
  20. data/lib/right_agent/command.rb +30 -0
  21. data/lib/right_agent/command/agent_manager_commands.rb +150 -0
  22. data/lib/right_agent/command/command_client.rb +136 -0
  23. data/lib/right_agent/command/command_constants.rb +33 -0
  24. data/lib/right_agent/command/command_io.rb +126 -0
  25. data/lib/right_agent/command/command_parser.rb +87 -0
  26. data/lib/right_agent/command/command_runner.rb +118 -0
  27. data/lib/right_agent/command/command_serializer.rb +63 -0
  28. data/lib/right_agent/connectivity_checker.rb +179 -0
  29. data/lib/right_agent/console.rb +65 -0
  30. data/lib/right_agent/core_payload_types.rb +44 -0
  31. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  32. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  33. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  34. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  35. data/lib/right_agent/core_payload_types/dev_repositories.rb +100 -0
  36. data/lib/right_agent/core_payload_types/dev_repository.rb +76 -0
  37. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  38. data/lib/right_agent/core_payload_types/executable_bundle.rb +130 -0
  39. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  40. data/lib/right_agent/core_payload_types/login_user.rb +79 -0
  41. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  42. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +73 -0
  43. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  44. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  45. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +94 -0
  46. data/lib/right_agent/core_payload_types/runlist_policy.rb +44 -0
  47. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  48. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  49. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  50. data/lib/right_agent/daemonize.rb +35 -0
  51. data/lib/right_agent/dispatched_cache.rb +109 -0
  52. data/lib/right_agent/dispatcher.rb +272 -0
  53. data/lib/right_agent/enrollment_result.rb +221 -0
  54. data/lib/right_agent/exceptions.rb +87 -0
  55. data/lib/right_agent/history.rb +145 -0
  56. data/lib/right_agent/log.rb +460 -0
  57. data/lib/right_agent/minimal.rb +46 -0
  58. data/lib/right_agent/monkey_patches.rb +30 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch.rb +55 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  64. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  65. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  66. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +60 -0
  67. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  68. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  69. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  70. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  71. data/lib/right_agent/multiplexer.rb +102 -0
  72. data/lib/right_agent/offline_handler.rb +270 -0
  73. data/lib/right_agent/operation_result.rb +300 -0
  74. data/lib/right_agent/packets.rb +673 -0
  75. data/lib/right_agent/payload_formatter.rb +104 -0
  76. data/lib/right_agent/pending_requests.rb +128 -0
  77. data/lib/right_agent/pid_file.rb +159 -0
  78. data/lib/right_agent/platform.rb +770 -0
  79. data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
  80. data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
  81. data/lib/right_agent/platform/unix/platform.rb +226 -0
  82. data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
  83. data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
  84. data/lib/right_agent/platform/windows/platform.rb +1808 -0
  85. data/lib/right_agent/protocol_version_mixin.rb +69 -0
  86. data/lib/right_agent/retryable_request.rb +195 -0
  87. data/lib/right_agent/scripts/agent_controller.rb +543 -0
  88. data/lib/right_agent/scripts/agent_deployer.rb +400 -0
  89. data/lib/right_agent/scripts/common_parser.rb +160 -0
  90. data/lib/right_agent/scripts/log_level_manager.rb +192 -0
  91. data/lib/right_agent/scripts/stats_manager.rb +268 -0
  92. data/lib/right_agent/scripts/usage.rb +58 -0
  93. data/lib/right_agent/secure_identity.rb +92 -0
  94. data/lib/right_agent/security.rb +32 -0
  95. data/lib/right_agent/security/cached_certificate_store_proxy.rb +77 -0
  96. data/lib/right_agent/security/certificate.rb +102 -0
  97. data/lib/right_agent/security/certificate_cache.rb +89 -0
  98. data/lib/right_agent/security/distinguished_name.rb +56 -0
  99. data/lib/right_agent/security/encrypted_document.rb +83 -0
  100. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  101. data/lib/right_agent/security/signature.rb +86 -0
  102. data/lib/right_agent/security/static_certificate_store.rb +85 -0
  103. data/lib/right_agent/sender.rb +792 -0
  104. data/lib/right_agent/serialize.rb +29 -0
  105. data/lib/right_agent/serialize/message_pack.rb +107 -0
  106. data/lib/right_agent/serialize/secure_serializer.rb +151 -0
  107. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  108. data/lib/right_agent/serialize/serializable.rb +151 -0
  109. data/lib/right_agent/serialize/serializer.rb +159 -0
  110. data/lib/right_agent/subprocess.rb +38 -0
  111. data/lib/right_agent/tracer.rb +124 -0
  112. data/right_agent.gemspec +101 -0
  113. data/spec/actor_registry_spec.rb +80 -0
  114. data/spec/actor_spec.rb +162 -0
  115. data/spec/agent_config_spec.rb +235 -0
  116. data/spec/agent_identity_spec.rb +78 -0
  117. data/spec/agent_spec.rb +734 -0
  118. data/spec/agent_tag_manager_spec.rb +319 -0
  119. data/spec/clients/api_client_spec.rb +423 -0
  120. data/spec/clients/auth_client_spec.rb +272 -0
  121. data/spec/clients/balanced_http_client_spec.rb +576 -0
  122. data/spec/clients/base_retry_client_spec.rb +635 -0
  123. data/spec/clients/router_client_spec.rb +594 -0
  124. data/spec/clients/spec_helper.rb +111 -0
  125. data/spec/command/agent_manager_commands_spec.rb +51 -0
  126. data/spec/command/command_io_spec.rb +93 -0
  127. data/spec/command/command_parser_spec.rb +79 -0
  128. data/spec/command/command_runner_spec.rb +107 -0
  129. data/spec/command/command_serializer_spec.rb +51 -0
  130. data/spec/connectivity_checker_spec.rb +83 -0
  131. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  132. data/spec/core_payload_types/dev_repository_spec.rb +33 -0
  133. data/spec/core_payload_types/executable_bundle_spec.rb +67 -0
  134. data/spec/core_payload_types/login_user_spec.rb +102 -0
  135. data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
  136. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  137. data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
  138. data/spec/core_payload_types/spec_helper.rb +23 -0
  139. data/spec/dispatched_cache_spec.rb +136 -0
  140. data/spec/dispatcher_spec.rb +324 -0
  141. data/spec/enrollment_result_spec.rb +53 -0
  142. data/spec/history_spec.rb +246 -0
  143. data/spec/log_spec.rb +192 -0
  144. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  145. data/spec/multiplexer_spec.rb +48 -0
  146. data/spec/offline_handler_spec.rb +340 -0
  147. data/spec/operation_result_spec.rb +208 -0
  148. data/spec/packets_spec.rb +461 -0
  149. data/spec/pending_requests_spec.rb +136 -0
  150. data/spec/platform/spec_helper.rb +216 -0
  151. data/spec/platform/unix/darwin/platform_spec.rb +181 -0
  152. data/spec/platform/unix/linux/platform_spec.rb +540 -0
  153. data/spec/platform/unix/spec_helper.rb +149 -0
  154. data/spec/platform/windows/mingw/platform_spec.rb +222 -0
  155. data/spec/platform/windows/mswin/platform_spec.rb +259 -0
  156. data/spec/platform/windows/spec_helper.rb +720 -0
  157. data/spec/retryable_request_spec.rb +306 -0
  158. data/spec/secure_identity_spec.rb +50 -0
  159. data/spec/security/cached_certificate_store_proxy_spec.rb +62 -0
  160. data/spec/security/certificate_cache_spec.rb +71 -0
  161. data/spec/security/certificate_spec.rb +49 -0
  162. data/spec/security/distinguished_name_spec.rb +46 -0
  163. data/spec/security/encrypted_document_spec.rb +55 -0
  164. data/spec/security/rsa_key_pair_spec.rb +55 -0
  165. data/spec/security/signature_spec.rb +66 -0
  166. data/spec/security/static_certificate_store_spec.rb +58 -0
  167. data/spec/sender_spec.rb +1045 -0
  168. data/spec/serialize/message_pack_spec.rb +131 -0
  169. data/spec/serialize/secure_serializer_spec.rb +132 -0
  170. data/spec/serialize/serializable_spec.rb +90 -0
  171. data/spec/serialize/serializer_spec.rb +197 -0
  172. data/spec/spec.opts +2 -0
  173. data/spec/spec.win32.opts +1 -0
  174. data/spec/spec_helper.rb +130 -0
  175. data/spec/tracer_spec.rb +114 -0
  176. 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