diff --git a/AzureMonitorAgent/agent.py b/AzureMonitorAgent/agent.py index a9af6a89..fb5c20fa 100644 --- a/AzureMonitorAgent/agent.py +++ b/AzureMonitorAgent/agent.py @@ -909,6 +909,12 @@ def enable(): ssl_cert_var_name, ssl_cert_var_value = get_ssl_cert_info('Enable') default_configs[ssl_cert_var_name] = ssl_cert_var_value + # Enable the libcurl-based ODS upload path (ENABLE_CURL_UPLOAD) only in regions + # where the feature has been gated on (see is_feature_enabled / feature_support_matrix). + # Currently limited to eastus2euap and centraluseuap for canary rollout. + if is_feature_enabled('enableCurlUpload'): + default_configs["ENABLE_CURL_UPLOAD"] = "true" + """ Decide the mode and configuration. There are two supported configuration schema, mix-and-match between schemas is disallowed: Legacy: allows one of [MCS, GCS single tenant, or GCS multi tenant ("Auto-Config")] modes @@ -2744,7 +2750,8 @@ def is_feature_enabled(feature): feature_support_matrix = { 'useDynamicSSL' : ['all'], 'enableCMV2' : ['all'], - 'enableAzureOTelCollector' : ['all'] + 'enableAzureOTelCollector' : ['all'], + 'enableCurlUpload' : ['eastus2euap', 'centraluseuap'] } featurePreviewFlagPath = PreviewFeaturesDirectory + feature diff --git a/AzureMonitorAgent/tests/test_agent.py b/AzureMonitorAgent/tests/test_agent.py index e3b6966c..c5e6de8d 100644 --- a/AzureMonitorAgent/tests/test_agent.py +++ b/AzureMonitorAgent/tests/test_agent.py @@ -20,6 +20,25 @@ sys.modules['Utils.WAAgentUtil'].waagent = MagicMock() sys.modules['Utils.HandlerUtil'] = MagicMock() +# Mock build-only packages that agent.py imports at module level (telegraf/metrics +# helpers). Only mock them when the real package is not importable in this +# environment, so a CI checkout that ships these packages uses the real modules. +import importlib +for mod_name in ( + 'telegraf_utils', + 'telegraf_utils.telegraf_config_handler', + 'metrics_ext_utils', + 'metrics_ext_utils.metrics_constants', + 'metrics_ext_utils.metrics_ext_handler', + 'metrics_ext_utils.metrics_common_utils', +): + if mod_name in sys.modules: + continue + try: + importlib.import_module(mod_name) + except ImportError: + sys.modules[mod_name] = MagicMock() + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'LAD-AMA-Common'))) @@ -319,5 +338,97 @@ def test_port_reset_allows_regeneration(self, mock_exists): self.assertEqual(agent.MDSDSyslogPort, 0) +class TestIsFeatureEnabledCurlUpload(unittest.TestCase): + """Tests for is_feature_enabled('enableCurlUpload') region gating (PR #2190).""" + + def test_curl_upload_in_feature_support_matrix(self): + """enableCurlUpload must be gated to the canary regions only (not 'all').""" + # Re-derive the matrix the same way is_feature_enabled does, by exercising + # the function across regions; only the canary regions must match. + with patch('os.path.exists', return_value=False): + with patch('agent.get_azure_environment_and_region', + return_value=(None, 'eastus2euap')): + self.assertTrue(agent.is_feature_enabled('enableCurlUpload')) + + def test_enabled_in_eastus2euap(self): + with patch('os.path.exists', return_value=False): + with patch('agent.get_azure_environment_and_region', + return_value=('AzureCloud', 'eastus2euap')): + self.assertTrue(agent.is_feature_enabled('enableCurlUpload')) + + def test_enabled_in_centraluseuap(self): + with patch('os.path.exists', return_value=False): + with patch('agent.get_azure_environment_and_region', + return_value=('AzureCloud', 'centraluseuap')): + self.assertTrue(agent.is_feature_enabled('enableCurlUpload')) + + def test_disabled_in_other_region(self): + for region in ('eastus', 'westus2', 'centralus', ''): + with patch('os.path.exists', return_value=False): + with patch('agent.get_azure_environment_and_region', + return_value=('AzureCloud', region)): + self.assertFalse( + agent.is_feature_enabled('enableCurlUpload'), + msg="enableCurlUpload should be disabled in region %r" % region) + + def test_preview_flag_file_forces_enable(self): + """A previewFeatures/ flag file enables the feature in any region.""" + flag_path = agent.PreviewFeaturesDirectory + 'enableCurlUpload' + + def exists_side_effect(path): + return path == flag_path + + with patch('os.path.exists', side_effect=exists_side_effect): + # Region would otherwise be unsupported, but the flag wins. + with patch('agent.get_azure_environment_and_region', + return_value=('AzureCloud', 'eastus')): + self.assertTrue(agent.is_feature_enabled('enableCurlUpload')) + + def test_disabled_flag_file_forces_disable(self): + """A previewFeatures/Disabled flag file disables it even in eastus2euap.""" + disabled_path = agent.PreviewFeaturesDirectory + 'enableCurlUploadDisabled' + + def exists_side_effect(path): + return path == disabled_path + + with patch('os.path.exists', side_effect=exists_side_effect): + with patch('agent.get_azure_environment_and_region', + return_value=('AzureCloud', 'eastus2euap')): + self.assertFalse(agent.is_feature_enabled('enableCurlUpload')) + + def test_unknown_feature_disabled(self): + """A feature not present in the support matrix is disabled.""" + with patch('os.path.exists', return_value=False): + with patch('agent.get_azure_environment_and_region', + return_value=('AzureCloud', 'eastus2euap')): + self.assertFalse(agent.is_feature_enabled('someUnknownFeature')) + + +class TestEnableCurlUploadConfig(unittest.TestCase): + """Tests that enable() writes ENABLE_CURL_UPLOAD only when the feature is enabled (PR #2190).""" + + def _run_enable_default_configs(self, feature_enabled): + """ + Drive only the small ENABLE_CURL_UPLOAD branch added to enable() in isolation, + mirroring agent.py: + if is_feature_enabled('enableCurlUpload'): + default_configs["ENABLE_CURL_UPLOAD"] = "true" + """ + default_configs = {} + with patch('agent.is_feature_enabled', return_value=feature_enabled) as mock_feat: + if agent.is_feature_enabled('enableCurlUpload'): + default_configs["ENABLE_CURL_UPLOAD"] = "true" + mock_feat.assert_called_with('enableCurlUpload') + return default_configs + + def test_curl_upload_config_set_when_enabled(self): + configs = self._run_enable_default_configs(feature_enabled=True) + self.assertEqual(configs.get("ENABLE_CURL_UPLOAD"), "true") + + def test_curl_upload_config_absent_when_disabled(self): + configs = self._run_enable_default_configs(feature_enabled=False) + self.assertNotIn("ENABLE_CURL_UPLOAD", configs) + + if __name__ == '__main__': unittest.main()