write one resolv config

When systemd-resolved is enabled dns info is only written to
/etc/systemd/resolved.conf.  If systemd-resolved is not enabled dns info is
only written to /etc/resolv.conf.  We do not write to /etc/resolv.conf
if systemd-resolved is enabled because /etc/resolv.conf is a symlink pointing
to ../run/systemd/resolve/stub-resolv.conf.  This target file does not exist
before systemd-resolved is run so glean would fail with:
    'FileNotFoundError: [Errno 2] No such file or directory: '/etc/resolv.conf'

Change-Id: I644e0b50cfb7bb00a108160b99c0c1359d6a9dd4
Signed-off-by: Matthew Thode <mthode@mthode.org>
This commit is contained in:
Matthew Thode 2020-04-03 11:35:18 -05:00
parent 4826490245
commit a39e9b3d21
No known key found for this signature in database
GPG Key ID: 64A37BEAAE19A4E8
6 changed files with 51 additions and 18 deletions

View File

@ -945,15 +945,26 @@ def write_debian_interfaces(interfaces, sys_interfaces):
def write_dns_info(dns_servers):
# will fail on non-systemd systems (what we want)
# will exit 1 if not enabled (what we want)
# will exit 0 if enabled (or indirectly enabled)
resolved_enabled = os.system('systemctl is-enabled systemd-resolved')
resolve_confs = {}
resolv_nameservers = ""
for server in dns_servers:
resolv_nameservers += "nameserver {0}\n".format(server)
resolve_confs['/etc/resolv.conf'] = resolv_nameservers
# set up resolved if available
if os.path.isfile('/etc/systemd/resolved.conf'):
# write resolv.conf if the file can be written to (if symlink is pointing
# to a non-existant file, writing will fail), will return false if the
# pointer is incomplete
if resolved_enabled != 0:
log.debug("resolved not in use, writing to /etc/resolv.conf")
resolv_nameservers = ""
for server in dns_servers:
resolv_nameservers += "nameserver {0}\n".format(server)
resolve_confs['/etc/resolv.conf'] = resolv_nameservers
# set up resolved if enabled
if resolved_enabled == 0:
log.debug("resolved in use, writing to /etc/systemd/resolved.conf")
# read the existing config so we only overwrite what's needed
resolved_conf = configparser.ConfigParser()
resolved_conf.optionxform = str
resolved_conf.read('/etc/systemd/resolved.conf')
# create config section if not created
if not resolved_conf.has_section('Resolve'):

View File

@ -1,6 +1,7 @@
### Write /etc/resolv.conf
nameserver 72.3.128.241
nameserver 72.3.128.240
### Write /etc/systemd/resolved.conf
[Resolve]
DNS = 72.3.128.241 72.3.128.240
### Write /etc/systemd/network/eth0.network
# Automatically generated, do not edit
[Match]

View File

@ -1,6 +1,7 @@
### Write /etc/resolv.conf
nameserver 69.20.0.196
nameserver 69.20.0.164
### Write /etc/systemd/resolved.conf
[Resolve]
DNS = 69.20.0.196 69.20.0.164
### Write /etc/systemd/network/eth0.network
# Automatically generated, do not edit
[Match]

View File

@ -1,6 +1,7 @@
### Write /etc/resolv.conf
nameserver 69.20.0.196
nameserver 69.20.0.164
### Write /etc/systemd/resolved.conf
[Resolve]
DNS = 69.20.0.196 69.20.0.164
### Write /etc/systemd/network/eth0.network
# Automatically generated, do not edit
[Match]

View File

@ -1,6 +1,7 @@
### Write /etc/resolv.conf
nameserver 72.3.128.241
nameserver 72.3.128.240
### Write /etc/systemd/resolved.conf
[Resolve]
DNS = 72.3.128.241 72.3.128.240
### Write /etc/systemd/network/eth0.network
# Automatically generated, do not edit
[Match]

View File

@ -142,6 +142,13 @@ class TestGlean(base.BaseTestCase):
return False
return real_path_exists(path)
def os_system_side_effect(self, distro, command):
if distro.lower() != 'networkd' and \
command == 'systemctl is-enabled systemd-resolved':
return 3
else:
return 0
@mock.patch('subprocess.call', return_value=0, new_callable=mock.Mock)
@mock.patch('os.fsync', return_value=0, new_callable=mock.Mock)
@mock.patch('os.unlink', return_value=0, new_callable=mock.Mock)
@ -183,6 +190,10 @@ class TestGlean(base.BaseTestCase):
self.os_listdir_side_effect, provider)
mock_open.side_effect = functools.partial(
self.open_side_effect, provider)
# we want os.system to return False for specific commands if
# running networkd
mock_os_system.side_effect = functools.partial(
self.os_system_side_effect, distro)
# default args
argv = ['--hostname']
@ -234,6 +245,9 @@ class TestGlean(base.BaseTestCase):
if skip_dns and '/etc/resolv.conf' in dest:
self.assertNotIn(dest, self.file_handle_mocks)
continue
if skip_dns and '/etc/systemd/resolved.conf' in dest:
self.assertNotIn(dest, self.file_handle_mocks)
continue
self.assertIn(dest, self.file_handle_mocks)
write_handle = self.file_handle_mocks[dest].write
write_handle.assert_called_once_with(content)
@ -279,6 +293,10 @@ class TestGlean(base.BaseTestCase):
self._assert_distro_provider(self.distro, self.style,
'eth0', skip_dns=True)
def test_glean_systemd_resolved(self):
with mock.patch('glean.systemlock.Lock'):
self._assert_distro_provider(self.distro, self.style, 'eth0')
def test_glean_skip_dns(self):
with mock.patch('glean.systemlock.Lock'):
self._assert_distro_provider(