right_agent 2.2.1-x86-mingw32 → 2.4.3-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 (48) hide show
  1. data/README.rdoc +2 -0
  2. data/lib/right_agent.rb +1 -0
  3. data/lib/right_agent/actor.rb +0 -28
  4. data/lib/right_agent/actors/agent_manager.rb +20 -18
  5. data/lib/right_agent/agent.rb +70 -87
  6. data/lib/right_agent/agent_config.rb +1 -1
  7. data/lib/right_agent/agent_tag_manager.rb +1 -1
  8. data/lib/right_agent/clients/api_client.rb +2 -1
  9. data/lib/right_agent/clients/auth_client.rb +2 -6
  10. data/lib/right_agent/clients/balanced_http_client.rb +22 -11
  11. data/lib/right_agent/clients/base_retry_client.rb +14 -22
  12. data/lib/right_agent/clients/non_blocking_client.rb +1 -0
  13. data/lib/right_agent/clients/right_http_client.rb +4 -8
  14. data/lib/right_agent/clients/router_client.rb +10 -16
  15. data/lib/right_agent/command/command_parser.rb +3 -3
  16. data/lib/right_agent/command/command_runner.rb +1 -1
  17. data/lib/right_agent/command/command_serializer.rb +0 -32
  18. data/lib/right_agent/connectivity_checker.rb +7 -11
  19. data/lib/right_agent/core_payload_types/dev_repository.rb +32 -0
  20. data/lib/right_agent/dispatcher.rb +8 -45
  21. data/lib/right_agent/enrollment_result.rb +2 -2
  22. data/lib/right_agent/error_tracker.rb +230 -0
  23. data/lib/right_agent/exceptions.rb +1 -1
  24. data/lib/right_agent/log.rb +8 -6
  25. data/lib/right_agent/packets.rb +5 -3
  26. data/lib/right_agent/pending_requests.rb +10 -4
  27. data/lib/right_agent/pid_file.rb +3 -3
  28. data/lib/right_agent/platform.rb +14 -14
  29. data/lib/right_agent/protocol_version_mixin.rb +6 -3
  30. data/lib/right_agent/scripts/agent_deployer.rb +13 -1
  31. data/lib/right_agent/sender.rb +16 -35
  32. data/lib/right_agent/serialize/secure_serializer.rb +6 -9
  33. data/lib/right_agent/serialize/serializer.rb +7 -3
  34. data/right_agent.gemspec +5 -5
  35. data/spec/agent_spec.rb +5 -5
  36. data/spec/clients/auth_client_spec.rb +1 -1
  37. data/spec/clients/balanced_http_client_spec.rb +20 -28
  38. data/spec/clients/base_retry_client_spec.rb +5 -6
  39. data/spec/clients/non_blocking_client_spec.rb +4 -0
  40. data/spec/clients/router_client_spec.rb +1 -4
  41. data/spec/dispatcher_spec.rb +6 -55
  42. data/spec/error_tracker_spec.rb +346 -0
  43. data/spec/log_spec.rb +4 -0
  44. data/spec/pending_requests_spec.rb +2 -2
  45. data/spec/sender_spec.rb +3 -3
  46. data/spec/serialize/serializer_spec.rb +14 -0
  47. data/spec/spec_helper.rb +4 -2
  48. metadata +13 -11
@@ -38,13 +38,12 @@ module RightScale
38
38
  # Timer while waiting for RightNet router ping response
39
39
  attr_accessor :ping_timer
40
40
 
41
- def initialize(sender, check_interval, ping_stats, exception_stats)
41
+ def initialize(sender, check_interval, ping_stats)
42
42
  @sender = sender
43
43
  @check_interval = check_interval
44
44
  @ping_timeouts = {}
45
45
  @ping_timer = nil
46
46
  @ping_stats = ping_stats
47
- @exception_stats = exception_stats
48
47
  @last_received = Time.now
49
48
  @message_received_callbacks = []
50
49
  restart_inactivity_timer if @check_interval > 0
@@ -98,17 +97,16 @@ module RightScale
98
97
  @ping_timer = nil
99
98
  @ping_timeouts[@ping_id] = (@ping_timeouts[@ping_id] || 0) + 1
100
99
  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")
100
+ ErrorTracker.log(self, "Mapper ping via broker #{@ping_id} timed out after #{PING_TIMEOUT} seconds and now " +
101
+ "reached maximum of #{max_ping_timeouts} timeout#{max_ping_timeouts > 1 ? 's' : ''}, " +
102
+ "attempting to reconnect")
104
103
  host, port, index, priority = @sender.client.identity_parts(@ping_id)
