Merge lp:~hloeung/ubuntu-repository-cache/monitor-path-to-synchost into lp:ubuntu-repository-cache

Proposed by Haw Loeung
Status: Merged
Approved by: Haw Loeung
Approved revision: 378
Merged at revision: 364
Proposed branch: lp:~hloeung/ubuntu-repository-cache/monitor-path-to-synchost
Merge into: lp:ubuntu-repository-cache
Diff against target: 292 lines (+212/-1)
5 files modified
config.yaml (+5/-0)
files/systemd/mtr-synchost.timer (+12/-0)
reactive/ubuntu_repository_cache.py (+54/-0)
templates/systemd/mtr-synchost.service (+15/-0)
tests/unit/test_ubuntu_repository_cache.py (+126/-1)
To merge this branch: bzr merge lp:~hloeung/ubuntu-repository-cache/monitor-path-to-synchost
Reviewer Review Type Date Requested Status
Thomas Cuthbert (community) Approve
Canonical IS Reviewers Pending
Review via email: mp+427705@code.launchpad.net

Commit message

Enable option to capture and monitor path to the sync host

This is useful for debugging reachability issues with specific vendor/provider/region.

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.

367. By Haw Loeung

Fix errors 'Executable path is not absolute' and 'Service lacks both ExecStart= and ExecStop= setting.'

368. By Haw Loeung

Fixed the unit test too

369. By Haw Loeung

Bump interval to 5mins at first, we can reduce it further if needed

370. By Haw Loeung

Fixed to use tmpdir

Revision history for this message
Thomas Cuthbert (tcuthbert) wrote :

looking good! can you update the commit message as to why this change is happening? rest of my comments are inline.

review: Needs Fixing
Revision history for this message
Haw Loeung (hloeung) :
371. By Haw Loeung

Fixed based on review

372. By Haw Loeung

Allow disabling of monitoring of path to the sync-host

373. By Haw Loeung

Make it disabled by default

Revision history for this message
Haw Loeung (hloeung) wrote :

Commit message updated.

374. By Haw Loeung

Fixed based on review

375. By Haw Loeung

We can't use DynamicUser as that's not supported in the systemd version shipped with Xenial

376. By Haw Loeung

Using one isn't enough to reveal some of the hops likely due to ICMP rate limiting

377. By Haw Loeung

Bump count / report cycles some more

Revision history for this message
Thomas Cuthbert (tcuthbert) wrote :

LGTM +1 with a comment inline.

review: Approve
378. By Haw Loeung

