right_link 5.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. data/actors/agent_manager.rb +88 -0
  2. data/actors/instance_scheduler.rb +321 -0
  3. data/actors/instance_services.rb +64 -0
  4. data/actors/instance_setup.rb +567 -0
  5. data/bin/cloud +25 -0
  6. data/bin/cook_runner +44 -0
  7. data/bin/deploy +120 -0
  8. data/bin/enroll +385 -0
  9. data/bin/rad +32 -0
  10. data/bin/rchk +29 -0
  11. data/bin/rnac +39 -0
  12. data/bin/rs_connect +33 -0
  13. data/bin/rs_log_level +31 -0
  14. data/bin/rs_ohai +28 -0
  15. data/bin/rs_reenroll +31 -0
  16. data/bin/rs_run_recipe +34 -0
  17. data/bin/rs_run_right_script +34 -0
  18. data/bin/rs_shutdown +33 -0
  19. data/bin/rs_tag +33 -0
  20. data/bin/rs_thunk +33 -0
  21. data/bin/rstat +31 -0
  22. data/bin/system +16 -0
  23. data/ext/Rakefile +18 -0
  24. data/init/config.yml +5 -0
  25. data/init/init.rb +79 -0
  26. data/lib/chef/ohai_setup.rb +51 -0
  27. data/lib/chef/plugins/cloud.rb +91 -0
  28. data/lib/chef/plugins/cloudstack.rb +23 -0
  29. data/lib/chef/plugins/ec2.rb +23 -0
  30. data/lib/chef/plugins/linux/block_device2.rb +24 -0
  31. data/lib/chef/plugins/rackspace.rb +23 -0
  32. data/lib/chef/plugins/rightscale.rb +125 -0
  33. data/lib/chef/plugins/windows/network.rb +114 -0
  34. data/lib/chef/plugins.rb +74 -0
  35. data/lib/chef/providers/dns_dnsmadeeasy_provider.rb +81 -0
  36. data/lib/chef/providers/dns_resource.rb +100 -0
  37. data/lib/chef/providers/executable_schedule_provider.rb +70 -0
  38. data/lib/chef/providers/executable_schedule_resource.rb +144 -0
  39. data/lib/chef/providers/remote_recipe_provider.rb +86 -0
  40. data/lib/chef/providers/remote_recipe_resource.rb +101 -0
  41. data/lib/chef/providers/right_link_tag_provider.rb +73 -0
  42. data/lib/chef/providers/right_link_tag_resource.rb +59 -0
  43. data/lib/chef/providers/right_script_provider.rb +190 -0
  44. data/lib/chef/providers/right_script_resource.rb +113 -0
  45. data/lib/chef/providers/rs_shutdown_provider.rb +75 -0
  46. data/lib/chef/providers/rs_shutdown_resource.rb +55 -0
  47. data/lib/chef/providers/server_collection_provider.rb +66 -0
  48. data/lib/chef/providers/server_collection_resource.rb +93 -0
  49. data/lib/chef/providers/windows/powershell_provider.rb +151 -0
  50. data/lib/chef/providers/windows/powershell_resource.rb +111 -0
  51. data/lib/chef/providers/windows/unsupported_provider.rb +51 -0
  52. data/lib/chef/right_providers.rb +55 -0
  53. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.csproj +104 -0
  54. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.dll-Help.xml +141 -0
  55. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Exceptions.cs +182 -0
  56. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeCommand.cs +58 -0
  57. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeRequest.cs +46 -0
  58. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeResponse.cs +45 -0
  59. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceCommand.cs +58 -0
  60. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceRequest.cs +46 -0
  61. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceResponse.cs +45 -0
  62. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceCommand.cs +58 -0
  63. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceRequest.cs +46 -0
  64. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceResponse.cs +45 -0
  65. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionCommand.cs +178 -0
  66. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionRequest.cs +67 -0
  67. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionResponse.cs +58 -0
  68. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueCommandBase.cs +142 -0
  69. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueRequestBase.cs +64 -0
  70. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueResponseBase.cs +69 -0
  71. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/JsonTransport.cs +110 -0
  72. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeClient.cs +158 -0
  73. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeServer.cs +142 -0
  74. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
  75. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolConstants.cs +55 -0
  76. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolUtilities.cs +77 -0
  77. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ReadMe.txt +53 -0
  78. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeCommand.cs +59 -0
  79. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeRequest.cs +46 -0
  80. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeResponse.cs +58 -0
  81. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceCommand.cs +59 -0
  82. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceRequest.cs +46 -0
  83. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceResponse.cs +40 -0
  84. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceCommand.cs +59 -0
  85. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceRequest.cs +46 -0
  86. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceResponse.cs +40 -0
  87. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueCommandBase.cs +293 -0
  88. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueRequestBase.cs +75 -0
  89. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueResponseBase.cs +45 -0
  90. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Transport.cs +91 -0
  91. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet.sln +35 -0
  92. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Program.cs +374 -0
  93. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
  94. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/TestChefNodeCmdlet.csproj +65 -0
  95. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Program.cs +136 -0
  96. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Properties/AssemblyInfo.cs +36 -0
  97. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/ReadMe.txt +46 -0
  98. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/TestNextActionCmdlet.csproj +68 -0
  99. data/lib/chef/windows/bin/Newtonsoft.Json.dll +0 -0
  100. data/lib/chef/windows/chef_node_server.rb +463 -0
  101. data/lib/chef/windows/dynamic_powershell_provider.rb +296 -0
  102. data/lib/chef/windows/pipe_server.rb +283 -0
  103. data/lib/chef/windows/powershell_host.rb +285 -0
  104. data/lib/chef/windows/powershell_pipe_server.rb +136 -0
  105. data/lib/chef/windows/powershell_provider_base.rb +92 -0
  106. data/lib/chef/windows/scripts/run_loop.ps1 +105 -0
  107. data/lib/clouds/cloud.rb +557 -0
  108. data/lib/clouds/cloud_factory.rb +250 -0
  109. data/lib/clouds/cloud_utilities.rb +244 -0
  110. data/lib/clouds/clouds/azure.rb +106 -0
  111. data/lib/clouds/clouds/cloudstack.rb +114 -0
  112. data/lib/clouds/clouds/ec2.rb +113 -0
  113. data/lib/clouds/clouds/eucalyptus.rb +46 -0
  114. data/lib/clouds/clouds/google.rb +102 -0
  115. data/lib/clouds/clouds/none.rb +76 -0
  116. data/lib/clouds/clouds/openstack.rb +30 -0
  117. data/lib/clouds/clouds/rackspace-ng.rb +54 -0
  118. data/lib/clouds/clouds/rackspace.rb +78 -0
  119. data/lib/clouds/clouds/softlayer.rb +91 -0
  120. data/lib/clouds/metadata_formatter.rb +108 -0
  121. data/lib/clouds/metadata_provider.rb +128 -0
  122. data/lib/clouds/metadata_source.rb +87 -0
  123. data/lib/clouds/metadata_sources/certificate_metadata_source.rb +207 -0
  124. data/lib/clouds/metadata_sources/config_drive_metadata_source.rb +129 -0
  125. data/lib/clouds/metadata_sources/file_metadata_source.rb +74 -0
  126. data/lib/clouds/metadata_sources/http_metadata_source.rb +277 -0
  127. data/lib/clouds/metadata_sources/selective_metadata_source.rb +122 -0
  128. data/lib/clouds/metadata_tree_climber.rb +144 -0
  129. data/lib/clouds/metadata_writer.rb +155 -0
  130. data/lib/clouds/metadata_writers/dictionary_metadata_writer.rb +72 -0
  131. data/lib/clouds/metadata_writers/ruby_metadata_writer.rb +76 -0
  132. data/lib/clouds/metadata_writers/shell_metadata_writer.rb +121 -0
  133. data/lib/clouds/register_clouds.rb +34 -0
  134. data/lib/clouds.rb +32 -0
  135. data/lib/gem_dependencies.rb +83 -0
  136. data/lib/git_hooks/commit-msg.rb +7 -0
  137. data/lib/instance/agent_config.rb +168 -0
  138. data/lib/instance/agent_watcher.rb +233 -0
  139. data/lib/instance/audit_cook_stub.rb +104 -0
  140. data/lib/instance/audit_proxy.rb +247 -0
  141. data/lib/instance/bundle_queue.rb +104 -0
  142. data/lib/instance/cook/agent_connection.rb +109 -0
  143. data/lib/instance/cook/audit_logger.rb +165 -0
  144. data/lib/instance/cook/audit_stub.rb +142 -0
  145. data/lib/instance/cook/ca-bundle.crt +2794 -0
  146. data/lib/instance/cook/chef_state.rb +211 -0
  147. data/lib/instance/cook/cook.rb +306 -0
  148. data/lib/instance/cook/cook_state.rb +298 -0
  149. data/lib/instance/cook/cookbook_path_mapping.rb +66 -0
  150. data/lib/instance/cook/cookbook_repo_retriever.rb +190 -0
  151. data/lib/instance/cook/executable_sequence.rb +765 -0
  152. data/lib/instance/cook/external_parameter_gatherer.rb +190 -0
  153. data/lib/instance/cook/repose_downloader.rb +349 -0
  154. data/lib/instance/cook/shutdown_request_proxy.rb +121 -0
  155. data/lib/instance/cook.rb +41 -0
  156. data/lib/instance/downloader.rb +208 -0
  157. data/lib/instance/duplicable.rb +67 -0
  158. data/lib/instance/exceptions.rb +49 -0
  159. data/lib/instance/executable_sequence_proxy.rb +278 -0
  160. data/lib/instance/instance_commands.rb +577 -0
  161. data/lib/instance/instance_state.rb +633 -0
  162. data/lib/instance/json_utilities.rb +102 -0
  163. data/lib/instance/login_manager.rb +533 -0
  164. data/lib/instance/login_user_manager.rb +522 -0
  165. data/lib/instance/message_encoder.rb +118 -0
  166. data/lib/instance/multi_thread_bundle_queue.rb +232 -0
  167. data/lib/instance/operation_context.rb +60 -0
  168. data/lib/instance/options_bag.rb +65 -0
  169. data/lib/instance/payload_formatter.rb +46 -0
  170. data/lib/instance/policy.rb +53 -0
  171. data/lib/instance/policy_audit.rb +100 -0
  172. data/lib/instance/policy_manager.rb +146 -0
  173. data/lib/instance/reenroll_manager.rb +104 -0
  174. data/lib/instance/right_scripts_cookbook.rb +181 -0
  175. data/lib/instance/shutdown_request.rb +221 -0
  176. data/lib/instance/single_thread_bundle_queue.rb +189 -0
  177. data/lib/instance/volume_management.rb +450 -0
  178. data/lib/instance.rb +50 -0
  179. data/lib/repo_conf_generators/apt_conf_generators.rb +106 -0
  180. data/lib/repo_conf_generators/gem_conf_generators.rb +80 -0
  181. data/lib/repo_conf_generators/rightscale_conf_generators.rb +254 -0
  182. data/lib/repo_conf_generators/rightscale_key.pub +17 -0
  183. data/lib/repo_conf_generators/yum_conf_generators.rb +225 -0
  184. data/lib/repo_conf_generators.rb +30 -0
  185. data/lib/run_shell.rb +28 -0
  186. data/scripts/agent_checker.rb +571 -0
  187. data/scripts/agent_controller.rb +247 -0
  188. data/scripts/agent_deployer.rb +148 -0
  189. data/scripts/bundle_runner.rb +336 -0
  190. data/scripts/cloud_controller.rb +176 -0
  191. data/scripts/log_level_manager.rb +142 -0
  192. data/scripts/ohai_runner.rb +33 -0
  193. data/scripts/reenroller.rb +193 -0
  194. data/scripts/server_importer.rb +293 -0
  195. data/scripts/shutdown_client.rb +183 -0
  196. data/scripts/system_configurator.rb +367 -0
  197. data/scripts/tagger.rb +381 -0
  198. data/scripts/thunker.rb +356 -0
  199. metadata +418 -0
