From a169227acfb126e348d3d3bd060fffd14660b08b Mon Sep 17 00:00:00 2001 From: Catherine Redfield Date: Tue, 26 Mar 2024 21:09:53 -0400 Subject: [PATCH] fix: wait for cloud-init.service to fully activate Once the systemd constraint on After cloud-config.service was removed, there are situations where cloud-config.service runs so long after pro that the python code that waits for cloud-config.service to finish activating is being bypassed as if cloud-init isn't on the system. This change forces pro to wait for cloud-config to finish if cloud-init is running at all. --- lib/daemon.py | 13 ++++++++++++- uaclient/tests/test_lib_daemon.py | 30 ++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/daemon.py b/lib/daemon.py index 63a223e5ec..7c8319237e 100644 --- a/lib/daemon.py +++ b/lib/daemon.py @@ -19,8 +19,19 @@ def _wait_for_cloud_config(): LOG.debug("waiting for cloud-config.service to finish") for i in range(WAIT_FOR_CLOUD_CONFIG_POLL_TIMES + 1): state = system.get_systemd_unit_active_state("cloud-config.service") + ci_state = system.get_systemd_unit_active_state("cloud-init.service") LOG.debug("cloud-config.service state: %r", state) - if state is not None and state == "activating": + LOG.debug("cloud-init.service state: %r", ci_state) + # if cloud-config.service is not yet activating but cloud-init is + # running, wait for cloud-config to start + if state is not None and ( + state == "activating" + or ( + state == "inactive" + and ci_state is not None + and (ci_state == "activating" or ci_state == "active") + ) + ): if i < WAIT_FOR_CLOUD_CONFIG_POLL_TIMES: LOG.debug( "cloud-config.service is activating. " diff --git a/uaclient/tests/test_lib_daemon.py b/uaclient/tests/test_lib_daemon.py index 489b60e195..68af848eea 100644 --- a/uaclient/tests/test_lib_daemon.py +++ b/uaclient/tests/test_lib_daemon.py @@ -17,24 +17,41 @@ class TestWaitForCloudConfig: ( # not activating ( - ["active"], + ["active"] * 2, [], ), + # inactive (all cloud-init) ( - ["inactive"], + ["inactive"] * 2, [], ), ( - [None], + [None] * 2, [], ), - # activating, then finishes + # cloud-config activating, then finishes + # cloud-init is active ( - (["activating"] * 11) + ["active"], + (["activating", "active"] * 11) + ["active"] * 2, [mock.call(WAIT_FOR_CLOUD_CONFIG_SLEEP_TIME)] * 11, ), ( - (["activating"] * 11) + ["failed"], + (["activating", "active"] * 11) + ["failed"] + ["active"], + [mock.call(WAIT_FOR_CLOUD_CONFIG_SLEEP_TIME)] * 11, + ), + # inactive cloud-config, active cloud-init + ( + (["inactive", "active"] * 6) + + (["activating", "active"] * 5) + + ["active"] * 2, + [mock.call(WAIT_FOR_CLOUD_CONFIG_SLEEP_TIME)] * 11, + ), + # inactive cloud-config, activating cloud-init + ( + (["inactive", "activating"] * 2) + + (["inactive", "active"] * 4) + + (["activating", "active"] * 5) + + ["active"] * 2, [mock.call(WAIT_FOR_CLOUD_CONFIG_SLEEP_TIME)] * 11, ), # still activating after polling maximum times @@ -54,6 +71,7 @@ def test_wait_for_cloud_config( active_state_side_effect, expected_sleep_calls, ): + print(active_state_side_effect) m_get_systemd_unit_active_state.side_effect = active_state_side_effect _wait_for_cloud_config() assert m_sleep.call_args_list == expected_sleep_calls