Class: Ridley::HostConnector::SSH

Inherits:
Base
  • Object
show all
Defined in:
lib/ridley-connectors/host_connector/ssh.rb

Constant Summary collapse

DEFAULT_PORT =
22
EMBEDDED_RUBY_PATH =
'/opt/chef/embedded/bin/ruby'.freeze

Constants inherited from Base

Base::CONNECTOR_PORT_ERRORS, Base::PORT_CHECK_TIMEOUT, Base::RETRY_COUNT

Instance Method Summary collapse

Methods inherited from Base

#connectable?, #port_open?

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):

  • :ssh (Hash)
    • :user (String) a shell user that will login to each node and perform the bootstrap command on

    • :password (String) the password for the shell user that will perform the bootstrap

    • :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password

    • :timeout (Float) timeout value for SSH bootstrap (5.0)

    • :sudo (Boolean) run as sudo

Returns:



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

def bootstrap(host, options = {})
  options = options.reverse_merge(ssh: Hash.new)
  options[:ssh].reverse_merge!(sudo: true, timeout: 5.0)
  context = BootstrapContext::Unix.new(options)

  log.info "Bootstrapping host: #{host}"
  log.filter_param(context.boot_command)
  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):

  • :ssh (Hash)
    • :user (String) a shell user that will login to each node and perform the bootstrap command on

    • :password (String) the password for the shell user that will perform the bootstrap

    • :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password

    • :timeout (Float) timeout value for SSH bootstrap (5.0)

    • :sudo (Boolean) run as sudo

Returns:



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/ridley-connectors/host_connector/ssh.rb', line 121

def chef_client(host, options = {})
  log_level = case log.level
    when 0
      "debug"
    when 1
      "info"
    when 2
      "warn"
    else
      "info"
    end
  run(host, "chef-client -l #{log_level}", options)
end

#connector_port_open?(host, options = {}) ⇒ Boolean

Checks to see if the given port is open for TCP connections on the given host. If a gateway is provided in the ssh options, then return true if we can connect to the gateway host. If no gateway config is found then just verify we can connect to the destination host.

Parameters:

  • host (String)

    the host to attempt to connect to

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

    a customizable set of options

Options Hash (options):

  • :ssh (Hash)
    • :gateway (String) user@host:port

    • :timeout (Float) timeout value for SSH

    • :port (Fixnum) the SSH port

Returns:

  • (Boolean)


244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/ridley-connectors/host_connector/ssh.rb', line 244

def connector_port_open?(host, options = {})
  options[:ssh]          ||= Hash.new
  options[:ssh][:port]   ||= HostConnector::SSH::DEFAULT_PORT

  if options[:ssh][:gateway]
    gw_host, gw_port, _ = gateway(options)
    log.info("Connecting to host '#{gw_host}' via SSH gateway over port '#{gw_port}'")
    port_open?(gw_host, gw_port, options[:ssh][:timeout])
  else
    port_open?(host, options[:ssh][:port], options[:ssh][:timeout])
  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):

  • :ssh (Hash)
    • :user (String) a shell user that will login to each node and perform the bootstrap command on

    • :password (String) the password for the shell user that will perform the bootstrap

    • :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password

    • :timeout (Float) timeout value for SSH bootstrap (5.0)

    • :sudo (Boolean) run as sudo

Returns:



150
151
152
153
154
# File 'lib/ridley-connectors/host_connector/ssh.rb', line 150

def put_secret(host, secret, options = {})
  log.filter_param(secret)
  cmd = "echo '#{secret}' > /etc/chef/encrypted_data_bag_secret; chmod 0600 /etc/chef/encrypted_data_bag_secret"
  run(host, cmd, 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):

  • :ssh (Hash)
    • :user (String) a shell user that will login to each node and perform the bootstrap command on

    • :password (String) the password for the shell user that will perform the bootstrap

    • :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password

    • :timeout (Float) timeout value for SSH bootstrap (5.0)

    • :sudo (Boolean) run as sudo

Returns:



171
172
173
# File 'lib/ridley-connectors/host_connector/ssh.rb', line 171

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

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

Execute a shell command on a node using ssh. If the gateway option is present then execute the command through the gateway.

Parameters:

  • host (String)

    the host to perform the action on

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

    a customizable set of options

