Repository: Azure/WALinuxAgent
Pull request #2965 (2023-10-31 00:12:29)

fix self-update frequency to spread over 24 hrs for regular type and …
…4 hrs for hotfix  (#2948)

* update self-update frequency

* address comment

* mark with comment

* addressed comment

(cherry picked from commit f15e6ef7b4d17d514b639e8bc2e78507a2d71096)

<!-- DO NOT DELETE THIS TEMPLATE -->

## Description

Issue # <!-- if any -->
<!--
Please add an informative description that covers that changes made by the pull request. 
This checklist is used to make sure that common issues in a pull request are addressed.
This will expedite the process of getting your pull request merged and avoid extra work on your part to fix issues discovered during the review process.
-->

---

### PR information
- [ ] The title of the PR is clear and informative.
- [ ] There are a small number of commits, each of which has an informative message. This means that previously merged commits do not appear in the history of the PR. For information on cleaning up the commits in your pull request, [see this page](https://github.com/Azure/azure-powershell/blob/master/documentation/development-docs/cleaning-up-commits.md).
- [ ] If applicable, the PR references the bug/issue that it fixes in the description.
- [ ] New Unit tests were added for the changes made

### Quality of Code and Contribution Guidelines
- [ ] I have read the [contribution guidelines](https://github.com/Azure/WALinuxAgent/blob/master/.github/CONTRIBUTING.md).

codecov[bot]: We're currently processing your upload.  This comment will be updated when the results are available.

Patch:
diff --git a/azurelinuxagent/ga/agent_update_handler.py b/azurelinuxagent/ga/agent_update_handler.py
index d311a5c4..a650f110 100644
--- a/azurelinuxagent/ga/agent_update_handler.py
+++ b/azurelinuxagent/ga/agent_update_handler.py
@@ -50,7 +50,7 @@ class AgentUpdateHandler(object):
         self._is_requested_version_update = True  # This is to track the current update type(requested version or self update)
         self.update_state = AgentUpdateHandlerUpdateState()
 
-    def __should_update_agent(self, requested_version):
+    def __check_if_agent_update_allowed_and_update_next_upgrade_times(self, requested_version):
         """
         requested version update:
             update is allowed once per (as specified in the conf.get_autoupdate_frequency())
@@ -70,6 +70,7 @@ class AgentUpdateHandler(object):
             if next_attempt_time > now:
                 return False
             # The time limit elapsed for us to allow updates.
+            self.update_state.last_attempted_requested_version_update_time = now
             return True
         else:
             next_hotfix_time, next_normal_time = self.__get_next_upgrade_times(now)
@@ -77,17 +78,12 @@ class AgentUpdateHandler(object):
 
             if (upgrade_type == AgentUpgradeType.Hotfix and next_hotfix_time <= now) or (
                     upgrade_type == AgentUpgradeType.Normal and next_normal_time <= now):
+                # Update the last upgrade check time even if no new agent is available for upgrade
+                self.update_state.last_attempted_hotfix_update_time = now
+                self.update_state.last_attempted_normal_update_time = now
                 return True
             return False
 
-    def __update_last_attempt_update_times(self):
-        now = datetime.datetime.now()
-        if self._is_requested_version_update:
-            self.update_state.last_attempted_requested_version_update_time = now
-        else:
-            self.update_state.last_attempted_normal_update_time = now
-            self.update_state.last_attempted_hotfix_update_time = now
-
     def __should_agent_attempt_manifest_download(self):
         """
         The agent should attempt to download the manifest if
@@ -306,6 +302,7 @@ class AgentUpdateHandler(object):
                 if not self.__should_agent_attempt_manifest_download():
                     return
                 if conf.get_enable_ga_versioning():  # log the warning only when ga versioning is enabled
+                    # TODO: Need to revisit this msg when version is missing in Goal state. We may need to handle better way to report the error
                     warn_msg = "Missing requested version in agent family: {0} for incarnation: {1}, fallback to largest version update".format(self._ga_family, self._gs_id)
                     GAUpdateReportState.report_error_msg = warn_msg
                 agent_manifest = goal_state.fetch_agent_manifest(agent_family.name, agent_family.uris)
@@ -319,49 +316,46 @@ class AgentUpdateHandler(object):
                 if "Missing requested version" in GAUpdateReportState.report_error_msg:
                     GAUpdateReportState.report_error_msg = ""
 
-            if requested_version == CURRENT_VERSION:
+            # Check if an update is allowed and update next upgrade times even if no new agent is available for upgrade
+            if not self.__check_if_agent_update_allowed_and_update_next_upgrade_times(requested_version):
                 return
 
-            # Check if an update is allowed
-            if not self.__should_update_agent(requested_version):
+            if requested_version == CURRENT_VERSION:
                 return
 
             if warn_msg != "":
                 self.__log_event(LogLevel.WARNING, warn_msg)
 
-            try:
-                # Downgrades are not allowed for self-update version
-                # Added it in try block after agent update timewindow check so that we don't log it too frequently
-                if not self.__check_if_downgrade_is_requested_and_allowed(requested_version):
-                    return
-
-                daemon_version = self.__get_daemon_version_for_update()
-                if requested_version < daemon_version:
-                    # Don't process the update if the requested version is less than daemon version,
-                    # as historically we don't support downgrades below daemon versions. So daemon will not pickup that requested version rather start with
-                    # installed latest version again. When that happens agent go into loop of downloading the requested version, exiting and start again with same version.
-                    #
-                    raise AgentUpdateError("The Agent received a request to downgrade to version {0}, but downgrading to a version less than "
-                                           "the Agent installed on the image ({1}) is not supported. Skipping downgrade.".format(requested_version, daemon_version))
-
-                msg = "Goal state {0} is requesting a new agent version {1}, will update the agent before processing the goal state.".format(
-                    self._gs_id, str(requested_version))
-                self.__log_event(LogLevel.INFO, msg)
-
-                agent = self.__download_and_get_agent(goal_state, agent_family, agent_manifest, requested_version)
+            # Downgrades are not allowed for self-update version
+            if not self.__check_if_downgrade_is_requested_and_allowed(requested_version):
+                return
 
-                if agent.is_blacklisted or not agent.is_downloaded:
-                    msg = "Downloaded agent version is in bad state : {0} , skipping agent update".format(
-                        str(agent.version))
-                    self.__log_event(LogLevel.WARNING, msg)
-                    return
+            daemon_version = self.__get_daemon_version_for_update()
+            if requested_version < daemon_version:
+                # Don't process the update if the requested version is less than daemon version,
+                # as historically we don't support downgrades below daemon versions. So daemon will not pickup that requested version rather start with
+                # installed latest version again. When that happens agent go into loop of downloading the requested version, exiting and start again with same version.
+                #
+                raise AgentUpdateError("The Agent received a request to downgrade to version {0}, but downgrading to a version less than "
+                                       "the Agent installed on the image ({1}) is not supported. Skipping downgrade.".format(requested_version, daemon_version))
+
+            # Todo: Need to update the message when we fix RSM stuff
+            msg = "Self-update discovered new agent version:{0} in agent manifest for goal state {1}, will update the agent before processing the goal state.".format(
+                str(requested_version), self._gs_id)
+            self.__log_event(LogLevel.INFO, msg)
+
+            agent = self.__download_and_get_agent(goal_state, agent_family, agent_manifest, requested_version)
+
+            if agent.is_blacklisted or not agent.is_downloaded:
+                msg = "Downloaded agent version is in bad state : {0} , skipping agent update".format(
+                    str(agent.version))
+                self.__log_event(LogLevel.WARNING, msg)
+                return
 
-                # We delete the directory and the zip package from the filesystem except current version and target version
-                self.__purge_extra_agents_from_disk(CURRENT_VERSION, known_agents=[agent])
-                self.__proceed_with_update(requested_version)
+            # We delete the directory and the zip package from the filesystem except current version and target version
+            self.__purge_extra_agents_from_disk(CURRENT_VERSION, known_agents=[agent])
+            self.__proceed_with_update(requested_version)
 
-            finally:
-                self.__update_last_attempt_update_times()
 
         except Exception as err:
             if isinstance(err, AgentUpgradeExitException):


Test patch:
diff --git a/tests/ga/test_agent_update_handler.py b/tests/ga/test_agent_update_handler.py
index d3871641..d91cbb80 100644
--- a/tests/ga/test_agent_update_handler.py
+++ b/tests/ga/test_agent_update_handler.py
@@ -77,7 +77,7 @@ class TestAgentUpdate(UpdateTestCase):
 
     def __assert_agent_requested_version_in_goal_state(self, mock_telemetry, inc=1, version="9.9.9.10"):
         upgrade_event_msgs = [kwarg['message'] for _, kwarg in mock_telemetry.call_args_list if
-                              'Goal state incarnation_{0} is requesting a new agent version {1}'.format(inc, version) in kwarg['message'] and kwarg[
+                              'discovered new agent version:{0} in agent manifest for goal state incarnation_{1}'.format(version, inc) in kwarg['message'] and kwarg[
                                   'op'] == WALAEventOperation.AgentUpgrade]
         self.assertEqual(1, len(upgrade_event_msgs),
                          "Did not find the event indicating that the agent requested version found. Got: {0}".format(
diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py
index b3cf7215..4c58c850 100644
--- a/tests/ga/test_update.py
+++ b/tests/ga/test_update.py
@@ -1593,7 +1593,7 @@ class TestAgentUpgrade(UpdateTestCase):
                       kwarg['op'] in (WALAEventOperation.AgentUpgrade, WALAEventOperation.Download)]
         # This will throw if corresponding message not found so not asserting on that
         requested_version_found = next(kwarg for kwarg in agent_msgs if
-                                       "Goal state incarnation_1 is requesting a new agent version 5.2.1.0, will update the agent before processing the goal state" in kwarg['message'])
+                                       "discovered new agent version:5.2.1.0 in agent manifest for goal state incarnation_1, will update the agent before processing the goal state" in kwarg['message'])
         self.assertTrue(requested_version_found['is_success'],
                         "The requested version found op should be reported as a success")