Merge ~vultaire/charm-sysconfig:lp1841809 into charm-sysconfig:master

Proposed by Paul Goins
Status: Merged
Approved by: Jeremy Lounder
Approved revision: d732035d8aff8c3a74d715fa9e4f88a435f0422c
Merged at revision: cb5240074d70796e3c06b257756b00013a0ffda4
Proposed branch: ~vultaire/charm-sysconfig:lp1841809
Merge into: charm-sysconfig:master
Diff against target: 473 lines (+171/-83)
6 files modified
src/config.yaml (+6/-0)
src/lib/lib_sysconfig.py (+48/-18)
src/reactive/sysconfig.py (+9/-1)
src/templates/etc.systemd.resolved.conf.j2 (+27/-0)
src/tests/functional/test_deploy.py (+40/-30)
src/tests/unit/test_lib.py (+41/-34)
Reviewer Review Type Date Requested Status
Jeremy Lounder (community) Approve
Alvaro Uria (community) Approve
Review via email: mp+378630@code.launchpad.net

Commit message

Adding resolved-cache-mode config option

Description of the change

* Added the feature
* Added tests for the feature
* Removed 'cosmic' option from test_deploy.py since it adds significant test overhead time (10 minutes on certain tests), due to there not being a cosmic charm available to deploy with.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Alvaro Uria (aluria) wrote :

* typo: Default value on config.yaml
* nitpick: code style
* Behavior around updating the resolved.conf file (systemd-resolved service should be restarted instead of notifying that the unit needs a reboot)

review: Needs Fixing
Revision history for this message
Paul Goins (vultaire) wrote :

Noting my responses. Will make changes.

Revision history for this message
Paul Goins (vultaire) :
Revision history for this message
Paul Goins (vultaire) wrote :

@aluria: Care to give this another look? I think I've addressed everything.

Revision history for this message
Alvaro Uria (aluria) wrote :

Hey Paul. I've added a comment inline, but I will repeat it here.
* checksum verification can be achieved via any_file_changed [1], helper from the reactive framework, A list of file(s) needs to be passed.
* "render" already checks if new content is going to be written, or it will skip the write
* As a suggestion of a diff approach, set_flag("systemd-resolved.restart") could always be set after the update and remove functions are called (maybe even merging the action into a single method?) and any_file_changed could be evaluated in the decorated function (within the reactive script), to see if a service restart is needed or not (+ clear the flag).

And think the above will make more sense in the comment inline.

Other than this, the change (and tests) look great.

1. https://github.com/juju-solutions/charms.reactive/blob/master/charms/reactive/helpers.py#L85

review: Needs Fixing
Revision history for this message
Paul Goins (vultaire) wrote :

Most comments addressed. Did not use set_flag(...) since cpufrequtils also handles a service restart in the same way (in the lib module rather than via a flag-based reactive hook); if we change it for resolved, I think we should change it for cpufrequtils as well - and that starts to work its way out of scope in my opinion.

Revision history for this message
Alvaro Uria (aluria) wrote :

Hey Paul, Thank you! I've added a minor suggestion for readability purpose, but I won't block it for merging :)

review: Approve
Revision history for this message
Alvaro Uria (aluria) wrote :

Hey Paul. I suggested the use of a list for [ONE_FILE], but failed to check the change. Good catch. I also failed to mention that "import hashlib" may not be needed anymore. Other than that, all looks good to me.