105
104
  @sender.agent.connect(host, port, index, priority, force = true)
106
105
  else
107
106
  Log.warning("Mapper ping via broker #{@ping_id} timed out after #{PING_TIMEOUT} seconds")
108
107
  end
109
108
  rescue Exception => e
110
- Log.error("Failed to reconnect to broker #{@ping_id}", e, :trace)
111
- @exception_stats.track("ping timeout", e)
109
+ ErrorTracker.log(self, "Failed to reconnect to broker #{@ping_id}", e)
112
110
  end
113
111
  else
114
112
  @ping_timer = nil
@@ -125,8 +123,7 @@ module RightScale
125
123
  @ping_id = nil
126
124
  end
127
125
  rescue Exception => e
128
- Log.error("Failed to cancel router ping", e, :trace)
129
- @exception_stats.track("cancel ping", e)
126
+ ErrorTracker.log(self, "Failed to cancel router ping", e)
130
127
  end
131
128
  end
132
129
  request = Request.new("/router/ping", nil, {:from => @sender.identity, :token => AgentIdentity.generate})
@@ -167,8 +164,7 @@ module RightScale
167
164
  begin
168
165
  check(id = nil, max_ping_timeouts = 1)
169
166
  rescue Exception => e
170
- Log.error("Failed connectivity check", e, :trace)
171
- @exception_stats.track("check connectivity", e)
167
+ ErrorTracker.log(self, "Failed connectivity check", e)
172
168
  end
173
169
  end
174
170
  true
@@ -72,5 +72,37 @@ module RightScale
72
72
  def serialized_members
73
73
  [ @repo_type, @url, @tag, @cookbooks_path, @ssh_key, @username, @password, @repo_sha, @positions ]
74
74
  end
75
+
76
+ # Maps the given DevRepository to a has that can be consumed by the RightScraper gem
77
+ #
78
+ # === Returns
79
+ # (Hash)::
80
+ # :repo_type (Symbol):: Type of repository: one of :git, :svn, :download or :local
81
+ # * :git denotes a 'git' repository that should be retrieved via 'git clone'
82
+ # * :svn denotes a 'svn' repository that should be retrieved via 'svn checkout'
83
+ # * :download denotes a tar ball that should be retrieved via HTTP GET (HTTPS if uri starts with https://)
84
+ # * :local denotes cookbook that is already local and doesn't need to be retrieved
85
+ # :url (String):: URL to repository (e.g. git://github.com/opscode/chef-repo.git)
86
+ # :tag (String):: git commit or svn branch that should be used to retrieve repository
87
+ # Optional, use 'master' for git and 'trunk' for svn if tag is nil.
88
+ # Not used for raw repositories.
89
+ # :cookbooks_path (Array):: Path to cookbooks inside repostory
90
+ # Optional (use location of repository as cookbook path if nil)
91
+ # :first_credential (String):: Either the Private SSH key used to retrieve git repositories, or the Username used to retrieve svn and raw repositories
92
+ # :second_credential (String):: Password used to retrieve svn and raw repositories
93
+ def to_scraper_hash
94
+ repo = {}
95
+ repo[:repo_type] = repo_type.to_sym unless repo_type.nil?
96
+ repo[:url] = url
97
+ repo[:tag] = tag
98
+ repo[:resources_path] = cookbooks_path
99
+ if !ssh_key.nil?
100
+ repo[:first_credential] = ssh_key
101
+ elsif !(username.nil? && password.nil?)
102
+ repo[:first_credential] = dev_repo.username
103
+ repo[:second_credential] = dev_repo.password
104
+ end
105
+ repo
106
+ end
75
107
  end
76
108
  end
@@ -27,8 +27,8 @@ module RightScale
27
27
 
28
28
  include ProtocolVersionMixin
29
29
 
30
- class InvalidRequestType < Exception; end
31
- class DuplicateRequest < Exception; end
30
+ class InvalidRequestType < RuntimeError; end
31
+ class DuplicateRequest < RuntimeError; end
32
32
 
33
33
  # (ActorRegistry) Registry for actors
34
34
  attr_reader :registry
@@ -88,7 +88,7 @@ module RightScale
88
88
  actor, method, idempotent = route(request)
89
89
  received_at = @request_stats.update(method, (token if request.is_a?(Request)))
90
90
  if (dup = duplicate?(request, method, idempotent))
