Merge lp:~hloeung/ubuntu-repository-cache/system-mail-admin into lp:ubuntu-repository-cache

Proposed by Haw Loeung
Status: Merged
Approved by: Haw Loeung
Approved revision: 357
Merged at revision: 354
Proposed branch: lp:~hloeung/ubuntu-repository-cache/system-mail-admin
Merge into: lp:ubuntu-repository-cache
Prerequisite: lp:~hloeung/ubuntu-repository-cache/maintain-xenial-support
Diff against target: 202 lines (+145/-0)
4 files modified
config.yaml (+5/-0)
layer.yaml (+1/-0)
reactive/ubuntu_repository_cache.py (+53/-0)
tests/unit/test_ubuntu_repository_cache.py (+86/-0)
To merge this branch: bzr merge lp:~hloeung/ubuntu-repository-cache/system-mail-admin
Reviewer Review Type Date Requested Status
Thomas Cuthbert (community) Approve
Canonical IS Reviewers Pending
Review via email: mp+427338@code.launchpad.net

Commit message

Allow specifying admin email for various system/root emails such as cron metadata sync failures

Description of the change

These are all all taken from the smtp-relay charm.

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
Thomas Cuthbert (tcuthbert) wrote :

LGTM +1

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

Change successfully merged at revision 354

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-22 05:24:57 +0000
3+++ config.yaml 2022-07-25 00:20:37 +0000
4@@ -1,4 +1,9 @@
5 options:
6+ admin_email:
7+ default: ""
8+ type: string
9+ description: |
10+ Email to use to send mail.
11 sync-host:
12 default: archive.ubuntu.com
13 type: string
14
15=== modified file 'layer.yaml'
16--- layer.yaml 2021-10-04 22:14:51 +0000
17+++ layer.yaml 2022-07-25 00:20:37 +0000
18@@ -6,6 +6,7 @@
19 options:
20 basic:
21 packages:
22+ - postfix
23 - run-one
24 - w3m
25 # python3-apt isn't available via pip so we'll need to use system packages
26
27=== modified file 'reactive/ubuntu_repository_cache.py'
28--- reactive/ubuntu_repository_cache.py 2021-05-25 02:25:00 +0000
29+++ reactive/ubuntu_repository_cache.py 2022-07-25 00:20:37 +0000
30@@ -4,9 +4,11 @@
31 This 'hooks' source constitutes all of the code for handle charm hooks.
32 '''
33
34+import grp
35 import os
36 import pwd
37 import shutil
38+import subprocess
39
40 from charms import reactive
41 from charms.layer import status
42@@ -73,6 +75,9 @@
43 # restart/reload apache at the same time.
44 shutil.copy2('files/cron_random_sleep.sh', '/etc/cron.daily/000-cron-random-sleep')
45
46+ config = hookenv.config()
47+ _update_aliases(config['admin_email'])
48+
49 reactive.set_flag('ubuntu-repository-cache.configured')
50
51
52@@ -314,3 +319,51 @@
53 fname = os.path.join(ssh_dir, f)
54 if os.path.exists(fname):
55 os.chown(fname, uid, -1)
56+
57+
58+def _write_file(source, dest_path, perms=0o644, owner=None, group=None):
59+ """Write file only on changes and return True if changes written."""
60+ # Compare and only write out file on change.
61+ dest = ''
62+
63+ try:
64+ with open(dest_path, 'r') as f:
65+ dest = f.read()
66+ if source == dest:
67+ return False
68+ except FileNotFoundError:
69+ pass
70+
71+ if owner is None:
72+ owner = pwd.getpwuid(os.getuid()).pw_name
73+ if group is None:
74+ group = grp.getgrgid(pwd.getpwnam(owner).pw_gid).gr_name
75+
76+ host.write_file(path=dest_path + '.new', content=source, perms=perms, owner=owner, group=group)
77+ os.rename(dest_path + '.new', dest_path)
78+ return True
79+
80+
81+def _update_aliases(admin_email='', aliases_path='/etc/aliases'):
82+
83+ aliases = []
84+ try:
85+ with open(aliases_path, 'r') as f:
86+ aliases = f.readlines()
87+ except FileNotFoundError:
88+ pass
89+
90+ new_aliases = []
91+ for line in aliases:
92+ if line.startswith('root:'):
93+ continue
94+ new_aliases.append(line)
95+
96+ if admin_email:
97+ new_aliases.append('root: {}\n'.format(admin_email))
98+
99+ changed = _write_file(''.join(new_aliases), aliases_path)
100+ if changed:
101+ subprocess.call(['newaliases'])
102+
103+ return
104
105=== modified file 'tests/unit/test_ubuntu_repository_cache.py'
106--- tests/unit/test_ubuntu_repository_cache.py 2021-03-08 20:55:34 +0000
107+++ tests/unit/test_ubuntu_repository_cache.py 2022-07-25 00:20:37 +0000
108@@ -1,4 +1,6 @@
109+import grp
110 import os
111+import pwd
112 import shutil
113 import sys
114 import tempfile
115@@ -87,3 +89,87 @@
116 status.active.reset_mock()
117 ubuntu_repository_cache.set_active(os.path.join(self.charm_dir, 'tests/unit/files/version5'))
118 status.active.assert_called_once_with('Ready (source version/commit commit-id)')
119+
120+ def test__write_file(self):
121+ source = '# User-provided config added here'
122+ dest = os.path.join(self.tmpdir, 'my-test-file')
123+
124+ self.assertTrue(ubuntu_repository_cache._write_file(source, dest))
125+ # Write again, should return False and not True per above.
126+ self.assertFalse(ubuntu_repository_cache._write_file(source, dest))
127+
128+ # Check contents
129+ with open(dest, 'r') as f:
130+ got = f.read()
131+ self.assertEqual(got, source)
132+
133+ @mock.patch('charmhelpers.core.host.write_file')
134+ @mock.patch('os.rename')
135+ def test__write_file_owner_group(self, rename, write_file):
136+ source = '# User-provided config added here'
137+ dest = os.path.join(self.tmpdir, 'my-test-file')
138+
139+ self.assertTrue(ubuntu_repository_cache._write_file(source, dest, owner='root', group='root'))
140+ want = [mock.call(path=dest + '.new', content=source, perms=420, owner='root', group='root')]
141+ write_file.assert_has_calls(want, any_order=True)
142+ self.assertEqual(len(want), len(write_file.mock_calls))
143+
144+ write_file.reset_mock()
145+ with mock.patch('builtins.open', side_effect=FileNotFoundError):
146+ ubuntu_repository_cache._write_file(source, dest, owner='root', group='root')
147+ want = [mock.call(path=dest + '.new', content=source, perms=420, owner='root', group='root')]
148+ write_file.assert_has_calls(want, any_order=True)
149+ self.assertEqual(len(want), len(write_file.mock_calls))
150+
151+ write_file.reset_mock()
152+ with mock.patch('builtins.open', side_effect=FileNotFoundError):
153+ ubuntu_repository_cache._write_file(source, dest)
154+ current_usr = pwd.getpwuid(os.getuid()).pw_name
155+ current_grp = grp.getgrgid(pwd.getpwnam(current_usr).pw_gid).gr_name
156+ want = [mock.call(path=dest + '.new', content=source, perms=420, owner=current_usr, group=current_grp)]
157+ write_file.assert_has_calls(want, any_order=True)
158+ self.assertEqual(len(want), len(write_file.mock_calls))
159+
160+ write_file.reset_mock()
161+ with mock.patch('builtins.open', side_effect=FileNotFoundError):
162+ ubuntu_repository_cache._write_file(source, dest, owner='nobody')
163+ current_grp = grp.getgrgid(pwd.getpwnam('nobody').pw_gid).gr_name
164+ want = [mock.call(path=dest + '.new', content=source, perms=420, owner='nobody', group=current_grp)]
165+ write_file.assert_has_calls(want, any_order=True)
166+ self.assertEqual(len(want), len(write_file.mock_calls))
167+
168+ @mock.patch('subprocess.call')
169+ def test__update_aliases(self, call):
170+ dest = os.path.join(self.tmpdir, 'aliases')
171+
172+ # Empty, does not exist.
173+ ubuntu_repository_cache._update_aliases('', dest)
174+ with open(dest, 'r', encoding='utf-8') as f:
175+ got = f.read()
176+ want = ''
177+ self.assertEqual(want, got)
178+ call.assert_called_with(['newaliases'])
179+
180+ # Admin email set.
181+ call.reset_mock()
182+ content = 'postmaster: root\n'
183+ with open(dest, 'w') as f:
184+ f.write(content)
185+ ubuntu_repository_cache._update_aliases('root@admin.mydomain.local', dest)
186+ want = 'postmaster: root\nroot: root@admin.mydomain.local\n'
187+ with open(dest, 'r', encoding='utf-8') as f:
188+ got = f.read()
189+ self.assertEqual(want, got)
190+ call.assert_called_with(['newaliases'])
191+
192+ # Has admin email, so do nothing and do not call newaliases.
193+ call.reset_mock()
194+ content = 'postmaster: root\nroot: root@admin.mydomain.local\n'
195+ with open(dest, 'w') as f:
196+ f.write(content)
197+ ubuntu_repository_cache._update_aliases('root@admin.mydomain.local', dest)
198+ want = content
199+ with open(dest, 'r', encoding='utf-8') as f:
200+ got = f.read()
201+ self.assertEqual(want, got)
202+ call.assert_not_called()

Subscribers

People subscribed via source and target branches