right_agent 0.5.1

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