Options Hash (options):

  • :ssh (Hash)
    • :user (String) a shell user that will login to each node and perform the bootstrap command on

    • :password (String) the password for the shell user that will perform the bootstrap

    • :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password

    • :timeout (Float) timeout value for SSH bootstrap (5.0)

    • :sudo (Boolean) run as sudo

    • :gateway (String) user@host:port

Returns:



26
27
28
29
30
31
32
33
34
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
# File 'lib/ridley-connectors/host_connector/ssh.rb', line 26

def run(host, command, options = {})
  options = options.reverse_merge(ssh: Hash.new)
  options[:ssh].reverse_merge!(port: DEFAULT_PORT, paranoid: false, sudo: false)

  command = "sudo -E #{command}" if options[:ssh][:sudo]

  Ridley::HostConnector::Response.new(host).tap do |response|
    begin
      log.info "Running SSH command: '#{command}' on: '#{host}' as: '#{options[:ssh][:user]}'"

      defer {
        ssh(host, options) do |ssh|
          ssh.open_channel do |channel|
            if options[:sudo]
              channel.request_pty do |channel, success|
                unless success
                  raise "Could not aquire pty: A pty is required for running sudo commands."
                end

                channel_exec(channel, command, host, response)
              end
            else
              channel_exec(channel, command, host, response)
            end
          end
          ssh.loop
        end
      }
    rescue Net::SSH::AuthenticationFailed => ex
      response.exit_code = -1
      response.stderr    = "Authentication failure for user #{ex}"
    rescue Net::SSH::ConnectionTimeout, Timeout::Error
      response.exit_code = -1
      response.stderr    = "Connection timed out"
    rescue SocketError, Errno::EHOSTUNREACH
      response.exit_code = -1
      response.stderr    = "Host unreachable"
    rescue Errno::ECONNREFUSED
      response.exit_code = -1
      response.stderr    = "Connection refused"
    rescue Net::SSH::Exception => ex
      response.exit_code = -1
      response.stderr    = ex.inspect
    rescue => ex
      response.exit_code = -1
      response.stderr    = "An unknown error occurred: #{ex.class} - #{ex.message}"
    end

    case response.exit_code
    when 0
      log.info "Successfully ran SSH command on: '#{host}' as: '#{options[:ssh][:user]}'"
    when -1
      log.info "Failed to run SSH command on: '#{host}' as: '#{options[:ssh][:user]}'"
    else
      log.info "Successfully ran SSH command on: '#{host}' as: '#{options[:ssh][:user]}' but it failed"
    end
  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.

  • :ssh (Hash)
    • :user (String) a shell user that will login to each node and perform the bootstrap command on

    • :password (String) the password for the shell user that will perform the bootstrap

    • :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password

    • :timeout (Float) timeout value for SSH bootstrap (5.0)

    • :sudo (Boolean) run as sudo (true)

Returns:



192
193
194
195
196
197
198
# File 'lib/ridley-connectors/host_connector/ssh.rb', line 192

def uninstall_chef(host, options = {})
  options = options.reverse_merge(ssh: Hash.new)
  options[:ssh].reverse_merge!(sudo: true, timeout: 5.0)

  log.info "Uninstalling Chef from host: #{host}"
  run(host, CommandContext::UnixUninstall.command(options), options)
end

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

Update a node’s Omnibus installation of Chef

Parameters:

  • host (String)

    the host to perform the action on

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

    a customizable set of options

Options Hash (options):

  • :chef_version (String)

    the version of Chef to install on the node

  • :prerelease (Boolean)

    install a prerelease version of Chef

  • :direct_url (String)

    a url pointing directly to a Chef package to install

  • :ssh (Hash)
    • :user (String) a shell user that will login to each node and perform the bootstrap command on

    • :password (String) the password for the shell user that will perform the bootstrap

    • :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password

    • :timeout (Float) timeout value for SSH bootstrap (5.0)

    • :sudo (Boolean) run as sudo (true)

  • :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:



223
224
225
226
227
228
229
# File 'lib/ridley-connectors/host_connector/ssh.rb', line 223

def update_omnibus(host, options = {})
  options = options.reverse_merge(ssh: Hash.new)
  options[:ssh].reverse_merge!(sudo: true, timeout: 5.0)

  log.info "Updating Omnibus installation on host: #{host}"
  run(host, CommandContext::UnixUpdateOmnibus.command(options), options)
end