Merge ~lamoura/ubuntu/+source/update-notifier:update-notifier-esm-support into ubuntu/+source/update-notifier:ubuntu/xenial-devel

Proposed by Lucas Albuquerque Medeiros de Moura
Status: Merged
Approved by: Bryce Harrington
Approved revision: c594b517053ba68ae34aab5ea9b1ee9a50755512
Merged at revision: 0b7f4623663af8a03c5217cf78c58e7d73a605cd
Proposed branch: ~lamoura/ubuntu/+source/update-notifier:update-notifier-esm-support
Merge into: ubuntu/+source/update-notifier:ubuntu/xenial-devel
Diff against target: 903 lines (+533/-149)
4 files modified
data/apt_check.py (+195/-82)
debian/changelog (+15/-0)
debian/control (+2/-0)
tests/test_motd.py (+321/-67)
Reviewer Review Type Date Requested Status
Bryce Harrington (community) Approve
Review via email: mp+401473@code.launchpad.net

Description of the change

Currently, the apt-check script is configured to only handle package count for ESM Infra. We are now updating the logic to also handle ESM Apps packages as well.

Furthermore, we are also updating the messaging that is created in apt-check. We are advertising ESM Apps if the service is disabled and only messaging about ESM Infra if the distro is already on ESM mode.

Finally, this PR also fixes LP #1883315, since we now check to see if the system has esm-infra or esm-apps before performing a package count for package with esm origins

To post a comment you must log in.
Revision history for this message
Lucas Albuquerque Medeiros de Moura (lamoura) wrote :

This is the result of running autopkgtest on the xenial package uploaded to:
https://launchpad.net/~lamoura/+archive/ubuntu/update-notifier-test-ppa/

----------------------
autopkgtest [10:59:37]: test nose-tests: [-----------------------
Get:1 http://localhost:46825/canary-file.txt [4 B]
Fetched 4 B in 0s (0 B/s)
..........Get:1 http://localhost:23274/canary-file.txt [4 B]
Fetched 4 B in 0s (0 B/s)
.Get:1 http://localhost:8629/canary-file.txt [4 B]
Fetched 4 B in 0s (0 B/s)
....Get:1 http://localhost:10077/canary-file.txt [4 B]
Fetched 4 B in 0s (0 B/s)
..
----------------------------------------------------------------------
Ran 17 tests in 2.312s

OK
autopkgtest [10:59:40]: test nose-tests: -----------------------]
autopkgtest [10:59:41]: test nose-tests: - - - - - - - - - - results - - - - - - - - - -
nose-tests PASS
autopkgtest [10:59:41]: @@@@@@@@@@@@@@@@@@@@ summary
nose-tests PASS
-------------------------------

I have redacted the output here because of the number of lines. But you can reproduce the command running:

autopkgtest -U ../out/update-notifier_3.168.14.dsc -- lxd ubuntu-daily:xenial

Revision history for this message
Lucas Albuquerque Medeiros de Moura (lamoura) wrote :

Also, I have uploaded a test script that can be used here in the SRU bug for this release;
https://bugs.launchpad.net/ubuntu/+source/update-notifier/+bug/1924766

Revision history for this message
Bryce Harrington (bryce) wrote :

Hi Lucas,

I'm still in the middle of reviewing but wanted to give some early feedback on the SRU bug report, you can consider working on in the interim.

I see this is aiming to be an SRU to xenial, that fixes two bugs. In general, the SRU team will want to see SRU templates on both referenced bugs; it looks to me like in this case that would be appropriate, so I'd suggest filling out LP: #1883315 with impact/test case/regression etc.

In both bug reports, for the [Where problems could occur], I like to think from the context of if after some user upgrades to this release and encounter a bug, how could they roughly determine if their issue might or might not relate to this change? IOW, what kinds of problems would you want to look at as perhaps having caused? In this case, in addition to what you wrote about unexpected oddnesses in the MOTD text, I would also consider if a crash (python stack trace) showed up in the log files for whatever triggers apt-check.py. Obviously also erroneous displays of ESM update availability or counts would also be worth consideration.

I notice that LP: #1924766 targeted xenial and -devel, but 1883315 targets only -devel. I've updated both bug reports to have tags for all the other currently supported releases. If the bug does not exist in some or all of those releases, has already been fixed, or is not going to be fixed for some reason, then those bug tasks can be marked Invalid, Fix Released, or WontFix, accordingly; this will communicate status to the SRU team and other bug reviewers. I did not add a bug task for trusty, as I'm not sure what the support status for ESM update-notifier is there, but we can also add that if it's at all relevant. You should also add yourself as assignee for the Xenial bug tasks for each bug report, and optionally add yourself for any other releases you plan on also SRUing.

Revision history for this message
Bryce Harrington (bryce) wrote :

Here's part 2 of my review, this one is focusing on the code and test changes.

I'm assuming this has already gone through a peer code review process, so am assuming the overall logic has been thoroughly doublechecked. Adding the two logical paths makes a bit of a state machine here, so the logic is starting to get a bit complex. However, the new test cases look like they're well thought out and thorough. I read through and spot checked code but found no obvious errors.

If we ever get to a point where a third path needs fitted in (like a third service type, or splitting stable/edge apps or some such), it may be worth moving to a class/subclass abstraction. Some of this logic feels like it might be easier to write tests for if it was encapsulated in a class object.

One minor thing I would recommend as 'needs fixing' is adding code docs to the new helper functions being added. The new is_* and have_* routines are short and simple so the docs should be easy to add as well.

review: Needs Fixing
Revision history for this message
Bryce Harrington (bryce) wrote :

For part 3, looking at the packaging changes; in this case jus adding python3-distro-info as a dependency. I doublechecked this package is in all supported releases and LGTM.

The only other import change is unittest.mock in the test case. This is already listed in Build-Depends.

For part 4 I'll run the tests and verify the functionality, but as it's already getting late in my day I'll leave that to tackle tomorrow.

Revision history for this message
Lucas Albuquerque Medeiros de Moura (lamoura) wrote :

Hi Bryce, thank you for the review. I agree that if add more services in this script, create a class structure for them will be the best way going forward.

Additionally, I have added the code docs for the new methods.

Revision history for this message
Bryce Harrington (bryce) wrote :

