Class: Ridley::HostConnector::WinRM

Inherits:
Base
  • Object
show all
Defined in:
lib/ridley/host_connector/winrm.rb,
lib/ridley/host_connector/winrm/command_uploader.rb

Defined Under Namespace

Classes: CommandUploader

Constant Summary collapse

DEFAULT_PORT =
5985
EMBEDDED_RUBY_PATH =
'C:\opscode\chef\embedded\bin\ruby'.freeze
SESSION_TYPE_COMMAND_METHODS =
{
  powershell: :run_powershell_script,
  cmd: :run_cmd
}.freeze

Instance Method Summary collapse

Methods included from Logging

logger, #logger, set_logger

Instance Method Details

#bootstrap(host, options = {}) ⇒ HostConnector::Response

Bootstrap a node

Parameters:

  • host (String)

    the host to perform the action on

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :winrm (Hash)
    • :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)

Returns:



119
120
121
122
123
124
# File 'lib/ridley/host_connector/winrm.rb', line 119

def bootstrap(host, options = {})
  context = BootstrapContext::Windows.new(options)

  log.info "Bootstrapping host: #{host}"
  run(host, context.boot_command, options)
end

#chef_client(host, options = {}) ⇒ HostConnector::Response

Perform a chef client run on a node

Parameters:

  • host (String)

    the host to perform the action on

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :winrm (Hash)
    • :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)

Returns:



137
138
139
# File 'lib/ridley/host_connector/winrm.rb', line 137

def chef_client(host, options = {})
  run(host, "chef-client", options)
end

#get_command(command, command_uploader) ⇒ String

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.

Parameters:

  • command (String)

Returns:

  • (String)


97
98
99
100
101
102
103
104
105
106
# File 'lib/ridley/host_connector/winrm.rb', line 97

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

#put_secret(host, secret, options = {}) ⇒ HostConnector::Response

Write your encrypted data bag secret on a node

Parameters:

  • host (String)

    the host to perform the action on

  • secret (String)

    your organization’s encrypted data bag secret

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :winrm (Hash)
    • :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)

Returns:



154
155
156
157
# File 'lib/ridley/host_connector/winrm.rb', line 154

def put_secret(host, secret, options = {})
  command = "echo #{secret} > C:\\chef\\encrypted_data_bag_secret"
  run(host, command, options)
end

#ruby_script(host, command_lines, options = {}) ⇒ HostConnector::Response

Execute line(s) of Ruby code on a node using Chef’s embedded Ruby

Parameters:

  • host (String)

    the host to perform the action on

  • command_lines (Array<String>)

    An Array of lines of the command to be executed

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :winrm (Hash)
    • :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)

Returns:



172
173
174
175
# File 'lib/ridley/host_connector/winrm.rb', line 172

def ruby_script(host, command_lines, options = {})
  command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
  run(host, command, options)
end

#run(host, command, options = {}) ⇒ HostConnector::Response

Execute a shell command on a node

Parameters:

  • host (String)

    the host to perform the action on

  • command (String)
  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :session_type (Symbol) — default: :cmd
    • :powershell - run the given command in a powershell session

    • :cmd - run the given command in a cmd session

  • :winrm (Hash)
    • :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)

Returns:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ridley/host_connector/winrm.rb', line 35

def run(host, command, options = {})
  options = options.reverse_merge(winrm: Hash.new, session_type: :cmd)
  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))

  unless command_method = SESSION_TYPE_COMMAND_METHODS[options[:session_type]]
    raise RuntimeError, "unknown session type: #{options[:session_type]}. Known session types " +
      "are: #{SESSION_TYPE_COMMAND_METHODS.keys}"
  end

  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}'"

      defer {
        output = connection.send(command_method, command) do |stdout, stderr|
          if stdout && stdout.present?
            response.stdout += stdout
            log.info "[#{host}](WinRM) #{stdout}"
          end

          if stderr && stderr.present?
            response.stderr += stderr
            log.info "[#{host}](WinRM) #{stderr}"
          end
        end
        response.exit_code = output[:exitcode]
      }
    rescue ::WinRM::WinRMHTTPTransportError => ex
      response.exit_code = -1
      response.stderr    = ex.message
    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
  begin
    command_uploaders.map(&:cleanup)
  rescue ::WinRM::WinRMHTTPTransportError => ex
    log.info "Error cleaning up leftover Powershell scripts on some hosts"
  end
end

#uninstall_chef(host, options = {}) ⇒ HostConnector::Response

Uninstall Chef from a node

Parameters:

  • host (String)

    the host to perform the action on

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :skip_chef (Boolena) — default: false

    skip removal of the Chef package and the contents of the installation directory. Setting this to true will only remove any data and configurations generated by running Chef client.

  • :winrm (Hash)
    • :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)

Returns:



192
193
194
195
196
# File 'lib/ridley/host_connector/winrm.rb', line 192

def uninstall_chef(host, options = {})
  options[:session_type] = :powershell
  log.info "Uninstalling Chef from host: #{host}"
  run(host, CommandContext::WindowsUninstall.command(options), options)
end