Class: Kitsune::Kit::Adapters::TransportFactory

Inherits:
Object
  • Object
show all
Defined in:
lib/kitsune/kit/adapters/transport_factory.rb

Constant Summary collapse

BOOTSTRAP_TIMEOUT =
120
BOOTSTRAP_RETRY_INTERVAL =
5

Instance Method Summary collapse

Constructor Details

#initialize(config:, provider:, root: Dir.pwd, host_key_confirmation: nil, maximum_timeout: nil, sleeper: Kernel, monotonic_clock: nil, bootstrap_timeout: BOOTSTRAP_TIMEOUT) ⇒ TransportFactory

Returns a new instance of TransportFactory.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/kitsune/kit/adapters/transport_factory.rb', line 15

def initialize(config:, provider:, root: Dir.pwd, host_key_confirmation: nil, maximum_timeout: nil,
               sleeper: Kernel, monotonic_clock: nil, bootstrap_timeout: BOOTSTRAP_TIMEOUT)
  if maximum_timeout && maximum_timeout <= 0
    raise Errors::ConfigurationError, "timeout must be greater than zero"
  end
  raise Errors::ConfigurationError, "SSH bootstrap timeout must be greater than zero" if bootstrap_timeout <= 0

  @config = config
  @provider = provider
  @known_hosts = File.expand_path(".kitsune/known_hosts", root)
  @host_key_verifier = ConfirmingHostKeyVerifier.new(&host_key_confirmation)
  @maximum_timeout = maximum_timeout
  @bootstrap_timeout = [bootstrap_timeout, maximum_timeout].compact.min
  @sleeper = sleeper
  @monotonic_clock = monotonic_clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
end

Instance Method Details

#bootstrapObject



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/kitsune/kit/adapters/transport_factory.rb', line 61

def bootstrap
  candidate = deploy
  return candidate if candidate.reachable?

  candidate = root
  return candidate if candidate.reachable?

  raise Errors::ConnectionError.new(
    "neither #{@config.ssh.user} nor root can access the server",
    hint: "Verify the SSH key and provider console before changing SSH policy."
  )
end

#deployObject



58
# File 'lib/kitsune/kit/adapters/transport_factory.rb', line 58

def deploy = self.for(user: @config.ssh.user)

#for(user:) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/kitsune/kit/adapters/transport_factory.rb', line 36

def for(user:)
  current = server
  unless current&.public_ip
    raise Errors::ConnectionError.new(
      "server is not ready for SSH",
      hint: "Apply the server operation first, then resume."
    )
  end
  FileUtils.mkdir_p(File.dirname(@known_hosts), mode: 0o700)
  FileUtils.touch(@known_hosts)
  File.chmod(0o600, @known_hosts)
  NetSshTransport.new(
    host: current.public_ip,
    user: user,
    port: @config.ssh.port,
    key_path: @config.ssh.key_path,
    known_hosts: @known_hosts,
    verify_host_key: @host_key_verifier,
    maximum_timeout: @maximum_timeout
  )
end

#rootObject



59
# File 'lib/kitsune/kit/adapters/transport_factory.rb', line 59

def root = self.for(user: "root")

#serverObject



32
33
34
# File 'lib/kitsune/kit/adapters/transport_factory.rb', line 32

def server
  @provider.find_server(name: @config.server.name, tags: @config.server.tags)
end

#wait_for_bootstrapObject



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/kitsune/kit/adapters/transport_factory.rb', line 74

def wait_for_bootstrap
  deadline = monotonic_time + @bootstrap_timeout
  loop do
    return bootstrap
  rescue Errors::ConnectionError
    remaining = deadline - monotonic_time
    break if remaining <= 0

    @sleeper.sleep([BOOTSTRAP_RETRY_INTERVAL, remaining].min)
  end

  raise Errors::ConnectionError.new(
    "SSH did not become reachable within #{@bootstrap_timeout} seconds",
    hint: "Wait for Ubuntu to finish booting and verify that the uploaded public key matches ssh.key_path.",
    context: { timeout: @bootstrap_timeout },
    retryable: true
  )
end