@@ -0,0 +1,285 @@
1
+ #
2
+ # Copyright (c) 2010-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
+ module RightScale
24
+
25
+ # This class is responsible for managing a Powershell process instance
26
+ # It allows running Powershell scripts in the associated instance and will
27
+ # log the script output.
28
+ class PowershellHost
29
+ # Start the Powershell process synchronously
30
+ # Set the instance variable :active to true once Powershell was
31
+ # successfully started
32
+ #
33
+ # === Parameters
34
+ # options[:node]:: Chef @node object
35
+ # options[:provider_name]:: Associated Chef powershell provider name
36
+ def initialize(options = {})
37
+ Log.debug(format_log_message("Initializing"))
38
+ @node = options[:node]
39
+ @provider_name = options[:provider_name]
40
+ @pipe_name = "#{@provider_name}_#{Time.now.strftime("%Y-%m-%d-%H%M%S")}"
41
+
42
+ @response_mutex = Mutex.new
43
+ @response_event = ConditionVariable.new
44
+
45
+ Log.debug(format_log_message("Starting pipe server"))
46
+ @pipe_server = RightScale::Windows::PowershellPipeServer.new(:pipe_name => @pipe_name) do |kind, payload|
47
+ case kind
48
+ when :is_ready then
49
+ query
50
+ when :respond then
51
+ respond(payload)
52
+ end
53
+ end
54
+
55
+ unless @pipe_server.start
56
+ @pipe_server = nil
57
+ return
58
+ end
59
+
60
+ Log.debug(format_log_message("Starting chef node server"))
61
+ RightScale::Windows::ChefNodeServer.instance.start(:node => @node)
62
+
63
+ Log.debug(format_log_message("Starting host"))
64
+ start_powershell_process
65
+
66
+ Log.debug(format_log_message("Initialized"))
67
+ end
68
+
69
+ def format_log_message(message)
70
+ "[PowershellHost #{@pipe_name}] - #{message}"
71
+ end
72
+
73
+ # Run Powershell script in associated Powershell process
74
+ # Log stdout and stderr to Chef logger
75
+ #
76
+ # === Argument
77
+ # script_path(String):: Full path to Powershell script to be run
78
+ #
79
+ # === Return
80
+ # res[exit_code](Number):: The exit code of the script that was run. nil if no exit status was available.
81
+ #
82
+ # === Raise
83
+ # RightScale::Exceptions:ApplicationError:: If Powershell process is not running (i.e. :active is false)
84
+ def run(script_path)
85
+ Log.debug(format_log_message("Running #{script_path}"))
86
+ res = run_command("&\"#{script_path}\"")
87
+ Log.debug(format_log_message("Finished #{script_path}"))
88
+ res
89
+ end
90
+
91
+ # Terminate associated Powershell process
92
+ # :run cannot be called after :terminate
93
+ # This method is idempotent
94
+ #
95
+ # === Return
96
+ # true:: Always return true
97
+ def terminate
98
+ Log.debug(format_log_message("Terminate requested"))
99
+ res = run_command("break")
100
+ Log.debug(format_log_message("Terminate signal sent"))
101
+
102
+ true
103
+ end
104
+
105
+ protected
106
+
107
+ # Is the Powershell process running?
108
+ #
109
+ # === Return
110
+ # true:: If the associated Powershell process is running
111
+ # false:: Otherwise
112
+ def active
113
+ !!@pipe_server
114
+ end
115
+
116
+ # Query whether there is a command to execute
117
+ # Also signal waiting Chef thread if a command executed
118
+ #
119
+ # === Return
120
+ # true:: If there is a command to execute
121
+ # false:: Otherwise
122
+ def query
123
+ @response_mutex.synchronize do
124
+ if @sent_command
125
+ Log.debug(format_log_message("Completed last command"))
126
+ @sent_command = false
127
+ @response_event.signal
128
+ end
129
+ end
130
+ return !!@pending_command
131
+ end
132
+
133
+ # Respond to pipe server request
134
+ # Send pending command
135
+ #
136
+ # === Return
137
+ # res(String):: Command to execute
138
+ def respond(payload)
139
+ if @last_command.nil?
140
+ Log.debug(format_log_message("Processing first command #{@pending_command}"))
141
+ # this is the first command to be requested, send the command
142
+ @last_command = @pending_command
143
+ res = @pending_command
144
+ @pending_command = ""
145
+ else
146
+ # this is the second time around, grab the results of the last command, and send a no-op
147
+ # the run_loop script will exit if an exception was thrown, and continue if the last
148
+ # command did not throw
149
+ @exit_code = payload[RightScale::Windows::PowershellPipeServer::LAST_EXIT_CODE_KEY]
150
+ @error_msg = payload[RightScale::Windows::PowershellPipeServer::LAST_ERROR_MESSAGE_KEY]
151
+
152
+ # setup the exit conditions
153
+ res = @pending_command
154
+ @pending_command = nil
155
+ @sent_command = true
156
+ end
157
+
158
+ Log.debug(format_log_message("Responding with pending command #{res}"))
159
+ return res
160
+ end
161
+
162
+ # Start the associated powershell process
163
+ #
164
+ # === Return
165
+ # true:: Always return true
166
+ def start_powershell_process
167
+ platform = RightScale::Platform
168
+ shell = platform.shell
169
+
170
+ # Import ChefNodeCmdlet.dll to allow powershell scripts to call get-ChefNode, etc.
171
+ # Also pass in name of pipe that client needs to connect to
172
+ lines_before_script = ["import-module #{CHEF_NODE_CMDLET_DLL_PATH}", "$RS_pipeName='#{@pipe_name}'"]
173
+
174
+ # enable debug and verbose powershell output if log level allows for it.
175
+ if Log.debug?
176
+ lines_before_script << "$VerbosePreference = 'Continue'"
177
+ lines_before_script << "$DebugPreference = 'Continue'"
178
+ end
179
+
180
+ # specifically disable additional error checking intended for RightScript
181
+ # and Powershell provider in Chef. doing this to ensure that existing
182
+ # dynamic provider scripts do not break if they have not cleared their
183
+ # $Error list before finishing.
184
+ lines_after_script = []
185
+
186
+ command = shell.format_powershell_command4(
187
+ ::RightScale::Platform::Shell::POWERSHELL_V1x0_EXECUTABLE_PATH,
188
+ lines_before_script,
189
+ lines_after_script,
190
+ RUN_LOOP_SCRIPT_PATH)
191
+
192
+ Log.debug(format_log_message("Starting powershell process for host #{command}"))
193
+
194
+ ::RightScale::RightPopen.popen3_async(
195
+ command,
196
+ :environment => nil,
197
+ :target => self,
198
+ :stdout_handler => :on_read_output,
199
+ :stderr_handler => :on_read_output,
200
+ :exit_handler => :on_exit)
201
+
202
+ return true
203
+ end
204
+
205
+ # executes a powershell command and waits until the command has completed
206
+ #
207
+ # === Argument
208
+ # command(String):: a powershell command
209
+ #
210
+ # === Return
211
+ # res[exit_code](Number):: The exit code of the script that was run. nil if no exit status was available.
212
+ #
213
+ # === Raise
214
+ # RightScale::Exceptions::Application:: If Powershell process is not running (i.e. :active is false)
215
+ def run_command(command)
216
+ raise RightScale::Exceptions::Application, "Powershell host not active, cannot run: #{command}" unless active
217
+ @response_mutex.synchronize do
218
+ @pending_command = command
219
+ @last_command = nil
220
+ Log.debug(format_log_message("Waiting to process #{command}"))
221
+ @response_event.wait(@response_mutex)
222
+ Log.debug(format_log_message("Finished processing #{command}"))
223
+ @pending_command = nil
224
+ @sent_command = false
225
+ end
226
+
227
+ res = {:exit_code => @exit_code, :error_msg => @error_msg}
228
+ res
229
+ end
230
+
231
+ TEMP_DIR_NAME = 'powershell_host-82D5D281-5E7C-423A-88C2-69E9B7D3F37E'
232
+ SOURCE_WINDOWS_PATH = ::File.normalize_path(::File.dirname(__FILE__))
233
+ LOCAL_WINDOWS_BIN_PATH = RightScale::Platform.filesystem.ensure_local_drive_path(::File.join(SOURCE_WINDOWS_PATH, 'bin'), TEMP_DIR_NAME)
234
+ LOCAL_WINDOWS_SCRIPTS_PATH = RightScale::Platform.filesystem.ensure_local_drive_path(::File.join(SOURCE_WINDOWS_PATH, 'scripts'), TEMP_DIR_NAME)
235
+ CHEF_NODE_CMDLET_DLL_PATH = ::File.normalize_path(::File.join(LOCAL_WINDOWS_BIN_PATH, 'ChefNodeCmdlet.dll')).gsub("/", "\\")
236
+ RUN_LOOP_SCRIPT_PATH = File.normalize_path(File.join(LOCAL_WINDOWS_SCRIPTS_PATH, 'run_loop.ps1')).gsub("/", "\\")
237
+
238
+ # Data available in STDOUT pipe event
239
+ # Audit raw output
240
+ #
241
+ # === Parameters
242
+ # data(String):: STDOUT data
243
+ #
244
+ # === Return
245
+ # true:: Always return true
246
+ def on_read_output(data)
247
+ write_output_to_log(data)
248
+ end
249
+
250
+ # Handles a tendency of Windows command line tools to append extraneous
251
+ # newlines by stripping whitespace before logging. this might accidentally
252
+ # strip whitespace on a buffer boundary, but the buffer will likely be
253
+ # read more frequently than it is written to and the after-boundary text
254
+ # would appear on a new logger line anyway.
255
+ #
256
+ # === Parameters
257
+ # data(String):: data to write
258
+ def write_output_to_log(data)
259
+ data = data.strip
260
+ unless data.empty?
261
+ ::Chef::Log.info(data)
262
+ end
263
+ end
264
+
265
+ # Process exited event
266
+ # Record duration and process exist status and signal Chef thread so it can resume
267
+ #
268
+ # === Parameters
269
+ # status(Process::Status):: Process exit status
270
+ #
271
+ # === Return
272
+ # true:: Always return true
273
+ def on_exit(status)
274
+ @exit_status = status
275
+
276
+ Log.debug(format_log_message("Stopping host server"))
277
+ @pipe_server.stop
278
+ @pipe_server = nil
279
+
280
+ # signal response to ensure proper termination
281
+ Log.debug(format_log_message("Terminated"))
282
+ @response_event.signal
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,136 @@
1
+ #
2
+ # Copyright (c) 2010-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
+ require 'rubygems'
23
+ require File.normalize_path(File.join(File.dirname(__FILE__), 'pipe_server'))
24
+ require 'json'
25
+ require 'set'
26
+
27
+ module RightScale
28
+
29
+ module Windows
30
+
31
+ # Provides a server for a named pipe connection which serves a series of
32
+ # commands to be executed in the powershell context. The final command is
33
+ # expected to be an "exit" statement.
34
+ class PowershellPipeServer
35
+
36
+ # Request hash key associated with previous execution exit code
37
+ LAST_EXIT_CODE_KEY = "LastExitCode"
38
+ LAST_ERROR_MESSAGE_KEY = "LastErrorMessage"
39
+
40
+ # Response hash key associated with action to run
41
+ NEXT_ACTION_KEY = :NextAction
42
+
43
+ # Initialize pipe server
44
+ #
45
+ # === Parameters
46
+ # options[:pipe_name](String):: Name of pipe to connect to (required)
47
+ #
48
+ # === Block
49
+ # Given block gets called back for each request
50
+ # It should take two arguments:
51
+ # * First argument is either :is_ready or :respond
52
+ # calls with :is_ready should return a boolean value set to true if there is a pending command
53
+ # calls with :respond should return the pending command
54
+ # * Second argument contains the request data (only set with :respond)
55
+ def initialize(options = {}, &callback)
56
+ raise ArgumentError, "Missing required :pipe_name" unless @pipe_name = options[:pipe_name]
57
+ @callback = callback
58
+ @pipe_eventable = nil
59
+ end
60
+
61
+ # Starts the pipe server by creating an asynchronous named pipe. Returns
62
+ # control to the caller after adding the pipe to the event machine.
63
+ #
64
+ # === Return
65
+ # true:: If server was successfully started
66
+ # false:: Otherwise
67
+ def start
68
+ flags = ::Win32::Pipe::ACCESS_DUPLEX | ::Win32::Pipe::OVERLAPPED
69
+ pipe = PipeServer.new(@pipe_name, 0, flags)
70
+ res = true
71
+ begin
72
+ options = {:target => self,
73
+ :request_handler => :request_handler,
74
+ :request_query => :request_query,
75
+ :pipe => pipe}
76
+ @pipe_eventable = EM.watch(pipe, PipeServerHandler, options)
77
+ @pipe_eventable.notify_readable = true
78
+ rescue Exception => e
79
+ pipe.close rescue nil
80
+ RightScale::Log.error("Failed to start pipe server", e, :trace)
81
+ res = false
82
+ end
83
+ res
84
+ end
85
+
86
+ # Stops the pipe server by detaching the eventable from the event machine.
87
+ #
88
+ # === Return
89
+ # true:: Always return true
90
+ def stop
91
+ @pipe_eventable.force_detach if @pipe_eventable
92
+ @pipe_eventable = nil
93
+ true
94
+ end
95
+
96
+ # Ready to respond if the next action queue is empty, otherwise continue
97
+ # blocking client.
98
+ #
99
+ # === Parameters
100
+ # request_data(String):: request data
101
+ #
102
+ # === Returns
103
+ # result(Boolean):: true if response is ready
104
+ def request_query(request_data)
105
+ return @callback.call(:is_ready, nil)
106
+ end
107
+
108
+ # Handler for next action requests. Expects complete requests and
109
+ # responses to appear serialized as JSON on individual lines (i.e.
110
+ # delimited by newlines). note that JSON text escapes newline characters
111
+ # within string values and normally only includes whitespace for human-
112
+ # readability.
113
+ #
114
+ # === Parameters
115
+ # request_data(String):: Request data
116
+ #
117
+ # === Returns
118
+ # response(String):: Request response
119
+ def request_handler(request_data)
120
+ # assume request_data is a single line with a possible newline trailing.
121
+ request = JSON.load(request_data.chomp)
122
+ if 2 == request.keys.size && request.has_key?(LAST_EXIT_CODE_KEY) && request.has_key?(LAST_ERROR_MESSAGE_KEY)
123
+ # pop the next action from the queue.
124
+ command = @callback.call(:respond, request)
125
+ return JSON.dump(NEXT_ACTION_KEY => command) + "\n";
126
+ end
127
+ raise ArgumentError, "Invalid request"
128
+ rescue Exception => e
129
+ return JSON.dump(:Error => "#{e.class}: #{e.message}", :Detail => e.backtrace.join("\n")) + "\n"
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+
136
+ end
@@ -0,0 +1,92 @@
1
+ #
2
+ # Copyright (c) 2010-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
+ module RightScale
24
+
25
+ # Base class to dynamically generated Powershell Chef providers
26
+ class PowershellProviderBase < Chef::Provider
27
+
28
+ def initialize(new_resource, run_context)
29
+ super(new_resource, run_context)
30
+ self.class.init(@run_context.node)
31
+ # Have to wait until the Chef node server has been initialized before setting the new resource
32
+ RightScale::Windows::ChefNodeServer.instance.new_resource = @new_resource
33
+ end
34
+
35
+ # Initialize Powershell host, should be called before :run and :terminate
36
+ #
37
+ # === Return
38
+ # true:: If init script must be run
39
+ # false:: Otherwise
40
+ def self.init(node)
41
+ run_init = @ps_instance.nil?
42
+ @ps_instance = PowershellHost.new(:node => node, :provider_name => self.to_s.gsub("::", "_")) unless @ps_instance
43
+ run_init
44
+ end
45
+
46
+ # Run powershell script in associated Powershell instance
47
+ #
48
+ # === Parameters
49
+ # script(String):: Fully qualified path to Powershell script
50
+ #
51
+ # === Return
52
+ # true:: Always return true
53
+ def self.run_script(script)
54
+ if @ps_instance
55
+ res = @ps_instance.run(script)
56
+
57
+ # the powershell provider script the host runs will return exit code of 100 if the last action threw an exception.
58
+ if res && res[:exit_code] && res[:exit_code] != 0
59
+ message = "Unexpected exit code from action. Expected 0 but returned #{res[:exit_code]}. Script: #{script}\n"
60
+ message += "#{res[:error_msg]}"
61
+ raise RightScale::Exceptions::Exec, message
62
+ end
63
+ end
64
+
65
+ true
66
+ end
67
+
68
+ # Terminate Powershell process if it was started
69
+ #
70
+ # === Return
71
+ # true:: Always return true
72
+ def self.terminate
73
+ if @ps_instance
74
+ begin
75
+ @ps_instance.terminate
76
+ ensure
77
+ @ps_instance = nil
78
+ end
79
+ end
80
+ true
81
+ end
82
+
83
+ # Must override Chef's load_current_resource
84
+ #
85
+ # === Return
86
+ # true:: Always return true
87
+ def load_current_resource
88
+ # Dummy
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,105 @@
1
+ $RS_lastExitCode = 0
2
+ $RS_lastErrorMessage = ""
3
+ $global:RS_lastErrorRecord = $NULL
4
+ while ($TRUE)
5
+ {
6
+ try
7
+ {
8
+ $Error.clear()
9
+ $LastExitCode = 0
10
+ $RS_nextAction = $NULL
11
+ $RS_nextAction = get-NextAction $RS_pipeName $RS_lastExitCode $RS_lastErrorMessage
12
+ if ($RS_lastErrorMessage -ne "" -or $RS_lastExitCode -ne 0)
13
+ {
14
+ exit $RS_lastExitCode
15
+ }
16
+ elseif ($Error.Count -eq 0)
17
+ {
18
+ # note that $RS_nextAction may be wrapped with additional instructions and so
19
+ # is too verbose for normal output. the next action usually refers to a locally
20
+ # cached script whose name but not path is meaningful to the user (with the
21
+ # exception of a terminating "exit" which doesn't need to be seen here). scripts
22
+ # should provide sufficient standard output to indicate what activity is
23
+ # occurring without our having to report which script is running.
24
+ write-verbose $RS_nextAction
25
+
26
+ # invoke next action.
27
+ invoke-command -scriptblock $RS_nextAction
28
+ $RS_lastExitCode = $global:LastExitCode
29
+ if ($NULL -eq $RS_lastExitCode)
30
+ {
31
+ $RS_lastExitCode = 0
32
+ }
33
+ $RS_lastErrorMessage = ""
34
+ }
35
+ else
36
+ {
37
+ break
38
+ }
39
+ }
40
+ catch
41
+ {
42
+ if ($RS_lastErrorMessage -ne "" -or $RS_lastExitCode -ne 0)
43
+ {
44
+ exit $RS_lastExitCode
45
+ }
46
+ if ($NULL -eq $global:RS_lastErrorRecord)
47
+ {
48
+ $global:RS_lastErrorRecord = $_
49
+ }
50
+ }
51
+
52
+ if ($NULL -ne $global:RS_lastErrorRecord)
53
+ {
54
+ $invocationInfo = $global:RS_lastErrorRecord.invocationInfo
55
+ write-debug ($invocationInfo | Out-String).TrimEnd()
56
+ $scriptSnip = ""
57
+ $scriptPath = $invocationInfo.ScriptName
58
+ if (($NULL -ne $scriptPath) -and ($scriptPath.Length -gt 0))
59
+ {
60
+ $scriptSource = get-content $scriptPath
61
+ $firstLine = [system.math]::max($invocationInfo.ScriptLineNumber - 4, 0)
62
+ $lastLine = [system.math]::min($invocationInfo.ScriptLineNumber + 4, $scriptSource.length)
63
+ for ($i = $firstLine; $i -lt $lastLine; ++$i)
64
+ {
65
+ $lineNumber = $i + 1
66
+ if ($lineNumber -eq $invocationInfo.ScriptLineNumber)
67
+ {
68
+ # the reported character offset in the offending line can be past the end for
69
+ # some exceptions. an example is missing inputs on a piped command line.
70
+ $lineOffset = $invocationInfo.OffsetInLine
71
+ $lineLength = $invocationInfo.Line.Length
72
+ if ($lineOffset -gt $lineLength)
73
+ {
74
+ $lineOffset = $lineLength
75
+ }
76
+ $firstPart = $invocationInfo.Line.Substring(0, $lineOffset)
77
+ $secondPart = $invocationInfo.Line.Substring($lineOffset, $lineLength - $lineOffset)
78
+ $scriptSnip += "`n + $lineNumber" + ":`t$FirstPart <<<< $SecondPart"
79
+ }
80
+ else
81
+ {
82
+ $scriptSnip += "`n + $LineNumber" + ":`t" + $scriptSource[$i]
83
+ }
84
+ }
85
+ }
86
+ else
87
+ {
88
+ # failure occurred in an internal code fragment which has no meaningful script or line information.
89
+ # the stringized error record contains sufficient information in this case.
90
+ $scriptSnip = $NULL
91
+ }
92
+ $RS_lastErrorMessage = ($global:RS_lastErrorRecord | Out-String).TrimEnd()
93
+ if ($NULL -ne $scriptSnip)
94
+ {
95
+ $RS_lastErrorMessage += "`n +`n + Script error near:" + $scriptSnip + "`n"
96
+ }
97
+ else
98
+ {
99
+ $RS_lastErrorMessage += "`n +`n + Error occurred in internal code block."
100
+ }
101
+ $RS_lastExitCode = 1
102
+ }
103
+ }
104
+
105
+ exit $RS_lastExitCode