diff -Nru update-notifier-3.192.30.7/data/apt_check.py update-notifier-3.192.30.8/data/apt_check.py --- update-notifier-3.192.30.7/data/apt_check.py 2021-04-22 21:56:22.000000000 +0000 +++ update-notifier-3.192.30.8/data/apt_check.py 2021-05-14 19:02:18.000000000 +0000 @@ -8,14 +8,59 @@ import os import sys from optparse import OptionParser +import re import gettext -import subprocess import distro_info +import subprocess + SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences" -DISTRO = subprocess.check_output( - ["lsb_release", "-c", "-s"], - universal_newlines=True).strip() +OS_RELEASE_PATH = "/etc/os-release" + + +def _get_info_from_os_release(key): + " get info directly from os-release file " + if os.path.exists(OS_RELEASE_PATH): + with open(OS_RELEASE_PATH) as f: + search_res = re.search( + r"{}=(?P.*)".format(key), + f.read() + ) + if search_res: + return search_res.group("name") + else: + raise Exception( + "Could not find {} in {}".format( + key, OS_RELEASE_PATH + ) + ) + else: + raise Exception( + "File {} was not found on the system".format( + OS_RELEASE_PATH + ) + ) + + +def _get_output_from_lsb_release(lsb_option): + " get info from lsb_release output " + return subprocess.check_output( + ["lsb_release", lsb_option, "-s"], universal_newlines=True + ).strip() + + +def get_distro(): + " get distro name " + try: + return _get_info_from_os_release(key="UBUNTU_CODENAME") + except Exception: + # If the system does not have os-release file or does not have the + # required entry in it, we will get the distro name from lsb_release + # command + return _get_output_from_lsb_release("-c") + + +DISTRO = get_distro() ESM_INFRA_ORIGIN = "UbuntuESM" ESM_APPS_ORIGIN = "UbuntuESMApps" @@ -31,6 +76,17 @@ sys.exit(-1) +def get_distro_version(): + " get distro version " + try: + return _get_info_from_os_release(key="VERSION_ID").replace('"', "") + except Exception: + # If the system does not have os-release file or does not have the + # required entry in it, we will get the distro name from lsb_release + # command + return _get_output_from_lsb_release("-r") + + def clean(cache, depcache): " unmark (clean) all changes from the given depcache " # mvo: looping is too inefficient with the new auto-mark code @@ -112,7 +168,7 @@ outstream.write("\n") outstream.write(gettext.dngettext("update-notifier", "%d of these updates " - "is an UA %s: ESM " + "is a UA %s: ESM " "security update.", "%d of these updates " "are UA %s: ESM " @@ -122,27 +178,37 @@ def _output_esm_package_alert( - outstream, service_type, disabled_pkg_count + outstream, service_type, disabled_pkg_count, is_esm=False ): " output the number of upgradable packages if esm service was enabled " outstream.write("\n") if disabled_pkg_count > 0: outstream.write("\n") + + if is_esm: + distro_version = get_distro_version() + esm_info_url = "https://ubuntu.com/{}".format( + distro_version.replace(".", "-") + ) + learn_msg_suffix = "for Ubuntu {} at\n{}".format( + distro_version, esm_info_url) + else: + learn_msg_suffix = "at https://ubuntu.com/esm" + outstream.write(gettext.dngettext("update-notifier", "%i additional security " "update can be applied " "with UA %s: ESM\nLearn " "more about enabling UA " - "%s: ESM service at " - "https://ubuntu.com/esm", + "%s: ESM service %s", "%i additional security " "updates can be applied " "with UA %s: ESM\nLearn " "more about enabling UA " - "%s: ESM service at " - "https://ubuntu.com/esm", + "%s: ESM service %s", disabled_pkg_count) % - (disabled_pkg_count, service_type, service_type)) + (disabled_pkg_count, service_type, service_type, + learn_msg_suffix)) else: outstream.write("\n") outstream.write(gettext.dgettext("update-notifier", @@ -159,36 +225,47 @@ ) +def _output_esm_service_status(outstream, have_esm_service, service_type): + if have_esm_service: + outstream.write(gettext.dgettext("update-notifier", + "UA %s: Extended " + "Security Maintenance (ESM) is " + "enabled.") % service_type) + else: + outstream.write(gettext.dgettext("update-notifier", + "UA %s: Extended " + "Security Maintenance (ESM) is " + "not enabled.") % service_type) + outstream.write("\n\n") + + def write_human_readable_summary(outstream, upgrades, security_updates, esm_infra_updates, esm_apps_updates, have_esm_infra, have_esm_apps, disabled_esm_infra_updates, disabled_esm_apps_updates): - " write out human summary summary to outstream " + " write out human summary to outstream " esm_distro = is_esm_distro() lts_distro = is_lts_distro() + if have_esm_infra is not None and esm_distro: - if have_esm_infra: - outstream.write(gettext.dgettext("update-notifier", - "UA Infra: Extended " - "Security Maintenance (ESM) is " - "enabled.")) - else: - outstream.write(gettext.dgettext("update-notifier", - "UA Infra: Extended " - "Security Maintenance (ESM) is " - "not enabled.")) - if upgrades > 0: - outstream.write("\n\n") - if upgrades > 0: - outstream.write( - gettext.dngettext("update-notifier", - "%i update can be applied immediately.", - "%i updates can be applied immediately.", - upgrades) % upgrades + _output_esm_service_status( + outstream, have_esm_infra, service_type="Infra" + ) + + if have_esm_apps is not None and lts_distro and not esm_distro: + _output_esm_service_status( + outstream, have_esm_apps, service_type="Apps" ) + outstream.write( + gettext.dngettext("update-notifier", + "%i update can be applied immediately.", + "%i updates can be applied immediately.", + upgrades) % upgrades + ) + _output_esm_package_count( outstream, service_type="Infra", esm_pkg_count=esm_infra_updates) _output_esm_package_count( @@ -210,7 +287,14 @@ "To see these additional updates " "run: apt list --upgradable")) - if have_esm_apps is not None and not have_esm_apps and lts_distro: + if all( + [ + have_esm_apps is not None, + not have_esm_apps, + lts_distro, + not esm_distro + ] + ): _output_esm_package_alert( outstream, service_type="Apps", disabled_pkg_count=disabled_esm_apps_updates) @@ -218,7 +302,9 @@ if have_esm_infra is not None and not have_esm_infra and esm_distro: _output_esm_package_alert( outstream, service_type="Infra", - disabled_pkg_count=disabled_esm_infra_updates) + disabled_pkg_count=disabled_esm_infra_updates, + is_esm=True + ) outstream.write("\n") diff -Nru update-notifier-3.192.30.7/debian/changelog update-notifier-3.192.30.8/debian/changelog --- update-notifier-3.192.30.7/debian/changelog 2021-04-22 21:56:22.000000000 +0000 +++ update-notifier-3.192.30.8/debian/changelog 2021-05-14 19:02:18.000000000 +0000 @@ -1,3 +1,20 @@ +update-notifier (3.192.30.8) focal; urgency=medium + + * debian/control: add lsb-release to the update-notifier-common depends as + it is used by apt-cdrom-check. (LP: #1927996) + * data/apt_check.py + - Only show esm-apps alerts on lts and non-esm distros (LP: #1926990) + - Update esm-infra alert for distros on ESM mode + - Show message with number of upgradable packages even if that + number is zero (LP: #1926819) + - Get distro name and version directly from /etc/os-release + - Fallback to lsb_release command if there are any problems using + /etc/os-release as source of information + - Show esm-apps status header when running on LTS distro that + has not yet entered Extended Security Maintenance + + -- Lucas Moura Fri, 14 May 2021 16:02:18 -0300 + update-notifier (3.192.30.7) focal; urgency=medium * data/apt_check.py: diff -Nru update-notifier-3.192.30.7/debian/control update-notifier-3.192.30.8/debian/control --- update-notifier-3.192.30.7/debian/control 2021-04-22 21:56:22.000000000 +0000 +++ update-notifier-3.192.30.8/debian/control 2021-05-14 19:02:18.000000000 +0000 @@ -56,6 +56,7 @@ python3-apt, python3-dbus, python3-debian, python3-debconf | debconf (<< 1.5.64~), python3-distro-info, + lsb-release, patch, update-manager-core (>= 1:17.04.2) Recommends: libpam-modules (>= 1.0.1-9ubuntu3) Suggests: policykit-1 diff -Nru update-notifier-3.192.30.7/tests/apt_check.py update-notifier-3.192.30.8/tests/apt_check.py --- update-notifier-3.192.30.7/tests/apt_check.py 2021-04-22 21:56:22.000000000 +0000 +++ update-notifier-3.192.30.8/tests/apt_check.py 2021-05-14 19:02:18.000000000 +0000 @@ -8,14 +8,59 @@ import os import sys from optparse import OptionParser +import re import gettext -import subprocess import distro_info +import subprocess + SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences" -DISTRO = subprocess.check_output( - ["lsb_release", "-c", "-s"], - universal_newlines=True).strip() +OS_RELEASE_PATH = "/etc/os-release" + + +def _get_info_from_os_release(key): + " get info directly from os-release file " + if os.path.exists(OS_RELEASE_PATH): + with open(OS_RELEASE_PATH) as f: + search_res = re.search( + r"{}=(?P.*)".format(key), + f.read() + ) + if search_res: + return search_res.group("name") + else: + raise Exception( + "Could not find {} in {}".format( + key, OS_RELEASE_PATH + ) + ) + else: + raise Exception( + "File {} was not found on the system".format( + OS_RELEASE_PATH + ) + ) + + +def _get_output_from_lsb_release(lsb_option): + " get info from lsb_release output " + return subprocess.check_output( + ["lsb_release", lsb_option, "-s"], universal_newlines=True + ).strip() + + +def get_distro(): + " get distro name " + try: + return _get_info_from_os_release(key="UBUNTU_CODENAME") + except Exception: + # If the system does not have os-release file or does not have the + # required entry in it, we will get the distro name from lsb_release + # command + return _get_output_from_lsb_release("-c") + + +DISTRO = get_distro() ESM_INFRA_ORIGIN = "UbuntuESM" ESM_APPS_ORIGIN = "UbuntuESMApps" @@ -31,6 +76,17 @@ sys.exit(-1) +def get_distro_version(): + " get distro version " + try: + return _get_info_from_os_release(key="VERSION_ID").replace('"', "") + except Exception: + # If the system does not have os-release file or does not have the + # required entry in it, we will get the distro name from lsb_release + # command + return _get_output_from_lsb_release("-r") + + def clean(cache, depcache): " unmark (clean) all changes from the given depcache " # mvo: looping is too inefficient with the new auto-mark code @@ -112,7 +168,7 @@ outstream.write("\n") outstream.write(gettext.dngettext("update-notifier", "%d of these updates " - "is an UA %s: ESM " + "is a UA %s: ESM " "security update.", "%d of these updates " "are UA %s: ESM " @@ -122,27 +178,37 @@ def _output_esm_package_alert( - outstream, service_type, disabled_pkg_count + outstream, service_type, disabled_pkg_count, is_esm=False ): " output the number of upgradable packages if esm service was enabled " outstream.write("\n") if disabled_pkg_count > 0: outstream.write("\n") + + if is_esm: + distro_version = get_distro_version() + esm_info_url = "https://ubuntu.com/{}".format( + distro_version.replace(".", "-") + ) + learn_msg_suffix = "for Ubuntu {} at\n{}".format( + distro_version, esm_info_url) + else: + learn_msg_suffix = "at https://ubuntu.com/esm" + outstream.write(gettext.dngettext("update-notifier", "%i additional security " "update can be applied " "with UA %s: ESM\nLearn " "more about enabling UA " - "%s: ESM service at " - "https://ubuntu.com/esm", + "%s: ESM service %s", "%i additional security " "updates can be applied " "with UA %s: ESM\nLearn " "more about enabling UA " - "%s: ESM service at " - "https://ubuntu.com/esm", + "%s: ESM service %s", disabled_pkg_count) % - (disabled_pkg_count, service_type, service_type)) + (disabled_pkg_count, service_type, service_type, + learn_msg_suffix)) else: outstream.write("\n") outstream.write(gettext.dgettext("update-notifier", @@ -159,36 +225,47 @@ ) +def _output_esm_service_status(outstream, have_esm_service, service_type): + if have_esm_service: + outstream.write(gettext.dgettext("update-notifier", + "UA %s: Extended " + "Security Maintenance (ESM) is " + "enabled.") % service_type) + else: + outstream.write(gettext.dgettext("update-notifier", + "UA %s: Extended " + "Security Maintenance (ESM) is " + "not enabled.") % service_type) + outstream.write("\n\n") + + def write_human_readable_summary(outstream, upgrades, security_updates, esm_infra_updates, esm_apps_updates, have_esm_infra, have_esm_apps, disabled_esm_infra_updates, disabled_esm_apps_updates): - " write out human summary summary to outstream " + " write out human summary to outstream " esm_distro = is_esm_distro() lts_distro = is_lts_distro() + if have_esm_infra is not None and esm_distro: - if have_esm_infra: - outstream.write(gettext.dgettext("update-notifier", - "UA Infra: Extended " - "Security Maintenance (ESM) is " - "enabled.")) - else: - outstream.write(gettext.dgettext("update-notifier", - "UA Infra: Extended " - "Security Maintenance (ESM) is " - "not enabled.")) - if upgrades > 0: - outstream.write("\n\n") - if upgrades > 0: - outstream.write( - gettext.dngettext("update-notifier", - "%i update can be applied immediately.", - "%i updates can be applied immediately.", - upgrades) % upgrades + _output_esm_service_status( + outstream, have_esm_infra, service_type="Infra" + ) + + if have_esm_apps is not None and lts_distro and not esm_distro: + _output_esm_service_status( + outstream, have_esm_apps, service_type="Apps" ) + outstream.write( + gettext.dngettext("update-notifier", + "%i update can be applied immediately.", + "%i updates can be applied immediately.", + upgrades) % upgrades + ) + _output_esm_package_count( outstream, service_type="Infra", esm_pkg_count=esm_infra_updates) _output_esm_package_count( @@ -210,7 +287,14 @@ "To see these additional updates " "run: apt list --upgradable")) - if have_esm_apps is not None and not have_esm_apps and lts_distro: + if all( + [ + have_esm_apps is not None, + not have_esm_apps, + lts_distro, + not esm_distro + ] + ): _output_esm_package_alert( outstream, service_type="Apps", disabled_pkg_count=disabled_esm_apps_updates) @@ -218,7 +302,9 @@ if have_esm_infra is not None and not have_esm_infra and esm_distro: _output_esm_package_alert( outstream, service_type="Infra", - disabled_pkg_count=disabled_esm_infra_updates) + disabled_pkg_count=disabled_esm_infra_updates, + is_esm=True + ) outstream.write("\n") diff -Nru update-notifier-3.192.30.7/tests/test_motd.py update-notifier-3.192.30.8/tests/test_motd.py --- update-notifier-3.192.30.7/tests/test_motd.py 2021-04-22 21:56:22.000000000 +0000 +++ update-notifier-3.192.30.8/tests/test_motd.py 2021-05-14 19:02:18.000000000 +0000 @@ -18,10 +18,11 @@ class TestMotd(unittest.TestCase): """ Validate /etc/motd text """ + @mock.patch("apt_check.get_distro_version", return_value="16.04") @mock.patch("apt_check.is_lts_distro", return_value=True) @mock.patch("apt_check.is_esm_distro", return_value=True) def test_esm_infra_disabled_upto_date_esm_avail( - self, _m_esm_distro, _m_is_lts + self, _m_esm_distro, _m_is_lts, _m_distro_version ): self.assertEqual( get_message(upgrades=0, security_updates=0, @@ -33,17 +34,18 @@ """\ UA Infra: Extended Security Maintenance (ESM) is not enabled. - Enable UA Apps: ESM to receive additional future security updates. - See https://ubuntu.com/esm or run: sudo ua status + 0 updates can be applied immediately. 1 additional security update can be applied with UA Infra: ESM - Learn more about enabling UA Infra: ESM service at https://ubuntu.com/esm + Learn more about enabling UA Infra: ESM service for Ubuntu 16.04 at + https://ubuntu.com/16-04 """)) + @mock.patch("apt_check.get_distro_version", return_value="18.04") @mock.patch("apt_check.is_lts_distro", return_value=True) @mock.patch("apt_check.is_esm_distro", return_value=True) def test_esm_infra_disabled_security_esm_avail( - self, _m_esm_distro, _m_is_lts + self, _m_esm_distro, _m_is_lts, _m_distro_version ): self.assertEqual( get_message(upgrades=15, security_updates=1, @@ -59,11 +61,9 @@ 1 of these updates is a standard security update. To see these additional updates run: apt list --upgradable - Enable UA Apps: ESM to receive additional future security updates. - See https://ubuntu.com/esm or run: sudo ua status - 23 additional security updates can be applied with UA Infra: ESM - Learn more about enabling UA Infra: ESM service at https://ubuntu.com/esm + Learn more about enabling UA Infra: ESM service for Ubuntu 18.04 at + https://ubuntu.com/18-04 """)) @mock.patch("apt_check.is_lts_distro", return_value=True) @@ -85,9 +85,6 @@ 7 of these updates are standard security updates. To see these additional updates run: apt list --upgradable - Enable UA Apps: ESM to receive additional future security updates. - See https://ubuntu.com/esm or run: sudo ua status - Enable UA Infra: ESM to receive additional future security updates. See https://ubuntu.com/esm or run: sudo ua status """)) @@ -110,9 +107,6 @@ 15 updates can be applied immediately. To see these additional updates run: apt list --upgradable - Enable UA Apps: ESM to receive additional future security updates. - See https://ubuntu.com/esm or run: sudo ua status - Enable UA Infra: ESM to receive additional future security updates. See https://ubuntu.com/esm or run: sudo ua status """)) @@ -132,8 +126,7 @@ """\ UA Infra: Extended Security Maintenance (ESM) is not enabled. - Enable UA Apps: ESM to receive additional future security updates. - See https://ubuntu.com/esm or run: sudo ua status + 0 updates can be applied immediately. Enable UA Infra: ESM to receive additional future security updates. See https://ubuntu.com/esm or run: sudo ua status @@ -157,9 +150,6 @@ 35 updates can be applied immediately. 13 of these updates are UA Infra: ESM security updates. To see these additional updates run: apt list --upgradable - - Enable UA Apps: ESM to receive additional future security updates. - See https://ubuntu.com/esm or run: sudo ua status """)) @mock.patch("apt_check.is_lts_distro", return_value=True) @@ -181,9 +171,6 @@ 13 of these updates are UA Infra: ESM security updates. 7 of these updates are standard security updates. To see these additional updates run: apt list --upgradable - - Enable UA Apps: ESM to receive additional future security updates. - See https://ubuntu.com/esm or run: sudo ua status """)) @mock.patch("apt_check.is_lts_distro", return_value=True) @@ -201,8 +188,7 @@ """\ UA Infra: Extended Security Maintenance (ESM) is enabled. - 10 additional security updates can be applied with UA Apps: ESM - Learn more about enabling UA Apps: ESM service at https://ubuntu.com/esm + 0 updates can be applied immediately. """)) @mock.patch("apt_check.is_lts_distro", return_value=True) @@ -250,10 +236,11 @@ See https://ubuntu.com/esm or run: sudo ua status """).lstrip()) + @mock.patch("apt_check.get_distro_version", return_value="16.04") @mock.patch("apt_check.is_lts_distro", return_value=True) @mock.patch("apt_check.is_esm_distro", return_value=True) def test_esm_infra_disabled_wih_pkgs_and_esm_apps_enabled( - self, _m_esm_distro, _m_is_lts + self, _m_esm_distro, _m_is_lts, _m_distro_version ): self.assertEqual( get_message(upgrades=30, security_updates=15, @@ -266,12 +253,34 @@ UA Infra: Extended Security Maintenance (ESM) is not enabled. 30 updates can be applied immediately. - 1 of these updates is an UA Apps: ESM security update. + 1 of these updates is a UA Apps: ESM security update. 15 of these updates are standard security updates. To see these additional updates run: apt list --upgradable 40 additional security updates can be applied with UA Infra: ESM - Learn more about enabling UA Infra: ESM service at https://ubuntu.com/esm + Learn more about enabling UA Infra: ESM service for Ubuntu 16.04 at + https://ubuntu.com/16-04 + """).lstrip()) + + @mock.patch("apt_check.is_lts_distro", return_value=True) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_esm_infra_disabled_wih_pkgs_and_esm_apps_enabled_on_lts_distro( + self, _m_esm_distro, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=30, security_updates=15, + esm_infra_updates=0, esm_apps_updates=1, + have_esm_infra=False, have_esm_apps=True, + disabled_esm_infra_updates=40, + disabled_esm_apps_updates=0), + textwrap.dedent( + """ + UA Apps: Extended Security Maintenance (ESM) is enabled. + + 30 updates can be applied immediately. + 1 of these updates is a UA Apps: ESM security update. + 15 of these updates are standard security updates. + To see these additional updates run: apt list --upgradable """).lstrip()) @mock.patch("apt_check.is_lts_distro", return_value=True) @@ -293,9 +302,6 @@ 18 of these updates are standard security updates. To see these additional updates run: apt list --upgradable - 40 additional security updates can be applied with UA Apps: ESM - Learn more about enabling UA Apps: ESM service at https://ubuntu.com/esm - Enable UA Infra: ESM to receive additional future security updates. See https://ubuntu.com/esm or run: sudo ua status """)) @@ -390,6 +396,8 @@ disabled_esm_apps_updates=40), textwrap.dedent( """\ + UA Apps: Extended Security Maintenance (ESM) is not enabled. + 30 updates can be applied immediately. 18 of these updates are standard security updates. To see these additional updates run: apt list --upgradable @@ -398,6 +406,271 @@ Learn more about enabling UA Apps: ESM service at https://ubuntu.com/esm """)) + @mock.patch("apt_check.is_lts_distro", return_value=False) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_message_no_upgrades_non_lts_distro_without_esm_sources( + self, _m_is_esm, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=0, security_updates=0, + esm_infra_updates=0, esm_apps_updates=0, + have_esm_infra=None, have_esm_apps=None, + disabled_esm_infra_updates=0, + disabled_esm_apps_updates=0), + textwrap.dedent( + """\ + 0 updates can be applied immediately. + """)) + + @mock.patch("apt_check.is_lts_distro", return_value=False) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_message_one_upgrade_non_lts_distro_without_esm_sources( + self, _m_is_esm, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=1, security_updates=0, + esm_infra_updates=0, esm_apps_updates=0, + have_esm_infra=None, have_esm_apps=None, + disabled_esm_infra_updates=0, + disabled_esm_apps_updates=0), + textwrap.dedent( + """\ + 1 update can be applied immediately. + To see these additional updates run: apt list --upgradable + """)) + + @mock.patch("apt_check.is_lts_distro", return_value=False) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_message_one_security_upgrade_non_lts_distro_without_esm_sources( + self, _m_is_esm, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=1, security_updates=1, + esm_infra_updates=0, esm_apps_updates=0, + have_esm_infra=None, have_esm_apps=None, + disabled_esm_infra_updates=0, + disabled_esm_apps_updates=0), + textwrap.dedent( + """\ + 1 update can be applied immediately. + 1 of these updates is a standard security update. + To see these additional updates run: apt list --upgradable + """)) + + @mock.patch("apt_check.is_lts_distro", return_value=True) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_message_no_upgrades_lts_distro_without_esm_sources( + self, _m_is_esm, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=0, security_updates=0, + esm_infra_updates=0, esm_apps_updates=0, + have_esm_infra=None, have_esm_apps=None, + disabled_esm_infra_updates=0, + disabled_esm_apps_updates=0), + textwrap.dedent( + """\ + 0 updates can be applied immediately. + """)) + + @mock.patch("apt_check.is_lts_distro", return_value=True) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_message_no_upgrades_lts_distro_with_esm_sources( + self, _m_is_esm, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=0, security_updates=0, + esm_infra_updates=0, esm_apps_updates=0, + have_esm_infra=False, have_esm_apps=False, + disabled_esm_infra_updates=0, + disabled_esm_apps_updates=0), + textwrap.dedent( + """\ + UA Apps: Extended Security Maintenance (ESM) is not enabled. + + 0 updates can be applied immediately. + + Enable UA Apps: ESM to receive additional future security updates. + See https://ubuntu.com/esm or run: sudo ua status + """)) + + @mock.patch("apt_check.is_lts_distro", return_value=True) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_message_one_upgrade_esm_distro_without_esm_sources( + self, _m_is_esm, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=1, security_updates=0, + esm_infra_updates=0, esm_apps_updates=0, + have_esm_infra=None, have_esm_apps=None, + disabled_esm_infra_updates=0, + disabled_esm_apps_updates=0), + textwrap.dedent( + """\ + 1 update can be applied immediately. + To see these additional updates run: apt list --upgradable + """)) + + @mock.patch("apt_check.is_lts_distro", return_value=True) + @mock.patch("apt_check.is_esm_distro", return_value=False) + def test_message_one_security_upgrade_esm_distro_without_esm_sources( + self, _m_is_esm, _m_is_lts + ): + self.assertEqual( + get_message(upgrades=1, security_updates=1, + esm_infra_updates=0, esm_apps_updates=0, + have_esm_infra=None, have_esm_apps=None, + disabled_esm_infra_updates=0, + disabled_esm_apps_updates=0), + textwrap.dedent( + """\ + 1 update can be applied immediately. + 1 of these updates is a standard security update. + To see these additional updates run: apt list --upgradable + """)) + + +class TestGetDistro(unittest.TestCase): + @mock.patch("os.path.exists", return_value=True) + def test_get_distro_happy_path(self, _m_path): + read_data = ( + "UBUNTU_CODENAME=test\n" + "NAME=Ubuntu\n" + "VERSION=version\n" + "ID=ubuntu\n" + "ID_LIKE=debian\n" + "PRETTY_NAME=Ubuntu XX.XX\n" + "VERSION_ID=XX.XX\n" + ) + + expected_value = "test" + with mock.patch( + "builtins.open", + mock.mock_open(read_data=read_data) + ) as m_open: + assert apt_check.get_distro() == expected_value + assert 1 == _m_path.call_count + assert [ + mock.call(apt_check.OS_RELEASE_PATH) + ] == m_open.call_args_list + + @mock.patch("os.path.exists", return_value=True) + @mock.patch("subprocess.check_output") + def test_get_distro_no_ubuntu_codename(self, m_check_output, _m_path): + read_data = ( + "NAME=Ubuntu\n" + "VERSION=version\n" + "ID=ubuntu\n" + "ID_LIKE=debian\n" + "PRETTY_NAME=Ubuntu XX.XX\n" + "VERSION_ID=XX.XX\n" + ) + expected_value = "codename" + m_check_output.return_value = expected_value + + with mock.patch( + "builtins.open", + mock.mock_open(read_data=read_data) + ) as m_open: + assert expected_value == apt_check.get_distro() + assert 1 == _m_path.call_count + assert [ + mock.call(apt_check.OS_RELEASE_PATH) + ] == m_open.call_args_list + assert [ + mock.call( + ["lsb_release", "-c", "-s"], + universal_newlines=True + ) + ] == m_check_output.call_args_list + + @mock.patch("os.path.exists", return_value=False) + @mock.patch("subprocess.check_output") + def test_get_distro_no_os_release(self, m_check_output, _m_path): + expected_value = "codename" + m_check_output.return_value = expected_value + + assert expected_value == apt_check.get_distro() + assert 1 == _m_path.call_count + assert [ + mock.call( + ["lsb_release", "-c", "-s"], + universal_newlines=True + ) + ] == m_check_output.call_args_list + + +class TestDistroVersion(unittest.TestCase): + @mock.patch("os.path.exists", return_value=True) + def test_get_distro_version_happy_path(self, _m_path): + read_data = ( + "UBUNTU_CODENAME=test\n" + "NAME=Ubuntu\n" + "VERSION=version\n" + "ID=ubuntu\n" + "ID_LIKE=debian\n" + "PRETTY_NAME=Ubuntu XX.XX\n" + 'VERSION_ID="XX.XX"\n' + ) + + expected_value = "XX.XX" + with mock.patch( + "builtins.open", + mock.mock_open(read_data=read_data) + ) as m_open: + assert apt_check.get_distro_version() == expected_value + assert 1 == _m_path.call_count + assert [ + mock.call(apt_check.OS_RELEASE_PATH) + ] == m_open.call_args_list + + @mock.patch("os.path.exists", return_value=True) + @mock.patch("subprocess.check_output") + def test_get_distro_version_no_ubuntu_version( + self, m_check_output, _m_path + ): + read_data = ( + "UBUNTU_CODENAME=test\n" + "NAME=Ubuntu\n" + "VERSION=version\n" + "ID=ubuntu\n" + "ID_LIKE=debian\n" + "PRETTY_NAME=Ubuntu XX.XX\n" + ) + expected_value = "XX.XX" + m_check_output.return_value = expected_value + + with mock.patch( + "builtins.open", + mock.mock_open(read_data=read_data) + ) as m_open: + assert expected_value == apt_check.get_distro_version() + assert 1 == _m_path.call_count + assert [ + mock.call(apt_check.OS_RELEASE_PATH) + ] == m_open.call_args_list + assert [ + mock.call( + ["lsb_release", "-r", "-s"], + universal_newlines=True + ) + ] == m_check_output.call_args_list + + @mock.patch("os.path.exists", return_value=False) + @mock.patch("subprocess.check_output") + def test_get_distro_version_no_os_release(self, m_check_output, _m_path): + expected_value = "codename" + m_check_output.return_value = expected_value + + assert expected_value == apt_check.get_distro_version() + assert 1 == _m_path.call_count + assert [ + mock.call( + ["lsb_release", "-r", "-s"], + universal_newlines=True + ) + ] == m_check_output.call_args_list + if __name__ == "__main__": import logging