right_agent 0.6.6 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/lib/right_agent/agent.rb +26 -25
  2. data/lib/right_agent/agent_config.rb +28 -2
  3. data/lib/right_agent/command/command_constants.rb +2 -2
  4. data/lib/right_agent/core_payload_types/executable_bundle.rb +3 -21
  5. data/lib/right_agent/core_payload_types/login_user.rb +19 -4
  6. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +7 -1
  7. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +7 -1
  8. data/lib/right_agent/dispatcher.rb +6 -19
  9. data/lib/right_agent/idempotent_request.rb +72 -17
  10. data/lib/right_agent/monkey_patches/ruby_patch.rb +0 -1
  11. data/lib/right_agent/monkey_patches.rb +0 -1
  12. data/lib/right_agent/operation_result.rb +27 -4
  13. data/lib/right_agent/packets.rb +47 -23
  14. data/lib/right_agent/platform/darwin.rb +33 -2
  15. data/lib/right_agent/platform/linux.rb +98 -2
  16. data/lib/right_agent/platform/windows.rb +41 -6
  17. data/lib/right_agent/platform.rb +11 -2
  18. data/lib/right_agent/scripts/agent_controller.rb +2 -1
  19. data/lib/right_agent/scripts/agent_deployer.rb +2 -2
  20. data/lib/right_agent/scripts/stats_manager.rb +7 -3
  21. data/lib/right_agent/sender.rb +45 -28
  22. data/lib/right_agent.rb +2 -5
  23. data/right_agent.gemspec +5 -3
  24. data/spec/agent_config_spec.rb +1 -1
  25. data/spec/agent_spec.rb +26 -20
  26. data/spec/core_payload_types/login_user_spec.rb +7 -3
  27. data/spec/idempotent_request_spec.rb +218 -48
  28. data/spec/operation_result_spec.rb +19 -0
  29. data/spec/packets_spec.rb +42 -1
  30. data/spec/platform/darwin.rb +11 -0
  31. data/spec/platform/linux.rb +23 -0
  32. data/spec/platform/linux_volume_manager_spec.rb +43 -43
  33. data/spec/platform/platform_spec.rb +35 -32
  34. data/spec/platform/windows.rb +11 -0
  35. data/spec/sender_spec.rb +21 -25
  36. metadata +47 -40
  37. data/lib/right_agent/broker_client.rb +0 -686
  38. data/lib/right_agent/ha_broker_client.rb +0 -1327
  39. data/lib/right_agent/monkey_patches/amqp_patch.rb +0 -274
  40. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +0 -107
  41. data/lib/right_agent/stats_helper.rb +0 -745
  42. data/spec/broker_client_spec.rb +0 -962
  43. data/spec/ha_broker_client_spec.rb +0 -1695
  44. data/spec/monkey_patches/amqp_patch_spec.rb +0 -100
  45. data/spec/monkey_patches/string_patch_spec.rb +0 -99
  46. data/spec/stats_helper_spec.rb +0 -686
