Class: Train::Transports::SSH::Connection
- Inherits:
-
BaseConnection
- Object
- BaseConnection
- Train::Transports::SSH::Connection
- Defined in:
- lib/train/transports/ssh_connection.rb
Overview
A Connection instance can be generated and re-generated, given new connection details such as connection port, hostname, credentials, etc. This object is responsible for carrying out the actions on the remote host such as executing commands, transferring files, etc.
Constant Summary collapse
- GNU_TIMEOUT_EXIT_STATUS =
If we use the GNU timeout utility to timout a command server-side, it will exit with this status code if the command timed out.
124
Instance Attribute Summary collapse
-
#hostname ⇒ Object
readonly
rubocop:disable Metrics/ClassLength.
-
#transport_options ⇒ Object
Returns the value of attribute transport_options.
Instance Method Summary collapse
- #check_proxy ⇒ Object
- #close ⇒ Object
- #download(remotes, local) ⇒ Object
-
#forward_remote(port, host, remote_port, remote_host = "127.0.0.1") ⇒ Object
remote_port_forwarding.
- #generate_proxy_command ⇒ Object
-
#initialize(options) ⇒ Connection
constructor
A new instance of Connection.
- #login_command ⇒ Object
- #obscured_options ⇒ Object
- #ssh_opts ⇒ Object
- #upload(locals, remote) ⇒ Object
- #uri ⇒ Object
- #wait_until_ready ⇒ Object
- #with_sudo_pty ⇒ Object
Constructor Details
#initialize(options) ⇒ Connection
Returns a new instance of Connection.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/train/transports/ssh_connection.rb', line 39 def initialize() # Track IOS command retries to prevent infinite loop on IOError. This must # be done before `super()` because the parent runs detection commands. @ios_cmd_retries = 0 super() @session = nil @username = @options.delete(:username) @hostname = @options.delete(:hostname) @port = @options[:port] # don't delete from options @connection_retries = @options.delete(:connection_retries) @connection_retry_sleep = @options.delete(:connection_retry_sleep) @max_wait_until_ready = @options.delete(:max_wait_until_ready) @max_ssh_sessions = @options.delete(:max_ssh_connections) { 9 } @transport_options = @options.delete(:transport_options) @proxy_command = @options.delete(:proxy_command) @bastion_host = @options.delete(:bastion_host) @bastion_user = @options.delete(:bastion_user) @bastion_port = @options.delete(:bastion_port) @cmd_wrapper = CommandWrapper.load(self, @transport_options) end |
Instance Attribute Details
#hostname ⇒ Object (readonly)
rubocop:disable Metrics/ClassLength
32 33 34 |
# File 'lib/train/transports/ssh_connection.rb', line 32 def hostname @hostname end |
#transport_options ⇒ Object
Returns the value of attribute transport_options.
33 34 35 |
# File 'lib/train/transports/ssh_connection.rb', line 33 def @transport_options end |
Instance Method Details
#check_proxy ⇒ Object
94 95 96 |
# File 'lib/train/transports/ssh_connection.rb', line 94 def check_proxy [@proxy_command, @bastion_host].any? { |type| !type.nil? } end |
#close ⇒ Object
64 65 66 67 68 69 70 71 |
# File 'lib/train/transports/ssh_connection.rb', line 64 def close return if @session.nil? logger.debug("[SSH] closing connection to #{self}") session.close ensure @session = nil end |
#download(remotes, local) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/train/transports/ssh_connection.rb', line 134 def download(remotes, local) waits = [] Array(remotes).map do |remote| opts = file(remote).directory? ? { recursive: true } : {} waits.push session.scp.download(remote, local, opts) do |_ch, name, recv, total| logger.debug("Downloaded #{name} (#{total} bytes)") if recv == total end waits.shift.wait while waits.length >= @max_ssh_sessions end waits.each(&:wait) rescue Net::SSH::Exception => ex raise Train::Transports::SSHFailed, "SCP download failed (#{ex.})" end |
#forward_remote(port, host, remote_port, remote_host = "127.0.0.1") ⇒ Object
remote_port_forwarding
165 166 167 |
# File 'lib/train/transports/ssh_connection.rb', line 165 def forward_remote(port, host, remote_port, remote_host = "127.0.0.1") @session.forward.remote(port, host, remote_port, remote_host) end |
#generate_proxy_command ⇒ Object
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/train/transports/ssh_connection.rb', line 98 def generate_proxy_command return @proxy_command unless @proxy_command.nil? args = %w{ ssh } args += ssh_opts args += %W{ #{@bastion_user}@#{@bastion_host} } args += %W{ -p #{@bastion_port} } args += %w{ -W %h:%p } args.join(" ") end |
#login_command ⇒ Object
110 111 112 113 114 115 116 |
# File 'lib/train/transports/ssh_connection.rb', line 110 def login_command args = ssh_opts args += %W{ -o ProxyCommand='#{generate_proxy_command}' } if check_proxy args += %W{ -p #{@port} } args += %W{ #{@username}@#{@hostname} } LoginCommand.new("ssh", args) end |
#obscured_options ⇒ Object
169 170 171 172 173 |
# File 'lib/train/transports/ssh_connection.rb', line 169 def = @options.clone [:password] = "<hidden>" if .key?(:password) end |
#ssh_opts ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/train/transports/ssh_connection.rb', line 73 def ssh_opts level = logger.debug? ? "VERBOSE" : "ERROR" fwd_agent = [:forward_agent] ? "yes" : "no" args = %w{ -o UserKnownHostsFile=/dev/null } args += %w{ -o StrictHostKeyChecking=no } args += %w{ -o BatchMode=yes } if [:non_interactive] args += %W{ -o LogLevel=#{level} } args += %W{ -o ForwardAgent=#{fwd_agent} } if .key?(:forward_agent) keys = Array([:keys]) unless keys.empty? args += %w{ -o IdentitiesOnly=yes } keys.each do |ssh_key| args += %W{ -i #{ssh_key} } end end args end |
#upload(locals, remote) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/train/transports/ssh_connection.rb', line 119 def upload(locals, remote) waits = [] Array(locals).each do |local| opts = File.directory?(local) ? { recursive: true } : {} waits.push session.scp.upload(local, remote, opts) do |_ch, name, sent, total| logger.debug("Uploaded #{name} (#{total} bytes)") if sent == total end waits.shift.wait while waits.length >= @max_ssh_sessions end waits.each(&:wait) rescue Net::SSH::Exception => ex raise Train::Transports::SSHFailed, "SCP upload failed (#{ex.})" end |
#uri ⇒ Object
160 161 162 |
# File 'lib/train/transports/ssh_connection.rb', line 160 def uri "ssh://#{@username}@#{@hostname}:#{@port}" end |
#wait_until_ready ⇒ Object
149 150 151 152 153 154 155 156 157 158 |
# File 'lib/train/transports/ssh_connection.rb', line 149 def wait_until_ready delay = 3 session( retries: @max_wait_until_ready / delay, delay: delay, message: "Waiting for SSH service on #{@hostname}:#{@port}, " \ "retrying in #{delay} seconds" ) run_command(PING_COMMAND.dup) end |
#with_sudo_pty ⇒ Object
175 176 177 178 179 180 181 182 |
# File 'lib/train/transports/ssh_connection.rb', line 175 def with_sudo_pty old_pty = [:pty] [:pty] = true if @sudo yield ensure [:pty] = old_pty end |