right_agent 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +86 -0
  4. data/lib/right_agent.rb +66 -0
  5. data/lib/right_agent/actor.rb +163 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +189 -0
  8. data/lib/right_agent/agent.rb +735 -0
  9. data/lib/right_agent/agent_config.rb +403 -0
  10. data/lib/right_agent/agent_identity.rb +209 -0
  11. data/lib/right_agent/agent_tags_manager.rb +213 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/broker_client.rb +683 -0
  14. data/lib/right_agent/command.rb +30 -0
  15. data/lib/right_agent/command/agent_manager_commands.rb +134 -0
  16. data/lib/right_agent/command/command_client.rb +136 -0
  17. data/lib/right_agent/command/command_constants.rb +42 -0
  18. data/lib/right_agent/command/command_io.rb +128 -0
  19. data/lib/right_agent/command/command_parser.rb +87 -0
  20. data/lib/right_agent/command/command_runner.rb +105 -0
  21. data/lib/right_agent/command/command_serializer.rb +63 -0
  22. data/lib/right_agent/console.rb +65 -0
  23. data/lib/right_agent/core_payload_types.rb +42 -0
  24. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  25. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  26. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  27. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  28. data/lib/right_agent/core_payload_types/dev_repositories.rb +90 -0
  29. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  30. data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
  31. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  32. data/lib/right_agent/core_payload_types/login_user.rb +62 -0
  33. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  34. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -0
  35. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  36. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  37. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +73 -0
  38. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  39. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  40. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  41. data/lib/right_agent/daemonize.rb +35 -0
  42. data/lib/right_agent/dispatcher.rb +348 -0
  43. data/lib/right_agent/enrollment_result.rb +217 -0
  44. data/lib/right_agent/exceptions.rb +30 -0
  45. data/lib/right_agent/ha_broker_client.rb +1278 -0
  46. data/lib/right_agent/idempotent_request.rb +140 -0
  47. data/lib/right_agent/log.rb +418 -0
  48. data/lib/right_agent/monkey_patches.rb +29 -0
  49. data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
  50. data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -0
  51. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  52. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  53. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  54. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  55. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  56. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +46 -0
  57. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -0
  58. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +90 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  64. data/lib/right_agent/multiplexer.rb +91 -0
  65. data/lib/right_agent/operation_result.rb +270 -0
  66. data/lib/right_agent/packets.rb +637 -0
  67. data/lib/right_agent/payload_formatter.rb +104 -0
  68. data/lib/right_agent/pid_file.rb +159 -0
  69. data/lib/right_agent/platform.rb +319 -0
  70. data/lib/right_agent/platform/darwin.rb +227 -0
  71. data/lib/right_agent/platform/linux.rb +268 -0
  72. data/lib/right_agent/platform/windows.rb +1204 -0
  73. data/lib/right_agent/scripts/agent_controller.rb +522 -0
  74. data/lib/right_agent/scripts/agent_deployer.rb +379 -0
  75. data/lib/right_agent/scripts/common_parser.rb +153 -0
  76. data/lib/right_agent/scripts/log_level_manager.rb +193 -0
  77. data/lib/right_agent/scripts/stats_manager.rb +256 -0
  78. data/lib/right_agent/scripts/usage.rb +58 -0
  79. data/lib/right_agent/secure_identity.rb +92 -0
  80. data/lib/right_agent/security.rb +32 -0
  81. data/lib/right_agent/security/cached_certificate_store_proxy.rb +63 -0
  82. data/lib/right_agent/security/certificate.rb +102 -0
  83. data/lib/right_agent/security/certificate_cache.rb +89 -0
  84. data/lib/right_agent/security/distinguished_name.rb +56 -0
  85. data/lib/right_agent/security/encrypted_document.rb +84 -0
  86. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  87. data/lib/right_agent/security/signature.rb +86 -0
  88. data/lib/right_agent/security/static_certificate_store.rb +69 -0
  89. data/lib/right_agent/sender.rb +937 -0
  90. data/lib/right_agent/serialize.rb +29 -0
  91. data/lib/right_agent/serialize/message_pack.rb +102 -0
  92. data/lib/right_agent/serialize/secure_serializer.rb +131 -0
  93. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  94. data/lib/right_agent/serialize/serializable.rb +135 -0
  95. data/lib/right_agent/serialize/serializer.rb +149 -0
  96. data/lib/right_agent/stats_helper.rb +731 -0
  97. data/lib/right_agent/subprocess.rb +38 -0
  98. data/lib/right_agent/tracer.rb +124 -0
  99. data/right_agent.gemspec +60 -0
  100. data/spec/actor_registry_spec.rb +81 -0
  101. data/spec/actor_spec.rb +99 -0
  102. data/spec/agent_config_spec.rb +226 -0
  103. data/spec/agent_identity_spec.rb +75 -0
  104. data/spec/agent_spec.rb +571 -0
  105. data/spec/broker_client_spec.rb +961 -0
  106. data/spec/command/agent_manager_commands_spec.rb +51 -0
  107. data/spec/command/command_io_spec.rb +93 -0
  108. data/spec/command/command_parser_spec.rb +79 -0
  109. data/spec/command/command_runner_spec.rb +72 -0
  110. data/spec/command/command_serializer_spec.rb +51 -0
  111. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  112. data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
  113. data/spec/core_payload_types/login_user_spec.rb +98 -0
  114. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  115. data/spec/core_payload_types/spec_helper.rb +23 -0
  116. data/spec/dispatcher_spec.rb +372 -0
  117. data/spec/enrollment_result_spec.rb +53 -0
  118. data/spec/ha_broker_client_spec.rb +1673 -0
  119. data/spec/idempotent_request_spec.rb +136 -0
  120. data/spec/log_spec.rb +177 -0
  121. data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
  122. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  123. data/spec/monkey_patches/string_patch_spec.rb +99 -0
  124. data/spec/multiplexer_spec.rb +48 -0
  125. data/spec/operation_result_spec.rb +171 -0
  126. data/spec/packets_spec.rb +418 -0
  127. data/spec/platform/platform_spec.rb +60 -0
  128. data/spec/results_mock.rb +45 -0
  129. data/spec/secure_identity_spec.rb +50 -0
  130. data/spec/security/cached_certificate_store_proxy_spec.rb +56 -0
  131. data/spec/security/certificate_cache_spec.rb +71 -0
  132. data/spec/security/certificate_spec.rb +49 -0
  133. data/spec/security/distinguished_name_spec.rb +46 -0
  134. data/spec/security/encrypted_document_spec.rb +55 -0
  135. data/spec/security/rsa_key_pair_spec.rb +55 -0
  136. data/spec/security/signature_spec.rb +66 -0
  137. data/spec/security/static_certificate_store_spec.rb +52 -0
  138. data/spec/sender_spec.rb +887 -0
  139. data/spec/serialize/message_pack_spec.rb +131 -0
  140. data/spec/serialize/secure_serializer_spec.rb +102 -0
  141. data/spec/serialize/serializable_spec.rb +90 -0
  142. data/spec/serialize/serializer_spec.rb +174 -0
  143. data/spec/spec.opts +2 -0
  144. data/spec/spec_helper.rb +77 -0
  145. data/spec/stats_helper_spec.rb +681 -0
  146. data/spec/tracer_spec.rb +114 -0
  147. metadata +320 -0
