right_agent 0.6.6 → 0.9.3

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 (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