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,87 @@
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
+ class CommandParser
27
+
28
+ # Register callback block
29
+ #
30
+ # === Block
31
+ # Block that will get called back whenever a command is successfully parsed
32
+ #
33
+ # === Raise
34
+ # (ArgumentError): If block is missing
35
+ def initialize &block
36
+ raise ArgumentError, 'Missing handler block' unless block
37
+ @callback = block
38
+ @buildup = ''
39
+ end
40
+
41
+ # Parse given input
42
+ # May cause multiple callbacks if multiple commands are successfully parsed
43
+ # Callback happens in next EM tick
44
+ #
45
+ # === Parameters
46
+ # chunk(String):: Chunck of serialized command(s) to be parsed
47
+ #
48
+ # === Return
49
+ # true:: If callback was called at least once
50
+ # false:: Otherwise
51
+ def parse_chunk(chunk)
52
+ @buildup << chunk
53
+ chunks = @buildup.split(CommandSerializer::SEPARATOR, -1)
54
+ if do_call = chunks.size > 1
55
+ commands = []
56
+ (0..chunks.size - 2).each do |i|
57
+ begin
58
+ commands << CommandSerializer.load(chunks[i])
59
+ rescue Exception => e
60
+ # log any exceptions caused by serializing individual chunks instead
61
+ # of halting EM. each command is discrete so we need to keep trying
62
+ # so long as there are more commands to process (although subsequent
63
+ # commands may lack context if previous commands failed).
64
+ Log.error("Failed parsing command chunk", e, :trace)
65
+ end
66
+ end
67
+ commands.each do |cmd|
68
+ EM.next_tick do
69
+ begin
70
+ @callback.call(cmd)
71
+ rescue Exception => e
72
+ # log any exceptions raised by callback instead of halting EM.
73
+ Log.error("Failed executing parsed command", e, :trace)
74
+ end
75
+ end
76
+ end
77
+ @buildup = chunks.last
78
+ end
79
+ do_call
80
+ rescue Exception => e
81
+ # log any other exceptions instead of halting EM.
82
+ Log.error("Failed parsing command chunk", e, :trace)
83
+ end
84
+
85
+ end # CommandParser
86
+
87
+ end # RightScale
@@ -0,0 +1,118 @@
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
+
25
+ # Run commands exposed by an agent.
26
+ # External processes can send commands through a socket with the specified port.
27
+ # Command runner accepts connections and unserializes commands using YAML.
28
+ # Each command is expected to be a hash containing the :name and :options keys.
29
+ class CommandRunner
30
+ class << self
31
+ # (Integer) Port command runner is listening on
32
+ attr_reader :listen_port
33
+
34
+ # (String) Cookie used by command protocol
35
+ attr_reader :cookie
36
+ end
37
+
38
+ # Start a command runner listening on a local TCP port.
39
+ #
40
+ # === Parameters
41
+ # socket_port(Integer):: Base socket port on which to listen for connection,
42
+ # increment and retry if port already taken
43
+ # identity(String):: Agent identity
44
+ # commands(Hash):: Commands exposed by agent
45
+ # fiber_pool(NB::FiberPool):: Pool of initialized fibers to be used for executing
46
+ # received commands in non-blocking fashion
47
+ #
48
+ # === Block
49
+ # If a block is provided, this method will yield after all setup has been completed,
50
+ # passing its PidFile to the block. This provides a customization hook, e.g. for
51
+ # changing the pid file's access mode or ownership.
52
+ #
53
+ # === Return
54
+ # cmd_options[:cookie](String):: Command protocol cookie
55
+ # cmd_options[:listen_port](Integer):: Command server listen port
56
+ #
57
+ # === Raise
58
+ # (Exceptions::Application):: If +start+ has already been called and +stop+ hasn't since
59
+ def self.start(socket_port, identity, commands, fiber_pool = nil)
60
+ cmd_options = nil
61
+ @listen_port = socket_port
62
+
63
+ begin
64
+ CommandIO.instance.listen(socket_port) do |c, conn|
65
+ begin
66
+ cmd_cookie = c[:cookie]
67
+ if cmd_cookie == @cookie
68
+ cmd_name = c[:name].to_sym
69
+ if commands.include?(cmd_name)
70
+ if fiber_pool
71
+ fiber_pool.spawn { commands[cmd_name].call(c, conn) }
72
+ else
73
+ commands[cmd_name].call(c, conn)
74
+ end
75
+ else
76
+ Log.warning("Unknown command '#{cmd_name}', known commands: #{commands.keys.join(', ')}")
77
+ end
78
+ else
79
+ Log.error("Invalid cookie used by command protocol client (#{cmd_cookie})")
80
+ end
81
+ rescue Exception => e
82
+ Log.warning("Command failed (#{e.message}) at\n#{e.backtrace.join("\n")}")
83
+ end
84
+ end
85
+
86
+ @cookie = AgentIdentity.generate
87
+ cmd_options = { :listen_port => @listen_port, :cookie => @cookie }
88
+ # Now update pid file with command port and cookie
89
+ pid_file = PidFile.new(identity)
90
+ if pid_file.exists?
91
+ pid_file.set_command_options(cmd_options)
92
+ yield(pid_file) if block_given?
93
+ else
94
+ Log.warning("Failed to update listen port in PID file - no pid file found for agent with identity #{identity}")
95
+ end
96
+
97
+ Log.info("[setup] Command server started listening on port #{@listen_port}")
98
+ rescue Exceptions::IO
99
+ # Port already taken, increment and retry
100
+ cmd_options = start(socket_port + 1, identity, commands)
101
+ end
102
+
103
+ cmd_options
104
+ end
105
+
106
+ # Stop command runner, cleanup all opened file descriptors and delete pipe
107
+ #
108
+ # === Return
109
+ # true:: If command listener was listening
110
+ # false:: Otherwise
111
+ def self.stop
112
+ CommandIO.instance.stop_listening
113
+ Log.info("[stop] Command server stopped listening")
114
+ end
115
+
116
+ end
117
+
118
+ end
@@ -0,0 +1,63 @@
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
+ class CommandSerializer
27
+
28
+ # String appended to serialized data to delimit between commands
29
+ SEPARATOR = "\n#GO\n"
30
+
31
+ # Serialize given command so it can be sent to command listener
32
+ #
33
+ # === Parameters
34
+ # command(Object):: Command to serialize
35
+ #
36
+ # === Return
37
+ # data(String):: Corresponding serialized data
38
+ def self.dump(command)
39
+ data = YAML::dump(command)
40
+ data += SEPARATOR
41
+ end
42
+
43
+ # Deserialize command that was previously serialized with +dump+
44
+ #
45
+ # === Parameters
46
+ # data(String):: String containing serialized data
47
+ #
48
+ # === Return
49
+ # command(Object):: Deserialized command
50
+ #
51
+ # === Raise
52
+ # (RightScale::Exceptions::IO): If serialized data is incorrect
53
+ def self.load(data)
54
+ command = YAML::load(data)
55
+ raise RightScale::Exceptions::IO, "Invalid serialized command:\n#{data}" unless command
56
+ command
57
+ rescue RightScale::Exceptions::IO
58
+ raise
59
+ rescue Exception => e
60
+ raise RightScale::Exceptions::IO, "Invalid serialized command: #{e.message}\n#{data}"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,179 @@
1
+ #
2
+ # Copyright (c) 2009-2013 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightScale
24
+
25
+ # Broker connectivity checker
26
+ # Checks connectivity when requested
27
+ class ConnectivityChecker
28
+
29
+ # Minimum number of seconds between restarts of the inactivity timer
30
+ MIN_RESTART_INACTIVITY_TIMER_INTERVAL = 60
31
+
32
+ # Number of seconds to wait for ping response from a RightNet router when checking connectivity
33
+ PING_TIMEOUT = 30
34
+
35
+ # Default maximum number of consecutive ping timeouts before attempt to reconnect
36
+ MAX_PING_TIMEOUTS = 3
37
+
38
+ # (EM::Timer) Timer while waiting for RightNet router ping response
39
+ attr_accessor :ping_timer
40
+
41
+ def initialize(sender, check_interval, ping_stats, exception_stats)
42
+ @sender = sender
43
+ @check_interval = check_interval
44
+ @ping_timeouts = {}
45
+ @ping_timer = nil
46
+ @ping_stats = ping_stats
47
+ @exception_stats = exception_stats
48
+ @last_received = Time.now
49
+ @message_received_callbacks = []
50
+ restart_inactivity_timer if @check_interval > 0
51
+ end
52
+
53
+ # Update the time this agent last received a request or response message
54
+ # and restart the inactivity timer thus deferring the next connectivity check
55
+ # Also forward this message receipt notification to any callbacks that have registered
56
+ #
57
+ # === Block
58
+ # Optional block without parameters that is activated when a message is received
59
+ #
60
+ # === Return
61
+ # true:: Always return true
62
+ def message_received(&callback)
63
+ if block_given?
64
+ @message_received_callbacks << callback
65
+ else
66
+ @message_received_callbacks.each { |c| c.call }
67
+ if @check_interval > 0
68
+ now = Time.now
69
+ if (now - @last_received) > MIN_RESTART_INACTIVITY_TIMER_INTERVAL
70
+ @last_received = now
71
+ restart_inactivity_timer
72
+ end
73
+ end
74
+ end
75
+ true
76
+ end
77
+
78
+ # Check whether broker connection is usable by pinging a router via that broker
79
+ # Attempt to reconnect if ping does not respond in PING_TIMEOUT seconds and
80
+ # if have reached timeout limit
81
+ # Ignore request if already checking a connection
82
+ #
83
+ # === Parameters
84
+ # id(String):: Identity of specific broker to use to send ping, defaults to any
85
+ # currently connected broker
86
+ # max_ping_timeouts(Integer):: Maximum number of ping timeouts before attempt
87
+ # to reconnect, defaults to MAX_PING_TIMEOUTS
88
+ #
89
+ # === Return
90
+ # true:: Always return true
91
+ def check(id = nil, max_ping_timeouts = MAX_PING_TIMEOUTS)
92
+ unless @terminating || @ping_timer || (id && !@sender.client.connected?(id))
93
+ @ping_id = id
94
+ @ping_timer = EM::Timer.new(PING_TIMEOUT) do
95
+ if @ping_id
96
+ begin
97
+ @ping_stats.update("timeout")
98
+ @ping_timer = nil
99
+ @ping_timeouts[@ping_id] = (@ping_timeouts[@ping_id] || 0) + 1
100
+ if @ping_timeouts[@ping_id] >= max_ping_timeouts
101
+ Log.error("Mapper ping via broker #{@ping_id} timed out after #{PING_TIMEOUT} seconds and now " +
102
+ "reached maximum of #{max_ping_timeouts} timeout#{max_ping_timeouts > 1 ? 's' : ''}, " +
103
+ "attempting to reconnect")
104
+ host, port, index, priority = @sender.client.identity_parts(@ping_id)
105
+ @sender.agent.connect(host, port, index, priority, force = true)
106
+ else
107
+ Log.warning("Mapper ping via broker #{@ping_id} timed out after #{PING_TIMEOUT} seconds")
108
+ end
109
+ rescue Exception => e
110
+ Log.error("Failed to reconnect to broker #{@ping_id}", e, :trace)
111
+ @exception_stats.track("ping timeout", e)
112
+ end
113
+ else
114
+ @ping_timer = nil
115
+ end
116
+ end
117
+
118
+ handler = lambda do |_|
119
+ begin
120
+ if @ping_timer
121
+ @ping_stats.update("success")
122
+ @ping_timer.cancel
123
+ @ping_timer = nil
124
+ @ping_timeouts[@ping_id] = 0
125
+ @ping_id = nil
126
+ end
127
+ rescue Exception => e
128
+ Log.error("Failed to cancel router ping", e, :trace)
129
+ @exception_stats.track("cancel ping", e)
130
+ end
131
+ end
132
+ request = Request.new("/router/ping", nil, {:from => @sender.identity, :token => AgentIdentity.generate})
133
+ @sender.pending_requests[request.token] = PendingRequest.new(Request, Time.now, handler)
134
+ ids = [@ping_id] if @ping_id
135
+ @ping_id = @sender.send(:publish, request, ids).first
136
+ end
137
+ true
138
+ end
139
+
140
+ # Prepare for agent termination
141
+ #
142
+ # === Return
143
+ # true:: Always return true
144
+ def terminate
145
+ @terminating = true
146
+ @check_interval = 0
147
+ if @ping_timer
148
+ @ping_timer.cancel
149
+ @ping_timer = nil
150
+ end
151
+ if @inactivity_timer
152
+ @inactivity_timer.cancel
153
+ @inactivity_timer = nil
154
+ end
155
+ true
156
+ end
157
+
158
+ protected
159
+
160
+ # Start timer that waits for inactive messaging period to end before checking connectivity
161
+ #
162
+ # === Return
163
+ # true:: Always return true
164
+ def restart_inactivity_timer
165
+ @inactivity_timer.cancel if @inactivity_timer
166
+ @inactivity_timer = EM::Timer.new(@check_interval) do
167
+ begin
168
+ check(id = nil, max_ping_timeouts = 1)
169
+ rescue Exception => e
170
+ Log.error("Failed connectivity check", e, :trace)
171
+ @exception_stats.track("check connectivity", e)
172
+ end
173
+ end
174
+ true
175
+ end
176
+
177
+ end # ConnectivityChecker
178
+
179
+ end # RightScale