71 lines
1.8 KiB
Ruby
71 lines
1.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'net/dns'
|
|
require 'resolv'
|
|
|
|
module Gitlab
|
|
module Database
|
|
module LoadBalancing
|
|
class Resolver
|
|
FAR_FUTURE_TTL = 100.years.from_now
|
|
|
|
UnresolvableNameserverError = Class.new(StandardError)
|
|
|
|
Response = Class.new do
|
|
attr_reader :address, :ttl
|
|
|
|
def initialize(address:, ttl:)
|
|
raise ArgumentError unless ttl.present? && address.present?
|
|
|
|
@address = address
|
|
@ttl = ttl
|
|
end
|
|
end
|
|
|
|
def initialize(nameserver)
|
|
@nameserver = nameserver
|
|
end
|
|
|
|
def resolve
|
|
address = ip_address || ip_address_from_hosts_file ||
|
|
ip_address_from_dns
|
|
|
|
unless address
|
|
raise UnresolvableNameserverError,
|
|
"could not resolve #{@nameserver}"
|
|
end
|
|
|
|
address
|
|
end
|
|
|
|
private
|
|
|
|
def ip_address
|
|
# IP addresses are valid forever
|
|
Response.new(address: IPAddr.new(@nameserver), ttl: FAR_FUTURE_TTL)
|
|
rescue IPAddr::InvalidAddressError
|
|
end
|
|
|
|
def ip_address_from_hosts_file
|
|
ip = Resolv::Hosts.new.getaddress(@nameserver)
|
|
Response.new(address: IPAddr.new(ip), ttl: FAR_FUTURE_TTL)
|
|
rescue Resolv::ResolvError
|
|
end
|
|
|
|
def ip_address_from_dns
|
|
answer = Net::DNS::Resolver.start(@nameserver, Net::DNS::A).answer
|
|
return if answer.empty?
|
|
|
|
raw_response = answer.first
|
|
|
|
# Defaults to 30 seconds if there is no TTL present
|
|
ttl_in_seconds = raw_response.ttl.presence || 30
|
|
|
|
Response.new(address: answer.first.address, ttl: ttl_in_seconds.seconds.from_now)
|
|
rescue Net::DNS::Resolver::NoResponseError
|
|
raise UnresolvableNameserverError, "no response from DNS server(s)"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|