Revision history for this message
Jeremy Lounder (jldev) :
review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision cb5240074d70796e3c06b257756b00013a0ffda4

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/config.yaml b/src/config.yaml
index bd60238..4b93f3c 100644
--- a/src/config.yaml
+++ b/src/config.yaml
@@ -122,3 +122,9 @@ options:
122 Recommended option when Bios control power is set to the OS.122 Recommended option when Bios control power is set to the OS.
123 - 'performance'123 - 'performance'
124 - 'powersave'124 - 'powersave'
125 resolved-cache-mode:
126 type: string
127 default: ""
128 description: |
129 Sets the "Cache" configuration value in /etc/systemd/resolved.conf. Valid
130 values are '' (system default), 'yes', 'no', and 'no-negative'.
diff --git a/src/lib/lib_sysconfig.py b/src/lib/lib_sysconfig.py
index 51bf049..73e1311 100644
--- a/src/lib/lib_sysconfig.py
+++ b/src/lib/lib_sysconfig.py
@@ -2,7 +2,6 @@
22
3Manage grub, systemd, coufrequtils and kernel version configuration.3Manage grub, systemd, coufrequtils and kernel version configuration.
4"""4"""
5
6import os5import os
7import subprocess6import subprocess
8from datetime import datetime, timedelta, timezone7from datetime import datetime, timedelta, timezone
@@ -11,15 +10,18 @@ from charmhelpers.contrib.openstack.utils import config_flags_parser
11from charmhelpers.core import hookenv, host, unitdata10from charmhelpers.core import hookenv, host, unitdata
12from charmhelpers.core.templating import render11from charmhelpers.core.templating import render
13from charmhelpers.fetch import apt_install, apt_update12from charmhelpers.fetch import apt_install, apt_update
13from charms.reactive.helpers import any_file_changed
1414
15GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}'15GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}'
16CPUFREQUTILS_TMPL = 'cpufrequtils.j2'16CPUFREQUTILS_TMPL = 'cpufrequtils.j2'
17GRUB_CONF_TMPL = 'grub.j2'17GRUB_CONF_TMPL = 'grub.j2'
18SYSTEMD_SYSTEM_TMPL = 'etc.systemd.system.conf.j2'18SYSTEMD_SYSTEM_TMPL = 'etc.systemd.system.conf.j2'
19SYSTEMD_RESOLVED_TMPL = 'etc.systemd.resolved.conf.j2'
1920
20CPUFREQUTILS = '/etc/default/cpufrequtils'21CPUFREQUTILS = '/etc/default/cpufrequtils'
21GRUB_CONF = '/etc/default/grub.d/90-sysconfig.cfg'22GRUB_CONF = '/etc/default/grub.d/90-sysconfig.cfg'
22SYSTEMD_SYSTEM = '/etc/systemd/system.conf'23SYSTEMD_SYSTEM = '/etc/systemd/system.conf'
24SYSTEMD_RESOLVED = '/etc/systemd/resolved.conf'
23KERNEL = 'kernel'25KERNEL = 'kernel'
2426
2527
@@ -155,25 +157,30 @@ class SysConfigHelper:
155157
156 @property158 @property
157 def systemd_config_flags(self):159 def systemd_config_flags(self):
158 """Return grub-config-flags config option."""160 """Return systemd-config-flags config option."""
159 return parse_config_flags(self.charm_config['systemd-config-flags'])161 return parse_config_flags(self.charm_config['systemd-config-flags'])
160162
161 @property163 @property
162 def kernel_version(self):164 def kernel_version(self):
163 """Return grub-config-flags config option."""165 """Return kernel-version config option."""
164 return self.charm_config['kernel-version']166 return self.charm_config['kernel-version']
165167
166 @property168 @property
167 def update_grub(self):169 def update_grub(self):
168 """Return grub-config-flags config option."""170 """Return update-grub config option."""
169 return self.charm_config['update-grub']171 return self.charm_config['update-grub']
170172
171 @property173 @property
172 def governor(self):174 def governor(self):
173 """Return grub-config-flags config option."""175 """Return governor config option."""
174 return self.charm_config['governor']176 return self.charm_config['governor']
175177
176 @property178 @property
179 def resolved_cache_mode(self):
180 """Return resolved-cache-mode config option."""
181 return self.charm_config['resolved-cache-mode']
182
183 @property
177 def config_flags(self):184 def config_flags(self):
178 """Return parsed config-flags into dict.185 """Return parsed config-flags into dict.
179186
@@ -187,9 +194,14 @@ class SysConfigHelper:
187194
188 def _render_boot_resource(self, source, target, context):195 def _render_boot_resource(self, source, target, context):
189 """Render the template and set the resource as changed."""196 """Render the template and set the resource as changed."""
190 render(source=source, templates_dir='templates', target=target, context=context)197 self._render_resource(source, target, context)
191 self.boot_resources.set_resource(target)198 self.boot_resources.set_resource(target)
192199
200 @staticmethod
201 def _render_resource(source, target, context):
202 """Render the template."""
203 render(source=source, templates_dir='templates', target=target, context=context)
204
193 def _is_kernel_already_running(self):205 def _is_kernel_already_running(self):
194 """Check if the kernel version required by charm config is equal to kernel running."""206 """Check if the kernel version required by charm config is equal to kernel running."""
195 configured = self.kernel_version207 configured = self.kernel_version
@@ -208,18 +220,15 @@ class SysConfigHelper:
208 """Validate config parameters."""220 """Validate config parameters."""
209 valid = True221 valid = True
210222
211 if self.reservation not in ['off', 'isolcpus', 'affinity']:223 for config_key, value, valid_values in (
212 hookenv.log('reservation not valid. Possible values: ["off", "isolcpus", "affinity"]', hookenv.DEBUG)224 ('reservation', self.reservation, ['off', 'isolcpus', 'affinity']),
213 valid = False225 ('raid-autodetection', self.raid_autodetection, ['', 'noautodetect', 'partitionable']),
214226 ('governor', self.governor, ['', 'powersave', 'performance']),
215 if self.raid_autodetection not in ['', 'noautodetect', 'partitionable']:227 ('resolved-cache-mode', self.resolved_cache_mode, ['', 'yes', 'no', 'no-negative']),
216 hookenv.log('raid-autodetection not valid. '228 ):
217 'Possible values: ["off", "noautodetect", "partitionable"]', hookenv.DEBUG)229 if value not in valid_values:
218 valid = False230 hookenv.log('{} not valid. Possible values: {}'.format(config_key, repr(valid_values)), hookenv.DEBUG)
219231 valid = False
220 if self.governor not in ['', 'powersave', 'performance']:
221 hookenv.log('governor not valid. Possible values: ["", "powersave", "performance"]', hookenv.DEBUG)
222 valid = False
223232
224 return valid233 return valid
225234
@@ -272,6 +281,14 @@ class SysConfigHelper:
272 self._render_boot_resource(SYSTEMD_SYSTEM_TMPL, SYSTEMD_SYSTEM, context)281 self._render_boot_resource(SYSTEMD_SYSTEM_TMPL, SYSTEMD_SYSTEM, context)
273 hookenv.log('systemd configuration updated')282 hookenv.log('systemd configuration updated')
274283
284 def update_systemd_resolved(self):
285 """Update /etc/systemd/resolved.conf according to charm configuration."""
286 context = {}
287 if self.resolved_cache_mode:
288 context['cache'] = self.resolved_cache_mode
289 self._update_systemd_resolved(context)
290 hookenv.log('systemd-resolved configuration updated')
291
275 def install_configured_kernel(self):292 def install_configured_kernel(self):
276 """Install kernel as given by the kernel-version config option.293 """Install kernel as given by the kernel-version config option.
277294
@@ -341,6 +358,19 @@ class SysConfigHelper:
341 hookenv.DEBUG358 hookenv.DEBUG
342 )359 )
343360
361 def remove_resolved_configuration(self):
362 """Remove systemd's resolved configuration.
363
364 Will render resolved config with defaults.
365 """
366 self._update_systemd_resolved({})
367 hookenv.log('deleted resolved configuration at '.format(SYSTEMD_RESOLVED), hookenv.DEBUG)
368
369 def _update_systemd_resolved(self, context):
370 self._render_resource(SYSTEMD_RESOLVED_TMPL, SYSTEMD_RESOLVED, context)
371 if any_file_changed([SYSTEMD_RESOLVED]):
372 host.service_restart('systemd-resolved')
373
344 def remove_cpufreq_configuration(self):374 def remove_cpufreq_configuration(self):
345 """Remove cpufrequtils configuration.375 """Remove cpufrequtils configuration.
346376
diff --git a/src/reactive/sysconfig.py b/src/reactive/sysconfig.py
index eba0916..6c70fba 100644
--- a/src/reactive/sysconfig.py
+++ b/src/reactive/sysconfig.py
@@ -28,7 +28,7 @@ from charms.reactive import (
28 when_not,28 when_not,
29)29)
3030
31from lib_sysconfig import CPUFREQUTILS, GRUB_CONF, KERNEL, SYSTEMD_SYSTEM, SysConfigHelper31from lib_sysconfig import CPUFREQUTILS, GRUB_CONF, KERNEL, SYSTEMD_SYSTEM, SYSTEMD_RESOLVED, SysConfigHelper
3232
3333
34@when_none('sysconfig.installed', 'sysconfig.unsupported')34@when_none('sysconfig.installed', 'sysconfig.unsupported')
@@ -54,6 +54,7 @@ def install_sysconfig():
54 syshelper.update_cpufreq()54 syshelper.update_cpufreq()
55 syshelper.update_grub_file()55 syshelper.update_grub_file()
56 syshelper.update_systemd_system_file()56 syshelper.update_systemd_system_file()
57 syshelper.update_systemd_resolved()
57 set_flag('sysconfig.installed')58 set_flag('sysconfig.installed')
58 update_status()59 update_status()
5960
@@ -103,6 +104,12 @@ def config_changed():
103 )) or helpers.any_file_changed([SYSTEMD_SYSTEM]):104 )) or helpers.any_file_changed([SYSTEMD_SYSTEM]):
104 syshelper.update_systemd_system_file()105 syshelper.update_systemd_system_file()
105106
107 # systemd resolved
108 if any(syshelper.charm_config.changed(flag) for flag in (
109 'resolved-cache-mode',
110 )) or helpers.any_file_changed([SYSTEMD_RESOLVED]):
111 syshelper.update_systemd_resolved()
112
106 update_status()113 update_status()
107114
108115
@@ -142,5 +149,6 @@ def remove_configuration():
142 syshelper.remove_cpufreq_configuration()149 syshelper.remove_cpufreq_configuration()
143 syshelper.remove_grub_configuration()150 syshelper.remove_grub_configuration()
144 syshelper.remove_systemd_configuration()151 syshelper.remove_systemd_configuration()
152 syshelper.remove_resolved_configuration()
145 clear_flag('sysconfig.installed')153 clear_flag('sysconfig.installed')
146 clear_flag('sysconfig.unsupported')154 clear_flag('sysconfig.unsupported')
diff --git a/src/templates/etc.systemd.resolved.conf.j2 b/src/templates/etc.systemd.resolved.conf.j2
147new file mode 100644155new file mode 100644
index 0000000..ab8832b
--- /dev/null
+++ b/src/templates/etc.systemd.resolved.conf.j2
@@ -0,0 +1,27 @@
1# This file is part of systemd.
2#
3# systemd is free software; you can redistribute it and/or modify it
4# under the terms of the GNU Lesser General Public License as published by
5# the Free Software Foundation; either version 2.1 of the License, or
6# (at your option) any later version.
7#
8# Entries in this file show the compile time defaults.
9# You can change settings by editing this file.
10# Defaults can be restored by simply deleting this file.
11#
12# See resolved.conf(5) for details
13
14[Resolve]
15#DNS=
16#FallbackDNS=
17#Domains=
18#LLMNR=no
19#MulticastDNS=no
20#DNSSEC=no
21#DNSOverTLS=no
22#Cache=yes
23{% if cache -%}
24Cache={{ cache }}
25{% endif -%}
26#DNSStubListener=yes
27#ReadEtcHosts=yes
diff --git a/src/tests/functional/test_deploy.py b/src/tests/functional/test_deploy.py
index 968288b..2900e06 100644
--- a/src/tests/functional/test_deploy.py
+++ b/src/tests/functional/test_deploy.py
@@ -2,9 +2,11 @@
22
3import asyncio3import asyncio
4import os4import os
5import re
5import subprocess6import subprocess
67
7import pytest8import pytest
9import websockets
810
911
10# Treat all tests as coroutines12# Treat all tests as coroutines
@@ -14,12 +16,12 @@ charm_build_dir = os.getenv('CHARM_BUILD_DIR', '..').rstrip('/')
1416
15series = ['xenial',17series = ['xenial',
16 'bionic',18 'bionic',
17 pytest.param('cosmic', marks=pytest.mark.xfail(reason='canary')),
18 ]19 ]
1920
20sources = [('local', '{}/builds/sysconfig'.format(charm_build_dir))]21sources = [('local', '{}/builds/sysconfig'.format(charm_build_dir))]
2122
22TIMEOUT = 60023TIMEOUT = 600
24MODEL_ACTIVE_TIMEOUT = 10
23GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}'25GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}'
24PRINCIPAL_APP_NAME = 'ubuntu-{}'26PRINCIPAL_APP_NAME = 'ubuntu-{}'
2527
@@ -269,14 +271,19 @@ async def test_wrong_reservation(app, model):
269 )271 )
270272
271273
272async def test_wrong_raid_autodetection(app, model):274@pytest.mark.parametrize('key,bad_value,good_value', [
273 """Tests wrong raid-autodetection value is used.275 ('raid-autodetection', 'changeme', ''),
276 ('governor', 'changeme', ''),
277 ('resolved-cache-mode', 'changeme', ''),
278])
279async def test_invalid_configuration_parameters(app, model, key, bad_value, good_value):
280 """Tests wrong config value is used.
274281
275 Expect application is blocked until correct value is set.282 Expect application is blocked until correct value is set.
276 """283 """
277 await app.set_config(284 await app.set_config(
278 {285 {
279 'raid-autodetection': 'changeme'286 key: bad_value
280 }287 }
281 )288 )
282 await model.block_until(289 await model.block_until(
@@ -289,7 +296,7 @@ async def test_wrong_raid_autodetection(app, model):
289296
290 await app.set_config(297 await app.set_config(
291 {298 {
292 'raid-autodetection': ''299 key: good_value
293 }300 }
294 )301 )
295 await model.block_until(302 await model.block_until(
@@ -298,33 +305,32 @@ async def test_wrong_raid_autodetection(app, model):
298 )305 )
299306
300307
301async def test_wrong_governor(app, model):308@pytest.mark.parametrize('cache_setting', [
302 """Tests wrong governor value is used.309 'yes',
310 'no',
311 'no-negative',
312])
313async def test_set_resolved_cache(app, model, jujutools, cache_setting):
314 """Tests resolved cache settings."""
315 def is_model_settled():
316 return app.units[0].workload_status == 'active' and app.units[0].agent_status == 'idle'
303317
304 Expect application is blocked until correct value is set.318 await model.block_until(is_model_settled, timeout=TIMEOUT)
305 """
306 await app.set_config(
307 {
308 'governor': 'changeme'
309 }
310 )
311 await model.block_until(
312 lambda: app.status == 'blocked',
313 timeout=TIMEOUT
314 )
315 assert app.status == 'blocked'
316 unit = app.units[0]
317 assert 'configuration parameters not valid.' in unit.workload_status_message
318319
319 await app.set_config(320 await app.set_config({'resolved-cache-mode': cache_setting})
320 {321 # NOTE: app.set_config() doesn't seem to wait for the model to go to a
321 'governor': ''322 # non-active/idle state.
322 }323 try:
323 )324 await model.block_until(lambda: not is_model_settled(), timeout=MODEL_ACTIVE_TIMEOUT)
324 await model.block_until(325 except websockets.ConnectionClosed:
325 lambda: app.status == 'active',326 # It's possible (although unlikely) that we missed the charm transitioning from
326 timeout=TIMEOUT327 # idle to active and back.
327 )328 pass
329
330 await model.block_until(is_model_settled, timeout=TIMEOUT)
331
332 resolved_conf_content = await jujutools.file_contents('/etc/systemd/resolved.conf', app.units[0])
333 assert re.search('^Cache={}$'.format(cache_setting), resolved_conf_content, re.MULTILINE)
328334
329335
330async def test_uninstall(app, model, jujutools, series):336async def test_uninstall(app, model, jujutools, series):
@@ -365,6 +371,10 @@ async def test_uninstall(app, model, jujutools, series):
365 systemd_content = await jujutools.file_contents(systemd_path, unit)371 systemd_content = await jujutools.file_contents(systemd_path, unit)
366 assert 'CPUAffinity=1,2,3,4' not in systemd_content372 assert 'CPUAffinity=1,2,3,4' not in systemd_content
367373
374 resolved_path = '/etc/systemd/resolved.conf'
375 resolved_content = await jujutools.file_contents(resolved_path, unit)
376 assert not re.search('^Cache=', resolved_content, re.MULTILINE)
377
368 cpufreq_path = '/etc/default/cpufrequtils'378 cpufreq_path = '/etc/default/cpufrequtils'
369 cpufreq_content = await jujutools.file_contents(cpufreq_path, unit)379 cpufreq_content = await jujutools.file_contents(cpufreq_path, unit)
370 assert 'GOVERNOR' not in cpufreq_content380 assert 'GOVERNOR' not in cpufreq_content
diff --git a/src/tests/unit/test_lib.py b/src/tests/unit/test_lib.py
index b7ac651..be25eb4 100644
--- a/src/tests/unit/test_lib.py
+++ b/src/tests/unit/test_lib.py
@@ -1,13 +1,14 @@
1#!/usr/bin/python31#!/usr/bin/python3
2"""Unit tests for SysConfigHelper and BootResourceState classes."""2"""Unit tests for SysConfigHelper and BootResourceState classes."""
33import os
4
5import subprocess4import subprocess
5import tempfile
6from datetime import datetime, timezone6from datetime import datetime, timezone
77
8import lib_sysconfig8import lib_sysconfig
99
10import mock10import mock
11import pytest
1112
1213
13class TestBootResourceState:14class TestBootResourceState:
@@ -479,45 +480,22 @@ class TestLib:
479 )480 )
480 restart.assert_called()481 restart.assert_called()
481482
483 @pytest.mark.parametrize("invalid_config_key", [
484 "reservation", "raid-autodetection", "governor", "resolved-cache-mode"])
482 @mock.patch("lib_sysconfig.hookenv.config")485 @mock.patch("lib_sysconfig.hookenv.config")
483 def test_wrong_reservation(self, config):486 def test_wrong_config(self, config, invalid_config_key):
484 """Test wrong reservation value.487 """Test wrong configuration value.
485
486 Expect that is_config_valid() return false
487 """
488 config.return_value = {
489 "reservation": "wrong",
490 "raid-autodetection": "",
491 "governor": ""
492 }
493 sysh = lib_sysconfig.SysConfigHelper()
494 assert not sysh.is_config_valid()
495
496 @mock.patch("lib_sysconfig.hookenv.config")
497 def test_wrong_raid(self, config):
498 """Test wrong raid autodetection value.
499
500 Expect that is_config_valid() return false
501 """
502 config.return_value = {
503 "reservation": "off",
504 "raid-autodetection": "wrong",
505 "governor": ""
506 }
507 sysh = lib_sysconfig.SysConfigHelper()
508 assert not sysh.is_config_valid()
509
510 @mock.patch("lib_sysconfig.hookenv.config")
511 def test_wrong_governor(self, config):
512 """Test wrong governor value.
513488
514 Expect that is_config_valid() return false489 Expect that is_config_valid() return false
515 """490 """
516 config.return_value = {491 return_value = {
517 "reservation": "off",492 "reservation": "off",
518 "raid-autodetection": "",493 "raid-autodetection": "",
519 "governor": "wrong"494 "governor": "",
495 "resolved-cache-mode": "",
496 invalid_config_key: 'wrong', # Will override the selected key with an invalid value
520 }497 }
498 config.return_value = return_value
521 sysh = lib_sysconfig.SysConfigHelper()499 sysh = lib_sysconfig.SysConfigHelper()
522 assert not sysh.is_config_valid()500 assert not sysh.is_config_valid()
523501
@@ -530,3 +508,32 @@ class TestLib:
530 sysh = lib_sysconfig.SysConfigHelper()508 sysh = lib_sysconfig.SysConfigHelper()
531509
532 assert sysh.enable_container510 assert sysh.enable_container
511
512 @mock.patch("lib_sysconfig.any_file_changed")
513 @mock.patch("lib_sysconfig.hookenv.config")
514 @mock.patch("lib_sysconfig.host.service_restart")
515 @mock.patch("lib_sysconfig.render")
516 def test_update_resolved_file_unchanged(self, render, restart, config, file_changed):
517 file_changed.return_value = True
518 self._test_update_resolved_common(render, config)
519 restart.assert_not_called()
520
521 @mock.patch("lib_sysconfig.any_file_changed")
522 @mock.patch("lib_sysconfig.hookenv.config")
523 @mock.patch("lib_sysconfig.host.service_restart")
524 @mock.patch("lib_sysconfig.render")
525 def test_update_resolved_file_changed(self, render, restart, config, file_changed):
526 file_changed.return_value = True
527 self._test_update_resolved_common(render, config)
528 restart.assert_called()
529
530 def _test_update_resolved_common(self, render, config):
531 config.return_value = {"resolved-cache-mode": "no-negative"}
532 sysh = lib_sysconfig.SysConfigHelper()
533 sysh.update_systemd_resolved()
534 render.assert_called_with(
535 source=lib_sysconfig.SYSTEMD_RESOLVED_TMPL,
536 target=lib_sysconfig.SYSTEMD_RESOLVED,
537 templates_dir="templates",
538 context={"cache": "no-negative"},
539 )

Subscribers

People subscribed via source and target branches

to all changes: