Commit 4e89c71c authored by Andreas Brandl's avatar Andreas Brandl

Merge branch 'use-etc-hosts-for-nameserver-ip' into 'master'

Check hosts file for nameserver IP

See merge request gitlab-org/gitlab-ee!9071
parents 9eadb163 5ab4771a
---
title: Check hosts file for nameserver IP
merge_request: 9071
author:
type: fixed
require 'net/dns'
require 'resolv'
module Gitlab
module Database
module LoadBalancing
class Resolver
UnresolvableNameserverError = Class.new(StandardError)
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
IPAddr.new(@nameserver)
rescue IPAddr::InvalidAddressError
end
def ip_address_from_hosts_file
ip = Resolv::Hosts.new.getaddress(@nameserver)
IPAddr.new(ip)
rescue Resolv::ResolvError
end
def ip_address_from_dns
answer = Net::DNS::Resolver.start(@nameserver, Net::DNS::A).answer
return if answer.empty?
answer.first.address
end
end
end
end
end
# frozen_string_literal: true
require 'net/dns'
require 'resolv'
module Gitlab
module Database
......@@ -16,8 +17,6 @@ module Gitlab
MAX_SLEEP_ADJUSTMENT = 10
UnresolvableNameserverError = Class.new(StandardError)
# nameserver - The nameserver to use for DNS lookups.
# port - The port of the nameserver.
# record - The DNS record to look up for retrieving the secondaries.
......@@ -127,25 +126,11 @@ module Gitlab
def resolver
@resolver ||= Net::DNS::Resolver.new(
nameservers: nameserver_ip(@nameserver),
nameservers: Resolver.new(@nameserver).resolve,
port: @port,
use_tcp: @use_tcp
)
end
private
def nameserver_ip(nameserver)
IPAddr.new(nameserver)
rescue IPAddr::InvalidAddressError
answer = Net::DNS::Resolver.start(nameserver, Net::DNS::A).answer
if answer.empty?
raise UnresolvableNameserverError, "could not resolve #{nameserver}"
end
answer.first.address
end
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Database::LoadBalancing::Resolver do
describe '#resolve' do
let(:ip_addr) { IPAddr.new('127.0.0.2') }
context 'when nameserver is an IP' do
it 'returns an IPAddr object' do
service = described_class.new('127.0.0.2')
expect(service.resolve).to eq(ip_addr)
end
end
context 'when nameserver is not an IP' do
subject { described_class.new('localhost').resolve }
it 'looks the nameserver up in the hosts file' do
allow_any_instance_of(Resolv::Hosts).to receive(:getaddress)
.with('localhost')
.and_return('127.0.0.2')
expect(subject).to eq(ip_addr)
end
context 'when nameserver is not in the hosts file' do
it 'looks the nameserver up in DNS' do
resource = double(:resource, address: ip_addr)
packet = double(:packet, answer: [resource])
allow_any_instance_of(Resolv::Hosts).to receive(:getaddress)
.with('localhost')
.and_raise(Resolv::ResolvError)
allow(Net::DNS::Resolver).to receive(:start)
.with('localhost', Net::DNS::A)
.and_return(packet)
expect(subject).to eq(ip_addr)
end
context 'when nameserver is not in DNS' do
it 'raises an exception' do
allow_any_instance_of(Resolv::Hosts).to receive(:getaddress)
.with('localhost')
.and_raise(Resolv::ResolvError)
allow(Net::DNS::Resolver).to receive(:start)
.with('localhost', Net::DNS::A)
.and_return(double(:packet, answer: []))
expect { subject }.to raise_exception(
described_class::UnresolvableNameserverError,
'could not resolve localhost'
)
end
end
end
end
end
end
......@@ -6,63 +6,14 @@ describe Gitlab::Database::LoadBalancing::ServiceDiscovery do
let(:service) do
described_class.new(nameserver: 'localhost', port: 8600, record: 'foo')
end
let(:localhost_packet) do
resource = double(:resource, address: IPAddr.new('127.0.0.1'))
double(:packet, answer: [resource])
end
before do
allow(Net::DNS::Resolver).to receive(:start)
.with('localhost', Net::DNS::A)
.and_return(localhost_packet)
end
describe '#initialize' do
context 'when nameserver is not an IP' do
it 'sets the resolver nameservers to the IP of the nameserver' do
allow(Net::DNS::Resolver).to receive(:start)
.with('foo.local', Net::DNS::A)
.and_return(localhost_packet)
service = described_class.new(
nameserver: 'foo.local',
port: 8600,
record: 'bar'
)
expect(service.resolver.nameservers).to eq(['127.0.0.1'])
end
end
context 'when nameserver is an IP' do
it 'sets the resolver nameservers to the IP of the nameserver' do
service = described_class.new(
nameserver: '127.0.0.2',
port: 8600,
record: 'bar'
)
expect(service.resolver.nameservers).to eq(['127.0.0.2'])
end
end
resource = double(:resource, address: IPAddr.new('127.0.0.1'))
packet = double(:packet, answer: [resource])
context 'when nameserver is unresolvable' do
it 'raises an exception' do
allow(Net::DNS::Resolver).to receive(:start)
.with('non-existent.localhost', Net::DNS::A)
.and_return(double(:packet, answer: []))
expect do
described_class.new(
nameserver: 'non-existent.localhost',
port: 8600,
record: 'bar'
).resolver
end.to raise_exception(
described_class::UnresolvableNameserverError,
'could not resolve non-existent.localhost'
)
end
end
.with('localhost', Net::DNS::A)
.and_return(packet)
end
describe '#start' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment