right_agent 2.2.1-x86-mingw32 → 2.4.3-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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