7 looks like a good balance with enough to reach archive.u.c during testing from AWS.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 364

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config.yaml'
2--- config.yaml 2022-07-25 00:51:24 +0000
3+++ config.yaml 2022-08-04 02:35:54 +0000
4@@ -198,6 +198,11 @@
5 This endpoint will check both metadata served from local storage as well
6 as pool files via the locally running squid-deb-proxy service.
7 default: True
8+ enable_sync-host_monitoring:
9+ type: boolean
10+ description: |
11+ Enable periodic mtr outputs to configured sync-host
12+ default: False
13 leader_unit:
14 type: string
15 default: ''
16
17=== added directory 'files/systemd'
18=== added file 'files/systemd/mtr-synchost.timer'
19--- files/systemd/mtr-synchost.timer 1970-01-01 00:00:00 +0000
20+++ files/systemd/mtr-synchost.timer 2022-08-04 02:35:54 +0000
21@@ -0,0 +1,12 @@
22+[Unit]
23+Description=Monitor path to sync host regularly
24+
25+[Timer]
26+Unit=mtr-synchost.service
27+OnActiveSec=5min
28+OnUnitActiveSec=5min
29+RemainAfterElapse=no
30+Persistent=false
31+
32+[Install]
33+WantedBy=timers.target
34
35=== modified file 'reactive/ubuntu_repository_cache.py'
36--- reactive/ubuntu_repository_cache.py 2022-08-01 01:24:02 +0000
37+++ reactive/ubuntu_repository_cache.py 2022-08-04 02:35:54 +0000
38@@ -10,6 +10,8 @@
39 import shutil
40 import subprocess
41
42+import jinja2
43+
44 from charms import reactive
45 from charms.layer import status
46 from charmhelpers.core import hookenv, host, unitdata
47@@ -146,6 +148,8 @@
48 reactive.clear_flag('ubuntu-repository-cache.configured')
49 reactive.clear_flag('nagios-nrpe.configured')
50
51+ reactive.set_flag('ubuntu-repository-cache.update-systemd-mtr-required')
52+
53
54 @reactive.hook('start')
55 def start():
56@@ -320,6 +324,50 @@
57 update_website_relation_data()
58
59
60+@reactive.when_any('config.changed.sync_host', 'config.changed.enable_sync-host_monitoring')
61+def update_systemd_mtr_required():
62+ reactive.set_flag('ubuntu-repository-cache.update-systemd-mtr-required')
63+
64+
65+@reactive.when('ubuntu-repository-cache.update-systemd-mtr-required')
66+def update_systemd_mtr(etc_systemd_path='/etc/systemd/system'):
67+ config = hookenv.config()
68+ reactive.clear_flag('ubuntu-repository-cache.active')
69+
70+ mtr_service = os.path.join(etc_systemd_path, 'mtr-synchost.service')
71+ mtr_timer = os.path.join(etc_systemd_path, 'mtr-synchost.timer')
72+
73+ if config['enable_sync-host_monitoring']:
74+ status.maintenance('Updating systemd mtr timer & service with sync-host {}'.format(config['sync-host']))
75+
76+ changed = False
77+
78+ base = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
79+ env = jinja2.Environment(loader=jinja2.FileSystemLoader(base), keep_trailing_newline=True)
80+ template = env.get_template('templates/systemd/mtr-synchost.service')
81+ contents = template.render({'sync_host': config['sync-host']})
82+ changed = _write_file(contents, mtr_service) or changed
83+ changed = _copy_file('files/systemd/mtr-synchost.timer', mtr_timer) or changed
84+
85+ if changed:
86+ status.maintenance('Calling systemctl daemon-reload')
87+ subprocess.call(['systemctl', 'daemon-reload'])
88+ subprocess.call(['systemctl', 'enable', '--now', 'mtr-synchost.timer'])
89+
90+ else:
91+ if os.path.exists(mtr_service) or os.path.exists(mtr_timer):
92+ status.maintenance('Disabling systemd mtr timer & service with sync-host {}'.format(config['sync-host']))
93+ if os.path.exists(mtr_service):
94+ subprocess.call(['systemctl', 'disable', '--now', 'mtr-synchost.service'])
95+ os.unlink(mtr_service)
96+ if os.path.exists(mtr_timer):
97+ subprocess.call(['systemctl', 'disable', '--now', 'mtr-synchost.timer'])
98+ os.unlink(mtr_timer)
99+ subprocess.call(['systemctl', 'daemon-reload'])
100+
101+ reactive.clear_flag('ubuntu-repository-cache.update-systemd-mtr-required')
102+
103+
104 # LP:1770071: Work around charm-helper's unison ownership
105 # https://github.com/juju/charm-helpers/issues/487
106 def _fix_ssh_ownership(user='www-sync'):
107@@ -331,6 +379,12 @@
108 os.chown(fname, uid, -1)
109
110
111+def _copy_file(source_path, dest_path, **kwargs):
112+ with open(source_path, 'r') as f:
113+ source = f.read()
114+ return _write_file(source, dest_path, **kwargs)
115+
116+
117 def _write_file(source, dest_path, perms=0o644, owner=None, group=None):
118 """Write file only on changes and return True if changes written."""
119 # Compare and only write out file on change.
120
121=== added directory 'templates/systemd'
122=== added file 'templates/systemd/mtr-synchost.service'
123--- templates/systemd/mtr-synchost.service 1970-01-01 00:00:00 +0000
124+++ templates/systemd/mtr-synchost.service 2022-08-04 02:35:54 +0000
125@@ -0,0 +1,15 @@
126+[Unit]
127+Description=Monitor path to sync host regularly
128+Wants=network-online.target
129+After=network-online.target
130+
131+[Service]
132+Type=simple
133+ExecStart=/usr/bin/mtr -z -c7 --tcp -P 80 --report-wide {{sync_host}}
134+ExecStop=
135+User=nobody
136+Restart=on-failure
137+TimeoutSec=5m
138+
139+[Install]
140+WantedBy=multi-user.target
141
142=== modified file 'tests/unit/test_ubuntu_repository_cache.py'
143--- tests/unit/test_ubuntu_repository_cache.py 2022-08-01 01:24:02 +0000
144+++ tests/unit/test_ubuntu_repository_cache.py 2022-08-04 02:35:54 +0000
145@@ -48,7 +48,10 @@
146 patcher = mock.patch('charmhelpers.core.hookenv.config')
147 self.mock_config = patcher.start()
148 self.addCleanup(patcher.stop)
149- self.mock_config.return_value = {'nagios_context': 'juju'}
150+ self.mock_config.return_value = {
151+ 'nagios_context': 'juju',
152+ 'sync-host': 'archive.ubuntu.com',
153+ }
154
155 patcher = mock.patch('charmhelpers.core.host.log')
156 self.mock_log = patcher.start()
157@@ -62,6 +65,19 @@
158 if os.path.exists('.juju-persistent-config'):
159 os.unlink('.juju-persistent-config')
160
161+ @mock.patch('charms.reactive.clear_flag')
162+ @mock.patch('charms.reactive.set_flag')
163+ def test_upgrade_charm(self, set_flag, clear_flag):
164+ ubuntu_repository_cache.upgrade_charm()
165+ status.maintenance.assert_called_once_with('Upgrading ubuntu-repository-cache')
166+ want = [
167+ mock.call('ubuntu-repository-cache.active'),
168+ mock.call('ubuntu-repository-cache.configured'),
169+ mock.call('nagios-nrpe.configured'),
170+ ]
171+ clear_flag.assert_has_calls(want, any_order=True)
172+ set_flag.assert_called_once_with('ubuntu-repository-cache.update-systemd-mtr-required')
173+
174 @mock.patch('charmhelpers.core.hookenv.leader_get')
175 @mock.patch('charms.reactive.set_flag')
176 def test_set_active(self, set_flag, leader_get):
177@@ -113,6 +129,115 @@
178 ubuntu_repository_cache.set_active(os.path.join(self.charm_dir, 'tests/unit/files/version5'))
179 status.active.assert_called_once_with('Ready (source version/commit commit-id)')
180
181+ @mock.patch('charms.reactive.set_flag')
182+ def test_update_systemd_mtr_required(self, set_flag):
183+ ubuntu_repository_cache.update_systemd_mtr_required()
184+ set_flag.assert_called_once_with('ubuntu-repository-cache.update-systemd-mtr-required')
185+
186+ @mock.patch('charms.reactive.clear_flag')
187+ @mock.patch('subprocess.call')
188+ def test_update_systemd_mtr(self, call, clear_flag):
189+ self.mock_config.return_value['enable_sync-host_monitoring'] = True
190+ ubuntu_repository_cache.update_systemd_mtr(self.tmpdir)
191+ want = [
192+ mock.call('ubuntu-repository-cache.active'),
193+ mock.call('ubuntu-repository-cache.update-systemd-mtr-required'),
194+ ]
195+ clear_flag.assert_has_calls(want, any_order=True)
196+ want = [
197+ mock.call(['systemctl', 'daemon-reload']),
198+ mock.call(['systemctl', 'enable', '--now', 'mtr-synchost.timer']),
199+ ]
200+ call.assert_has_calls(want, any_order=True)
201+
202+ want = '''[Unit]
203+Description=Monitor path to sync host regularly
204+Wants=network-online.target
205+After=network-online.target
206+
207+[Service]
208+Type=simple
209+ExecStart=/usr/bin/mtr -z -c7 --tcp -P 80 --report-wide archive.ubuntu.com
210+ExecStop=
211+User=nobody
212+Restart=on-failure
213+TimeoutSec=5m
214+
215+[Install]
216+WantedBy=multi-user.target
217+''' # NOQA: E501
218+ with open(os.path.join(self.tmpdir, 'mtr-synchost.service'), 'r', encoding='utf-8') as f:
219+ got = f.read()
220+ self.assertEqual(want, got)
221+
222+ with open('files/systemd/mtr-synchost.timer', 'r', encoding='utf-8') as f:
223+ want = f.read()
224+ with open(os.path.join(self.tmpdir, 'mtr-synchost.timer'), 'r', encoding='utf-8') as f:
225+ got = f.read()
226+ self.assertEqual(want, got)
227+
228+ call.reset_mock()
229+ ubuntu_repository_cache.update_systemd_mtr(self.tmpdir)
230+ call.assert_not_called()
231+
232+ call.reset_mock()
233+ self.mock_config.return_value['enable_sync-host_monitoring'] = False
234+ ubuntu_repository_cache.update_systemd_mtr(self.tmpdir)
235+ want = [
236+ mock.call('ubuntu-repository-cache.active'),
237+ mock.call('ubuntu-repository-cache.update-systemd-mtr-required'),
238+ ]
239+ clear_flag.assert_has_calls(want, any_order=True)
240+ want = [
241+ mock.call(['systemctl', 'disable', '--now', 'mtr-synchost.service']),
242+ mock.call(['systemctl', 'disable', '--now', 'mtr-synchost.timer']),
243+ mock.call(['systemctl', 'daemon-reload']),
244+ ]
245+ call.assert_has_calls(want, any_order=True)
246+
247+ call.reset_mock()
248+ self.mock_config.return_value['enable_sync-host_monitoring'] = False
249+ ubuntu_repository_cache.update_systemd_mtr(self.tmpdir)
250+ call.assert_not_called()
251+
252+ call.reset_mock()
253+ with open(os.path.join(self.tmpdir, 'mtr-synchost.service'), 'w') as f:
254+ f.write("Testing")
255+ self.mock_config.return_value['enable_sync-host_monitoring'] = False
256+ ubuntu_repository_cache.update_systemd_mtr(self.tmpdir)
257+ want = [
258+ mock.call(['systemctl', 'disable', '--now', 'mtr-synchost.service']),
259+ mock.call(['systemctl', 'daemon-reload']),
260+ ]
261+ call.assert_has_calls(want, any_order=True)
262+
263+ call.reset_mock()
264+ self.mock_config.return_value['enable_sync-host_monitoring'] = False
265+ with open(os.path.join(self.tmpdir, 'mtr-synchost.timer'), 'w') as f:
266+ f.write("Testing")
267+ ubuntu_repository_cache.update_systemd_mtr(self.tmpdir)
268+ ubuntu_repository_cache.update_systemd_mtr(self.tmpdir)
269+ want = [
270+ mock.call(['systemctl', 'disable', '--now', 'mtr-synchost.timer']),
271+ mock.call(['systemctl', 'daemon-reload']),
272+ ]
273+ call.assert_has_calls(want, any_order=True)
274+
275+ def test__copy_file(self):
276+ source = os.path.join(self.charm_dir, 'metadata.yaml')
277+ dest = os.path.join(self.tmpdir, os.path.basename(source))
278+
279+ self.assertTrue(ubuntu_repository_cache._copy_file(source, dest))
280+ # Write again, should return False and not True per above.
281+ self.assertFalse(ubuntu_repository_cache._copy_file(source, dest))
282+
283+ # Check contents
284+ with open(source, 'r') as f:
285+ want = f.read()
286+ with open(dest, 'r') as f:
287+ got = f.read()
288+ self.assertEqual(got, want)
289+
290 def test__write_file(self):
291 source = '# User-provided config added here'
292 dest = os.path.join(self.tmpdir, 'my-test-file')

Subscribers

People subscribed via source and target branches