Yesterday I ran through the tests and found the functionality seems to be in order. The updates look good.

$ debuild -S -uc -us -i
 dpkg-buildpackage -rfakeroot -d -us -uc -i -I.bzr -I.svn -I.git -S -i
dpkg-buildpackage: source package update-notifier
dpkg-buildpackage: source version 3.168.14
dpkg-buildpackage: source distribution xenial
dpkg-buildpackage: source changed by Lucas Moura <email address hidden>
 dpkg-source -i -I.bzr -I.svn -I.git -i --before-build update-notifier-gu
 fakeroot debian/rules clean
dh clean --with python3
   dh_testdir
   dh_auto_clean
   dh_clean
 dpkg-source -i -I.bzr -I.svn -I.git -i -b update-notifier-gu
dpkg-source: info: using source format '3.0 (native)'
dpkg-source: info: building update-notifier in update-notifier_3.168.14.tar.xz
dpkg-source: info: building update-notifier in update-notifier_3.168.14.dsc
 dpkg-genchanges -S >../update-notifier_3.168.14_source.changes
dpkg-genchanges: including full source code in upload
 dpkg-source -i -I.bzr -I.svn -I.git -i --after-build update-notifier-gu
dpkg-buildpackage: source-only upload: Debian-native package
Now running lintian...
W: update-notifier source: ancient-standards-version 3.9.4 (current is 3.9.7)
Finished running lintian.

$ debsign ../update-notifier_3.168.14_source.changes
 signfile dsc update-notifier_3.168.14.dsc A661100B3DAC1D4F2CAD8A54E603B2578FB8F0FB

 fixup_changes dsc update-notifier_3.168.14.dsc update-notifier_3.168.14_source.changes
 signfile changes update-notifier_3.168.14_source.changes A661100B3DAC1D4F2CAD8A54E603B2578FB8F0FB

Successfully signed dsc, changes files

$ dput ubuntu update-notifier_3.168.14_source.changes
Checking signature on .changes
gpg: /home/bryce/pkg/UpdateNotifier/review-mp401473/update-notifier_3.168.14_source.changes: Valid signature from E603B2578FB8F0FB
Checking signature on .dsc
gpg: /home/bryce/pkg/UpdateNotifier/review-mp401473/update-notifier_3.168.14.dsc: Valid signature from E603B2578FB8F0FB
Uploading to ubuntu (via ftp to upload.ubuntu.com):
  Uploading update-notifier_3.168.14.dsc: done.
  Uploading update-notifier_3.168.14.tar.xz: done.
  Uploading update-notifier_3.168.14_source.changes: done.
Successfully uploaded packages.

$ git ubuntu tag --upload
$ git push pkg upload/3.168.14
Counting objects: 13, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (13/13), done.
Writing objects: 100% (13/13), 4.35 KiB | 0 bytes/s, done.
Total 13 (delta 9), reused 0 (delta 0)
To ssh://<email address hidden>/ubuntu/+source/update-notifier
 * [new tag] upload/3.168.14 -> upload/3.168.14

review: Approve
Revision history for this message
Bryce Harrington (bryce) wrote :

There are some errors for a few of the dngettext() calls. This routine chooses between two strings based on singular vs. plural, but in a few places the plural form is being used for both. I suggest some edits to correct these inline. I think I caught them all but you should doublecheck.

It would probably be wise to also add test cases for the singular cases of each string.

review: Needs Fixing
Revision history for this message
Bryce Harrington (bryce) wrote :

Fixes look good, thanks, and I've verified the tests pass and did a local smoke test.

Tagged and uploaded for xenial:

triage-xenial+16.04:~/pkg/UpdateNotifier/review-mp401473$ dput -f ubuntu update-notifier_3.168.14_source.changes
Checking signature on .changes
gpg: Signature made Tue 27 Apr 2021 12:50:46 AM UTC using RSA key ID 8FB8F0FB
gpg: Good signature from "Bryce Harrington <email address hidden>"
gpg: aka "Bryce Harrington <email address hidden>"
gpg: aka "Bryce Harrington <email address hidden>"
Good signature on /home/bryce/pkg/UpdateNotifier/review-mp401473/update-notifier_3.168.14_source.changes.
Checking signature on .dsc
gpg: Signature made Tue 27 Apr 2021 12:50:46 AM UTC using RSA key ID 8FB8F0FB
gpg: Good signature from "Bryce Harrington <email address hidden>"
gpg: aka "Bryce Harrington <email address hidden>"
gpg: aka "Bryce Harrington <email address hidden>"
Good signature on /home/bryce/pkg/UpdateNotifier/review-mp401473/update-notifier_3.168.14.dsc.
Uploading to ubuntu (via ftp to upload.ubuntu.com):
  Uploading update-notifier_3.168.14.dsc: done.
  Uploading update-notifier_3.168.14.tar.xz: done.
  Uploading update-notifier_3.168.14_source.changes: done.
Successfully uploaded packages.

triage-xenial+16.04:~/pkg/UpdateNotifier/review-mp401473/update-notifier-gu$ git ubuntu tag -f --upload
/snap/git-ubuntu/current/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Updated tag 'upload/3.168.14' (was 1f8fcad)
triage-xenial+16.04:~/pkg/UpdateNotifier/review-mp401473/update-notifier-gu$ git push -f pkg upload/3.168.14
Counting objects: 17, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (17/17), done.
Writing objects: 100% (17/17), 2.97 KiB | 0 bytes/s, done.
Total 17 (delta 13), reused 0 (delta 0)
To ssh://<email address hidden>/ubuntu/+source/update-notifier
 + 1f8fcad...7474f3b upload/3.168.14 -> upload/3.168.14 (forced update)

