Class: Xshellz::Ssh::NetSshTransport
- Inherits:
-
Object
- Object
- Xshellz::Ssh::NetSshTransport
- Defined in:
- lib/xshellz/ssh.rb
Overview
Real SSH transport: exec over a session channel, files over SFTP.
The private key is handed to net-ssh in memory via key_data - it
never touches disk. Requires the ed25519 + bcrypt_pbkdf gems (declared
as runtime dependencies) for net-ssh to accept ed25519 keys.
Constant Summary collapse
- POLL_INTERVAL =
0.02
Instance Method Summary collapse
- #close ⇒ Object
- #download(remote_path, local_path) ⇒ Object
-
#exec(command, timeout: nil, &block) ⇒ Object
Execute a command; optional block receives (:stdout | :stderr, chunk) as output arrives.
-
#initialize(host:, port:, private_key_openssh:, username: "root", connect_timeout: 30) ⇒ NetSshTransport
constructor
A new instance of NetSshTransport.
- #read_file(path) ⇒ Object
- #upload(local_path, remote_path) ⇒ Object
- #write_file(path, data) ⇒ Object
Constructor Details
#initialize(host:, port:, private_key_openssh:, username: "root", connect_timeout: 30) ⇒ NetSshTransport
Returns a new instance of NetSshTransport.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/xshellz/ssh.rb', line 56 def initialize(host:, port:, private_key_openssh:, username: "root", connect_timeout: 30) @ssh = Net::SSH.start( host, username, port: port, key_data: [private_key_openssh], keys: [], keys_only: true, auth_methods: ["publickey"], non_interactive: true, # Sandbox host keys are minted at spawn time; nothing to pin against. verify_host_key: :never, timeout: connect_timeout ) @sftp = nil end |
Instance Method Details
#close ⇒ Object
134 135 136 137 |
# File 'lib/xshellz/ssh.rb', line 134 def close @sftp = nil @ssh.close unless @ssh.closed? end |
#download(remote_path, local_path) ⇒ Object
129 130 131 132 |
# File 'lib/xshellz/ssh.rb', line 129 def download(remote_path, local_path) sftp.download!(remote_path, local_path) nil end |
#exec(command, timeout: nil, &block) ⇒ Object
Execute a command; optional block receives (:stdout | :stderr, chunk) as output arrives. Returns a CommandResult; a non-zero exit code does NOT raise.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/xshellz/ssh.rb', line 76 def exec(command, timeout: nil, &block) stdout = +"" stderr = +"" exit_code = nil channel = @ssh.open_channel do |ch| ch.exec(command) do |_, success| raise Error, "Failed to start command on the sandbox: #{command.inspect}" unless success ch.on_data do |_, data| stdout << data block&.call(:stdout, data) end ch.on_extended_data do |_, _type, data| stderr << data block&.call(:stderr, data) end ch.on_request("exit-status") do |_, buffer| exit_code = buffer.read_long end end end deadline = timeout && (monotonic + timeout) timed_out = false @ssh.loop(POLL_INTERVAL) do timed_out = !deadline.nil? && monotonic > deadline channel.active? && !timed_out end if timed_out && channel.active? close_channel_quietly(channel) raise CommandTimeoutError, "Command did not finish within #{timeout} seconds: #{command.inspect}" end CommandResult.new(stdout: stdout, stderr: stderr, exit_code: exit_code || -1) end |
#read_file(path) ⇒ Object
115 116 117 |
# File 'lib/xshellz/ssh.rb', line 115 def read_file(path) sftp.download!(path) end |
#upload(local_path, remote_path) ⇒ Object
124 125 126 127 |
# File 'lib/xshellz/ssh.rb', line 124 def upload(local_path, remote_path) sftp.upload!(local_path, remote_path) nil end |
#write_file(path, data) ⇒ Object
119 120 121 122 |
# File 'lib/xshellz/ssh.rb', line 119 def write_file(path, data) sftp.upload!(StringIO.new(data), path) nil end |