right_link 5.9.0

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