I had to force push since the previous MP upload was already in place. I'm assuming the previous upload was pulled early enough we don't have to re-rev the version numbers; if not we'll know soon enough...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/data/apt_check.py b/data/apt_check.py
2index 28026a4..2f2bb3d 100755
3--- a/data/apt_check.py
4+++ b/data/apt_check.py
5@@ -10,13 +10,16 @@ import sys
6 from optparse import OptionParser
7 import gettext
8 import subprocess
9+import distro_info
10
11 SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences"
12 DISTRO = subprocess.check_output(
13 ["lsb_release", "-c", "-s"],
14 universal_newlines=True).strip()
15
16-ESM_ORIGINS = ("UbuntuESM", "UbuntuESMApps")
17+ESM_INFRA_ORIGIN = "UbuntuESM"
18+ESM_APPS_ORIGIN = "UbuntuESMApps"
19+ESM_ORIGINS = (ESM_INFRA_ORIGIN, ESM_APPS_ORIGIN)
20
21
22 def _(msg):
23@@ -47,8 +50,8 @@ def saveDistUpgrade(cache, depcache):
24 def isSecurityUpgrade(ver):
25 " check if the given version is a security update (or masks one) "
26 security_pockets = [("Ubuntu", "%s-security" % DISTRO),
27- ("UbuntuESM", "%s-infra-security" % DISTRO),
28- ("UbuntuESMApps", "%s-apps-security" % DISTRO),
29+ (ESM_INFRA_ORIGIN, "%s-infra-security" % DISTRO),
30+ (ESM_APPS_ORIGIN, "%s-apps-security" % DISTRO),
31 ("gNewSense", "%s-security" % DISTRO),
32 ("Debian", "%s-updates" % DISTRO)]
33 for (file, index) in ver.file_list:
34@@ -58,14 +61,24 @@ def isSecurityUpgrade(ver):
35 return False
36
37
38-def isESMUpgrade(ver):
39+def _isESMUpgrade(ver, esm_origin):
40 " check if the given version is a security update (or masks one) "
41 for (file, index) in ver.file_list:
42- if file.origin in ESM_ORIGINS and file.archive.startswith(DISTRO):
43+ if file.origin == esm_origin and file.archive.startswith(DISTRO):
44 return True
45 return False
46
47
48+def isESMAppsUpgrade(ver):
49+ " check if the given version is a ESM Apps upgrade "
50+ return _isESMUpgrade(ver, esm_origin=ESM_APPS_ORIGIN)
51+
52+
53+def isESMInfraUpgrade(ver):
54+ " check if the given version is a ESM Infra upgrade "
55+ return _isESMUpgrade(ver, esm_origin=ESM_INFRA_ORIGIN)
56+
57+
58 def write_package_names(outstream, cache, depcache):
59 " write out package names that change to outstream "
60 pkgs = [pkg for pkg in cache.packages if depcache.marked_install(pkg)
61@@ -73,11 +86,90 @@ def write_package_names(outstream, cache, depcache):
62 outstream.write("\n".join([p.name for p in pkgs]))
63
64
65+def is_esm_distro():
66+ " check if the current distro is ESM or not "
67+ ubuntu_distro = distro_info.UbuntuDistroInfo()
68+
69+ is_esm_supported = bool(
70+ DISTRO in ubuntu_distro.supported_esm()
71+ )
72+
73+ is_not_currently_supported = bool(
74+ DISTRO in ubuntu_distro.unsupported()
75+ )
76+
77+ return is_esm_supported and is_not_currently_supported
78+
79+
80+def is_lts_distro():
81+ " check if the current distro is LTS or not"
82+ return distro_info.UbuntuDistroInfo().is_lts(DISTRO)
83+
84+
85+def _output_esm_package_count(outstream, service_type, esm_pkg_count):
86+ " output the number of packages upgrades related to esm service "
87+ if esm_pkg_count > 0:
88+ outstream.write("\n")
89+ outstream.write(gettext.dngettext("update-notifier",
90+ "%d of these updates "
91+ "is an UA %s: ESM "
92+ "security update.",
93+ "%d of these updates "
94+ "are UA %s: ESM "
95+ "security updates.",
96+ esm_pkg_count) %
97+ (esm_pkg_count, service_type))
98+
99+
100+def _output_esm_package_alert(
101+ outstream, service_type, disabled_pkg_count
102+):
103+ " output the number of upgradable packages if esm service was enabled "
104+ outstream.write("\n")
105+ if disabled_pkg_count > 0:
106+ outstream.write("\n")
107+ outstream.write(gettext.dngettext("update-notifier",
108+ "%i additional security "
109+ "update can be applied "
110+ "with UA %s: ESM\nLearn "
111+ "more about enabling UA "
112+ "%s: ESM service at "
113+ "https://ubuntu.com/esm",
114+ "%i additional security "
115+ "updates can be applied "
116+ "with UA %s: ESM\nLearn "
117+ "more about enabling UA "
118+ "%s: ESM service at "
119+ "https://ubuntu.com/esm",
120+ disabled_pkg_count) %
121+ (disabled_pkg_count, service_type, service_type))
122+ else:
123+ outstream.write("\n")
124+ outstream.write(gettext.dgettext("update-notifier",
125+ "Enable UA %s: ESM to "
126+ "receive additional future "
127+ "security updates.") %
128+ service_type)
129+
130+ outstream.write("\n")
131+ outstream.write(
132+ gettext.dgettext("update-notifier",
133+ "See https://ubuntu.com/esm "
134+ "or run: sudo ua status")
135+ )
136+
137+
138 def write_human_readable_summary(outstream, upgrades, security_updates,
139- esm_updates, have_esm, disabled_esm_updates):
140+ esm_infra_updates, esm_apps_updates,
141+ have_esm_infra, have_esm_apps,
142+ disabled_esm_infra_updates,
143+ disabled_esm_apps_updates):
144+
145 " write out human summary summary to outstream "
146- if have_esm is not None:
147- if have_esm:
148+ esm_distro = is_esm_distro()
149+ lts_distro = is_lts_distro()
150+ if have_esm_infra is not None and esm_distro:
151+ if have_esm_infra:
152 outstream.write(gettext.dgettext("update-notifier",
153 "UA Infra: Extended "
154 "Security Maintenance (ESM) is "
155@@ -87,63 +179,51 @@ def write_human_readable_summary(outstream, upgrades, security_updates,
156 "UA Infra: Extended "
157 "Security Maintenance (ESM) is "
158 "not enabled."))
159- outstream.write("\n\n")
160-
161- outstream.write(gettext.dngettext("update-notifier",
162- "%i package can be updated.",
163- "%i packages can be updated.",
164- upgrades) %
165- upgrades)
166- outstream.write("\n")
167- if esm_updates > 0:
168+ if upgrades > 0:
169+ outstream.write("\n\n")
170+ if upgrades > 0:
171+ outstream.write(
172+ gettext.dngettext("update-notifier",
173+ "%i update can be applied immediately.",
174+ "%i updates can be applied immediately.",
175+ upgrades) % upgrades
176+ )
177+
178+ _output_esm_package_count(
179+ outstream, service_type="Infra", esm_pkg_count=esm_infra_updates)
180+ _output_esm_package_count(
181+ outstream, service_type="Apps", esm_pkg_count=esm_apps_updates)
182+
183+ if security_updates > 0:
184+ outstream.write("\n")
185 outstream.write(gettext.dngettext("update-notifier",
186- "%i of these updates is fixed "
187- "through UA Infra: ESM.",
188+ "%i of these updates is a "
189+ "standard security update.",
190 "%i of these updates are "
191- "fixed through UA "
192- "Infra: ESM.",
193- esm_updates) %
194- esm_updates)
195- outstream.write("\n")
196- outstream.write(gettext.dngettext("update-notifier",
197- "%i of these updates is a "
198- "security update.",
199- "%i of these updates are "
200- "security updates.",
201- security_updates) %
202- security_updates)
203- if upgrades > 0 or security_updates > 0 or esm_updates > 0:
204+ "standard security updates.",
205+ security_updates) %
206+ security_updates)
207+
208+ if any([upgrades, security_updates, esm_infra_updates, esm_apps_updates]):
209 outstream.write("\n")
210 outstream.write(gettext.dgettext("update-notifier",
211 "To see these additional updates "
212 "run: apt list --upgradable"))
213- if have_esm is not None and not have_esm:
214- outstream.write("\n")
215- if disabled_esm_updates > 0:
216- outstream.write("\n")
217- outstream.write(gettext.dngettext("update-notifier",
218- "Enable UA Infra: ESM "
219- "to receive %i additional "
220- "security update.",
221- "Enable UA Infra: ESM "
222- "to receive %i additional "
223- "security updates.",
224- disabled_esm_updates) %
225- disabled_esm_updates)
226- else:
227- outstream.write("\n")
228- outstream.write(gettext.dgettext("update-notifier",
229- "Enable UA Infra: ESM to "
230- "receive additional future "
231- "security updates."))
232- outstream.write("\n")
233- outstream.write(gettext.dgettext("update-notifier",
234- "See https://ubuntu.com/security/esm "
235- "or run: sudo ua status"))
236+
237+ if have_esm_apps is not None and not have_esm_apps and lts_distro:
238+ _output_esm_package_alert(
239+ outstream, service_type="Apps",
240+ disabled_pkg_count=disabled_esm_apps_updates)
241+
242+ if have_esm_infra is not None and not have_esm_infra and esm_distro:
243+ _output_esm_package_alert(
244+ outstream, service_type="Infra",
245+ disabled_pkg_count=disabled_esm_infra_updates)
246+
247 outstream.write("\n")
248
249
250-def has_disabled_esm_security_update(depcache, pkg):
251+def has_disabled_esm_security_update(depcache, pkg, esm_origin):
252 " check if we have a disabled ESM security update "
253 inst_ver = pkg.current_ver
254 if not inst_ver:
255@@ -154,12 +234,42 @@ def has_disabled_esm_security_update(depcache, pkg):
256 break
257
258 for (file, index) in ver.file_list:
259- if (file.origin in ESM_ORIGINS and file.archive.startswith(DISTRO)
260+ if (file.origin == esm_origin and file.archive.startswith(DISTRO)
261 and depcache.policy.get_priority(file) == -32768):
262 return True
263 return False
264
265
266+def has_disabled_esm_apps_security_update(depcache, pkg):
267+ " check if we have a disabled ESM Apps security update "
268+ return has_disabled_esm_security_update(depcache, pkg, ESM_APPS_ORIGIN)
269+
270+
271+def has_disabled_esm_infra_security_update(depcache, pkg):
272+ " check if we have a disabled ESM Infra security update "
273+ return has_disabled_esm_security_update(depcache, pkg, ESM_INFRA_ORIGIN)
274+
275+
276+def has_esm_service(cache, depcache, esm_origin):
277+ " check if we have an enabled ESM service in the machine "
278+ have_esm = None
279+ for file in cache.file_list:
280+ origin = file.origin
281+ if origin == esm_origin and file.archive.startswith(DISTRO):
282+ # In case of multiple ESM repos, one enabled is sufficient.
283+ if depcache.policy.get_priority(file) == -32768:
284+ # We found a disabled ESM repository, but we'll only count
285+ # ESM as disabled here if we have not found any other ESM
286+ # repo, so one ESM repo being enabled means ESM is enabled.
287+ if have_esm is None:
288+ have_esm = False
289+ else:
290+ have_esm = True
291+ break
292+
293+ return have_esm
294+
295+
296 def init():
297 " init the system, be nice "
298 # FIXME: do a ionice here too?
299@@ -202,31 +312,26 @@ def run(options=None):
300
301 # Check if we have ESM enabled or disabled; and if it exists in the
302 # first place.
303- have_esm = None # None == does not exist
304- for file in cache.file_list:
305- if file.origin in ESM_ORIGINS and file.archive.startswith(DISTRO):
306- # In case of multiple ESM repos, one enabled is sufficient.
307- if depcache.policy.get_priority(file) == -32768:
308- # We found a disabled ESM repository, but we'll only count
309- # ESM as disabled here if we have not found any other ESM
310- # repo, so one ESM repo being enabled means ESM is enabled.
311- if have_esm is None:
312- have_esm = False
313- else:
314- have_esm = True
315- break
316+ have_esm_infra = has_esm_service(
317+ cache, depcache, esm_origin=ESM_INFRA_ORIGIN)
318+ have_esm_apps = has_esm_service(
319+ cache, depcache, esm_origin=ESM_APPS_ORIGIN)
320
321 # analyze the ugprade
322 upgrades = 0
323 security_updates = 0
324- esm_updates = 0
325- disabled_esm_updates = 0
326+ esm_apps_updates = 0
327+ esm_infra_updates = 0
328+ disabled_esm_apps_updates = 0
329+ disabled_esm_infra_updates = 0
330
331 # we need another cache that has more pkg details
332 with apt.Cache() as aptcache:
333 for pkg in cache.packages:
334- if has_disabled_esm_security_update(depcache, pkg):
335- disabled_esm_updates += 1
336+ if has_disabled_esm_apps_security_update(depcache, pkg):
337+ disabled_esm_apps_updates += 1
338+ if has_disabled_esm_infra_security_update(depcache, pkg):
339+ disabled_esm_infra_updates += 1
340
341 # skip packages that are not marked upgraded/installed
342 if not (depcache.marked_install(pkg)
343@@ -240,10 +345,14 @@ def run(options=None):
344 continue
345 # check for security upgrades
346 if isSecurityUpgrade(cand_ver):
347- if isESMUpgrade(cand_ver):
348- esm_updates += 1
349+ if have_esm_apps and isESMAppsUpgrade(cand_ver):
350+ esm_apps_updates += 1
351+ elif have_esm_infra and isESMInfraUpgrade(cand_ver):
352+ esm_infra_updates += 1
353+ else:
354+ security_updates += 1
355+
356 upgrades += 1
357- security_updates += 1
358 continue
359
360 # check to see if the update is a phased one
361@@ -267,9 +376,11 @@ def run(options=None):
362 and apt_pkg.version_compare(ver.ver_str,
363 inst_ver.ver_str) <= 0):
364 continue
365- if isESMUpgrade(ver):
366- esm_updates += 1
367- if isSecurityUpgrade(ver):
368+ if have_esm_apps and isESMAppsUpgrade(cand_ver):
369+ esm_apps_updates += 1
370+ elif have_esm_infra and isESMInfraUpgrade(cand_ver):
371+ esm_infra_updates += 1
372+ elif isSecurityUpgrade(ver):
373 security_updates += 1
374 break
375
376@@ -278,8 +389,10 @@ def run(options=None):
377 write_package_names(sys.stderr, cache, depcache)
378 elif options and options.readable_output:
379 write_human_readable_summary(sys.stdout, upgrades, security_updates,
380- esm_updates, have_esm,
381- disabled_esm_updates)
382+ esm_infra_updates, esm_apps_updates,
383+ have_esm_infra, have_esm_apps,
384+ disabled_esm_infra_updates,
385+ disabled_esm_apps_updates)
386 else:
387 # print the number of regular upgrades and the number of
388 # security upgrades
389diff --git a/debian/changelog b/debian/changelog
390index 197b459..333a5b4 100644
391--- a/debian/changelog
392+++ b/debian/changelog
393@@ -1,3 +1,18 @@
394+update-notifier (3.168.14) xenial; urgency=medium
395+
396+ * data/apt_check.py:
397+ - Add support to handle packages from ESM Apps in addition to ESM Infra
398+ and only display alerts if the distro is ESM. (LP: #1924766)
399+ - Do not display a count of ESM packages if the system does not have ESM
400+ enabled. (LP: #1883315)
401+ - Make distinction betweem standard security updates and ESM updates
402+ when performing package counts. (LP: #1926208)
403+ - use 'applied' instead of 'installed', redact 0 of these updates are
404+ security updates, and correct singular messages
405+ * debian/control: Add a dependency on python3-distro-info.
406+
407+ -- Lucas Moura <lucas.moura@canonical.com> Tue, 20 Apr 2021 10:20:21 -0300
408+
409 update-notifier (3.168.13) xenial; urgency=medium
410
411 * Fix pep8 autopkgtest failures in the right control file (LP: #1906436)
412diff --git a/debian/control b/debian/control
413index a3b383a..1966167 100644
414--- a/debian/control
415+++ b/debian/control
416@@ -15,6 +15,7 @@ Build-Depends: debhelper (>= 7.0.50~),
417 python3,
418 python3-apt,
419 python3-debian,
420+ python3-distro-info,
421 python3-mock
422 Standards-Version: 3.9.4
423 Vcs-Bzr: http://bazaar.launchpad.net/~ubuntu-core-dev/update-notifier/ubuntu
424@@ -48,6 +49,7 @@ Depends: ${shlibs:Depends},
425 ${misc:Depends},
426 python3:any,
427 python3-apt, python3-dbus, python3-debian, debconf,
428+ python3-distro-info,
429 patch, update-manager-core (>= 1:16.04.6)
430 Recommends: libpam-modules (>= 1.0.1-9ubuntu3)
431 Suggests: policykit-1
432diff --git a/tests/test_motd.py b/tests/test_motd.py
433index a876937..335adce 100755
434--- a/tests/test_motd.py
435+++ b/tests/test_motd.py
436@@ -3,11 +3,11 @@
437
438 import apt_check
439 import io
440-import os
441-import subprocess
442 import unittest
443 import textwrap
444
445+from unittest import mock
446+
447
448 def get_message(*args, **kwds):
449 with io.StringIO() as stream:
450@@ -16,134 +16,388 @@ def get_message(*args, **kwds):
451
452
453 class TestMotd(unittest.TestCase):
454- """ ensure that the tree is pep8 clean """
455+ """ Validate /etc/motd text """
456
457- def test_esm_disabled_upto_date_esm_avail(self):
458+ @mock.patch("apt_check.is_lts_distro", return_value=True)
459+ @mock.patch("apt_check.is_esm_distro", return_value=True)
460+ def test_esm_infra_disabled_upto_date_esm_avail(
461+ self, _m_esm_distro, _m_is_lts
462+ ):
463 self.assertEqual(
464 get_message(upgrades=0, security_updates=0,
465- esm_updates=0, have_esm=False,
466- disabled_esm_updates=23),
467+ esm_infra_updates=0, esm_apps_updates=0,
468+ have_esm_infra=False, have_esm_apps=False,
469+ disabled_esm_infra_updates=1,
470+ disabled_esm_apps_updates=0),
471 textwrap.dedent(
472- """
473+ """\
474 UA Infra: Extended Security Maintenance (ESM) is not enabled.
475
476- 0 updates can be installed immediately.
477- 0 of these updates are security updates.
478+ Enable UA Apps: ESM to receive additional future security updates.
479+ See https://ubuntu.com/esm or run: sudo ua status
480
481- Enable UA Infra: ESM to receive 23 additional security updates.
482- See https://ubuntu.com/security/esm or run: sudo ua status
483- """).lstrip())
484+ 1 additional security update can be applied with UA Infra: ESM
485+ Learn more about enabling UA Infra: ESM service at https://ubuntu.com/esm
486+ """))
487
488- def test_esm_disabled_security_esm_avail(self):
489+ @mock.patch("apt_check.is_lts_distro", return_value=True)
490+ @mock.patch("apt_check.is_esm_distro", return_value=True)
491+ def test_esm_infra_disabled_security_esm_avail(
492+ self, _m_esm_distro, _m_is_lts
493+ ):
494 self.assertEqual(
495- get_message(upgrades=15, security_updates=7,
496- esm_updates=0, have_esm=False,
497- disabled_esm_updates=23),
498+ get_message(upgrades=15, security_updates=1,
499+ esm_infra_updates=0, esm_apps_updates=0,
500+ have_esm_infra=False, have_esm_apps=False,
501+ disabled_esm_infra_updates=23,
502+ disabled_esm_apps_updates=0),
503 textwrap.dedent(
504- """
505+ """\
506 UA Infra: Extended Security Maintenance (ESM) is not enabled.
507
508- 15 updates can be installed immediately.
509- 7 of these updates are security updates.
510+ 15 updates can be applied immediately.
511+ 1 of these updates is a standard security update.
512 To see these additional updates run: apt list --upgradable
513
514- Enable UA Infra: ESM to receive 23 additional security updates.
515- See https://ubuntu.com/security/esm or run: sudo ua status
516- """).lstrip())
517+ Enable UA Apps: ESM to receive additional future security updates.
518+ See https://ubuntu.com/esm or run: sudo ua status
519+
520+ 23 additional security updates can be applied with UA Infra: ESM
521+ Learn more about enabling UA Infra: ESM service at https://ubuntu.com/esm
522+ """))
523
524- def test_esm_disabled_security_no_esm_avail(self):
525+ @mock.patch("apt_check.is_lts_distro", return_value=True)
526+ @mock.patch("apt_check.is_esm_distro", return_value=True)
527+ def test_esm_infra_disabled_security_no_esm_avail(
528+ self, _m_esm_distro, _m_is_lts
529+ ):
530 self.assertEqual(
531 get_message(upgrades=15, security_updates=7,
532- esm_updates=0, have_esm=False,
533- disabled_esm_updates=0),
534+ esm_infra_updates=0, esm_apps_updates=0,
535+ have_esm_infra=False, have_esm_apps=False,
536+ disabled_esm_infra_updates=0,
537+ disabled_esm_apps_updates=0),
538 textwrap.dedent(
539- """
540+ """\
541 UA Infra: Extended Security Maintenance (ESM) is not enabled.
542
543- 15 updates can be installed immediately.
544- 7 of these updates are security updates.
545+ 15 updates can be applied immediately.
546+ 7 of these updates are standard security updates.
547 To see these additional updates run: apt list --upgradable
548
549+ Enable UA Apps: ESM to receive additional future security updates.
550+ See https://ubuntu.com/esm or run: sudo ua status
551+
552 Enable UA Infra: ESM to receive additional future security updates.
553- See https://ubuntu.com/security/esm or run: sudo ua status
554- """).lstrip())
555+ See https://ubuntu.com/esm or run: sudo ua status
556+ """))
557
558- def test_esm_disabled_nosecurity(self):
559+ @mock.patch("apt_check.is_lts_distro", return_value=True)
560+ @mock.patch("apt_check.is_esm_distro", return_value=True)
561+ def test_esm_infra_disabled_nosecurity(
562+ self, _m_esm_distro, _m_is_lts
563+ ):
564 self.assertEqual(
565 get_message(upgrades=15, security_updates=0,
566- esm_updates=0, have_esm=False,
567- disabled_esm_updates=0),
568+ esm_infra_updates=0, esm_apps_updates=0,
569+ have_esm_infra=False, have_esm_apps=False,
570+ disabled_esm_infra_updates=0,
571+ disabled_esm_apps_updates=0),
572 textwrap.dedent(
573- """
574+ """\
575 UA Infra: Extended Security Maintenance (ESM) is not enabled.
576
577- 15 updates can be installed immediately.
578- 0 of these updates are security updates.
579+ 15 updates can be applied immediately.
580 To see these additional updates run: apt list --upgradable
581
582+ Enable UA Apps: ESM to receive additional future security updates.
583+ See https://ubuntu.com/esm or run: sudo ua status
584+
585 Enable UA Infra: ESM to receive additional future security updates.
586- See https://ubuntu.com/security/esm or run: sudo ua status
587- """).lstrip())
588+ See https://ubuntu.com/esm or run: sudo ua status
589+ """))
590
591- def test_esm_disabled_noupdates(self):
592+ @mock.patch("apt_check.is_lts_distro", return_value=True)
593+ @mock.patch("apt_check.is_esm_distro", return_value=True)
594+ def test_esm_infra_disabled_noupdates(
595+ self, _m_esm_distro, _m_is_lts
596+ ):
597 self.assertEqual(
598 get_message(upgrades=0, security_updates=0,
599- esm_updates=0, have_esm=False,
600- disabled_esm_updates=0),
601+ esm_infra_updates=0, esm_apps_updates=0,
602+ have_esm_infra=False, have_esm_apps=False,
603+ disabled_esm_infra_updates=0,
604+ disabled_esm_apps_updates=0),
605 textwrap.dedent(
606- """
607+ """\
608 UA Infra: Extended Security Maintenance (ESM) is not enabled.
609
610- 0 updates can be installed immediately.
611- 0 of these updates are security updates.
612+ Enable UA Apps: ESM to receive additional future security updates.
613+ See https://ubuntu.com/esm or run: sudo ua status
614
615 Enable UA Infra: ESM to receive additional future security updates.
616- See https://ubuntu.com/security/esm or run: sudo ua status
617- """).lstrip())
618+ See https://ubuntu.com/esm or run: sudo ua status
619+ """))
620
621- def test_esm_enabled_nosecurity(self):
622+ @mock.patch("apt_check.is_lts_distro", return_value=True)
623+ @mock.patch("apt_check.is_esm_distro", return_value=True)
624+ def test_esm_infra_enabled_nosecurity(
625+ self, _m_esm_distro, _m_is_lts
626+ ):
627 self.assertEqual(
628 get_message(upgrades=35, security_updates=0,
629- esm_updates=13, have_esm=True,
630- disabled_esm_updates=0),
631+ esm_infra_updates=13, esm_apps_updates=0,
632+ have_esm_infra=True, have_esm_apps=False,
633+ disabled_esm_infra_updates=0,
634+ disabled_esm_apps_updates=0),
635 textwrap.dedent(
636- """
637+ """\
638 UA Infra: Extended Security Maintenance (ESM) is enabled.
639
640- 35 updates can be installed immediately.
641- 13 of these updates are fixed through UA Infra: ESM.
642- 0 of these updates are security updates.
643+ 35 updates can be applied immediately.
644+ 13 of these updates are UA Infra: ESM security updates.
645 To see these additional updates run: apt list --upgradable
646- """).lstrip())
647
648- def test_esm_enabled_somesecurity(self):
649+ Enable UA Apps: ESM to receive additional future security updates.
650+ See https://ubuntu.com/esm or run: sudo ua status
651+ """))
652+
653+ @mock.patch("apt_check.is_lts_distro", return_value=True)
654+ @mock.patch("apt_check.is_esm_distro", return_value=True)
655+ def test_esm_infra_enabled_somesecurity(
656+ self, _m_esm_distro, _m_is_lts
657+ ):
658 self.assertEqual(
659 get_message(upgrades=47, security_updates=7,
660- esm_updates=13, have_esm=True,
661- disabled_esm_updates=0),
662+ esm_infra_updates=13, esm_apps_updates=0,
663+ have_esm_infra=True, have_esm_apps=False,
664+ disabled_esm_infra_updates=0,
665+ disabled_esm_apps_updates=0),
666 textwrap.dedent(
667- """
668+ """\
669 UA Infra: Extended Security Maintenance (ESM) is enabled.
670
671- 47 updates can be installed immediately.
672- 13 of these updates are fixed through UA Infra: ESM.
673- 7 of these updates are security updates.
674+ 47 updates can be applied immediately.
675+ 13 of these updates are UA Infra: ESM security updates.
676+ 7 of these updates are standard security updates.
677 To see these additional updates run: apt list --upgradable
678- """).lstrip())
679
680- def test_esm_enabled_noupdates(self):
681+ Enable UA Apps: ESM to receive additional future security updates.
682+ See https://ubuntu.com/esm or run: sudo ua status
683+ """))
684+
685+ @mock.patch("apt_check.is_lts_distro", return_value=True)
686+ @mock.patch("apt_check.is_esm_distro", return_value=True)
687+ def test_esm_infra_enabled_noupdates(
688+ self, _m_esm_distro, _m_is_lts
689+ ):
690 self.assertEqual(
691 get_message(upgrades=0, security_updates=0,
692- esm_updates=0, have_esm=True,
693- disabled_esm_updates=0),
694+ esm_infra_updates=0, esm_apps_updates=0,
695+ have_esm_infra=True, have_esm_apps=False,
696+ disabled_esm_infra_updates=0,
697+ disabled_esm_apps_updates=10),
698+ textwrap.dedent(
699+ """\
700+ UA Infra: Extended Security Maintenance (ESM) is enabled.
701+
702+ 10 additional security updates can be applied with UA Apps: ESM
703+ Learn more about enabling UA Apps: ESM service at https://ubuntu.com/esm
704+ """))
705+
706+ @mock.patch("apt_check.is_lts_distro", return_value=True)
707+ @mock.patch("apt_check.is_esm_distro", return_value=True)
708+ def test_esm_infra_and_esm_apps_enabled(
709+ self, _m_esm_distro, _m_is_lts
710+ ):
711+ self.assertEqual(
712+ get_message(upgrades=30, security_updates=0,
713+ esm_infra_updates=15, esm_apps_updates=15,
714+ have_esm_infra=True, have_esm_apps=True,
715+ disabled_esm_infra_updates=0,
716+ disabled_esm_apps_updates=0),
717 textwrap.dedent(
718 """
719 UA Infra: Extended Security Maintenance (ESM) is enabled.
720
721- 0 updates can be installed immediately.
722- 0 of these updates are security updates.
723+ 30 updates can be applied immediately.
724+ 15 of these updates are UA Infra: ESM security updates.
725+ 15 of these updates are UA Apps: ESM security updates.
726+ To see these additional updates run: apt list --upgradable
727 """).lstrip())
728
729+ @mock.patch("apt_check.is_lts_distro", return_value=True)
730+ @mock.patch("apt_check.is_esm_distro", return_value=True)
731+ def test_esm_infra_disable_and_esm_apps_enabled(
732+ self, _m_esm_distro, _m_is_lts
733+ ):
734+ self.assertEqual(
735+ get_message(upgrades=30, security_updates=12,
736+ esm_infra_updates=0, esm_apps_updates=15,
737+ have_esm_infra=False, have_esm_apps=True,
738+ disabled_esm_infra_updates=0,
739+ disabled_esm_apps_updates=0),
740+ textwrap.dedent(
741+ """
742+ UA Infra: Extended Security Maintenance (ESM) is not enabled.
743+
744+ 30 updates can be applied immediately.
745+ 15 of these updates are UA Apps: ESM security updates.
746+ 12 of these updates are standard security updates.
747+ To see these additional updates run: apt list --upgradable
748+
749+ Enable UA Infra: ESM to receive additional future security updates.
750+ See https://ubuntu.com/esm or run: sudo ua status
751+ """).lstrip())
752+
753+ @mock.patch("apt_check.is_lts_distro", return_value=True)
754+ @mock.patch("apt_check.is_esm_distro", return_value=True)
755+ def test_esm_infra_disabled_wih_pkgs_and_esm_apps_enabled(
756+ self, _m_esm_distro, _m_is_lts
757+ ):
758+ self.assertEqual(
759+ get_message(upgrades=30, security_updates=15,
760+ esm_infra_updates=0, esm_apps_updates=1,
761+ have_esm_infra=False, have_esm_apps=True,
762+ disabled_esm_infra_updates=40,
763+ disabled_esm_apps_updates=0),
764+ textwrap.dedent(
765+ """
766+ UA Infra: Extended Security Maintenance (ESM) is not enabled.
767+
768+ 30 updates can be applied immediately.
769+ 1 of these updates is an UA Apps: ESM security update.
770+ 15 of these updates are standard security updates.
771+ To see these additional updates run: apt list --upgradable
772+
773+ 40 additional security updates can be applied with UA Infra: ESM
774+ Learn more about enabling UA Infra: ESM service at https://ubuntu.com/esm
775+ """).lstrip())
776+
777+ @mock.patch("apt_check.is_lts_distro", return_value=True)
778+ @mock.patch("apt_check.is_esm_distro", return_value=True)
779+ def test_esm_infra_disabled_and_esm_apps_disabled_with_pkgs(
780+ self, _m_esm_distro, _m_is_lts
781+ ):
782+ self.assertEqual(
783+ get_message(upgrades=30, security_updates=18,
784+ esm_infra_updates=0, esm_apps_updates=0,
785+ have_esm_infra=False, have_esm_apps=False,
786+ disabled_esm_infra_updates=0,
787+ disabled_esm_apps_updates=40),
788+ textwrap.dedent(
789+ """\
790+ UA Infra: Extended Security Maintenance (ESM) is not enabled.
791+
792+ 30 updates can be applied immediately.
793+ 18 of these updates are standard security updates.
794+ To see these additional updates run: apt list --upgradable
795+
796+ 40 additional security updates can be applied with UA Apps: ESM
797+ Learn more about enabling UA Apps: ESM service at https://ubuntu.com/esm
798+
799+ Enable UA Infra: ESM to receive additional future security updates.
800+ See https://ubuntu.com/esm or run: sudo ua status
801+ """))
802+
803+ @mock.patch("apt_check.is_lts_distro", return_value=True)
804+ @mock.patch("apt_check.is_esm_distro", return_value=True)
805+ def test_no_esm_infra_and_apps_index_in_esm_distro(
806+ self, _m_esm_distro, _m_is_lts
807+ ):
808+ self.assertEqual(
809+ get_message(upgrades=30, security_updates=18,
810+ esm_infra_updates=0, esm_apps_updates=0,
811+ have_esm_infra=None, have_esm_apps=None,
812+ disabled_esm_infra_updates=0,
813+ disabled_esm_apps_updates=0),
814+ textwrap.dedent(
815+ """\
816+ 30 updates can be applied immediately.
817+ 18 of these updates are standard security updates.
818+ To see these additional updates run: apt list --upgradable
819+ """))
820+
821+ @mock.patch("apt_check.is_lts_distro", return_value=False)
822+ @mock.patch("apt_check.is_esm_distro", return_value=False)
823+ def test_no_esm_infra_and_apps_index_in_non_lts_distro(
824+ self, _m_esm_distro, _m_is_lts
825+ ):
826+ self.assertEqual(
827+ get_message(upgrades=30, security_updates=18,
828+ esm_infra_updates=0, esm_apps_updates=0,
829+ have_esm_infra=None, have_esm_apps=None,
830+ disabled_esm_infra_updates=0,
831+ disabled_esm_apps_updates=0),
832+ textwrap.dedent(
833+ """\
834+ 30 updates can be applied immediately.
835+ 18 of these updates are standard security updates.
836+ To see these additional updates run: apt list --upgradable
837+ """))
838+
839+ @mock.patch("apt_check.is_lts_distro", return_value=False)
840+ @mock.patch("apt_check.is_esm_distro", return_value=False)
841+ def test_esm_infra_disabled_and_esm_apps_disabled_with_no_esm_distro(
842+ self, _m_esm_distro, _m_is_lts,
843+ ):
844+ self.assertEqual(
845+ get_message(upgrades=30, security_updates=18,
846+ esm_infra_updates=0, esm_apps_updates=0,
847+ have_esm_infra=False, have_esm_apps=False,
848+ disabled_esm_infra_updates=0,
849+ disabled_esm_apps_updates=40),
850+ textwrap.dedent(
851+ """\
852+ 30 updates can be applied immediately.
853+ 18 of these updates are standard security updates.
854+ To see these additional updates run: apt list --upgradable
855+ """))
856+
857+ @mock.patch("apt_check.is_lts_distro", return_value=False)
858+ @mock.patch("subprocess.Popen")
859+ def test_message_for_distro_that_will_not_go_into_esm_mode(
860+ self, m_popen, _m_is_lts
861+ ):
862+ comm_mock = mock.MagicMock()
863+ comm_mock.communicate.return_value = (
864+ "(unknown)".encode("utf-8"), "")
865+ m_popen.return_value = comm_mock
866+
867+ self.assertEqual(
868+ get_message(upgrades=30, security_updates=18,
869+ esm_infra_updates=0, esm_apps_updates=0,
870+ have_esm_infra=False, have_esm_apps=False,
871+ disabled_esm_infra_updates=0,
872+ disabled_esm_apps_updates=40),
873+ textwrap.dedent(
874+ """\
875+ 30 updates can be applied immediately.
876+ 18 of these updates are standard security updates.
877+ To see these additional updates run: apt list --upgradable
878+ """))
879+
880+ @mock.patch("apt_check.is_lts_distro", return_value=True)
881+ @mock.patch("apt_check.is_esm_distro", return_value=False)
882+ def test_message_for_lts_distro_not_in_esm_mode_yet(
883+ self, _m_is_esm, _m_is_lts
884+ ):
885+ self.assertEqual(
886+ get_message(upgrades=30, security_updates=18,
887+ esm_infra_updates=0, esm_apps_updates=0,
888+ have_esm_infra=False, have_esm_apps=False,
889+ disabled_esm_infra_updates=10,
890+ disabled_esm_apps_updates=40),
891+ textwrap.dedent(
892+ """\
893+ 30 updates can be applied immediately.
894+ 18 of these updates are standard security updates.
895+ To see these additional updates run: apt list --upgradable
896+
897+ 40 additional security updates can be applied with UA Apps: ESM
898+ Learn more about enabling UA Apps: ESM service at https://ubuntu.com/esm
899+ """))
900+
901
902 if __name__ == "__main__":
903 import logging

Subscribers

People subscribed via source and target branches