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,69 @@
1
+ ##
2
+ # Copyright (c) 2014 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
+ # Mixin for testing for availability of features in agents based on protocol version
26
+ module ProtocolVersionMixin
27
+
28
+ # Mix this module into its own eigenclass to make the module-instance methods
29
+ # become callable as module methods
30
+ extend self
31
+
32
+ # Test whether given version of agent has the protocol version embedded in each
33
+ # packet (this is generally inferred by the version in the received packet not being
34
+ # Packet::DEFAULT_VERSION, which is true of all with version >= 12)
35
+ def can_put_version_in_packet?(version); version && version != 0 end
36
+
37
+ # Test whether given version of agent uses /mapper/query_tags rather than the
38
+ # deprecated TagQuery packet
39
+ def can_use_mapper_query_tags?(version); version && version >= 8 end
40
+
41
+ # Test whether given version of agent can handle a request that is being retried
42
+ # as indicated by a retries count in the Request packet
43
+ def can_handle_request_retries?(version); version && version >= 9 end
44
+
45
+ # Test whether given version of agent can handle serialized identity and queue name
46
+ # that does not incorporate 'nanite'
47
+ def can_handle_non_nanite_ids?(version); version && version >= 10 end
48
+
49
+ # Test whether given version of agent supports routing results to mapper response queue
50
+ # rather than to the identity queue of the mapper that routed the request
51
+ def can_route_to_response_queue?(version); version && version >= 10 end
52
+
53
+ # Test whether given version of agent can handle receipt of a result containing an
54
+ # OperationResult with MULTICAST status
55
+ def can_handle_multicast_result?(version); version && [10, 11].include?(version) end
56
+
57
+ # Test whether given version of agent can handle msgpack encoding
58
+ def can_handle_msgpack_result?(version); version && version >= 12 end
59
+
60
+ # Test whether given version of agent can handle receipt of a result containing an
61
+ # OperationResult with NON_DELIVERY status
62
+ def can_handle_non_delivery_result?(version); version && version >= 13 end
63
+
64
+ # Test whether given version of agent can handle HTTP communication mode
65
+ def can_handle_http?(version); version && version >= 23 end
66
+
67
+ end # ProtocolVersionMixin
68
+
69
+ end # RightScale
@@ -0,0 +1,195 @@
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
+ # This is a retryable request for use when the execution of the request by the
26
+ # receiver is known to be idempotent and when there is a need to indefinitely
27
+ # pursue getting a usable response, e.g., when an instance is launching.
28
+ # It is implemented as an EM::Deferrable and as such invokes the Proc defined
29
+ # with its #callback method with the result content from a OperationResult::SUCCESS
30
+ # response, or it will invoke the Proc defined with its #errback method with error
31
+ # content if the response is an OperationResult::ERROR or CANCEL, or if the request
32
+ # has timed out. The request can be canceled with the #cancel method, or the receiver
33
+ # of the request may respond with a CANCEL result to cause the request to be canceled.
34
+ # This is useful in situations where the request is never expected to succeed
35
+ # regardless of the number of retries. By default if the response to the request
36
+ # is an OperationResult::RETRY or NON_DELIVERY indication, the request is automatically
37
+ # retried, as is also the case for an ERROR indication if the :retry_on_error option
38
+ # is specified. The retry algorithm is controlled by the :retry_delay, :retry_delay_count,
39
+ # and :max_retry_delay settings. The initial retry interval is the default or specified
40
+ # :retry_delay and this interval is used :retry_delay_count times, at which point the
41
+ # :retry_delay is doubled and the :retry_delay_count is halved. This backoff is again
42
+ # applied after the new :retry_delay_count is reached, and so on until :retry_delay
43
+ # reaches :max_retry_delay which then is used as the interval until the default or
44
+ # specified :timeout is reached. The default :timeout is 4 days.
45
+ class RetryableRequest
46
+
47
+ include OperationResultHelper
48
+ include EM::Deferrable
49
+
50
+ # Default delay before initial retry in case of failure with -1 meaning no delay
51
+ DEFAULT_RETRY_DELAY = 5
52
+
53
+ # Default minimum number of retries before beginning backoff
54
+ DEFAULT_RETRY_DELAY_COUNT = 60
55
+
56
+ # Maximum default delay before retry when backing off
57
+ DEFAULT_MAX_RETRY_DELAY = 60
58
+
59
+ # Factor used for exponential backoff of retry delay
60
+ RETRY_BACKOFF_FACTOR = 2
61
+
62
+ # Default timeout with -1 meaning never timeout
63
+ DEFAULT_TIMEOUT = 4 * 24 * 60 * 60
64
+
65
+ attr_reader :raw_response
66
+
67
+ # Send idempotent request
68
+ # Retry until timeout is reached (indefinitely if timeout <= 0)
69
+ # Calls deferrable callback on completion, error callback on timeout
70
+ #
71
+ # === Parameters
72
+ # operation(String):: Request operation (e.g., '/booter/get_boot_bundle')
73
+ # payload(Hash):: Request payload
74
+ # options(Hash):: Request options
75
+ # :targets(Array):: Target agent identities from which to randomly choose one
76
+ # :retry_on_error(Boolean):: Whether request should be retried if recipient returned an error
77
+ # :retry_delay(Fixnum):: Number of seconds delay before initial retry with -1 meaning no delay,
78
+ # defaults to DEFAULT_RETRY_DELAY
79
+ # :retry_delay_count(Fixnum):: Minimum number of retries at initial :retry_delay value before
80
+ # increasing delay exponentially and decreasing this count exponentially, defaults to
81
+ # DEFAULT_RETRY_DELAY_COUNT
82
+ # :max_retry_delay(Fixnum):: Maximum number of seconds of retry delay, defaults to DEFAULT_MAX_RETRY_DELAY
83
+ # :timeout(Fixnum):: Number of seconds with no response before error callback gets called, with
84
+ # -1 meaning never, defaults to DEFAULT_TIMEOUT
85
+ #
86
+ # === Raises
87
+ # ArgumentError:: If operation or payload not specified
88
+ def initialize(operation, payload, options = {})
89
+ raise ArgumentError.new("operation is required") unless (@operation = operation)
90
+ raise ArgumentError.new("payload is required") unless (@payload = payload)
91
+ @retry_on_error = options[:retry_on_error] || false
92
+ @timeout = options[:timeout] || DEFAULT_TIMEOUT
93
+ @retry_delay = options[:retry_delay] || DEFAULT_RETRY_DELAY
94
+ @retry_delay_count = options[:retry_delay_count] || DEFAULT_RETRY_DELAY_COUNT
95
+ @max_retry_delay = options[:max_retry_delay] || DEFAULT_MAX_RETRY_DELAY
96
+ @retries = 0
97
+ @targets = options[:targets]
98
+ @raw_response = nil
99
+ @done = false
100
+ end
101
+
102
+ # Send request and retry until timeout is reached or response is received
103
+ # Ignore duplicate responses
104
+ #
105
+ # === Return
106
+ # true:: Always return true
107
+ def run
108
+ Sender.instance.send_request(@operation, @payload, retrieve_target(@targets)) { |r| handle_response(r) }
109
+ if @cancel_timer.nil? && @timeout > 0
110
+ @cancel_timer = EM::Timer.new(@timeout) do
111
+ msg = "Request #{@operation} timed out after #{@timeout} seconds"
112
+ Log.info(msg)
113
+ cancel(msg)
114
+ end
115
+ end
116
+ true
117
+ end
118
+
119
+ # Cancel request and call error callback
120
+ #
121
+ # === Parameters
122
+ # msg(String):: Reason why request is cancelled, given to error callback
123
+ #
124
+ # === Return
125
+ # true:: Always return true
126
+ def cancel(msg)
127
+ if @cancel_timer
128
+ @cancel_timer.cancel
129
+ @cancel_timer = nil
130
+ end
131
+ @done = true
132
+ fail(msg)
133
+ true
134
+ end
135
+
136
+ protected
137
+
138
+ # Process request response and retry if needed
139
+ #
140
+ # === Parameters
141
+ # r(Result):: Request result
142
+ #
143
+ # === Return
144
+ # true:: Always return true
145
+ def handle_response(r)
146
+ return true if @done
147
+ @raw_response = r
148
+ res = result_from(r)
149
+ if res.success?
150
+ if @cancel_timer
151
+ @cancel_timer.cancel
152
+ @cancel_timer = nil
153
+ end
154
+ @done = true
155
+ succeed(res.content)
156
+ else
157
+ reason = res.content
158
+ if res.non_delivery?
159
+ Log.info("Request non-delivery (#{reason}) for #{@operation}")
160
+ elsif res.retry?
161
+ reason = (reason && !reason.empty?) ? reason : "RightScale not ready"
162
+ Log.info("Request #{@operation} failed (#{reason}) and should be retried")
163
+ elsif res.cancel?
164
+ reason = (reason && !reason.empty?) ? reason : "RightScale cannot execute request"
165
+ Log.info("Request #{@operation} canceled (#{reason})")
166
+ else
167
+ Log.info("Request #{@operation} failed (#{reason})")
168
+ end
169
+ if (res.non_delivery? || res.retry? || @retry_on_error) && !res.cancel?
170
+ Log.info("Retrying in #{@retry_delay} seconds...")
171
+ if @retry_delay > 0
172
+ this_delay = @retry_delay
173
+ if (@retries += 1) >= @retry_delay_count
174
+ @retry_delay = [@retry_delay * RETRY_BACKOFF_FACTOR, @max_retry_delay].min
175
+ @retry_delay_count = [@retry_delay_count / RETRY_BACKOFF_FACTOR, 1].max
176
+ @retries = 0
177
+ end
178
+ EM.add_timer(this_delay) { run }
179
+ else
180
+ EM.next_tick { run }
181
+ end
182
+ else
183
+ cancel(res.content)
184
+ end
185
+ end
186
+ true
187
+ end
188
+
189
+ def retrieve_target(targets)
190
+ {:agent_id => targets[rand(0xffff) % targets.size]} if targets && targets.any?
191
+ end
192
+
193
+ end # RetryableRequest
194
+
195
+ end # RightScale
@@ -0,0 +1,543 @@
1
+ # === Synopsis:
2
+ # RightScale RightAgent Controller (rnac) - (c) 2009-2012 RightScale Inc
3
+ #
4
+ # rnac is a command line tool for managing a RightAgent
5
+ #
6
+ # === Examples:
7
+ # Start new agent named AGENT:
8
+ # rnac --start AGENT
9
+ # rnac -s AGENT
10
+ #
11
+ # Stop running agent named AGENT:
12
+ # rnac --stop AGENT
13
+ # rnac -p AGENT
14
+ #
15
+ # Stop agent with given serialized ID:
16
+ # rnac --stop-agent ID
17
+ #
18
+ # Terminate all agents on local machine:
19
+ # rnac --killall
20
+ # rnac -K
21
+ #
22
+ # List agents configured on local machine:
23
+ # rnac --list
24
+ # rnac -l
25
+ #
26
+ # List status of agents configured on local machine:
27
+ # rnac --status
28
+ # rnac -U
29
+ #
30
+ # Start new agent named AGENT in foreground:
31
+ # rnac --start AGENT --foreground
32
+ # rnac -s AGENT -f
33
+ #
34
+ # Start new agent named AGENT of type TYPE:
35
+ # rnac --start AGENT --type TYPE
36
+ # rnac -s AGENT -t TYPE
37
+ #
38
+ # Note: To start multiple agents of the same type generate one
39
+ # config.yml file with rad and then start each agent with rnac:
40
+ # rad my_agent
41
+ # rnac -s my_agent_1 -t my_agent
42
+ # rnac -s my_agent_2 -t my_agent
43
+ #
44
+ # === Usage:
45
+ # rnac [options]
46
+ #
47
+ # options:
48
+ # --start, -s AGENT Start agent named AGENT
49
+ # --stop, -p AGENT Stop agent named AGENT
50
+ # --stop-agent ID Stop agent with serialized identity ID
51
+ # --kill, -k PIDFILE Kill process with given process id file
52
+ # --killall, -K Stop all running agents
53
+ # --status, -U List running agents on local machine
54
+ # --identity, -i ID Use this as base ID to build agent's identity
55
+ # --token, -t TOKEN Use this token to build agent's identity with it plugging
56
+ # directly in unless --secure-identity is specified
57
+ # --secure-identity, -S Derive token used in agent identity from given TOKEN and ID
58
+ # --prefix, -x PREFIX Use this prefix to build agent's identity
59
+ # --type TYPE Use this agent type to build agent's' identity;
60
+ # defaults to AGENT with any trailing '_[0-9]+' removed
61
+ # --list, -l List all configured agents
62
+ # --user, -u USER Set AMQP user
63
+ # --pass, -p PASS Set AMQP password
64
+ # --vhost, -v VHOST Set AMQP vhost
65
+ # --host, -h HOST Set AMQP server hostname
66
+ # --port, -P PORT Set AMQP server port
67
+ # --cfg-dir, -c DIR Set directory containing configuration for all agents
68
+ # --pid-dir, -z DIR Set directory containing agent process id files
69
+ # --log-dir DIR Set log directory
70
+ # --log-level LVL Log level (debug, info, warning, error or fatal)
71
+ # --foreground, -f Run agent in foreground
72
+ # --interactive, -I Spawn an irb console after starting agent
73
+ # --test Use test settings
74
+ # --help Display help
75
+
76
+ require 'rubygems'
77
+ require 'optparse'
78
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'minimal'))
79
+ require File.normalize_path(File.join(File.dirname(__FILE__), 'usage'))
80
+ require File.normalize_path(File.join(File.dirname(__FILE__), 'common_parser'))
81
+
82
+ module RightScale
83
+
84
+ class AgentController
85
+
86
+ include CommonParser
87
+
88
+ FORCED_OPTIONS =
89
+ {
90
+ }
91
+
92
+ DEFAULT_OPTIONS =
93
+ {
94
+ :log_dir => Platform.filesystem.log_dir,
95
+ :daemonize => true
96
+ }
97
+
98
+ @@agent = nil
99
+
100
+ # Create and run controller
101
+ #
102
+ # === Return
103
+ # true:: Always return true
104
+ def self.run
105
+ c = AgentController.new
106
+ c.control(c.parse_args)
107
+ end
108
+
109
+ # Parse arguments and execute request
110
+ #
111
+ # === Parameters
112
+ # options(Hash):: Command line options
113
+ #
114
+ # === Return
115
+ # true:: Always return true
116
+ def control(options)
117
+
118
+ # Initialize directory settings
119
+ AgentConfig.cfg_dir = options[:cfg_dir]
120
+ AgentConfig.pid_dir = options[:pid_dir]
121
+
122
+ # List agents if requested
123
+ list_configured_agents if options[:list]
124
+
125
+ # Validate arguments
126
+ action = options.delete(:action)
127
+ fail("No action specified on the command line.", print_usage = true) unless action
128
+ if action == 'kill' && (options[:pid_file].nil? || !File.file?(options[:pid_file]))
129
+ fail("Missing or invalid pid file #{options[:pid_file]}", print_usage = true)
130
+ end
131
+ if options[:agent_name]
132
+ if action == 'start'
133
+ cfg = configure_agent(action, options)
134
+ else
135
+ cfg = AgentConfig.load_cfg(options[:agent_name])
136
+ fail("Deployment is missing configuration file #{AgentConfig.cfg_file(options[:agent_name]).inspect}.") unless cfg
137
+ end
138
+ options.delete(:identity)
139
+ options = cfg.merge(options)
140
+ AgentConfig.root_dir = options[:root_dir]
141
+ AgentConfig.pid_dir = options[:pid_dir]
142
+ Log.program_name = syslog_program_name(options)
143
+ Log.facility = syslog_facility(options)
144
+ Log.log_to_file_only(options[:log_to_file_only])
145
+ configure_proxy(options[:http_proxy], options[:http_no_proxy]) if options[:http_proxy]
146
+ elsif options[:identity]
147
+ options[:agent_name] = AgentConfig.agent_name(options[:identity])
148
+ end
149
+ @options = DEFAULT_OPTIONS.clone.merge(options.merge(FORCED_OPTIONS))
150
+ FileUtils.mkdir_p(@options[:pid_dir]) unless @options[:pid_dir].nil? || File.directory?(@options[:pid_dir])
151
+
152
+ # Execute request
153
+ success = case action
154
+ when /show|killall/
155
+ action = 'stop' if action == 'killall'
156
+ s = true
157
+ AgentConfig.cfg_agents.each { |agent_name| s &&= dispatch(action, agent_name) }
158
+ s
159
+ when 'kill'
160
+ kill_process
161
+ else
162
+ dispatch(action, @options[:agent_name])
163
+ end
164
+
165
+ exit(1) unless success
166
+ end
167
+
168
+ # Create options hash from command line arguments
169
+ #
170
+ # === Return
171
+ # options(Hash):: Parsed options
172
+ def parse_args
173
+ options = {:thin_command_client => false}
174
+
175
+ opts = OptionParser.new do |opts|
176
+ parse_common(opts, options)
177
+ parse_other_args(opts, options)
178
+
179
+ opts.on("-s", "--start AGENT") do |a|
180
+ options[:action] = 'start'
181
+ options[:agent_name] = a
182
+ end
183
+
184
+ opts.on("-p", "--stop AGENT") do |a|
185
+ options[:action] = 'stop'
186
+ options[:agent_name] = a
187
+ end
188
+
189
+ opts.on("--stop-agent ID") do |id|
190
+ options[:action] = 'stop'
191
+ options[:identity] = id
192
+ end
193
+
194
+ opts.on("-k", "--kill PIDFILE") do |file|
195
+ options[:pid_file] = file
196
+ options[:action] = 'kill'
197
+ end
198
+
199
+ opts.on("-K", "--killall") do
200
+ options[:action] = 'killall'
201
+ end
202
+
203
+ opts.on("-U", "--status") do
204
+ options[:action] = 'show'
205
+ end
206
+
207
+ opts.on("-l", "--list") do
208
+ options[:list] = true
209
+ end
210
+
211
+ opts.on("--log-level LVL") do |lvl|
212
+ options[:log_level] = lvl
213
+ end
214
+
215
+ opts.on("-c", "--cfg-dir DIR") do |d|
216
+ options[:cfg_dir] = d
217
+ end
218
+
219
+ opts.on("-z", "--pid-dir DIR") do |dir|
220
+ options[:pid_dir] = dir
221
+ end
222
+
223
+ opts.on("--log-dir DIR") do |dir|
224
+ options[:log_dir] = dir
225
+
226
+ # Ensure log directory exists (for windows, etc.)
227
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
228
+ end
229
+
230
+ opts.on("-f", "--foreground") do
231
+ options[:daemonize] = false
232
+ #Squelch Ruby VM warnings about various things
233
+ $VERBOSE = nil
234
+ end
235
+
236
+ opts.on("-I", "--interactive") do
237
+ options[:console] = true
238
+ end
239
+
240
+ opts.on_tail("--help") do
241
+ puts Usage.scan(__FILE__)
242
+ exit
243
+ end
244
+
245
+ end
246
+
247
+ begin
248
+ opts.parse(ARGV)
249
+ rescue Exception => e
250
+ exit 0 if e.is_a?(SystemExit)
251
+ fail(e.message, print_usage = true)
252
+ end
253
+
254
+ # allow specific arguments to use a thin command client for faster
255
+ # execution (on Windows, etc.)
256
+ unless options[:thin_command_client]
257
+ # require full right_agent for any commands which do not specify thin
258
+ # command client.
259
+ require File.normalize_path(File.join(File.dirname(__FILE__), '..', '..', 'right_agent'))
260
+ end
261
+ resolve_identity(options)
262
+ options
263
+ end
264
+
265
+ protected
266
+
267
+ # Parse any other arguments used by agent
268
+ #
269
+ # === Parameters
270
+ # opts(OptionParser):: Options parser with options to be parsed
271
+ # options(Hash):: Storage for options that are parsed
272
+ #
273
+ # === Return
274
+ # true:: Always return true
275
+ def parse_other_args(opts, options)
276
+ true
277
+ end
278
+
279
+ # Dispatch action
280
+ #
281
+ # === Parameters
282
+ # action(String):: Action to be performed
283
+ # agent_name(String):: Agent name
284
+ #
285
+ # === Return
286
+ # true:: Always return true
287
+ def dispatch(action, agent_name)
288
+ # Setup the environment from config if necessary
289
+ begin
290
+ eval("#{action}_agent(agent_name)")
291
+ rescue SystemExit
292
+ true
293
+ rescue SignalException
294
+ true
295
+ rescue Exception => e
296
+ puts Log.format("Failed to #{action} #{agent_name}", e, :trace)
297
+ end
298
+ true
299
+ end
300
+
301
+ # Kill process defined in pid file
302
+ #
303
+ # === Parameters
304
+ # sig(String):: Signal to be used for kill
305
+ #
306
+ # === Return
307
+ # true:: Always return true
308
+ def kill_process(sig = 'TERM')
309
+ content = IO.read(@options[:pid_file])
310
+ pid = content.to_i
311
+ fail("Invalid pid file content #{content.inspect}") if pid == 0
312
+ begin
313
+ Process.kill(sig, pid)
314
+ rescue Errno::ESRCH => e
315
+ fail("Could not find process with pid #{pid}")
316
+ rescue Errno::EPERM => e
317
+ fail("You don't have permissions to stop process #{pid}")
318
+ rescue Exception => e
319
+ fail(e.message)
320
+ end
321
+ true
322
+ end
323
+
324
+ # Start agent
325
+ #
326
+ # === Parameters
327
+ # agent_name(String):: Agent name
328
+ # agent_class(Agent):: Agent class
329
+ #
330
+ # === Return
331
+ # true:: Always return true
332
+ def start_agent(agent_name, agent_class = Agent)
333
+ puts "#{human_readable_name} being started"
334
+
335
+ EM.error_handler do |e|
336
+ Log.error("EM block execution failed with exception", e, :trace)
337
+ Log.error("\n\n===== Exiting due to EM block exception =====\n\n")
338
+ # Cannot rely on EM.stop at this point, so exit to give chance for monitor restart
339
+ exit(1)
340
+ end
341
+
342
+ EM.run do
343
+ begin
344
+ @@agent = agent_class.start(@options)
345
+ rescue SystemExit
346
+ raise # Let parents of forked (daemonized) processes die
347
+ rescue PidFile::AlreadyRunning
348
+ puts "#{human_readable_name} already running"
349
+ EM.stop
350
+ rescue Exception => e
351
+ puts Log.format("#{human_readable_name} failed", e, :trace)
352
+ EM.stop
353
+ end
354
+ end
355
+ true
356
+ end
357
+
358
+ # Stop agent process
359
+ #
360
+ # === Parameters
361
+ # agent_name(String):: Agent name
362
+ #
363
+ # === Return
364
+ # (Boolean):: true if process was stopped, otherwise false
365
+ def stop_agent(agent_name)
366
+ res = false
367
+ if pid_file = AgentConfig.pid_file(agent_name)
368
+ name = human_readable_name(agent_name, pid_file.identity)
369
+ if pid = pid_file.read_pid[:pid]
370
+ begin
371
+ Process.kill('TERM', pid)
372
+ res = true
373
+ puts "#{name} stopped"
374
+ rescue Errno::ESRCH
375
+ puts "#{name} not running"
376
+ end
377
+ elsif File.file?(pid_file.to_s)
378
+ puts "Invalid pid file '#{pid_file.to_s}' content: #{IO.read(pid_file.to_s)}"
379
+ else
380
+ puts "#{name} not running"
381
+ end
382
+ else
383
+ puts "Non-existent pid file for #{agent_name}"
384
+ end
385
+ res
386
+ end
387
+
388
+ # Show status of agent
389
+ #
390
+ # === Parameters
391
+ # agent_name(String):: Agent name
392
+ #
393
+ # === Return
394
+ # (Boolean):: true if process is running, otherwise false
395
+ def show_agent(agent_name)
396
+ res = false
397
+ if (pid_file = AgentConfig.pid_file(agent_name)) && (pid = pid_file.read_pid[:pid])
398
+ pgid = Process.getpgid(pid) rescue -1
399
+ name = human_readable_name(agent_name, pid_file.identity)
400
+ if pgid != -1
401
+ psdata = `ps up #{pid}`.split("\n").last.split
402
+ memory = (psdata[5].to_i / 1024)
403
+ puts "#{name} is alive, using #{memory}MB of memory"
404
+ res = true
405
+ else
406
+ puts "#{name} is not running but has a stale pid file at #{pid_file}"
407
+ end
408
+ elsif identity = AgentConfig.agent_options(agent_name)[:identity]
409
+ puts "#{human_readable_name(agent_name, identity)} is not running"
410
+ end
411
+ res
412
+ end
413
+
414
+ # Generate human readable name for agent
415
+ #
416
+ # === Return
417
+ # (String):: Human readable name
418
+ def human_readable_name(agent_name = nil, identity = nil)
419
+ agent_name ||= @options[:agent_name]
420
+ "Agent #{agent_name + ' ' if agent_name}with ID #{identity || @options[:identity]}"
421
+ end
422
+
423
+ # List all configured agents
424
+ #
425
+ # === Return
426
+ # never
427
+ def list_configured_agents
428
+ agents = AgentConfig.cfg_agents
429
+ if agents.empty?
430
+ puts "Found no configured agents"
431
+ else
432
+ puts "Configured agents:"
433
+ agents.each { |a| puts " - #{a}" }
434
+ end
435
+ exit
436
+ end
437
+
438
+ # Determine syslog program name based on options
439
+ #
440
+ # === Parameters
441
+ # options(Hash):: Command line options
442
+ #
443
+ # === Return
444
+ # (String):: Program name
445
+ def syslog_program_name(options)
446
+ 'RightAgent'
447
+ end
448
+
449
+ # Determine syslog facility based on options
450
+ #
451
+ # === Parameters
452
+ # options(Hash):: Command line options
453
+ #
454
+ # === Return
455
+ # (String):: 'local0'
456
+ def syslog_facility(options)
457
+ 'local0'
458
+ end
459
+
460
+ # Determine configuration settings for this agent and persist them if needed
461
+ # Reuse existing agent identity when possible
462
+ #
463
+ # === Parameters
464
+ # action(String):: Requested action
465
+ # options(Hash):: Command line options
466
+ #
467
+ # === Return
468
+ # cfg(Hash):: Persisted configuration options
469
+ def configure_agent(action, options)
470
+ agent_type = options[:agent_type]
471
+ agent_name = options[:agent_name]
472
+ if agent_name != agent_type && cfg = AgentConfig.load_cfg(agent_type)
473
+ base_id = (options[:base_id] || AgentIdentity.parse(cfg[:identity]).base_id.to_s).to_i
474
+ unless (identity = AgentConfig.agent_options(agent_name)[:identity]) &&
475
+ AgentIdentity.parse(identity).base_id == base_id
476
+ identity = AgentIdentity.new(options[:prefix] || 'rs', options[:agent_type], base_id, options[:token]).to_s
477
+ end
478
+ cfg.merge!(:identity => identity)
479
+ cfg_file = AgentConfig.store_cfg(agent_name, cfg)
480
+ puts "Generated configuration file for #{agent_name} agent: #{cfg_file}"
481
+ elsif !(cfg = AgentConfig.load_cfg(agent_name))
482
+ fail("Deployment is missing configuration file #{AgentConfig.cfg_file(agent_name).inspect}")
483
+ end
484
+ cfg
485
+ end
486
+
487
+ # Enable the use of an HTTP proxy for this process and its subprocesses
488
+ #
489
+ # === Parameters
490
+ # proxy_setting(String):: Proxy to use
491
+ # exceptions(String):: Comma-separated list of proxy exceptions (e.g. metadata server)
492
+ #
493
+ # === Return
494
+ # true:: Always return true
495
+ def configure_proxy(proxy_setting, exceptions)
496
+ ENV['HTTP_PROXY'] = proxy_setting
497
+ ENV['http_proxy'] = proxy_setting
498
+ ENV['HTTPS_PROXY'] = proxy_setting
499
+ ENV['https_proxy'] = proxy_setting
500
+ ENV['NO_PROXY'] = exceptions
501
+ ENV['no_proxy'] = exceptions
502
+ true
503
+ end
504
+
505
+ # Print error on console and exit abnormally
506
+ #
507
+ # === Parameters
508
+ # message(String):: Error message to be displayed
509
+ # print_usage(Boolean):: Whether to display usage information
510
+ #
511
+ # === Return
512
+ # never
513
+ def fail(message, print_usage = false)
514
+ puts "** #{message}"
515
+ puts Usage.scan(__FILE__) if print_usage
516
+ exit(1)
517
+ end
518
+
519
+ end # AgentController
520
+
521
+ end # RightScale
522
+
523
+ #
524
+ # Copyright (c) 2009-2012 RightScale Inc
525
+ #
526
+ # Permission is hereby granted, free of charge, to any person obtaining
527
+ # a copy of this software and associated documentation files (the
528
+ # "Software"), to deal in the Software without restriction, including
529
+ # without limitation the rights to use, copy, modify, merge, publish,
530
+ # distribute, sublicense, and/or sell copies of the Software, and to
531
+ # permit persons to whom the Software is furnished to do so, subject to
532
+ # the following conditions:
533
+ #
534
+ # The above copyright notice and this permission notice shall be
535
+ # included in all copies or substantial portions of the Software.
536
+ #
537
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
538
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
539
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
540
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
541
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
542
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
543
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.