Module: Aptible::CLI::Helpers::Ssh

Included in:
Agent, Database, Operation
Defined in:
lib/aptible/cli/helpers/ssh.rb

Instance Method Summary collapse

Instance Method Details

#connect_to_ssh_portal(operation, *extra_ssh_args) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/aptible/cli/helpers/ssh.rb', line 5

def connect_to_ssh_portal(operation, *extra_ssh_args)
  # NOTE: This is a little tricky to get rigt, so before you make any
  # changes, read this.
  #
  # - The first gotcha is that we cannot use Kernel.exec here, because
  # we need to perform cleanup when exiting from
  # operation#with_ssh_cmd.
  #
  # - The second gotcha is that we need to somehow capture the exit
  # status, so that CLI commands that call the SSH portal can proxy
  # this back to their own caller (the most important one here is
  # aptible ssh).
  #
  # To do this, we have to handle interrutps as a signal, as opposed to
  # handle an Interrupt exception. The reason for this has to do with
  # how Ruby's wait is implemented (this happens in process.c's
  # rb_waitpid). There are two main considerations here:
  #
  # - It automatically resumes when it receives EINTR, so our control
  # is pretty high-level here.
  # - It handles interrupts prior to setting $? (this appears to have
  # changed between Ruby 2.2 and 2.3, perhaps the newer implementation
  # behaves differently).
  #
  # Unfortunately, this means that if we receive SIGINT while in
  # Process::wait2, then we never get access to SSH's exitstatus: Ruby
  # throws a Interrupt so we don't have a return value, and it doesn't
  # set $?, so we can't read it back there.
  #
  # Of course, we can't just call Proces::wait2 again, because at this
  # point, we've reaped our child.
  #
  # To solve this, we add our own signal handler on SIGINT, which
  # simply proxies SIGINT to SSH if we happen to have a different
  # process group (which shouldn't be the case), just to be safe and
  # let users exit the CLI.
  with_ssh_cmd(operation) do |base_ssh_cmd|
    spawn_passthrough(base_ssh_cmd + extra_ssh_args)
  end
end

#exit_with_ssh_portal(*args) ⇒ Object



46
47
48
# File 'lib/aptible/cli/helpers/ssh.rb', line 46

def exit_with_ssh_portal(*args)
  exit connect_to_ssh_portal(*args)
end

#with_ssh_cmd(operation) ⇒ Object



50
51
52
53
54
55
56
57
58
# File 'lib/aptible/cli/helpers/ssh.rb', line 50

def with_ssh_cmd(operation)
  ensure_ssh_dir!
  ensure_config!
  ensure_key!

  operation.with_ssh_cmd(private_key_file) do |cmd, connection|
    yield cmd + common_ssh_args, connection
  end
end