91
- raise DuplicateRequest.new(dup)
91
+ raise DuplicateRequest, dup
92
92
  end
93
93
  unless (result = expired?(request, method))
94
94
  result = perform(request, actor, method, idempotent)
@@ -110,23 +110,17 @@ module RightScale
110
110
  # or nil if empty
111
111
  # "dispatch failures"(Hash|nil):: Dispatch failure activity stats with keys "total", "percent", "last", and "rate"
112
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
113
  # "rejects"(Hash|nil):: Request reject activity stats with keys "total", "percent", "last", and "rate"
117
114
  # with percentage breakdown per reason ("duplicate (<method>)", "retry duplicate (<method>)", or
118
115
  # "stale (<method>)"), or nil if none
119
116
  # "requests"(Hash|nil):: Request activity stats with keys "total", "percent", "last", and "rate"
120
117
  # 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
118
  def stats(reset = false)
123
119
  stats = {
124
120
  "dispatched cache" => (@dispatched_cache.stats if @dispatched_cache),
125
121
  "dispatch failures" => @dispatch_failure_stats.all,
126
- "exceptions" => @exception_stats.stats,
127
122
  "rejects" => @reject_stats.all,
128
- "requests" => @request_stats.all,
129
- "response time" => @request_stats.avg_duration
123
+ "requests" => @request_stats.all
130
124
  }
131
125
  reset_stats if reset
132
126
  stats
@@ -142,7 +136,6 @@ module RightScale
142
136
  @reject_stats = RightSupport::Stats::Activity.new
143
137
  @request_stats = RightSupport::Stats::Activity.new
144
138
  @dispatch_failure_stats = RightSupport::Stats::Activity.new
145
- @exception_stats = RightSupport::Stats::Exceptions.new(@agent, @agent.exception_callback)
146
139
  true
147
140
  end
148
141
 
@@ -209,7 +202,7 @@ module RightScale
209
202
  method = method.to_sym
210
203
  actor = @registry.actor_for(prefix)
211
204
  if actor.nil? || !actor.respond_to?(method)
212
- raise InvalidRequestType.new("Unknown actor or method for dispatching request <#{request.token}> of type #{request.type}")
205
+ raise InvalidRequestType, "Unknown actor or method for dispatching request <#{request.token}> of type #{request.type}"
213
206
  end
214
207
  [actor, method, actor.class.idempotent?(method)]
215
208
  end
@@ -231,40 +224,10 @@ module RightScale
231
224
  else
232
225
  actor.send(method, request.payload, request)
233
226
  end
234
- rescue Exception => e
227
+ rescue StandardError => e
228
+ ErrorTracker.log(self, "Failed dispatching #{request.trace}", e, request)
235
229
  @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)
230
+ OperationResult.error("Could not handle #{request.type} request", e)
268
231
  end
269
232
 
270
233
  end # Dispatcher
@@ -20,8 +20,8 @@ module RightScale
20
20
  # Versions 5 and above use an identical format for the enrollment result
21
21
  SUPPORTED_VERSIONS = 5..AgentConfig.protocol_version
22
22
 
23
- class IntegrityFailure < Exception; end
24
- class VersionError < Exception; end
23
+ class IntegrityFailure < StandardError; end
24
+ class VersionError < StandardError; end
25
25
 
26
26
  attr_reader :r_s_version, :timestamp, :router_cert, :id_cert, :id_key
27
27
 
