lib/ridley/host_connector/winrm.rb in ridley-0.12.4 vs lib/ridley/host_connector/winrm.rb in ridley-1.0.0.rc1
- old
+ new
@@ -1,56 +1,181 @@
+require 'active_support/core_ext/kernel/reporting'
+# Silencing warnings because not all versions of GSSAPI support all of the GSSAPI methods
+# the gssapi gem attempts to attach to and these warnings are dumped to STDERR.
+silence_warnings do
+ require 'winrm'
+end
+
module Ridley
module HostConnector
# @author Kyle Allan <kallan@riotgames.com>
- class WinRM
+ class WinRM < HostConnector::Base
require_relative 'winrm/command_uploader'
- require_relative 'winrm/worker'
- class << self
- # @param [Ridley::NodeResource, Array<Ridley::NodeResource>] nodes
- # @param [Hash] options
- def start(nodes, options = {}, &block)
- runner = new(nodes, options)
- result = yield runner
- runner.terminate
+ DEFAULT_PORT = 5985
+ EMBEDDED_RUBY_PATH = 'C:\opscode\chef\embedded\bin\ruby'.freeze
- result
- ensure
- runner.terminate if runner && runner.alive?
+ # Execute a shell command on a node
+ #
+ # @param [String] host
+ # the host to perform the action on
+ # @param [String] command
+ #
+ # @option options [Hash] :winrm
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
+ # * :password (String) the password for the user that will perform the bootstrap (required)
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
+ #
+ # @return [HostConnector::Response]
+ def run(host, command, options = {})
+ options = options.reverse_merge(winrm: Hash.new)
+ options[:winrm].reverse_merge!(port: DEFAULT_PORT)
+
+ command_uploaders = Array.new
+ user = options[:winrm][:user]
+ password = options[:winrm][:password]
+ port = options[:winrm][:port]
+ connection = winrm(host, port, options[:winrm].slice(:user, :password))
+
+ HostConnector::Response.new(host).tap do |response|
+ command_uploaders << command_uploader = CommandUploader.new(connection)
+ command = get_command(command, command_uploader)
+
+ begin
+ log.info "Running WinRM Command: '#{command}' on: '#{host}' as: '#{user}'"
+
+ output = connection.run_cmd(command) do |stdout, stderr|
+ if stdout
+ response.stdout += stdout
+ log.info "[#{host}](WinRM) #{stdout}"
+ end
+
+ if stderr
+ response.stderr += stderr unless stderr.nil?
+ log.info "[#{host}](WinRM) #{stdout}"
+ end
+ end
+
+ response.exit_code = output[:exitcode]
+ rescue ::WinRM::WinRMHTTPTransportError => ex
+ response.exit_code = -1
+ response.stderr = ex.message
+ return response
+ end
+
+ case response.exit_code
+ when 0
+ log.info "Successfully ran WinRM command on: '#{host}' as: '#{user}'"
+ else
+ log.info "Successfully ran WinRM command on: '#{host}' as: '#{user}', but it failed"
+ end
end
+ ensure
+ command_uploaders.map(&:cleanup)
end
- include Celluloid
- include Celluloid::Logger
+ # 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.
+ #
+ # @param command [String]
+ #
+ # @return [String]
+ def get_command(command, command_uploader)
+ if command.length < CommandUploader::CHUNK_LIMIT
+ command
+ else
+ log.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
- attr_reader :nodes
- attr_reader :options
+ # Bootstrap a node
+ #
+ # @param [String] host
+ # the host to perform the action on
+ #
+ # @option options [Hash] :winrm
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
+ # * :password (String) the password for the user that will perform the bootstrap (required)
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
+ #
+ # @return [HostConnector::Response]
+ def bootstrap(host, options = {})
+ context = BootstrapContext::Windows.new(options)
- # @param [Ridley::NodeResource, Array<Ridley::NodeResource>] nodes
- # @param [Hash] options
- def initialize(nodes, options = {})
- @nodes = Array(nodes)
- @options = options
+ log.info "Bootstrapping host: #{host}"
+ run(host, context.boot_command, options)
end
- # @param [String] command
+ # Perform a chef client run on a node
#
- # @return [Array]
- def run(command)
- workers = Array.new
- futures = self.nodes.collect do |node|
- workers << worker = Worker.new(node.public_hostname, self.options.freeze)
- worker.future.run(command)
- end
+ # @param [String] host
+ # the host to perform the action on
+ #
+ # @option options [Hash] :winrm
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
+ # * :password (String) the password for the user that will perform the bootstrap (required)
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
+ #
+ # @return [HostConnector::Response]
+ def chef_client(host, options = {})
+ run(host, "chef-client", options)
+ end
- Ridley::HostConnector::ResponseSet.new.tap do |response_set|
- futures.each do |future|
- status, response = future.value
- response_set.add_response(response)
- end
- end
- ensure
- workers.map(&:terminate)
+ # Write your encrypted data bag secret on a node
+ #
+ # @param [String] host
+ # the host to perform the action on
+ # @param [String] secret
+ # your organization's encrypted data bag secret
+ #
+ # @option options [Hash] :winrm
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
+ # * :password (String) the password for the user that will perform the bootstrap (required)
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
+ #
+ # @return [HostConnector::Response]
+ def put_secret(host, secret, options = {})
+ command = "echo #{secret} > C:\\chef\\encrypted_data_bag_secret"
+ run(host, command, options)
end
+
+ # Execute line(s) of Ruby code on a node using Chef's embedded Ruby
+ #
+ # @param [String] host
+ # the host to perform the action on
+ # @param [Array<String>] command_lines
+ # An Array of lines of the command to be executed
+ #
+ # @option options [Hash] :winrm
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
+ # * :password (String) the password for the user that will perform the bootstrap (required)
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
+ #
+ # @return [HostConnector::Response]
+ def ruby_script(host, command_lines, options = {})
+ command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
+ run(host, command, options)
+ end
+
+ private
+
+ # @param [String] host
+ # @param [Integer] port
+ #
+ # @option options [String] :user
+ # @option options [String] :password
+ #
+ # @return [WinRM::WinRMWebService]
+ def winrm(host, port, options = {})
+ winrm_opts = { disable_sspi: true, basic_auth_only: true }
+ winrm_opts[:user] = options[:user]
+ winrm_opts[:pass] = options[:password]
+ client = ::WinRM::WinRMWebService.new("http://#{host}:#{port}/wsman", :plaintext, winrm_opts)
+ client.set_timeout(6000)
+ client
+ end
end
end
end