Class: Aptible::CLI::Helpers::Tunnel

Inherits:
Object
  • Object
show all
Defined in:
lib/aptible/cli/helpers/tunnel.rb

Instance Method Summary collapse

Constructor Details

#initialize(env, ssh_cmd, socket_path) ⇒ Tunnel

Returns a new instance of Tunnel.



28
29
30
31
32
# File 'lib/aptible/cli/helpers/tunnel.rb', line 28

def initialize(env, ssh_cmd, socket_path)
  @env = env
  @ssh_cmd = ssh_cmd
  @socket_path = socket_path
end

Instance Method Details

#portObject



100
101
102
103
# File 'lib/aptible/cli/helpers/tunnel.rb', line 100

def port
  raise 'You must call #start before calling #port!' if @local_port.nil?
  @local_port
end

#start(desired_port = 0) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/aptible/cli/helpers/tunnel.rb', line 34

def start(desired_port = 0)
  @local_port = desired_port
  @local_port = random_local_port if @local_port.zero?

  tunnel_cmd = @ssh_cmd + [
    '-L', "#{@local_port}:#{@socket_path}",
    '-o', 'ExitOnForwardFailure=yes'
  ]

  out_read, out_write = IO.pipe
  err_read, err_write = IO.pipe

  @pid = Process.spawn(@env, *tunnel_cmd, SPAWN_OPTS
    .merge(in: :close, out: out_write, err: err_write))

  # Wait for the tunnel to come up before returning. The other end
  # will send a message on stdout to indicate that the tunnel is ready.
  [out_write, err_write].map(&:close)
  begin
    out_read.readline
  rescue EOFError
    stop
    raise UserError, "Tunnel did not come up: #{err_read.read}"
  ensure
    [out_read, err_read].map(&:close)
  end
end

#stopObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/aptible/cli/helpers/tunnel.rb', line 62

def stop
  raise 'You must call #start before calling #stop' if @pid.nil?

  begin
    Process.kill(STOP_SIGNAL, @pid)
  rescue Errno::ESRCH
    # Already dead.
    return
  end

  begin
    STOP_TIMEOUT.times do
      return if Process.wait(@pid, Process::WNOHANG)
      sleep 1
    end
    Process.kill(:SIGKILL, @pid)
  rescue Errno::ECHILD, Errno::ESRCH
    # Died at some point, that's fine.
  end
end

#waitObject



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/aptible/cli/helpers/tunnel.rb', line 83

def wait
  # NOTE: Ruby is kind enough to retry when EINTR is thrown, so we
  # don't need to retry or anything here.
  _, status = Process.wait2(@pid)

  code = status.exitstatus

  case code
  when 0
    # No-op: we're happy with this.
  when 124
    raise Thor::Error, 'Tunnel timed out'
  else
    raise Thor::Error, "Tunnel crashed (#{code})"
  end
end