@@ -0,0 +1,230 @@
1
+ # Copyright (c) 2014 RightScale, Inc, All Rights Reserved Worldwide.
2
+ #
3
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
4
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
5
+ # reproduction, modification, or disclosure of this program is
6
+ # strictly prohibited. Any use of this program by an authorized
7
+ # licensee is strictly subject to the terms and conditions,
8
+ # including confidentiality obligations, set forth in the applicable
9
+ # License Agreement between RightScale.com, Inc. and
10
+ # the licensee.
11
+
12
+ module RightScale
13
+
14
+ # Tracker for unexpected errors
15
+ # Logs them with appropriate trace information
16
+ # Accumulates statistics about exceptions
17
+ # Reports exceptions to external Errbit service via HydraulicBrake
18
+ class ErrorTracker
19
+
20
+ include RightSupport::Ruby::EasySingleton
21
+
22
+ # Text used for filtered parameter value
23
+ FILTERED_PARAM_VALUE = "<hidden>"
24
+
25
+ # Container for exception statistics
26
+ attr_reader :exception_stats
27
+
28
+ # Initialize error tracker
29
+ #
30
+ # @param [Object] agent object using this tracker
31
+ # @param [String] agent_name uniquely identifying agent process on given server
32
+ #
33
+ # @option options [Integer, NilClass] :shard_id identifying shard of database in use
34
+ # @option options [Hash] :trace_level for restricting backtracing and Errbit reporting
35
+ # with exception class as key and :no_trace, :caller, or :trace as value; exceptions
36
+ # with :no_trace are not backtraced when logging nor are they recorded in stats
37
+ # or reported to Errbit
38
+ # @option options [Array<Symbol, String>] :filter_params names whose values are to be
39
+ # filtered when notifying
40
+ # @option options [String] :airbrake_endpoint URL for Airbrake for reporting exceptions
41
+ # to Errbit
42
+ # @option options [String] :airbrake_api_key for using the Airbrake API to access Errbit
43
+ #
44
+ # @return [TrueClass] always true
45
+ def init(agent, agent_name, options = {})
46
+ @agent = agent
47
+ @trace_level = options[:trace_level] || {}
48
+ notify_init(agent_name, options)
49
+ reset_stats
50
+ true
51
+ end
52
+
53
+ # Log error and optionally track in stats
54
+ # Errbit notification is left to the callback configured in the stats tracker
55
+ # Logging works even if init was never called
56
+ #
57
+ # @param [String, Object] component reporting error; non-string is snake-cased
58
+ # @param [String] description of failure for use in logging
59
+ # @param [Exception, String] exception to be logged and tracked in stats;
60
+ # string errors are logged but not tracked in stats
61
+ # @param [Packet, Hash, NilClass] packet associated with exception
62
+ # @param [Symbol, NilClass] trace level override unless excluded by configured
63
+ # trace levels
64
+ #
65
+ # @return [Boolean] true if successfully logged, otherwise false
66
+ def log(component, description, exception = nil, packet = nil, trace = nil)
67
+ if exception.nil?
68
+ Log.error(description)
69
+ elsif exception.is_a?(String)
70
+ Log.error(description, exception)
71
+ else
72
+ trace = (@trace_level && @trace_level[exception.class]) || trace || :trace
73
+ Log.error(description, exception, trace)
74
+ track(component, exception, packet) if trace != :no_trace
75
+ end
76
+ true
77
+ rescue StandardError => e
78
+ Log.error("Failed to log error", e, :trace) rescue nil
79
+ false
80
+ end
81
+
82
+ # Track error in stats
83
+ #
84
+ # @param [String] component reporting error
85
+ # @param [Exception] exception to be tracked
86
+ # @param [Packet, Hash, NilClass] packet associated with exception
87
+ #
88
+ # @return [TrueClass] always true
89
+ def track(component, exception, packet = nil)
90
+ if @exception_stats
91
+ component = component.class.name.split("::").last.snake_case unless component.is_a?(String)
92
+ @exception_stats.track(component, exception, packet)
93
+ end
94
+ true
95
+ end
96
+
97
+ # Notify Errbit of error if notification enabled
98
+ #
99
+ # @param [Exception, String] exception raised
100
+ # @param [Packet, Hash] packet associated with exception
101
+ # @param [Object] agent object reporting error
102
+ # @param [String] component or service area where error occurred
103
+ #
104
+ # @return [TrueClass] always true
105
+ def notify(exception, packet = nil, agent = nil, component = nil)
106
+ if @notify_enabled
107
+ data = {
108
+ :error_message => exception.respond_to?(:message) ? exception.message : exception.to_s,
109
+ :backtrace => exception.respond_to?(:backtrace) ? exception.backtrace : caller,
110
+ :environment_name => ENV["RAILS_ENV"],
111
+ }
112
+ if agent
113
+ data[:cgi_data] = (@cgi_data || {}).merge(:agent_class => agent.class.name)
114
+ elsif @cgi_data
115
+ data[:cgi_data] = @cgi_data
116
+ end
117
+ data[:error_class] = exception.class.name if exception.is_a?(Exception)
118
+ data[:component] = component if component
119
+ if packet && packet.is_a?(Packet)
120
+ data[:action] = packet.type.split("/").last if packet.respond_to?(:type)
121
+ params = packet.respond_to?(:payload) && packet.payload
122
+ uuid = packet.respond_to?(:token) && packet.token
123
+ elsif packet.is_a?(Hash)
124
+ action = packet[:path] || packet["path"]
125
+ data[:action] = action.split("/").last if action
126
+ params = packet[:data] || packet["data"]
127
+ uuid = packet[:uuid] || packet["uuid"]
128
+ else
129
+ params = uuid = nil
130
+ end
131
+ data[:parameters] = params.is_a?(Hash) ? filter(params) : {:param => params} if params
132
+ data[:session_data] = {:uuid => uuid} if uuid
133
+ HydraulicBrake.notify(data)
134
+ end
135
+ true
136
+ rescue Exception => e
137
+ Log.error("Failed to notify Errbit", e, :trace)
138
+ end
139
+
140
+ # Create proc for making callback to notifier
141
+ #
142
+ # @return [Proc] notifier callback
143
+ def notify_callback
144
+ Proc.new do |exception, packet, agent, component|
145
+ notify(exception, packet, agent, component)
146
+ end
147
+ end
148
+
149
+ # Get exception statistics
150
+ #
151
+ # @param reset [Boolean] Whether to reset the statistics after getting the current ones
152
+ #
153
+ # @return [Hash] current statistics
154
+ def stats(reset = false)
155
+ stats = {"exceptions" => @exception_stats && @exception_stats.all}
156
+ reset_stats if reset
157
+ stats
158
+ end
159
+
160
+ protected
161
+
162
+ # Reset statistics
163
+ # Do not recreate exception stats since may be referenced externally
164
+ #
165
+ # @return [TrueClass] always true
166
+ def reset_stats
167
+ @exception_stats ||= RightSupport::Stats::Exceptions.new(@agent, notify_callback)
168
+ @exception_stats.reset
169
+ end
170
+
171
+ # Configure HydraulicBreak for exception notification
172
+ #
173
+ # @param [String] agent_name uniquely identifying agent process on given server
174
+ #
175
+ # @option options [Integer, NilClass] :shard_id identifying shard of database in use
176
+ # @option options [Array<Symbol, String>] :filter_params names whose values are to be
177
+ # filtered when notifying
178
+ # @option options [String] :airbrake_endpoint URL for Airbrake for reporting exceptions
179
+ # to Errbit
180
+ # @option options [String] :airbrake_api_key for using the Airbrake API to access Errbit
181
+ #
182
+ # @return [TrueClass] always true
183
+ #
184
+ # @raise [RuntimeError] hydraulic_brake gem missing
185
+ def notify_init(agent_name, options)
186
+ if options[:airbrake_endpoint] && options[:airbrake_api_key]
187
+ unless require_succeeds?("hydraulic_brake")
188
+ raise RuntimeError, "hydraulic_brake gem missing - required if airbrake options used in ErrorTracker"
189
+ end
190
+
191
+ @cgi_data = {
192
+ :process => $0,
193
+ :pid => Process.pid,
194
+ :agent_name => agent_name
195
+ }
196
+ @cgi_data[:shard_id] = options[:shard_id] if options[:shard_id]
197
+ @cgi_data[:sha] = CURRENT_SOURCE_SHA if defined?(CURRENT_SOURCE_SHA)
198
+
199
+ uri = URI.parse(options[:airbrake_endpoint])
200
+ HydraulicBrake.configure do |config|
201
+ config.secure = (uri.scheme == "https")
202
+ config.host = uri.host
203
+ config.port = uri.port
204
+ config.api_key = options[:airbrake_api_key]
205
+ config.project_root = AgentConfig.root_dir
206
+ end
207
+ @filter_params = (options[:filter_params] || []).map { |p| p.to_s }
208
+ @notify_enabled = true
209
+ else
210
+ @notify_enabled = false
211
+ end
212
+ true
213
+ end
214
+
215
+ # Apply parameter filter
216
+ #
217
+ # @param [Hash] params to be filtered
218
+ #
219
+ # @return [Hash] filtered parameters
220
+ def filter(params)
221
+ if @filter_params
222
+ filtered_params = {}
223
+ params.each { |k, p| filtered_params[k] = @filter_params.include?(k.to_s) ? FILTERED_PARAM_VALUE : p }
224
+ filtered_params
225
+ end
226
+ end
227
+
228
+ end # ErrorTracker
229
+
230
+ end # RightScale
@@ -36,7 +36,7 @@ module RightScale
36
36
  end
37
37
 
38
38
  # Internal application error
39
- class Application < StandardError; end
39
+ class Application < NestedException; end
40
40
 
41
41
  # Agent command IO error
42
42
  class IO < RuntimeError; end