Skip to content

Commit

Permalink
api: fix unattended-upgrades for pkg not installed
Browse files Browse the repository at this point in the history
When unattended-upgrades is uninstalled in the machine,
we display an error on the api endpoint that doesn't directly
tell the user that the package is not installed. We are updating
the logic to properly tell the user that the package is not
installed.

Fixes: #2807
  • Loading branch information
lucasmoura committed Oct 25, 2023
1 parent 6f16cd6 commit 76730ef
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
6 changes: 6 additions & 0 deletions features/api_unattended_upgrades.feature
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ Feature: api.u.unattended_upgrades.status.v1
"vim"
]
"""
When I run `apt remove unattended-upgrades -y` with sudo
And I run `pro api u.unattended_upgrades.status.v1` as non-root
Then stdout matches regexp:
"""
{"_schema_version": "v1", "data": {"attributes": {"apt_periodic_job_enabled": false, "package_lists_refresh_frequency_days": 0, "systemd_apt_timer_enabled": false, "unattended_upgrades_allowed_origins": \[\], "unattended_upgrades_disabled_reason": {"code": "unattended-upgrades-uninstalled", "msg": "unattended-upgrades packages is not installed"}, "unattended_upgrades_frequency_days": 0, "unattended_upgrades_last_run": null, "unattended_upgrades_running": false}, "meta": {"environment_vars": \[\]}, "type": "UnattendedUpgradesStatus"}, "errors": \[\], "result": "success", "version": ".*", "warnings": \[\]}
"""

Examples: ubuntu release
| release | extra_field |
Expand Down
28 changes: 28 additions & 0 deletions uaclient/api/tests/test_api_u_unattended_upgrades_status_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
UNATTENDED_UPGRADES_CFG_LIST_VALUE_EMPTY,
UNATTENDED_UPGRADES_CFG_VALUE_TURNED_OFF,
UNATTENDED_UPGRADES_SYSTEMD_JOB_DISABLED,
UNATTENDED_UPGRADES_UNINSTALLED,
)

M_PATH = "uaclient.api.u.unattended_upgrades.status.v1"
Expand Down Expand Up @@ -92,6 +93,7 @@ def test_unattended_upgrades_last_run_when_file_not_present(


class TestUnattendedUpgradesStatusV1:
@mock.patch("uaclient.apt.is_installed", return_value=True)
@mock.patch(M_PATH + ".get_apt_config_keys")
@mock.patch(M_PATH + ".get_apt_config_values")
@mock.patch(M_PATH + "._is_unattended_upgrades_running")
Expand All @@ -104,6 +106,7 @@ def test_unattended_upgrades_status_v1(
m_is_running,
m_apt_cfg_values,
m_apt_cfg_keys,
_m_apt_is_installed,
FakeConfig,
):
expected_datetime = datetime.datetime(2023, 2, 23, 15, 0, 0, 102490)
Expand Down Expand Up @@ -154,3 +157,28 @@ def test_unattended_upgrades_status_v1(
assert m_apt_job_status.call_count == 1
assert m_apt_cfg_values.call_count == 1
assert m_last_run.call_count == 1
assert _m_apt_is_installed.call_count == 1

@mock.patch("uaclient.apt.is_installed", return_value=False)
def test_unattended_upgrades_status_v1_when_pkg_not_installed(
self,
_m_apt_is_installed,
FakeConfig,
):
actual_return = api._status(FakeConfig())
assert False is actual_return.apt_periodic_job_enabled
assert False is actual_return.systemd_apt_timer_enabled
assert 0 == actual_return.package_lists_refresh_frequency_days
assert 0 == actual_return.unattended_upgrades_frequency_days
assert [] == actual_return.unattended_upgrades_allowed_origins
assert False is actual_return.unattended_upgrades_running
assert None is actual_return.unattended_upgrades_last_run
assert (
api.UnattendedUpgradesDisabledReason(
msg=UNATTENDED_UPGRADES_UNINSTALLED.msg,
code=UNATTENDED_UPGRADES_UNINSTALLED.name,
)
== actual_return.unattended_upgrades_disabled_reason
)

assert _m_apt_is_installed.call_count == 1
32 changes: 26 additions & 6 deletions uaclient/api/u/unattended_upgrades/status/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
from typing import Dict, List, Optional, Tuple, Union

from uaclient import exceptions, messages, system
from uaclient import apt, exceptions, messages, system
from uaclient.api.api import APIEndpoint
from uaclient.api.data_types import AdditionalInfo
from uaclient.api.exceptions import UnattendedUpgradesError
Expand All @@ -15,6 +15,7 @@
Field,
IntDataValue,
StringDataValue,
data_list,
)

UNATTENDED_UPGRADES_CONFIG_KEYS = [
Expand Down Expand Up @@ -42,18 +43,22 @@ class UnattendedUpgradesStatusResult(DataObject, AdditionalInfo):
fields = [
Field("systemd_apt_timer_enabled", BoolDataValue),
Field("apt_periodic_job_enabled", BoolDataValue),
Field("package_lists_refresh_frequency_days", IntDataValue),
Field(
"package_lists_refresh_frequency_days",
IntDataValue,
),
Field("unattended_upgrades_frequency_days", IntDataValue),
Field("unattended_upgrades_allowed_origins", StringDataValue),
Field(
"unattended_upgrades_allowed_origins",
data_list(StringDataValue),
),
Field("unattended_upgrades_running", BoolDataValue),
Field(
"unattended_upgrades_disabled_reason",
UnattendedUpgradesDisabledReason,
required=False,
),
Field(
"unattended_upgrades_last_run", DatetimeDataValue, required=False
),
Field("unattended_upgrades_last_run", DatetimeDataValue),
]

def __init__(
Expand Down Expand Up @@ -145,6 +150,21 @@ def status() -> UnattendedUpgradesStatusResult:


def _status(cfg: UAConfig) -> UnattendedUpgradesStatusResult:
if not apt.is_installed("unattended-upgrades"):
return UnattendedUpgradesStatusResult(
systemd_apt_timer_enabled=False,
apt_periodic_job_enabled=False,
package_lists_refresh_frequency_days=0,
unattended_upgrades_frequency_days=0,
unattended_upgrades_allowed_origins=[],
unattended_upgrades_disabled_reason=UnattendedUpgradesDisabledReason( # noqa
msg=messages.UNATTENDED_UPGRADES_UNINSTALLED.msg,
code=messages.UNATTENDED_UPGRADES_UNINSTALLED.name,
),
unattended_upgrades_running=False,
unattended_upgrades_last_run=None,
)

systemd_apt_timer_enabled = _get_apt_daily_job_status()
unattended_upgrades_last_run = _get_unattended_upgrades_last_run()

Expand Down
4 changes: 4 additions & 0 deletions uaclient/messages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1850,6 +1850,10 @@ def __repr__(self):
"unattended-upgrades-cfg-value-turned-off",
t.gettext("{cfg_name} is turned off"),
)
UNATTENDED_UPGRADES_UNINSTALLED = NamedMessage(
"unattended-upgrades-uninstalled",
"unattended-upgrades packages is not installed",
)

LANDSCAPE_CLIENT_NOT_INSTALLED = NamedMessage(
"landscape-client-not-installed",
Expand Down

0 comments on commit 76730ef

Please sign in to comment.