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.
- data/lib/right_agent/agent.rb +26 -25
- data/lib/right_agent/agent_config.rb +28 -2
- data/lib/right_agent/command/command_constants.rb +2 -2
- data/lib/right_agent/core_payload_types/executable_bundle.rb +3 -21
- data/lib/right_agent/core_payload_types/login_user.rb +19 -4
- data/lib/right_agent/core_payload_types/recipe_instantiation.rb +7 -1
- data/lib/right_agent/core_payload_types/right_script_instantiation.rb +7 -1
- data/lib/right_agent/dispatcher.rb +6 -19
- data/lib/right_agent/idempotent_request.rb +72 -17
- data/lib/right_agent/monkey_patches/ruby_patch.rb +0 -1
- data/lib/right_agent/monkey_patches.rb +0 -1
- data/lib/right_agent/operation_result.rb +27 -4
- data/lib/right_agent/packets.rb +47 -23
- data/lib/right_agent/platform/darwin.rb +33 -2
- data/lib/right_agent/platform/linux.rb +98 -2
- data/lib/right_agent/platform/windows.rb +41 -6
- data/lib/right_agent/platform.rb +11 -2
- data/lib/right_agent/scripts/agent_controller.rb +2 -1
- data/lib/right_agent/scripts/agent_deployer.rb +2 -2
- data/lib/right_agent/scripts/stats_manager.rb +7 -3
- data/lib/right_agent/sender.rb +45 -28
- data/lib/right_agent.rb +2 -5
- data/right_agent.gemspec +5 -3
- data/spec/agent_config_spec.rb +1 -1
- data/spec/agent_spec.rb +26 -20
- data/spec/core_payload_types/login_user_spec.rb +7 -3
- data/spec/idempotent_request_spec.rb +218 -48
- data/spec/operation_result_spec.rb +19 -0
- data/spec/packets_spec.rb +42 -1
- data/spec/platform/darwin.rb +11 -0
- data/spec/platform/linux.rb +23 -0
- data/spec/platform/linux_volume_manager_spec.rb +43 -43
- data/spec/platform/platform_spec.rb +35 -32
- data/spec/platform/windows.rb +11 -0
- data/spec/sender_spec.rb +21 -25
- metadata +47 -40
- data/lib/right_agent/broker_client.rb +0 -686
- data/lib/right_agent/ha_broker_client.rb +0 -1327
- data/lib/right_agent/monkey_patches/amqp_patch.rb +0 -274
- data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +0 -107
- data/lib/right_agent/stats_helper.rb +0 -745
- data/spec/broker_client_spec.rb +0 -962
- data/spec/ha_broker_client_spec.rb +0 -1695
- data/spec/monkey_patches/amqp_patch_spec.rb +0 -100
- data/spec/monkey_patches/string_patch_spec.rb +0 -99
- 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
|