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.
- data/actors/agent_manager.rb +88 -0
- data/actors/instance_scheduler.rb +321 -0
- data/actors/instance_services.rb +64 -0
- data/actors/instance_setup.rb +567 -0
- data/bin/cloud +25 -0
- data/bin/cook_runner +44 -0
- data/bin/deploy +120 -0
- data/bin/enroll +385 -0
- data/bin/rad +32 -0
- data/bin/rchk +29 -0
- data/bin/rnac +39 -0
- data/bin/rs_connect +33 -0
- data/bin/rs_log_level +31 -0
- data/bin/rs_ohai +28 -0
- data/bin/rs_reenroll +31 -0
- data/bin/rs_run_recipe +34 -0
- data/bin/rs_run_right_script +34 -0
- data/bin/rs_shutdown +33 -0
- data/bin/rs_tag +33 -0
- data/bin/rs_thunk +33 -0
- data/bin/rstat +31 -0
- data/bin/system +16 -0
- data/ext/Rakefile +18 -0
- data/init/config.yml +5 -0
- data/init/init.rb +79 -0
- data/lib/chef/ohai_setup.rb +51 -0
- data/lib/chef/plugins/cloud.rb +91 -0
- data/lib/chef/plugins/cloudstack.rb +23 -0
- data/lib/chef/plugins/ec2.rb +23 -0
- data/lib/chef/plugins/linux/block_device2.rb +24 -0
- data/lib/chef/plugins/rackspace.rb +23 -0
- data/lib/chef/plugins/rightscale.rb +125 -0
- data/lib/chef/plugins/windows/network.rb +114 -0
- data/lib/chef/plugins.rb +74 -0
- data/lib/chef/providers/dns_dnsmadeeasy_provider.rb +81 -0
- data/lib/chef/providers/dns_resource.rb +100 -0
- data/lib/chef/providers/executable_schedule_provider.rb +70 -0
- data/lib/chef/providers/executable_schedule_resource.rb +144 -0
- data/lib/chef/providers/remote_recipe_provider.rb +86 -0
- data/lib/chef/providers/remote_recipe_resource.rb +101 -0
- data/lib/chef/providers/right_link_tag_provider.rb +73 -0
- data/lib/chef/providers/right_link_tag_resource.rb +59 -0
- data/lib/chef/providers/right_script_provider.rb +190 -0
- data/lib/chef/providers/right_script_resource.rb +113 -0
- data/lib/chef/providers/rs_shutdown_provider.rb +75 -0
- data/lib/chef/providers/rs_shutdown_resource.rb +55 -0
- data/lib/chef/providers/server_collection_provider.rb +66 -0
- data/lib/chef/providers/server_collection_resource.rb +93 -0
- data/lib/chef/providers/windows/powershell_provider.rb +151 -0
- data/lib/chef/providers/windows/powershell_resource.rb +111 -0
- data/lib/chef/providers/windows/unsupported_provider.rb +51 -0
- data/lib/chef/right_providers.rb +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.csproj +104 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.dll-Help.xml +141 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Exceptions.cs +182 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionCommand.cs +178 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionRequest.cs +67 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueCommandBase.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueRequestBase.cs +64 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueResponseBase.cs +69 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/JsonTransport.cs +110 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeClient.cs +158 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeServer.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolConstants.cs +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolUtilities.cs +77 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ReadMe.txt +53 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueCommandBase.cs +293 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueRequestBase.cs +75 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueResponseBase.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Transport.cs +91 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet.sln +35 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Program.cs +374 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/TestChefNodeCmdlet.csproj +65 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Program.cs +136 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Properties/AssemblyInfo.cs +36 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/ReadMe.txt +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/TestNextActionCmdlet.csproj +68 -0
- data/lib/chef/windows/bin/Newtonsoft.Json.dll +0 -0
- data/lib/chef/windows/chef_node_server.rb +463 -0
- data/lib/chef/windows/dynamic_powershell_provider.rb +296 -0
- data/lib/chef/windows/pipe_server.rb +283 -0
- data/lib/chef/windows/powershell_host.rb +285 -0
- data/lib/chef/windows/powershell_pipe_server.rb +136 -0
- data/lib/chef/windows/powershell_provider_base.rb +92 -0
- data/lib/chef/windows/scripts/run_loop.ps1 +105 -0
- data/lib/clouds/cloud.rb +557 -0
- data/lib/clouds/cloud_factory.rb +250 -0
- data/lib/clouds/cloud_utilities.rb +244 -0
- data/lib/clouds/clouds/azure.rb +106 -0
- data/lib/clouds/clouds/cloudstack.rb +114 -0
- data/lib/clouds/clouds/ec2.rb +113 -0
- data/lib/clouds/clouds/eucalyptus.rb +46 -0
- data/lib/clouds/clouds/google.rb +102 -0
- data/lib/clouds/clouds/none.rb +76 -0
- data/lib/clouds/clouds/openstack.rb +30 -0
- data/lib/clouds/clouds/rackspace-ng.rb +54 -0
- data/lib/clouds/clouds/rackspace.rb +78 -0
- data/lib/clouds/clouds/softlayer.rb +91 -0
- data/lib/clouds/metadata_formatter.rb +108 -0
- data/lib/clouds/metadata_provider.rb +128 -0
- data/lib/clouds/metadata_source.rb +87 -0
- data/lib/clouds/metadata_sources/certificate_metadata_source.rb +207 -0
- data/lib/clouds/metadata_sources/config_drive_metadata_source.rb +129 -0
- data/lib/clouds/metadata_sources/file_metadata_source.rb +74 -0
- data/lib/clouds/metadata_sources/http_metadata_source.rb +277 -0
- data/lib/clouds/metadata_sources/selective_metadata_source.rb +122 -0
- data/lib/clouds/metadata_tree_climber.rb +144 -0
- data/lib/clouds/metadata_writer.rb +155 -0
- data/lib/clouds/metadata_writers/dictionary_metadata_writer.rb +72 -0
- data/lib/clouds/metadata_writers/ruby_metadata_writer.rb +76 -0
- data/lib/clouds/metadata_writers/shell_metadata_writer.rb +121 -0
- data/lib/clouds/register_clouds.rb +34 -0
- data/lib/clouds.rb +32 -0
- data/lib/gem_dependencies.rb +83 -0
- data/lib/git_hooks/commit-msg.rb +7 -0
- data/lib/instance/agent_config.rb +168 -0
- data/lib/instance/agent_watcher.rb +233 -0
- data/lib/instance/audit_cook_stub.rb +104 -0
- data/lib/instance/audit_proxy.rb +247 -0
- data/lib/instance/bundle_queue.rb +104 -0
- data/lib/instance/cook/agent_connection.rb +109 -0
- data/lib/instance/cook/audit_logger.rb +165 -0
- data/lib/instance/cook/audit_stub.rb +142 -0
- data/lib/instance/cook/ca-bundle.crt +2794 -0
- data/lib/instance/cook/chef_state.rb +211 -0
- data/lib/instance/cook/cook.rb +306 -0
- data/lib/instance/cook/cook_state.rb +298 -0
- data/lib/instance/cook/cookbook_path_mapping.rb +66 -0
- data/lib/instance/cook/cookbook_repo_retriever.rb +190 -0
- data/lib/instance/cook/executable_sequence.rb +765 -0
- data/lib/instance/cook/external_parameter_gatherer.rb +190 -0
- data/lib/instance/cook/repose_downloader.rb +349 -0
- data/lib/instance/cook/shutdown_request_proxy.rb +121 -0
- data/lib/instance/cook.rb +41 -0
- data/lib/instance/downloader.rb +208 -0
- data/lib/instance/duplicable.rb +67 -0
- data/lib/instance/exceptions.rb +49 -0
- data/lib/instance/executable_sequence_proxy.rb +278 -0
- data/lib/instance/instance_commands.rb +577 -0
- data/lib/instance/instance_state.rb +633 -0
- data/lib/instance/json_utilities.rb +102 -0
- data/lib/instance/login_manager.rb +533 -0
- data/lib/instance/login_user_manager.rb +522 -0
- data/lib/instance/message_encoder.rb +118 -0
- data/lib/instance/multi_thread_bundle_queue.rb +232 -0
- data/lib/instance/operation_context.rb +60 -0
- data/lib/instance/options_bag.rb +65 -0
- data/lib/instance/payload_formatter.rb +46 -0
- data/lib/instance/policy.rb +53 -0
- data/lib/instance/policy_audit.rb +100 -0
- data/lib/instance/policy_manager.rb +146 -0
- data/lib/instance/reenroll_manager.rb +104 -0
- data/lib/instance/right_scripts_cookbook.rb +181 -0
- data/lib/instance/shutdown_request.rb +221 -0
- data/lib/instance/single_thread_bundle_queue.rb +189 -0
- data/lib/instance/volume_management.rb +450 -0
- data/lib/instance.rb +50 -0
- data/lib/repo_conf_generators/apt_conf_generators.rb +106 -0
- data/lib/repo_conf_generators/gem_conf_generators.rb +80 -0
- data/lib/repo_conf_generators/rightscale_conf_generators.rb +254 -0
- data/lib/repo_conf_generators/rightscale_key.pub +17 -0
- data/lib/repo_conf_generators/yum_conf_generators.rb +225 -0
- data/lib/repo_conf_generators.rb +30 -0
- data/lib/run_shell.rb +28 -0
- data/scripts/agent_checker.rb +571 -0
- data/scripts/agent_controller.rb +247 -0
- data/scripts/agent_deployer.rb +148 -0
- data/scripts/bundle_runner.rb +336 -0
- data/scripts/cloud_controller.rb +176 -0
- data/scripts/log_level_manager.rb +142 -0
- data/scripts/ohai_runner.rb +33 -0
- data/scripts/reenroller.rb +193 -0
- data/scripts/server_importer.rb +293 -0
- data/scripts/shutdown_client.rb +183 -0
- data/scripts/system_configurator.rb +367 -0
- data/scripts/tagger.rb +381 -0
- data/scripts/thunker.rb +356 -0
- 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
|