Module: Utils

Included in:
Cluster, Kubernetes::Client
Defined in:
lib/hetzner/utils.rb

Constant Summary collapse

CMD_FILE_PATH =
'/tmp/cli.cmd'

Instance Method Summary collapse

Instance Method Details

#run(command, kubeconfig_path:) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/hetzner/utils.rb', line 23

def run(command, kubeconfig_path:)
  write_file CMD_FILE_PATH, <<-CONTENT
  set -euo pipefail
  #{command}
  CONTENT

  FileUtils.chmod('+x', CMD_FILE_PATH)

  begin
    process = ChildProcess.build('bash', '-c', CMD_FILE_PATH)
    process.io.inherit!
    process.environment['KUBECONFIG'] = kubeconfig_path
    process.environment['HCLOUD_TOKEN'] = ENV.fetch('HCLOUD_TOKEN', '')

    at_exit do
      process.stop
    rescue Errno::ESRCH, Interrupt
      # ignore
    end

    process.start
    process.wait
  rescue Interrupt
    puts 'Command interrupted'
    exit 1
  end
end

#ssh(server, command, print_output: false) ⇒ Object



71
72
73
74
75
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
114
# File 'lib/hetzner/utils.rb', line 71

def ssh(server, command, print_output: false)
  debug = ENV.fetch('SSH_DEBUG', false)
  retries = 0

  public_ip = server.dig('public_net', 'ipv4', 'ip')
  output = ''

  params = { verify_host_key: (verify_host_key ? :always : :never) }

  params[:keys] = private_ssh_key_path && [private_ssh_key_path]
  params[:verbose] = :debug if debug

  Net::SSH.start(public_ip, 'root', params) do |session|
    session.exec!(command) do |_channel, _stream, data|
      output = "#{output}#{data}"
      puts data if print_output
    end
  end
  output.chop
rescue Timeout::Error, IOError, Errno::EBADF => e
  puts "SSH CONNECTION DEBUG: #{e.message}" if debug
  retries += 1
  retry unless retries > 15
rescue Net::SSH::Disconnect => e
  puts "SSH CONNECTION DEBUG: #{e.message}" if debug
  retries += 1
  retry unless retries > 15 || e.message =~ /Too many authentication failures/
rescue Net::SSH::ConnectionTimeout, Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::EHOSTUNREACH => e
  puts "SSH CONNECTION DEBUG: #{e.message}" if debug
  retries += 1
  retry if retries <= 15
rescue Net::SSH::AuthenticationFailed => e
  puts "SSH CONNECTION DEBUG: #{e.message}" if debug
  puts '\nCannot continue: SSH authentication failed. Please ensure that the private SSH key is correct.'
  exit 1
rescue Net::SSH::HostKeyMismatch => e
  puts "SSH CONNECTION DEBUG: #{e.message}" if debug
  puts <<-MESSAGE
  Cannot continue: Unable to SSH into server with IP #{public_ip} because the existing fingerprint in the known_hosts file does not match that of the actual host key.\n
  This is due to a security check but can also happen when creating a new server that gets assigned the same IP address as another server you've owned in the past.\n
  If are sure no security is being violated here and you're just creating new servers, you can eiher remove the relevant lines from your known_hosts (see IPs from the cloud console) or disable host key verification by setting the option 'verify_host_key' to false in the configuration file for the cluster.
  MESSAGE
  exit 1
end

#verify_host_keyObject



116
117
118
# File 'lib/hetzner/utils.rb', line 116

def verify_host_key
  @verify_host_key ||= configuration.fetch('verify_host_key', false)
end

#wait_for_ssh(server) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/hetzner/utils.rb', line 51

def wait_for_ssh(server)
  retries = 0

  Timeout.timeout(5) do
    server_name = server['name']

    puts "Waiting for server #{server_name} to be up..."

    loop do
      result = ssh(server, 'cat /etc/ready')
      break if result == 'true'
    end

    puts "...server #{server_name} is now up."
  end
rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH, Timeout::Error, IOError, Errno::ECONNRESET
  retries += 1
  retry if retries <= 15
end

#which(cmd) ⇒ Object



8
9
10
11
12
13
14
15
16
17
# File 'lib/hetzner/utils.rb', line 8

def which(cmd)
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
    exts.each do |ext|
      exe = File.join(path, "#{cmd}#{ext}")
      return exe if File.executable?(exe) && !File.directory?(exe)
    end
  end
  nil
end

#write_file(path, content, append: false) ⇒ Object



19
20
21
# File 'lib/hetzner/utils.rb', line 19

def write_file(path, content, append: false)
  File.open(path, append ? 'a' : 'w') { |file| file.write(content) }
end