Module: Odysseus::CLI::DoctorCommands

Included in:
CLI
Defined in:
lib/odysseus/cli/doctor_commands.rb

Instance Method Summary collapse

Instance Method Details

#doctor(options = {}) ⇒ Object

Read-only diagnosis of every host in the config, as the user the config names. Its own command rather than a mode of setup, because it lasts: "is this host usable by odysseus as my deploy user" is worth asking on any host, including one a provisioning tool built.



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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/odysseus/cli/doctor_commands.rb', line 14

def doctor(options = {})
  config_file = options[:config] || 'deploy.yml'
  config = load_config(config_file)

  @ui.header 'Odysseus Doctor'
  @ui.info 'Service', config[:service]
  @ui.info 'Deploy user', config[:ssh][:user]
  @ui.blank

  worst = :ok

  executor = Odysseus::Deployer::Executor.new(config_file)

  executor.host_roles.each_key do |host|
    @ui.section host
    ssh = connect_to_server(host, config)

    begin
      Odysseus::HostVerifier.new(ssh: ssh, config: config).verify.each do |result|
        worst = escalate(worst, result.status)
        render_check(result)
      end
    rescue StandardError => e
      # A check failing is a Result with status: :fail, produced by
      # HostVerifier without raising. This rescues something else: the
      # connection itself dying mid-survey. A drop there can surface as
      # IOError, Net::SSH::Disconnect (a RuntimeError) or
      # Errno::EPIPE/ECONNRESET (a SystemCallError) — three branches of
      # StandardError with no narrower ancestor in common, so nothing
      # tighter than StandardError could catch all of them in one
      # rescue. A narrower rescue would also reintroduce the defect
      # this exists to fix: one host's failure aborting the survey for
      # everyone after it. The risk of that width is masking a genuine
      # HostVerifier bug as "host unreachable"; naming the exception's
      # own class and message in the detail is what keeps a
      # NoMethodError legible as a bug rather than indistinguishable
      # from a dropped connection.
      worst = escalate(worst, :fail)
      render_check(
        Odysseus::HostVerifier::Result.new(
          check: :reachable, status: :fail, detail: "#{host}: #{e.class}: #{e.message}"
        )
      )
    ensure
      ssh.close
    end
  end

  @ui.blank
  case worst
  when :fail then exit 1
  when :warn then @ui.warn 'Deploys will work, but read the warnings above.'
  else @ui.success 'This host is ready.'
  end
rescue Odysseus::Error => e
  @ui.error e.message
  exit 1
end