Class: Kitsune::Kit::Adapters::NetSshTransport

Inherits:
Transport
  • Object
show all
Defined in:
lib/kitsune/kit/adapters/net_ssh_transport.rb

Instance Method Summary collapse

Constructor Details

#initialize(host:, user:, port:, key_path:, known_hosts: nil, verify_host_key: :always, maximum_timeout: nil) ⇒ NetSshTransport

Returns a new instance of NetSshTransport.



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/kitsune/kit/adapters/net_ssh_transport.rb', line 14

def initialize(host:, user:, port:, key_path:, known_hosts: nil, verify_host_key: :always,
               maximum_timeout: nil)
  super()
  @host = host
  @user = user
  @port = Integer(port)
  @key_path = key_path
  @known_hosts = known_hosts
  @verify_host_key = verify_host_key
  @maximum_timeout = maximum_timeout
end

Instance Method Details

#execute(command, arguments: [], timeout: 30, stdin: nil) ⇒ Object



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
# File 'lib/kitsune/kit/adapters/net_ssh_transport.rb', line 43

def execute(command, arguments: [], timeout: 30, stdin: nil)
  validate_command!(command)
  shell_command = ([command] + arguments.map { |argument| Shellwords.escape(argument.to_s) }).join(" ")
  started_at = monotonic_time
  stdout = +""
  stderr = +""
  exit_status = nil

  effective_timeout = @maximum_timeout ? [timeout, @maximum_timeout].min : timeout
  Timeout.timeout(effective_timeout) do
    with_connection do |ssh|
      ssh.open_channel do |channel|
        channel.exec(shell_command) do |_exec_channel, success|
          raise Errors::RemoteCommandError, "remote command could not be started" unless success

          channel.send_data(stdin) if stdin
          channel.eof! if stdin
          channel.on_data { |_data_channel, data| stdout << data }
          channel.on_extended_data { |_data_channel, _type, data| stderr << data }
          channel.on_request("exit-status") { |_status_channel, data| exit_status = data.read_long }
        end
      end
      ssh.loop
    end
  end

  command_result(stdout, stderr, exit_status, started_at)
rescue Timeout::Error
  raise Errors::TimeoutError.new("remote command timed out after #{effective_timeout || timeout} seconds",
                                 context: { command: command })
end

#reachable?Boolean

Returns:

  • (Boolean)


26
27
28
29
30
# File 'lib/kitsune/kit/adapters/net_ssh_transport.rb', line 26

def reachable?
  with_connection { true }
rescue Errors::ConnectionError
  false
end

#upload(content:, remote_path:, mode: "0600") ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/kitsune/kit/adapters/net_ssh_transport.rb', line 75

def upload(content:, remote_path:, mode: "0600")
  validate_remote_path!(remote_path)
  validate_mode!(mode)
  encoded = Base64.strict_encode64(content)
  result = execute(
    "sh",
    arguments: ["-c", "base64 --decode > \"$1\" && chmod \"$2\" \"$1\"", "kitsune-upload", remote_path, mode],
    stdin: encoded,
    timeout: 30
  )
  raise_remote_failure!(result, "upload #{remote_path}") unless result.success?

  true
end

#with_sessionObject



32
33
34
35
36
37
38
39
40
41
# File 'lib/kitsune/kit/adapters/net_ssh_transport.rb', line 32

def with_session
  return yield if @persistent_session

  open_connection do |ssh|
    @persistent_session = ssh
    yield
  ensure
    @persistent_session = nil
  end
end