Repository: Azure/WALinuxAgent
Pull request #2261 (2021-05-25 19:45:33)

Enable MultiConfig + Minor fixes
<!-- DO NOT DELETE THIS TEMPLATE -->

## Description
Enable MultiConfig + Minor fixes

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]: # [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) Report
> Merging [#2261](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) (ecd6514) into [develop](https://codecov.io/gh/Azure/WALinuxAgent/commit/dfcd8354e7a9f3e7bdcdd6fc33bb596f5c6f5e17?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) (dfcd835) will **decrease** coverage by `0.04%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head ecd6514 differs from pull request most recent head 576facc. Consider uploading reports for the commit 576facc to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261/graphs/tree.svg?width=650&height=150&src=pr&token=ae90E2KYCw&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure)](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure)

```diff
@@             Coverage Diff             @@
##           develop    #2261      +/-   ##
===========================================
- Coverage    70.87%   70.82%   -0.05%     
===========================================
  Files           96       96              
  Lines        13809    13812       +3     
  Branches      1970     1972       +2     
===========================================
- Hits          9787     9783       -4     
- Misses        3603     3606       +3     
- Partials       419      423       +4     
```


| [Impacted Files](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | Coverage Δ | |
|---|---|---|
| [azurelinuxagent/common/agent\_supported\_feature.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9hZ2VudF9zdXBwb3J0ZWRfZmVhdHVyZS5weQ==) | `100.00% <100.00%> (ø)` | |
| [azurelinuxagent/common/utils/archive.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi91dGlscy9hcmNoaXZlLnB5) | `83.67% <100.00%> (ø)` | |
| [azurelinuxagent/ga/exthandlers.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#diff-YXp1cmVsaW51eGFnZW50L2dhL2V4dGhhbmRsZXJzLnB5) | `86.83% <100.00%> (+0.02%)` | :arrow_up: |
| [azurelinuxagent/ga/collect\_telemetry\_events.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#diff-YXp1cmVsaW51eGFnZW50L2dhL2NvbGxlY3RfdGVsZW1ldHJ5X2V2ZW50cy5weQ==) | `89.72% <0.00%> (-2.40%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure). Last update [dfcd835...576facc](https://codecov.io/gh/Azure/WALinuxAgent/pull/2261?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure).


Patch:
diff --git a/azurelinuxagent/common/agent_supported_feature.py b/azurelinuxagent/common/agent_supported_feature.py
index ed2044d6..e10abfec 100644
--- a/azurelinuxagent/common/agent_supported_feature.py
+++ b/azurelinuxagent/common/agent_supported_feature.py
@@ -51,7 +51,7 @@ class _MultiConfigFeature(AgentSupportedFeature):
 
     __NAME = SupportedFeatureNames.MultiConfig
     __VERSION = "1.0"
-    __SUPPORTED = False
+    __SUPPORTED = True
 
     def __init__(self):
         super(_MultiConfigFeature, self).__init__(name=_MultiConfigFeature.__NAME,
diff --git a/azurelinuxagent/common/utils/archive.py b/azurelinuxagent/common/utils/archive.py
index e87c50ee..e18b1b06 100644
--- a/azurelinuxagent/common/utils/archive.py
+++ b/azurelinuxagent/common/utils/archive.py
@@ -36,7 +36,7 @@ The timestamp is an ISO8601 formatted value.
 """
 # pylint: enable=W0105
 
-_ARCHIVE_DIRECTORY_NAME = 'history'
+ARCHIVE_DIRECTORY_NAME = 'history'
 
 _MAX_ARCHIVED_STATES = 50
 
@@ -59,7 +59,7 @@ class StateFlusher(object):
     def __init__(self, lib_dir):
         self._source = lib_dir
 
-        directory = os.path.join(self._source, _ARCHIVE_DIRECTORY_NAME)
+        directory = os.path.join(self._source, ARCHIVE_DIRECTORY_NAME)
         if not os.path.exists(directory):
             try:
                 fileutil.mkdir(directory)
@@ -82,7 +82,7 @@ class StateFlusher(object):
             self._purge(files)
 
     def history_dir(self, name):
-        return os.path.join(self._source, _ARCHIVE_DIRECTORY_NAME, name)
+        return os.path.join(self._source, ARCHIVE_DIRECTORY_NAME, name)
 
     @staticmethod
     def _get_archive_name(files):
@@ -209,7 +209,7 @@ class StateDirectory(State):
 
 class StateArchiver(object):
     def __init__(self, lib_dir):
-        self._source = os.path.join(lib_dir, _ARCHIVE_DIRECTORY_NAME)
+        self._source = os.path.join(lib_dir, ARCHIVE_DIRECTORY_NAME)
 
         if not os.path.isdir(self._source):
             try:
diff --git a/azurelinuxagent/ga/exthandlers.py b/azurelinuxagent/ga/exthandlers.py
index c340fea3..af99cfcf 100644
--- a/azurelinuxagent/ga/exthandlers.py
+++ b/azurelinuxagent/ga/exthandlers.py
@@ -50,6 +50,7 @@ from azurelinuxagent.common.exception import ExtensionDownloadError, ExtensionEr
 from azurelinuxagent.common.future import ustr, is_file_not_found_error
 from azurelinuxagent.common.protocol.restapi import ExtensionStatus, ExtensionSubStatus, ExtHandler, ExtHandlerStatus, \
     VMStatus, GoalStateAggregateStatus, ExtensionState, ExtHandlerRequestedState, Extension
+from azurelinuxagent.common.utils.archive import ARCHIVE_DIRECTORY_NAME
 from azurelinuxagent.common.utils.flexible_version import FlexibleVersion
 from azurelinuxagent.common.version import AGENT_NAME, CURRENT_VERSION, DISTRO_NAME, DISTRO_VERSION, \
     GOAL_STATE_AGENT_VERSION, PY_VERSION_MAJOR, PY_VERSION_MICRO, PY_VERSION_MINOR
@@ -257,11 +258,14 @@ def get_exthandlers_handler(protocol):
     return ExtHandlersHandler(protocol)
 
 
-def list_agent_lib_directory(skip_agent_package=True):
+def list_agent_lib_directory(skip_agent_package=True, ignore_names=None):
     lib_dir = conf.get_lib_dir()
     for name in os.listdir(lib_dir):
         path = os.path.join(lib_dir, name)
 
+        if ignore_names is not None and any(ignore_names) and name in ignore_names:
+            continue
+
         if skip_agent_package and (version.is_agent_package(path) or version.is_agent_path(path)):
             continue
 
@@ -867,7 +871,9 @@ class ExtHandlersHandler(object):
 
     def __get_handlers_on_file_system(self, incarnation_changed):
         handlers_to_report = []
-        for item, path in list_agent_lib_directory(skip_agent_package=True):
+        # Ignoring the `history` and `events` directories as they're not handlers and are agent-generated
+        for item, path in list_agent_lib_directory(skip_agent_package=True,
+                                                   ignore_names=[EVENTS_DIRECTORY, ARCHIVE_DIRECTORY_NAME]):
             try:
                 handler_instance = ExtHandlersHandler.get_ext_handler_instance_from_path(name=item,
                                                                                          path=path,
@@ -973,7 +979,9 @@ class ExtHandlersHandler(object):
             "goal_state_version": str(GOAL_STATE_AGENT_VERSION),
             "distro_details": "{0}:{1}".format(DISTRO_NAME, DISTRO_VERSION),
             "last_successful_status_upload_time": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
-            "python_version": "Python: {0}.{1}.{2}".format(PY_VERSION_MAJOR, PY_VERSION_MINOR, PY_VERSION_MICRO)
+            "python_version": "Python: {0}.{1}.{2}".format(PY_VERSION_MAJOR, PY_VERSION_MINOR, PY_VERSION_MICRO),
+            "crp_supported_features": [name for name, _ in get_agent_supported_features_list_for_crp().items()],
+            "extension_supported_features": [name for name, _ in get_agent_supported_features_list_for_extensions().items()]
         }
 
         # Convert VMStatus class to Dict.


Test patch:
diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py
index 22de84fa..a6ed9cbd 100644
--- a/tests/ga/test_extension.py
+++ b/tests/ga/test_extension.py
@@ -28,6 +28,8 @@ import unittest
 import uuid
 
 from azurelinuxagent.common import conf
+from azurelinuxagent.common.agent_supported_feature import get_agent_supported_features_list_for_crp, \
+    get_agent_supported_features_list_for_extensions
 from azurelinuxagent.common.cgroupconfigurator import CGroupConfigurator
 from azurelinuxagent.common.datacontract import get_properties
 from azurelinuxagent.common.event import WALAEventOperation
@@ -1051,54 +1053,49 @@ class TestExtension(AgentTestCase):
 
     @patch('time.gmtime', MagicMock(return_value=time.gmtime(0)))
     def test_ext_handler_reporting_status_file(self, *args):
-        expected_status = '''
-{{
-    "agent_name": "{agent_name}",
-    "current_version": "{current_version}",
-    "goal_state_version": "{goal_state_version}",
-    "distro_details": "{distro_details}",
-    "last_successful_status_upload_time": "{last_successful_status_upload_time}",
-    "python_version": "{python_version}",
-    "extensions_status": [
-        {{
-            "name": "OSTCExtensions.ExampleHandlerLinux",
-            "version": "1.0.0",
-            "status": "Ready",
-            "supports_multi_config": false
-        }},
-        {{
-            "name": "Microsoft.Powershell.ExampleExtension",
-            "version": "1.0.0",
-            "status": "Ready",
-            "supports_multi_config": false
-        }},
-        {{
-            "name": "Microsoft.EnterpriseCloud.Monitoring.ExampleHandlerLinux",
-            "version": "1.0.0",
-            "status": "Ready",
-            "supports_multi_config": false
-        }},
-        {{
-            "name": "Microsoft.CPlat.Core.ExampleExtensionLinux",
-            "version": "1.0.0",
-            "status": "Ready",
-            "supports_multi_config": false
-        }},
-        {{
-            "name": "Microsoft.OSTCExtensions.Edp.ExampleExtensionLinuxInTest",
-            "version": "1.0.0",
-            "status": "Ready",
-            "supports_multi_config": false
-        }}
-    ]
-}}'''.format(agent_name=AGENT_NAME,
-             current_version=str(CURRENT_VERSION),
-             goal_state_version=str(GOAL_STATE_AGENT_VERSION),
-             distro_details="{0}:{1}".format(DISTRO_NAME, DISTRO_VERSION),
-             last_successful_status_upload_time=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
-             python_version="Python: {0}.{1}.{2}".format(PY_VERSION_MAJOR, PY_VERSION_MINOR, PY_VERSION_MICRO))
-
-        expected_status_json = json.loads(expected_status)
+
+        expected_status = {
+            "agent_name": AGENT_NAME,
+            "current_version": str(CURRENT_VERSION),
+            "goal_state_version": str(GOAL_STATE_AGENT_VERSION),
+            "distro_details": "{0}:{1}".format(DISTRO_NAME, DISTRO_VERSION),
+            "last_successful_status_upload_time": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
+            "python_version": "Python: {0}.{1}.{2}".format(PY_VERSION_MAJOR, PY_VERSION_MINOR, PY_VERSION_MICRO),
+            "crp_supported_features": [name for name, _ in get_agent_supported_features_list_for_crp().items()],
+            "extension_supported_features": [name for name, _ in get_agent_supported_features_list_for_extensions().items()],
+            "extensions_status": [
+                {
+                    "name": "OSTCExtensions.ExampleHandlerLinux",
+                    "version": "1.0.0",
+                    "status": "Ready",
+                    "supports_multi_config": False
+                },
+                {
+                    "name": "Microsoft.Powershell.ExampleExtension",
+                    "version": "1.0.0",
+                    "status": "Ready",
+                    "supports_multi_config": False
+                },
+                {
+                    "name": "Microsoft.EnterpriseCloud.Monitoring.ExampleHandlerLinux",
+                    "version": "1.0.0",
+                    "status": "Ready",
+                    "supports_multi_config": False
+                },
+                {
+                    "name": "Microsoft.CPlat.Core.ExampleExtensionLinux",
+                    "version": "1.0.0",
+                    "status": "Ready",
+                    "supports_multi_config": False
+                },
+                {
+                    "name": "Microsoft.OSTCExtensions.Edp.ExampleExtensionLinuxInTest",
+                    "version": "1.0.0",
+                    "status": "Ready",
+                    "supports_multi_config": False
+                }
+            ]
+        }
 
         test_data = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE_MULTIPLE_EXT)
         exthandlers_handler, protocol = self._create_mock(test_data, *args)  # pylint: disable=unused-variable,no-value-for-parameter
@@ -1107,7 +1104,7 @@ class TestExtension(AgentTestCase):
         status_path = os.path.join(conf.get_lib_dir(), AGENT_STATUS_FILE)
         actual_status_json = json.loads(fileutil.read_file(status_path))
 
-        self.assertEqual(expected_status_json, actual_status_json)
+        self.assertEqual(expected_status, actual_status_json)
 
     def test_ext_handler_rollingupgrade(self, *args):
         # Test enable scenario.
diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py
index f26ab1b6..5efe0d46 100644
--- a/tests/protocol/test_wire.py
+++ b/tests/protocol/test_wire.py
@@ -360,11 +360,22 @@ class TestWireProtocol(AgentTestCase):
             'guestAgentStatus': v1_ga_status,
             'handlerAggregateStatus': []
         }
+
+        supported_features = []
+        for _, feature in get_agent_supported_features_list_for_crp().items():
+            supported_features.append(
+                {
+                    "Key": feature.name,
+                    "Value": feature.version
+                }
+            )
+
         v1_vm_status = {
             'version': '1.1',
             'timestampUTC': timestamp,
             'aggregateStatus': v1_agg_status,
-            'guestOSInfo': v1_ga_guest_info
+            'guestOSInfo': v1_ga_guest_info,
+            'supportedFeatures': supported_features
         }
         self.assertEqual(json.dumps(v1_vm_status), actual.to_json())