Repository: Azure/WALinuxAgent
Pull request #2181 (2021-02-19 19:19:11)

Add agent supported features list needed by Extensions 
<!-- DO NOT DELETE THIS TEMPLATE -->

## Description
This PR adds the ability for the agent to pass the list of supported features needed by extensions (like ETP). 
It also does a bit of refactoring around the code.

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/2181?src=pr&el=h1) Report
> Merging [#2181](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181?src=pr&el=desc) (14cdd85) into [release-2.2.54](https://codecov.io/gh/Azure/WALinuxAgent/commit/0c1768e7c2a7a5e5677db9edc08150b2eaa86bf4?el=desc) (0c1768e) will **increase** coverage by `0.07%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181/graphs/tree.svg?width=650&height=150&src=pr&token=ae90E2KYCw)](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181?src=pr&el=tree)

```diff
@@                Coverage Diff                 @@
##           release-2.2.54    #2181      +/-   ##
==================================================
+ Coverage           70.73%   70.81%   +0.07%     
==================================================
  Files                  96       96              
  Lines               13379    13399      +20     
  Branches             1878     1882       +4     
==================================================
+ Hits                 9464     9488      +24     
+ Misses               3502     3500       -2     
+ Partials              413      411       -2     
```


| [Impacted Files](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [azurelinuxagent/common/agent\_supported\_feature.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9hZ2VudF9zdXBwb3J0ZWRfZmVhdHVyZS5weQ==) | `100.00% <100.00%> (ø)` | |
| [azurelinuxagent/ga/collect\_telemetry\_events.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL2NvbGxlY3RfdGVsZW1ldHJ5X2V2ZW50cy5weQ==) | `91.09% <100.00%> (+1.44%)` | :arrow_up: |
| [azurelinuxagent/ga/exthandlers.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL2V4dGhhbmRsZXJzLnB5) | `86.88% <100.00%> (+0.06%)` | :arrow_up: |
| [azurelinuxagent/ga/send\_telemetry\_events.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL3NlbmRfdGVsZW1ldHJ5X2V2ZW50cy5weQ==) | `95.12% <100.00%> (ø)` | |
| [azurelinuxagent/ga/update.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL3VwZGF0ZS5weQ==) | `88.40% <100.00%> (+0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181?src=pr&el=footer). Last update [0c1768e...f8fa653](https://codecov.io/gh/Azure/WALinuxAgent/pull/2181?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/azurelinuxagent/common/agent_supported_feature.py b/azurelinuxagent/common/agent_supported_feature.py
index 5b0f7f7f..ef5463cd 100644
--- a/azurelinuxagent/common/agent_supported_feature.py
+++ b/azurelinuxagent/common/agent_supported_feature.py
@@ -16,11 +16,12 @@
 #
 
 
-class CRPSupportedFeatureNames(object):
+class SupportedFeatureNames(object):
     """
-    Enum for defining the Feature Names for all features that we report back to CRP
+    Enum for defining the Feature Names for all features that we the agent supports
     """
     MultiConfig = "MultipleExtensionsPerHandler"
+    ExtensionTelemetryPipeline = "ExtensionTelemetryPipeline"
 
 
 class AgentSupportedFeature(object):
@@ -48,7 +49,7 @@ class AgentSupportedFeature(object):
 
 class _MultiConfigFeature(AgentSupportedFeature):
 
-    __NAME = CRPSupportedFeatureNames.MultiConfig
+    __NAME = SupportedFeatureNames.MultiConfig
     __VERSION = "1.0"
     __SUPPORTED = False
 
@@ -58,14 +59,36 @@ class _MultiConfigFeature(AgentSupportedFeature):
                                                   supported=_MultiConfigFeature.__SUPPORTED)
 
 
-__CRP_SUPPORTED_FEATURES = {
-    CRPSupportedFeatureNames.MultiConfig: _MultiConfigFeature()
+class _ETPFeature(AgentSupportedFeature):
+
+    __NAME = SupportedFeatureNames.ExtensionTelemetryPipeline
+    __VERSION = "1.0"
+    __SUPPORTED = True
+
+    def __init__(self):
+        super(_ETPFeature, self).__init__(name=self.__NAME,
+                                          version=self.__VERSION,
+                                          supported=self.__SUPPORTED)
+
+
+# This is the list of features that Agent supports and we advertise to CRP
+__CRP_ADVERTISED_FEATURES = {
+    SupportedFeatureNames.MultiConfig: _MultiConfigFeature()
+}
+
+
+# This is the list of features that Agent supports and we advertise to Extensions
+__EXTENSION_ADVERTISED_FEATURES = {
+    SupportedFeatureNames.ExtensionTelemetryPipeline: _ETPFeature()
 }
 
 
 def get_supported_feature_by_name(feature_name):
-    if feature_name in __CRP_SUPPORTED_FEATURES:
-        return __CRP_SUPPORTED_FEATURES[feature_name]
+    if feature_name in __CRP_ADVERTISED_FEATURES:
+        return __CRP_ADVERTISED_FEATURES[feature_name]
+
+    if feature_name in __EXTENSION_ADVERTISED_FEATURES:
+        return __EXTENSION_ADVERTISED_FEATURES[feature_name]
 
     raise NotImplementedError("Feature with Name: {0} not found".format(feature_name))
 
@@ -80,4 +103,18 @@ def get_agent_supported_features_list_for_crp():
                 MultipleExtensionsPerHandler: _MultiConfigFeature()
             }
     """
-    return __CRP_SUPPORTED_FEATURES
+    return __CRP_ADVERTISED_FEATURES
+
+
+def get_agent_supported_features_list_for_extensions():
+    """
+    List of features that the GuestAgent currently supports (like Extension Telemetry Pipeline, etc) needed by Extensions.
+    We need to send this list as environment variables when calling extension commands to inform Extensions of all the
+    features the agent supports.
+    :return: Dict containing all Extension supported features with the key as their names and the AgentFeature object as
+             the value
+        Eg: {
+                CRPSupportedFeatureNames.ExtensionTelemetryPipeline: _ETPFeature()
+            }
+    """
+    return __EXTENSION_ADVERTISED_FEATURES
diff --git a/azurelinuxagent/ga/collect_telemetry_events.py b/azurelinuxagent/ga/collect_telemetry_events.py
index 94d4d68e..ee02474e 100644
--- a/azurelinuxagent/ga/collect_telemetry_events.py
+++ b/azurelinuxagent/ga/collect_telemetry_events.py
@@ -26,6 +26,7 @@ from collections import defaultdict
 
 import azurelinuxagent.common.logger as logger
 from azurelinuxagent.common import conf
+from azurelinuxagent.common.agent_supported_feature import get_supported_feature_by_name, SupportedFeatureNames
 from azurelinuxagent.common.event import EVENTS_DIRECTORY, TELEMETRY_LOG_EVENT_ID, \
     TELEMETRY_LOG_PROVIDER_ID, add_event, WALAEventOperation, add_log_event, get_event_logger, \
     CollectOrReportEventDebugInfo, EVENT_FILE_REGEX, parse_event
@@ -34,7 +35,7 @@ from azurelinuxagent.common.future import ustr
 from azurelinuxagent.common.interfaces import ThreadHandlerInterface
 from azurelinuxagent.common.telemetryevent import TelemetryEvent, TelemetryEventParam, \
     GuestAgentGenericLogsSchema, GuestAgentExtensionEventsSchema
-from azurelinuxagent.ga.exthandlers import HANDLER_NAME_PATTERN, is_extension_telemetry_pipeline_enabled
+from azurelinuxagent.ga.exthandlers import HANDLER_NAME_PATTERN
 from azurelinuxagent.ga.periodic_operation import PeriodicOperation
 
 
@@ -561,9 +562,10 @@ class CollectTelemetryEventsHandler(ThreadHandlerInterface):
             _CollectAndEnqueueEventsPeriodicOperation(self._send_telemetry_events_handler)
         ]
 
+        is_etp_enabled = get_supported_feature_by_name(SupportedFeatureNames.ExtensionTelemetryPipeline).is_supported
         logger.info("Extension Telemetry pipeline enabled: {0}".format(
-            is_extension_telemetry_pipeline_enabled()))
-        if is_extension_telemetry_pipeline_enabled():
+            is_etp_enabled))
+        if is_etp_enabled:
             periodic_operations.append(_ProcessExtensionEventsPeriodicOperation(self._send_telemetry_events_handler))
 
         logger.info("Successfully started the {0} thread".format(self.get_thread_name()))
diff --git a/azurelinuxagent/ga/exthandlers.py b/azurelinuxagent/ga/exthandlers.py
index 0d23d8ea..ca39ddf0 100644
--- a/azurelinuxagent/ga/exthandlers.py
+++ b/azurelinuxagent/ga/exthandlers.py
@@ -36,6 +36,8 @@ import azurelinuxagent.common.conf as conf
 import azurelinuxagent.common.logger as logger
 import azurelinuxagent.common.utils.fileutil as fileutil
 import azurelinuxagent.common.version as version
+from azurelinuxagent.common.agent_supported_feature import get_agent_supported_features_list_for_extensions, \
+    SupportedFeatureNames, get_supported_feature_by_name
 from azurelinuxagent.common.cgroupconfigurator import CGroupConfigurator
 from azurelinuxagent.common.datacontract import get_properties, set_properties
 from azurelinuxagent.common.errorstate import ErrorState
@@ -81,12 +83,6 @@ _TRUNCATED_SUFFIX = u" ... [TRUNCATED]"
 _NUM_OF_STATUS_FILE_RETRIES = 5
 _STATUS_FILE_RETRY_DELAY = 2  # seconds
 
-_ENABLE_EXTENSION_TELEMETRY_PIPELINE = True
-
-
-def is_extension_telemetry_pipeline_enabled():
-    return _ENABLE_EXTENSION_TELEMETRY_PIPELINE
-
 
 class ValidHandlerStatus(object):
     transitioning = "transitioning"
@@ -108,6 +104,7 @@ class ExtCommandEnvVariable(object):
     ExtensionSeqNumber = "ConfigSequenceNumber"  # At par with Windows Guest Agent
     UpdatingFromVersion = "{0}_UPDATING_FROM_VERSION".format(Prefix)
     WireProtocolAddress = "{0}_WIRE_PROTOCOL_ADDRESS".format(Prefix)
+    ExtensionSupportedFeatures = "{0}_EXTENSION_SUPPORTED_FEATURES".format(Prefix)
 
 
 def get_traceback(e):  # pylint: disable=R1710
@@ -1049,7 +1046,7 @@ class ExtHandlerInstance(object):
             conf_dir = self.get_conf_dir()
             fileutil.mkdir(conf_dir, mode=0o700)
 
-            if is_extension_telemetry_pipeline_enabled():
+            if get_supported_feature_by_name(SupportedFeatureNames.ExtensionTelemetryPipeline).is_supported:
                 fileutil.mkdir(self.get_extension_events_dir(), mode=0o700)
 
             seq_no, status_path = self.get_status_file_path()  # pylint: disable=W0612
@@ -1410,6 +1407,20 @@ class ExtHandlerInstance(object):
                     ExtCommandEnvVariable.WireProtocolAddress: self.protocol.get_endpoint()
                 })
 
+                supported_features = []
+                for _, feature in get_agent_supported_features_list_for_extensions().items():
+                    if feature.is_supported:
+                        supported_features.append(
+                            {
+                                "Key": feature.name,
+                                "Value": feature.version
+                            }
+                        )
+                if supported_features:
+                    env.update({
+                        ExtCommandEnvVariable.ExtensionSupportedFeatures: json.dumps(supported_features)
+                    })
+
                 try:
                     # Some extensions erroneously begin cmd with a slash; don't interpret those
                     # as root-relative. (Issue #1170)
@@ -1492,7 +1503,7 @@ class ExtHandlerInstance(object):
                 HandlerEnvironment.heartbeatFile: self.get_heartbeat_file() 
             }
 
-        if is_extension_telemetry_pipeline_enabled():
+        if get_supported_feature_by_name(SupportedFeatureNames.ExtensionTelemetryPipeline).is_supported:
             handler_env[HandlerEnvironment.eventsFolder] = self.get_extension_events_dir()
 
         env = [{
diff --git a/azurelinuxagent/ga/send_telemetry_events.py b/azurelinuxagent/ga/send_telemetry_events.py
index 6bccfa17..0f52686c 100644
--- a/azurelinuxagent/ga/send_telemetry_events.py
+++ b/azurelinuxagent/ga/send_telemetry_events.py
@@ -43,7 +43,6 @@ class SendTelemetryEventsHandler(ThreadHandlerInterface):
     _MIN_EVENTS_TO_BATCH = 30
     _MIN_BATCH_WAIT_TIME = datetime.timedelta(seconds=5)
 
-
     def __init__(self, protocol_util):
         self._protocol = protocol_util.get_protocol()
         self.should_run = True
@@ -63,7 +62,7 @@ class SendTelemetryEventsHandler(ThreadHandlerInterface):
         return SendTelemetryEventsHandler._THREAD_NAME
 
     def run(self):
-        logger.info("Start Extension Telemetry service.")
+        logger.info("Start SendTelemetryHandler service.")
         self.start()
 
     def is_alive(self):
diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py
index 1dc3c2b1..c5a9864b 100644
--- a/azurelinuxagent/ga/update.py
+++ b/azurelinuxagent/ga/update.py
@@ -38,6 +38,7 @@ import azurelinuxagent.common.logger as logger
 import azurelinuxagent.common.utils.fileutil as fileutil
 import azurelinuxagent.common.utils.restutil as restutil
 import azurelinuxagent.common.utils.textutil as textutil
+from azurelinuxagent.common.agent_supported_feature import get_supported_feature_by_name, SupportedFeatureNames
 from azurelinuxagent.common.persist_firewall_rules import PersistFirewallRulesHandler
 from azurelinuxagent.common.cgroupapi import CGroupsApi
 from azurelinuxagent.common.cgroupconfigurator import CGroupConfigurator
@@ -57,8 +58,7 @@ from azurelinuxagent.ga.collect_logs import get_collect_logs_handler, is_log_col
 from azurelinuxagent.ga.env import get_env_handler
 from azurelinuxagent.ga.collect_telemetry_events import get_collect_telemetry_events_handler
 
-from azurelinuxagent.ga.exthandlers import HandlerManifest, get_traceback, ExtHandlersHandler, list_agent_lib_directory, \
-    is_extension_telemetry_pipeline_enabled
+from azurelinuxagent.ga.exthandlers import HandlerManifest, get_traceback, ExtHandlersHandler, list_agent_lib_directory
 from azurelinuxagent.ga.monitor import get_monitor_handler
 
 from azurelinuxagent.ga.send_telemetry_events import get_send_telemetry_events_handler
@@ -822,7 +822,7 @@ class UpdateHandler(object):
                 continue
 
         try:
-            if not is_extension_telemetry_pipeline_enabled():
+            if not get_supported_feature_by_name(SupportedFeatureNames.ExtensionTelemetryPipeline).is_supported:
                 # If extension telemetry pipeline is disabled, ensure we delete all existing extension events directory
                 # because the agent will not be listening on those events.
                 extension_event_dirs = glob.glob(os.path.join(conf.get_ext_log_dir(), "*", EVENTS_DIRECTORY))


Test patch:
diff --git a/tests/common/test_agent_supported_feature.py b/tests/common/test_agent_supported_feature.py
index 29574b74..fcae9ec6 100644
--- a/tests/common/test_agent_supported_feature.py
+++ b/tests/common/test_agent_supported_feature.py
@@ -14,20 +14,29 @@
 #
 # Requires Python 2.6+ and Openssl 1.0+
 #
-from azurelinuxagent.common.agent_supported_feature import CRPSupportedFeatureNames, \
-    get_agent_supported_features_list_for_crp, get_supported_feature_by_name
+from azurelinuxagent.common.agent_supported_feature import SupportedFeatureNames, \
+    get_agent_supported_features_list_for_crp, get_supported_feature_by_name, \
+    get_agent_supported_features_list_for_extensions
 from tests.tools import AgentTestCase
 
 
 class TestAgentSupportedFeature(AgentTestCase):
 
     def test_it_should_return_features_properly(self):
-        self.assertIn(CRPSupportedFeatureNames.MultiConfig, get_agent_supported_features_list_for_crp(),
+        self.assertIn(SupportedFeatureNames.MultiConfig, get_agent_supported_features_list_for_crp(),
                       "Multi-config should be fetched in crp_supported_features")
-        self.assertEqual(CRPSupportedFeatureNames.MultiConfig,
-                         get_supported_feature_by_name(CRPSupportedFeatureNames.MultiConfig).name,
+        self.assertEqual(SupportedFeatureNames.MultiConfig,
+                         get_supported_feature_by_name(SupportedFeatureNames.MultiConfig).name,
                          "Invalid/Wrong feature returned")
 
         # Raise error if feature name not found
         with self.assertRaises(NotImplementedError):
             get_supported_feature_by_name("ABC")
+
+    def test_it_should_return_extension_supported_features_properly(self):
+        self.assertIn(SupportedFeatureNames.ExtensionTelemetryPipeline,
+                      get_agent_supported_features_list_for_extensions(), "ETP should be in supported features list")
+
+        self.assertEqual(SupportedFeatureNames.ExtensionTelemetryPipeline,
+                         get_supported_feature_by_name(SupportedFeatureNames.ExtensionTelemetryPipeline).name,
+                         "Invalid/Wrong feature returned")
diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py
index 52a79cd7..d2659691 100644
--- a/tests/ga/test_extension.py
+++ b/tests/ga/test_extension.py
@@ -1063,8 +1063,7 @@ class TestExtension(ExtensionTestCase):
         for enable_extensions in [False, True]:
             tmp_lib_dir = tempfile.mkdtemp(prefix="ExtensionEnabled{0}".format(enable_extensions))
             with patch("azurelinuxagent.common.conf.get_lib_dir", return_value=tmp_lib_dir):
-                with patch('azurelinuxagent.ga.exthandlers.is_extension_telemetry_pipeline_enabled',
-                           return_value=enable_extensions):
+                with patch("azurelinuxagent.common.agent_supported_feature._ETPFeature.is_supported", enable_extensions):
                     # Create new object for each run to force re-installation of extensions as we
                     # only create handler_environment on installation
                     test_data = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE_MULTIPLE_EXT)
@@ -1099,7 +1098,7 @@ class TestExtension(ExtensionTestCase):
         test_data = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE)
         exthandlers_handler, protocol = self._create_mock(test_data, *args)  # pylint: disable=no-value-for-parameter
 
-        with patch('azurelinuxagent.ga.exthandlers.is_extension_telemetry_pipeline_enabled', return_value=True):
+        with patch("azurelinuxagent.common.agent_supported_feature._ETPFeature.is_supported", True):
             exthandlers_handler.run()
             self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0")
             self._assert_ext_status(protocol.report_ext_status, "success", 0)
diff --git a/tests/ga/test_exthandlers.py b/tests/ga/test_exthandlers.py
index 343a4d0d..3a547a48 100644
--- a/tests/ga/test_exthandlers.py
+++ b/tests/ga/test_exthandlers.py
@@ -21,6 +21,7 @@ import subprocess
 import time
 import uuid
 
+from azurelinuxagent.common.agent_supported_feature import AgentSupportedFeature
 from azurelinuxagent.common.cgroupconfigurator import CGroupConfigurator
 from azurelinuxagent.common.event import AGENT_EVENT_FILE_EXTENSION, WALAEventOperation
 from azurelinuxagent.common.exception import ExtensionError, ExtensionErrorCodes
@@ -721,7 +722,10 @@ sys.stderr.write("STDERR")
         self.create_script(os.path.join(self.ext_handler_instance.get_base_dir(), test_file), command)
 
         with patch("subprocess.Popen", wraps=subprocess.Popen) as patch_popen:
-            output = ext_handler_instance.launch_command(test_file)
+            # Returning empty list for get_agent_supported_features_list_for_extensions as we have a separate test for it
+            with patch("azurelinuxagent.ga.exthandlers.get_agent_supported_features_list_for_extensions",
+                       return_value={}):
+                output = ext_handler_instance.launch_command(test_file)
 
             args, kwagrs = patch_popen.call_args  # pylint: disable=unused-variable
             without_os_env = dict((k, v) for (k, v) in kwagrs['env'].items() if k not in os.environ)
@@ -732,3 +736,64 @@ sys.stderr.write("STDERR")
             # This check is checking if the expected values are set for the extension commands
             for helper_var in helper_env_vars:
                 self.assertIn("%s=%s" % (helper_var, helper_env_vars[helper_var]), output)
+
+    def test_it_should_pass_supported_features_list_as_environment_variables(self):
+
+        class TestFeature(AgentSupportedFeature):
+
+            def __init__(self, name, version, supported):
+                super(TestFeature, self).__init__(name=name,
+                                                  version=version,
+                                                  supported=supported)
+
+        test_name = str(uuid.uuid4())
+        test_version = str(uuid.uuid4())
+
+        command = "check_env.py"
+        self.create_script(os.path.join(self.ext_handler_instance.get_base_dir(), command), '''
+import os
+import json
+import sys
+
+features = os.getenv("{0}")
+if not features:
+    print("{0} not found in environment")
+    sys.exit(0)
+l = json.loads(features)
+found = False
+for feature in l:
+    if feature['Key'] == "{1}" and feature['Value'] == "{2}":
+        found = True
+        break
+    
+print("Found Feature %s: %s" % ("{1}", found))
+'''.format(ExtCommandEnvVariable.ExtensionSupportedFeatures, test_name, test_version))
+
+        # It should include all supported features and pass it as Environment Variable to extensions
+        test_supported_features = {test_name: TestFeature(name=test_name, version=test_version, supported=True)}
+        with patch("azurelinuxagent.ga.exthandlers.get_agent_supported_features_list_for_extensions",
+                   return_value=test_supported_features):
+            output = self.ext_handler_instance.launch_command(command)
+
+            self.assertIn("[stdout]\nFound Feature {0}: True".format(test_name), output, "Feature not found")
+
+        # It should not include the feature if feature not supported
+        test_supported_features = {
+            test_name: TestFeature(name=test_name, version=test_version, supported=False),
+            "testFeature": TestFeature(name="testFeature", version="1.2.1", supported=True)
+        }
+        with patch("azurelinuxagent.ga.exthandlers.get_agent_supported_features_list_for_extensions",
+                   return_value=test_supported_features):
+            output = self.ext_handler_instance.launch_command(command)
+
+            self.assertIn("[stdout]\nFound Feature {0}: False".format(test_name), output, "Feature wrongfully found")
+
+        # It should not include the SupportedFeatures Key in Environment variables if no features supported
+        test_supported_features = {test_name: TestFeature(name=test_name, version=test_version, supported=False)}
+        with patch("azurelinuxagent.ga.exthandlers.get_agent_supported_features_list_for_extensions",
+                   return_value=test_supported_features):
+            output = self.ext_handler_instance.launch_command(command)
+
+            self.assertIn(
+                "[stdout]\n{0} not found in environment".format(ExtCommandEnvVariable.ExtensionSupportedFeatures),
+                output, "Environment variable should not be found")
diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py
index 91744907..294ccbaf 100644
--- a/tests/ga/test_update.py
+++ b/tests/ga/test_update.py
@@ -1612,7 +1612,7 @@ Description=Slice for Azure VM Extensions""")
 
             # Rerun the update handler and ensure that the HandlerEnvironment file is recreated with eventsFolder
             # flag in HandlerEnvironment.json file
-            with patch('azurelinuxagent.ga.exthandlers.is_extension_telemetry_pipeline_enabled', return_value=True):
+            with patch("azurelinuxagent.common.agent_supported_feature._ETPFeature.is_supported", True):
                 update_handler.set_iterations(1)
                 update_handler.run(debug=True)
 
@@ -1674,7 +1674,7 @@ Description=Slice for Azure VM Extensions""")
     def _setup_test_for_ext_event_dirs_retention(self):
         try:
             with self._get_update_handler(test_data=DATA_FILE_MULTIPLE_EXT) as (update_handler, protocol):
-                with patch('azurelinuxagent.ga.exthandlers._ENABLE_EXTENSION_TELEMETRY_PIPELINE', True):
+                with patch("azurelinuxagent.common.agent_supported_feature._ETPFeature.is_supported", True):
                     update_handler.run(debug=True)
                     expected_events_dirs = glob.glob(os.path.join(conf.get_ext_log_dir(), "*", EVENTS_DIRECTORY))
                     no_of_extensions = protocol.mock_wire_data.get_no_of_plugins_in_extension_config()
@@ -1694,7 +1694,7 @@ Description=Slice for Azure VM Extensions""")
     def test_it_should_delete_extension_events_directory_if_extension_telemetry_pipeline_disabled(self):
         # Disable extension telemetry pipeline and ensure events directory got deleted
         with self._setup_test_for_ext_event_dirs_retention() as (update_handler, expected_events_dirs):
-            with patch('azurelinuxagent.ga.exthandlers._ENABLE_EXTENSION_TELEMETRY_PIPELINE', False):
+            with patch("azurelinuxagent.common.agent_supported_feature._ETPFeature.is_supported", False):
                 update_handler.run(debug=True)
                 for ext_dir in expected_events_dirs:
                     self.assertFalse(os.path.exists(ext_dir), "Extension directory {0} still exists!".format(ext_dir))
diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py
index 86b37069..e30439e3 100644
--- a/tests/protocol/test_wire.py
+++ b/tests/protocol/test_wire.py
@@ -27,7 +27,7 @@ import uuid
 from datetime import datetime, timedelta
 
 from azurelinuxagent.common import conf
-from azurelinuxagent.common.agent_supported_feature import CRPSupportedFeatureNames, get_supported_feature_by_name, \
+from azurelinuxagent.common.agent_supported_feature import SupportedFeatureNames, get_supported_feature_by_name, \
     get_agent_supported_features_list_for_crp
 from azurelinuxagent.common.exception import ResourceGoneError, ProtocolError, \
     ExtensionDownloadError, HttpError
@@ -384,7 +384,7 @@ class TestWireProtocol(AgentTestCase):
                 exthandlers_handler.run()
                 self.assertIsNotNone(protocol.aggregate_status, "Aggregate status should not be None")
                 self.assertIn("supportedFeatures", protocol.aggregate_status, "supported features not reported")
-                multi_config_feature = get_supported_feature_by_name(CRPSupportedFeatureNames.MultiConfig)
+                multi_config_feature = get_supported_feature_by_name(SupportedFeatureNames.MultiConfig)
                 found = False
                 for feature in protocol.aggregate_status['supportedFeatures']:
                     if feature['Key'] == multi_config_feature.name and feature['Value'] == multi_config_feature.version:
@@ -405,7 +405,7 @@ class TestWireProtocol(AgentTestCase):
                     return
 
                 # If there are other features available, confirm MultiConfig was not reported
-                multi_config_feature = get_supported_feature_by_name(CRPSupportedFeatureNames.MultiConfig)
+                multi_config_feature = get_supported_feature_by_name(SupportedFeatureNames.MultiConfig)
                 found = False
                 for feature in protocol.aggregate_status['supportedFeatures']:
                     if feature['Key'] == multi_config_feature.name and feature['Value'] == multi_config_feature.version: