lib/ridley/host_connector/winrm/worker.rb in ridley-0.10.0.rc1 vs lib/ridley/host_connector/winrm/worker.rb in ridley-0.10.0.rc2

- old
+ new

@@ -15,36 +15,58 @@ attr_reader :host # @return [Hash] attr_reader :options # @return [String] attr_reader :winrm_endpoint + # @return [CommandUploader] + attr_reader :command_uploader + # @return [Array] + attr_reader :command_uploaders + finalizer :finalizer + + EMBEDDED_RUBY_PATH = 'C:\opscode\chef\embedded\bin\ruby'.freeze + # @param host [String] # the host the worker is going to work on # @option options [Hash] :winrm # * :user (String) a user that will login to each node and perform the bootstrap command on (required) # * :password (String) the password for the user that will perform the bootstrap # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985) def initialize(host, options = {}) - @options = options.deep_symbolize_keys - @options = options[:winrm] if options[:winrm] - @host = host - @user = @options[:user] - @password = @options[:password] - @winrm_endpoint = "http://#{host}:#{winrm_port}/wsman" + options = options.deep_symbolize_keys + @options = options[:winrm] || Hash.new + @host = host + @user = @options[:user] + @password = @options[:password] + @winrm_endpoint = "http://#{host}:#{winrm_port}/wsman" + @command_uploaders = Array.new end + def finalizer + command_uploaders.map(&:cleanup) + end + def run(command) - command = get_command(command) + command_uploaders << command_uploader = CommandUploader.new(winrm) + command = get_command(command, command_uploader) response = Ridley::HostConnector::Response.new(host) debug "Running WinRM Command: '#{command}' on: '#{host}' as: '#{user}'" output = winrm.run_cmd(command) do |stdout, stderr| - response.stdout += stdout unless stdout.nil? - response.stderr += stderr unless stderr.nil? + if stdout + response.stdout += stdout + info "NODE[#{host}] #{stdout}" + end + + if stderr + response.stderr += stderr unless stderr.nil? + info "NODE[#{host}] #{stdout}" + end end + response.exit_code = output[:exitcode] case response.exit_code when 0 debug "Successfully ran WinRM command on: '#{host}' as: '#{user}'" @@ -60,67 +82,65 @@ response.exit_code = -1 response.stderr = e.message [ :error, response ] end - # @return [::WinRM::WinRMWebService] + # @return [WinRM::WinRMWebService] def winrm ::WinRM::WinRMWebService.new(winrm_endpoint, :plaintext, user: user, pass: password, disable_sspi: true, basic_auth_only: true) end # @return [Fixnum] def winrm_port options[:port] || Ridley::HostConnector::DEFAULT_WINRM_PORT end # Returns the command if it does not break the WinRM command length - # limit. Otherwise, we return an execution of the command as a batch file. + # limit. Otherwise, we return an execution of the command as a batch file. # # @param command [String] - # + # # @return [String] - def get_command(command) - if command.length < 2047 + def get_command(command, command_uploader) + if command.length < CommandUploader::CHUNK_LIMIT command else - debug "Detected a command that was longer than 2047 characters, uploading command as a file to the host." - upload_command_to_host(command) + debug "Detected a command that was longer than #{CommandUploader::CHUNK_LIMIT} characters, \ + uploading command as a file to the host." + command_uploader.upload(command) + command_uploader.command end end - private + # Executes a chef-client run on the nodes + # + # @return [#run] + def chef_client + run("chef-client") + end - # Uploads the command encoded as base64 to a file on the host - # and then uses Powershell to transform the base64 file into the - # command that was originally passed through. - # - # @param command [String] - # - # @return [String] the command to execute the uploaded file - def upload_command_to_host(command) - base64_file = "winrm-upload-base64-#{Process.pid}-#{Time.now.to_i}" - base64_file_name = get_file_path(base64_file) + # Executes a copy of the encrypted_data_bag_secret to the nodes + # + # @param [String] encrypted_data_bag_secret_path + # the path to the encrypted_data_bag_secret + # + # @return [#run] + def put_secret(encrypted_data_bag_secret_path) + secret = File.read(encrypted_data_bag_secret_path).chomp + command = "echo #{secret} > C:\\chef\\encrypted_data_bag_secret" + run(command) + end - Base64.encode64(command).gsub("\n", '').chars.to_a.each_slice(8000 - base64_file_name.size) do |chunk| - out = winrm.run_cmd( "echo #{chunk.join} >> \"#{base64_file_name}\"" ) - end - - command_file = "winrm-upload-#{Process.pid}-#{Time.now.to_i}.bat" - command_file_name = get_file_path(command_file) - winrm.powershell <<-POWERSHELL - $base64_string = Get-Content \"#{base64_file_name}\" - $bytes = [System.Convert]::FromBase64String($base64_string) - $new_file = [System.IO.Path]::GetFullPath(\"#{command_file_name}\") - [System.IO.File]::WriteAllBytes($new_file,$bytes) - POWERSHELL - - "cmd.exe /C #{command_file_name}" - end - - # @return [String] - def get_file_path(file) - (winrm.run_cmd("echo %TEMP%\\#{file}"))[:data][0][:stdout].chomp - end + # Executes a provided Ruby script in the embedded Ruby installation + # + # @param [Array<String>] command_lines + # An Array of lines of the command to be executed + # + # @return [#run] + def ruby_script(command_lines) + command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\"" + run(command) + end end end end end