@@ -0,0 +1,29 @@
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
+ require 'rubygems'
24
+ require 'rbconfig'
25
+
26
+ MONKEY_PATCHES_BASE_DIR = File.join(File.dirname(__FILE__), 'monkey_patches')
27
+
28
+ require File.normalize_path(File.join(MONKEY_PATCHES_BASE_DIR, 'amqp_patch'))
29
+ require File.normalize_path(File.join(MONKEY_PATCHES_BASE_DIR, 'ruby_patch'))
@@ -0,0 +1,274 @@
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
+
@@ -0,0 +1,49 @@
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
+ # This file may get required twice on Windows: Once using long path and once
24
+ # using short path. Since this is where we define the File.normalize_path
25
+ # method to alleviate this issue, we have a chicken & egg problem. So detect if
26
+ # we already required this file and skip the rest if that was the case.
27
+ unless defined?(RUBY_PATCH_BASE_DIR)
28
+
29
+ # Load platform-specific patches before any other patches (in order to define
30
+ # File.normalize_path, etc.)
31
+ case (family = RbConfig::CONFIG['host_os'])
32
+ when /mswin|win32|dos|mingw|cygwin/i
33
+ require File.expand_path(File.join(File.dirname(__FILE__), 'ruby_patch', 'windows_patch'))
34
+ when /linux/i
35
+ require File.expand_path(File.join(File.dirname(__FILE__), 'ruby_patch', 'linux_patch'))
36
+ when /darwin/i
37
+ require File.expand_path(File.join(File.dirname(__FILE__), 'ruby_patch', 'darwin_patch'))
38
+ else
39
+ raise LoadError, "Unsupported platform: #{family}"
40
+ end
41
+
42
+ RUBY_PATCH_BASE_DIR = File.join(File.dirname(__FILE__), 'ruby_patch')
43
+
44
+ require File.normalize_path(File.join(RUBY_PATCH_BASE_DIR, 'array_patch'))
45
+ require File.normalize_path(File.join(RUBY_PATCH_BASE_DIR, 'string_patch'))
46
+ require File.normalize_path(File.join(RUBY_PATCH_BASE_DIR, 'object_patch'))
47
+ require File.normalize_path(File.join(RUBY_PATCH_BASE_DIR, 'singleton_patch'))
48
+
49
+ end # Unless already defined
@@ -0,0 +1,29 @@
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 Array
24
+
25
+ def rotate!
26
+ push shift
27
+ end
28
+
29
+ end
@@ -0,0 +1,24 @@
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
+ # darwin currently has no difference from linux platform patches.
24
+ require File.expand_path(File.join(File.dirname(__FILE__), 'linux_patch'))
@@ -0,0 +1,24 @@
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
+ # load File monkey-patch first to enable use of File.normalize_path
24
+ require File.expand_path(File.join(File.dirname(__FILE__), 'linux_patch', 'file_patch'))
@@ -0,0 +1,30 @@
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 File
24
+
25
+ # On *nix systems, resolves to File.expand_path
26
+ def self.normalize_path(file_name, *dir_string)
27
+ File.expand_path(file_name, *dir_string)
28
+ end
29
+
30
+ end