Class: Kitchen::SSH

Inherits:
Object
  • Object
show all
Defined in:
lib/kitchen/ssh.rb

Overview

Class to help establish SSH connections, issue remote commands, and transfer files between a local system and remote node.

Author:

Instance Method Summary collapse

Constructor Details

#initialize(hostname, username, options = {}) {|self| ... } ⇒ SSH

Constructs a new SSH object.

Examples:

basic usage


ssh = Kitchen::SSH.new("remote.example.com", "root")
ssh.exec("sudo apt-get update")
ssh.upload!("/tmp/data.txt", "/var/lib/data.txt")
ssh.shutdown

block usage


Kitchen::SSH.new("remote.example.com", "root") do |ssh|
  ssh.exec("sudo apt-get update")
  ssh.upload!("/tmp/data.txt", "/var/lib/data.txt")
end

Parameters:

  • hostname (String)

    the remote hostname (IP address, FQDN, etc.)

  • username (String)

    the username for the remote host

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

    configuration options

Options Hash (options):

  • :logger (Logger)

    the logger to use (default: ‘::Logger.new(STDOUT)`)

Yields:

  • (self)

    if a block is given then the constructed object yields itself and calls ‘#shutdown` at the end, closing the remote connection



62
63
64
65
66
67
68
69
70
71
72
# File 'lib/kitchen/ssh.rb', line 62

def initialize(hostname, username, options = {})
  @hostname = hostname
  @username = username
  @options = options.dup
  @logger = @options.delete(:logger) || ::Logger.new(STDOUT)

  if block_given?
    yield self
    shutdown
  end
end

Instance Method Details

#exec(cmd) ⇒ Object

Execute a command on the remote host.

Parameters:

  • cmd (String)

    command string to execute

Raises:

  • (SSHFailed)

    if the command does not exit with a 0 code



78
79
80
81
82
83
84
85
86
87
# File 'lib/kitchen/ssh.rb', line 78

def exec(cmd)
  string_to_mask = "[SSH] #{self} (#{cmd})"
  masked_string = Util.mask_values(string_to_mask, %w{password ssh_http_proxy_password})
  logger.debug(masked_string)
  exit_code = exec_with_exit(cmd)

  if exit_code != 0
    raise SSHFailed, "SSH exited (#{exit_code}) for command: [#{cmd}]"
  end
end

#login_commandLoginCommand

Builds a LoginCommand which can be used to open an interactive session on the remote host.

Returns:



160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/kitchen/ssh.rb', line 160

def 
  args  = %w{ -o UserKnownHostsFile=/dev/null }
  args += %w{ -o StrictHostKeyChecking=no }
  args += %w{ -o IdentitiesOnly=yes } if options[:keys]
  args += %W{ -o LogLevel=#{logger.debug? ? "VERBOSE" : "ERROR"} }
  if options.key?(:forward_agent)
    args += %W{ -o ForwardAgent=#{options[:forward_agent] ? "yes" : "no"} }
  end
  Array(options[:keys]).each { |ssh_key| args += %W{ -i #{ssh_key} } }
  args += %W{ -p #{port} }
  args += %W{ #{username}@#{hostname} }

  LoginCommand.new("ssh", args)
end

#shutdownObject

Shuts down the session connection, if it is still active.



140
141
142
143
144
145
146
147
148
149
# File 'lib/kitchen/ssh.rb', line 140

def shutdown
  return if @session.nil?

  string_to_mask = "[SSH] closing connection to #{self}"
  masked_string = Util.mask_values(string_to_mask, %w{password ssh_http_proxy_password})
  logger.debug(masked_string)
  session.shutdown!
ensure
  @session = nil
end

#upload(local, remote, options = {}, &progress) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/kitchen/ssh.rb', line 107

def upload(local, remote, options = {}, &progress)
  require "net/scp" unless defined?(Net::SCP)
  if progress.nil?
    progress = lambda do |_ch, name, sent, total|
      if sent == total
        logger.debug("Async Uploaded #{name} (#{total} bytes)")
      end
    end
  end

  session.scp.upload(local, remote, options, &progress)
end

#upload!(local, remote, options = {}, &progress) ⇒ Object

Uploads a local file to remote host.

Parameters:

  • local (String)

    path to local file

  • remote (String)

    path to remote file destination

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

    configuration options that are passed to ‘Net::SCP.upload`

See Also:



96
97
98
99
100
101
102
103
104
105
# File 'lib/kitchen/ssh.rb', line 96

def upload!(local, remote, options = {}, &progress)
  require "net/scp" unless defined?(Net::SCP)
  if progress.nil?
    progress = lambda do |_ch, name, sent, total|
      logger.debug("Uploaded #{name} (#{total} bytes)") if sent == total
    end
  end

  session.scp.upload!(local, remote, options, &progress)
end

#upload_path(local, remote, options = {}, &progress) ⇒ Object



134
135
136
137
# File 'lib/kitchen/ssh.rb', line 134

def upload_path(local, remote, options = {}, &progress)
  options = { recursive: true }.merge(options)
  upload(local, remote, options, &progress)
end

#upload_path!(local, remote, options = {}, &progress) ⇒ Object

Uploads a recursive directory to remote host.

Parameters:

  • local (String)

    path to local file or directory

  • remote (String)

    path to remote file destination

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

    configuration options that are passed to ‘Net::SCP.upload`

Options Hash (options):

  • :recursive (true, false)

    recursive copy (default: ‘true`)

See Also:



128
129
130
131
132
# File 'lib/kitchen/ssh.rb', line 128

def upload_path!(local, remote, options = {}, &progress)
  options = { recursive: true }.merge(options)

  upload!(local, remote, options, &progress)
end

#waitObject

Blocks until the remote host’s SSH TCP port is listening.



152
153
154
# File 'lib/kitchen/ssh.rb', line 152

def wait
  logger.info("Waiting for #{hostname}:#{port}...") until test_ssh
end