@@ -1,274 +0,0 @@
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
- class MQ
24
-
25
- class Queue
26
- # Asks the broker to redeliver all unacknowledged messages on a
27
- # specified channel. Zero or more messages may be redelivered.
28
- #
29
- # * requeue (default false)
30
- # If this parameter is false, the message will be redelivered to the original recipient.
31
- # If this flag is true, the server will attempt to requeue the message, potentially then
32
- # delivering it to an alternative subscriber.
33
- #
34
- def recover(requeue = false)
35
- @mq.callback{
36
- @mq.send Protocol::Basic::Recover.new({ :requeue => requeue })
37
- }
38
- self
39
- end
40
- end
41
-
42
- # May raise a MQ::Error exception when the frame payload contains a
43
- # Protocol::Channel::Close object.
44
- #
45
- # This usually occurs when a client attempts to perform an illegal
46
- # operation. A short, and incomplete, list of potential illegal operations
47
- # follows:
48
- # * publish a message to a deleted exchange (NOT_FOUND)
49
- # * declare an exchange using the reserved 'amq.' naming structure (ACCESS_REFUSED)
50
- #
51
- def process_frame frame
52
- log :received, frame
53
-
54
- case frame
55
- when Frame::Header
56
- @header = frame.payload
57
- @body = ''
58
-
59
- when Frame::Body
60
- @body << frame.payload
61
- if @body.length >= @header.size
62
- if @method.is_a? Protocol::Basic::Return
63
- @on_return_message.call @method, @body if @on_return_message
64
- else
65
- @header.properties.update(@method.arguments)
66
- @consumer.receive @header, @body if @consumer
67
- end
68
- @body = @header = @consumer = @method = nil
69
- end
70
-
71
- when Frame::Method
72
- case method = frame.payload
73
- when Protocol::Channel::OpenOk
74
- send Protocol::Access::Request.new(:realm => '/data',
75
- :read => true,
76
- :write => true,
77
- :active => true,
78
- :passive => true)
79
-
80
- when Protocol::Access::RequestOk
81
- @ticket = method.ticket
82
- callback{
83
- send Protocol::Channel::Close.new(:reply_code => 200,
84
- :reply_text => 'bye',
85
- :method_id => 0,
86
- :class_id => 0)
87
- } if @closing
88
- succeed
89
-
90
- when Protocol::Basic::CancelOk
91
- if @consumer = consumers[ method.consumer_tag ]
92
- @consumer.cancelled
93
- else
94
- MQ.error "Basic.CancelOk for invalid consumer tag: #{method.consumer_tag}"
95
- end
96
-
97
- when Protocol::Queue::DeclareOk
98
- queues[ method.queue ].receive_status method
99
-
100
- when Protocol::Basic::Deliver, Protocol::Basic::GetOk
101
- @method = method
102
- @header = nil
103
- @body = ''
104
-
105
- if method.is_a? Protocol::Basic::GetOk
106
- @consumer = get_queue{|q| q.shift }
107
- MQ.error "No pending Basic.GetOk requests" unless @consumer
108
- else
109
- @consumer = consumers[ method.consumer_tag ]
110
- MQ.error "Basic.Deliver for invalid consumer tag: #{method.consumer_tag}" unless @consumer
111
- end
112
-
113
- when Protocol::Basic::GetEmpty
114
- if @consumer = get_queue{|q| q.shift }
115
- @consumer.receive nil, nil
116
- else
117
- MQ.error "Basic.GetEmpty for invalid consumer"
118
- end
119
-
120
- when Protocol::Basic::Return
121
- @method = method
122
- @header = nil
123
- @body = ''
124
-
125
- when Protocol::Channel::Close
126
- raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]} on #{@channel}"
127
-
128
- when Protocol::Channel::CloseOk
129
- @closing = false
130
- conn.callback{ |c|
131
- c.channels.delete @channel
132
- c.close if c.channels.empty?
133
- }
134
-
135
- when Protocol::Basic::ConsumeOk
136
- if @consumer = consumers[ method.consumer_tag ]
137
- @consumer.confirm_subscribe
138
- else
139
- MQ.error "Basic.ConsumeOk for invalid consumer tag: #{method.consumer_tag}"
140
- end
141
- end
142
- end
143
- end
144
-
145
- # Provide callback to be activated when a message is returned
146
- def return_message(&blk)
147
- @on_return_message = blk
148
- end
149
-
150
- end
151
-
152
- # monkey patch to the amqp gem that adds :no_declare => true option for new Queue objects.
153
- # This allows an instance that has no configuration privileges to enroll without blowing
154
- # up the AMQP gem when it tries to subscribe to its queue before it has been created.
155
- # Exchange :no_declare support is already in the eventmachine-0.12.10 gem.
156
- # temporary until we get this into amqp proper
157
- MQ::Queue.class_eval do
158
- def initialize mq, name, opts = {}
159
- @mq = mq
160
- @opts = opts
161
- @bindings ||= {}
162
- @mq.queues[@name = name] ||= self
163
- unless opts[:no_declare]
164
- @mq.callback{
165
- @mq.send AMQP::Protocol::Queue::Declare.new({ :queue => name,
166
- :nowait => true }.merge(opts))
167
- }
168
- end
169
- end
170
- end
171
-
172
- begin
173
- # Monkey patch AMQP reconnect backoff
174
- AMQP::Client.module_eval do
175
- def initialize opts = {}
176
- @settings = opts
177
- extend AMQP.client
178
-
179
- @on_disconnect ||= proc{ @connection_status.call(:failed) if @connection_status }
180
-
181
- timeout @settings[:timeout] if @settings[:timeout]
182
- errback{ @on_disconnect.call } unless @reconnecting
183
-
184
- @connected = false
185
- end
186
-
187
- def reconnect(force = false)
188
- if @reconnecting and not force
189
- # Wait after first reconnect attempt and in between each subsequent attempt
190
- EM.add_timer(@settings[:reconnect_interval] || 5) { reconnect(true) }
191
- return
192
- end
193
-
194
- unless @reconnecting
195
- @deferred_status = nil
196
- initialize(@settings)
197
-
198
- mqs = @channels
199
- @channels = {}
200
- mqs.each{ |_,mq| mq.reset } if mqs
201
-
202
- @reconnecting = true
203
-
204
- again = @settings[:reconnect_delay]
205
- again = again.call if again.is_a?(Proc)
206
- if again.is_a?(Numeric)
207
- # Wait before making initial reconnect attempt
208
- EM.add_timer(again) { reconnect(true) }
209
- return
210
- elsif ![nil, true].include?(again)
211
- raise ::AMQP::Error, "Could not interpret :reconnect_delay => #{again.inspect}; expected nil, true, or Numeric"
212
- end
213
- end
214
-
215
- RightScale::Log.warning("Attempting to reconnect to broker " +
216
- "#{RightScale::AgentIdentity.new('rs', 'broker', @settings[:port].to_i, @settings[:host].gsub('-', '~')).to_s}")
217
- log 'reconnecting'
218
- EM.reconnect(@settings[:host], @settings[:port], self)
219
- end
220
- end
221
-
222
- # Monkey patch AMQP to clean up @conn when an error is raised after a broker request failure,
223
- # otherwise AMQP becomes unusable
224
- AMQP.module_eval do
225
- def self.start *args, &blk
226
- begin
227
- EM.run{
228
- @conn ||= connect *args
229
- @conn.callback(&blk) if blk
230
- @conn
231
- }
232
- rescue Exception => e
233
- @conn = nil
234
- raise e
235
- end
236
- end
237
- end
238
-
239
- # This monkey patch catches exceptions that would otherwise cause EM to stop or be in a bad
240
- # state if a top level EM error handler was setup. Instead close the connection and leave EM
241
- # alone.
242
- # Don't log an error if the environment variable IGNORE_AMQP_FAILURES is set (used in the
243
- # enroll script)
244
- AMQP::Client.module_eval do
245
- alias :orig_receive_data :receive_data
246
- def receive_data(*args)
247
- begin
248
- orig_receive_data(*args)
249
- rescue Exception => e
250
- RightScale::Log.error("Exception caught while processing AMQP frame, closing connection",
251
- e, :trace) unless ENV['IGNORE_AMQP_FAILURES']
252
- close_connection
253
- end
254
- end
255
- end
256
-
257
- # Add a new callback to amqp gem that triggers once the handshake with the broker completed
258
- # The 'connected' status callback happens before the handshake is done and if it results in
259
- # a lot of activity it might prevent EM from being able to call the code handling the
260
- # incoming handshake packet in a timely fashion causing the broker to close the connection
261
- AMQP::BasicClient.module_eval do
262
- alias :orig_process_frame :process_frame
263
- def process_frame(frame)
264
- orig_process_frame(frame)
265
- @connection_status.call(:ready) if @connection_status && frame.payload.is_a?(AMQP::Protocol::Connection::Start)
266
- end
267
- end
268
-
269
- rescue LoadError => e
270
- # Make sure we're dealing with a legitimate missing-file LoadError
271
- raise e unless e.message =~ /^no such file to load/
272
- # Missing 'amqp' indicates that the AMQP gem is not installed; we can ignore this
273
- end
274
-
@@ -1,107 +0,0 @@
1
- #
2
- # Copyright (c) 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
- class String
24
- ##
25
- # Convert to snake case.
26
- #
27
- # "FooBar".snake_case #=> "foo_bar"
28
- # "HeadlineCNNNews".snake_case #=> "headline_cnn_news"
29
- # "CNN".snake_case #=> "cnn"
30
- #
31
- # @return [String] Receiver converted to snake case.
32
- #
33
- # @api public
34
- def snake_case
35
- return downcase if match(/\A[A-Z]+\z/)
36
- gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
37
- gsub(/([a-z])([A-Z])/, '\1_\2').
38
- downcase
39
- end
40
-
41
- ##
42
- # Convert a constant name to a path, assuming a conventional structure.
43
- #
44
- # "FooBar::Baz".to_const_path # => "foo_bar/baz"
45
- #
46
- # @return [String] Path to the file containing the constant named by receiver
47
- # (constantized string), assuming a conventional structure.
48
- #
49
- # @api public
50
- def to_const_path
51
- snake_case.gsub(/::/, "/")
52
- end
53
-
54
- # Convert constant name to constant
55
- #
56
- # "FooBar::Baz".to_const => FooBar::Baz
57
- #
58
- # @return [Constant] Constant corresponding to given name or nil if no
59
- # constant with that name exists
60
- #
61
- # @api public
62
- def to_const
63
- names = split('::')
64
- names.shift if names.empty? || names.first.empty?
65
-
66
- constant = Object
67
- names.each do |name|
68
- # modified to return nil instead of raising an const_missing error
69
- constant = constant && constant.const_defined?(name) ? constant.const_get(name) : nil
70
- end
71
- constant
72
- end
73
-
74
- # Reverse operation of snake case:
75
- #
76
- # "some_string/some_other_string" => "SomeString::SomeOtherString"
77
- #
78
- # @return [String] Camelized string
79
- #
80
- # @api public
81
- if !String.public_method_defined?(:camelize)
82
- def camelize(first_letter = :upper)
83
- case first_letter
84
- when :upper then gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
85
- when :lower then first + camelize(self)[1..-1]
86
- end
87
- end
88
- end
89
-
90
- # Add ability to output colored text to console
91
- # e.g.: puts "Hello".red
92
- def bold; colorize("\e[1m\e[29m"); end
93
- def grey; colorize("\e[30m"); end
94
- def red; colorize("\e[1m\e[31m"); end
95
- def dark_red; colorize("\e[31m"); end
96
- def green; colorize("\e[1m\e[32m"); end
97
- def dark_green; colorize("\e[32m"); end
98
- def yellow; colorize("\e[1m\e[33m"); end
99
- def blue; colorize("\e[1m\e[34m"); end
100
- def dark_blue; colorize("\e[34m"); end
101
- def pur; colorize("\e[1m\e[35m"); end
102
- def colorize(color_code)
103
- # Doesn't work with the Windows prompt...
104
- (RightScale::Platform.windows? || !$stdout.isatty) ? to_s : "#{color_code}#{to_s}\e[0m"
105
- end
106
-
107
- end