Skip to content

Commit

Permalink
VM Metadata API: Adds an option to disable VM metadata API call (#4568)
Browse files Browse the repository at this point in the history
* add env variable

* fixed tests

* removed try-catch

* add doc

* remove else

* put try-catch around test

* remove debugger

* update env variable name and updated known issue section

* updstade doc
  • Loading branch information
sourabh1007 authored Jul 8, 2024
1 parent 5287ffb commit 6d36c36
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 25 deletions.
17 changes: 15 additions & 2 deletions Microsoft.Azure.Cosmos/src/Telemetry/VmMetadataApiHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,23 @@ internal static class VmMetadataApiHandler

private static bool isInitialized = false;
private static AzureVMMetadata azMetadata = null;


/// <summary>
/// Check for environment variable COSMOS_DISABLE_IMDS_ACCESS to decide if VM metadata call should be made or not.
/// If environment variable is set to true, then VM metadata call will not be made.
/// If environment variable is set to false, then VM metadata call will be made.
/// If environment variable is not set, then VM metadata call will be made.
/// </summary>.
/// <param name="httpClient"></param>
internal static void TryInitialize(CosmosHttpClient httpClient)
{
bool isVMMetadataAccessDisabled =
ConfigurationManager.GetEnvironmentVariable<bool>("COSMOS_DISABLE_IMDS_ACCESS", false);
if (isVMMetadataAccessDisabled)
{
return;
}

if (VmMetadataApiHandler.isInitialized)
{
return;
Expand All @@ -55,7 +69,6 @@ internal static void TryInitialize(CosmosHttpClient httpClient)

_ = Task.Run(() => MetadataApiCallAsync(httpClient), default);
}

}

private static async Task MetadataApiCallAsync(CosmosHttpClient httpClient)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@ namespace Microsoft.Azure.Cosmos
using Microsoft.Azure.Cosmos.Telemetry.Models;
using Microsoft.Azure.Cosmos.Tests;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Newtonsoft.Json;
using Util;

[TestClass]
public class VmMetadataApiHandlerTest
{
[TestInitialize]
public void Intialize()
public void Initialize()
{
var isInitializedField = typeof(VmMetadataApiHandler).GetField("isInitialized",
BindingFlags.Static |
Expand All @@ -39,27 +38,54 @@ public void Intialize()
}

[TestMethod]
public async Task GetVmIdAsMachineIdTest()
[DataRow("true", DisplayName = "When COSMOS_DISABLE_IMDS_ACCESS is set as true, VM ID should not be fetched")]
[DataRow("false", DisplayName = "When COSMOS_DISABLE_IMDS_ACCESS is set as false, VM ID should be fetched")]
[DataRow(null, DisplayName = "When COSMOS_DISABLE_IMDS_ACCESS is NOT set, VM ID should be fetched")]
public async Task GetVmIdAsMachineIdTest(string isVmMetadataAccessDisabled)
{
static Task<HttpResponseMessage> sendFunc(HttpRequestMessage request, CancellationToken cancellationToken)
if (isVmMetadataAccessDisabled != null)
{
object jsonObject = JsonConvert.DeserializeObject("{\"compute\":{\"azEnvironment\":\"AzurePublicCloud\",\"customData\":\"\",\"isHostCompatibilityLayerVm\":\"false\",\"licenseType\":\"\",\"location\":\"eastus\",\"name\":\"sourabh-testing\",\"offer\":\"UbuntuServer\",\"osProfile\":{\"adminUsername\":\"azureuser\",\"computerName\":\"sourabh-testing\"},\"osType\":\"Linux\",\"placementGroupId\":\"\",\"plan\":{\"name\":\"\",\"product\":\"\",\"publisher\":\"\"},\"platformFaultDomain\":\"0\",\"platformUpdateDomain\":\"0\",\"provider\":\"Microsoft.Compute\",\"publicKeys\":[{\"keyData\":\"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5uCeOAm3ehmhI+2PbMoMl17Eo\r\nqfHKCycSaBJsv9qxlmBOuFheSJc1XknJleXUSsuTO016/d1PyWpevnqOZNRksWoa\r\nJvQ23sDTxcK+X2OP3QlCUeX4cMjPXqlL8z1UYzU4Bx3fFvf8fs67G3N72sxWBw5P\r\nZyuXyhBm0NCe/2NYMKgEDT4ma8XszO0ikbhoPKbMbgHAQk/ktWQHNcqYOPQKEWqp\r\nEK1R0rjS2nmtovfScP/ZGXcvOpJ1/NDBo4dh1K+OxOGM/4PSH/F448J5Zy4eAyEk\r\nscys+IpeIOTOlRUy/703SNIX0LEWlnYqbyL9c1ypcYLQqF76fKkDfzzFI/OWVlGw\r\nhj/S9uP8iMsR+fhGIbn6MAa7O4DWPWLuedSp7KDYyjY09gqNJsfuaAJN4LiC6bPy\r\nhknm0PVLK3ux7EUOt+cZrHCdIFWbdOtxiPNIl1tkv9kV5aE5Aj2gJm4MeB9uXYhS\r\nOuksboBc0wyUGrl9+XZJ1+NlZOf7IjVi86CieK8= generated-by-azure\r\n\",\"path\":\"/home/azureuser/.ssh/authorized_keys\"}],\"publisher\":\"Canonical\",\"resourceGroupName\":\"sourabh-telemetry-sdk\",\"resourceId\":\"/subscriptions/8fba6d4f-7c37-4d13-9063-fd58ad2b86e2/resourceGroups/sourabh-telemetry-sdk/providers/Microsoft.Compute/virtualMachines/sourabh-testing\",\"securityProfile\":{\"secureBootEnabled\":\"false\",\"virtualTpmEnabled\":\"false\"},\"sku\":\"18.04-LTS\",\"storageProfile\":{\"dataDisks\":[],\"imageReference\":{\"id\":\"\",\"offer\":\"UbuntuServer\",\"publisher\":\"Canonical\",\"sku\":\"18.04-LTS\",\"version\":\"latest\"},\"osDisk\":{\"caching\":\"ReadWrite\",\"createOption\":\"FromImage\",\"diffDiskSettings\":{\"option\":\"\"},\"diskSizeGB\":\"30\",\"encryptionSettings\":{\"enabled\":\"false\"},\"image\":{\"uri\":\"\"},\"managedDisk\":{\"id\":\"/subscriptions/8fba6d4f-7c37-4d13-9063-fd58ad2b86e2/resourceGroups/sourabh-telemetry-sdk/providers/Microsoft.Compute/disks/sourabh-testing_OsDisk_1_9a54abfc5ba149c6a106bd9e5b558c2a\",\"storageAccountType\":\"Premium_LRS\"},\"name\":\"sourabh-testing_OsDisk_1_9a54abfc5ba149c6a106bd9e5b558c2a\",\"osType\":\"Linux\",\"vhd\":{\"uri\":\"\"},\"writeAcceleratorEnabled\":\"false\"}},\"subscriptionId\":\"8fba6d4f-7c37-4d13-9063-fd58ad2b86e2\",\"tags\":\"azsecpack:nonprod;platformsettings.host_environment.service.platform_optedin_for_rootcerts:true\",\"tagsList\":[{\"name\":\"azsecpack\",\"value\":\"nonprod\"},{\"name\":\"platformsettings.host_environment.service.platform_optedin_for_rootcerts\",\"value\":\"true\"}],\"version\":\"18.04.202103250\",\"vmId\":\"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd\",\"vmScaleSetName\":\"\",\"vmSize\":\"Standard_D2s_v3\",\"zone\":\"1\"},\"network\":{\"interface\":[{\"ipv4\":{\"ipAddress\":[{\"privateIpAddress\":\"10.0.7.5\",\"publicIpAddress\":\"\"}],\"subnet\":[{\"address\":\"10.0.7.0\",\"prefix\":\"24\"}]},\"ipv6\":{\"ipAddress\":[]},\"macAddress\":\"000D3A8F8BA0\"}]}}");
string payload = JsonConvert.SerializeObject(jsonObject);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
Environment.SetEnvironmentVariable("COSMOS_DISABLE_IMDS_ACCESS", isVmMetadataAccessDisabled);
}

try
{
static Task<HttpResponseMessage> sendFunc(HttpRequestMessage request, CancellationToken cancellationToken)
{
Content = new StringContent(payload, Encoding.UTF8, "application/json")
};
return Task.FromResult(response);
}
object jsonObject = JsonConvert.DeserializeObject("{\"compute\":{\"azEnvironment\":\"AzurePublicCloud\",\"customData\":\"\",\"isHostCompatibilityLayerVm\":\"false\",\"licenseType\":\"\",\"location\":\"eastus\",\"name\":\"sourabh-testing\",\"offer\":\"UbuntuServer\",\"osProfile\":{\"adminUsername\":\"azureuser\",\"computerName\":\"sourabh-testing\"},\"osType\":\"Linux\",\"placementGroupId\":\"\",\"plan\":{\"name\":\"\",\"product\":\"\",\"publisher\":\"\"},\"platformFaultDomain\":\"0\",\"platformUpdateDomain\":\"0\",\"provider\":\"Microsoft.Compute\",\"publicKeys\":[{\"keyData\":\"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5uCeOAm3ehmhI+2PbMoMl17Eo\r\nqfHKCycSaBJsv9qxlmBOuFheSJc1XknJleXUSsuTO016/d1PyWpevnqOZNRksWoa\r\nJvQ23sDTxcK+X2OP3QlCUeX4cMjPXqlL8z1UYzU4Bx3fFvf8fs67G3N72sxWBw5P\r\nZyuXyhBm0NCe/2NYMKgEDT4ma8XszO0ikbhoPKbMbgHAQk/ktWQHNcqYOPQKEWqp\r\nEK1R0rjS2nmtovfScP/ZGXcvOpJ1/NDBo4dh1K+OxOGM/4PSH/F448J5Zy4eAyEk\r\nscys+IpeIOTOlRUy/703SNIX0LEWlnYqbyL9c1ypcYLQqF76fKkDfzzFI/OWVlGw\r\nhj/S9uP8iMsR+fhGIbn6MAa7O4DWPWLuedSp7KDYyjY09gqNJsfuaAJN4LiC6bPy\r\nhknm0PVLK3ux7EUOt+cZrHCdIFWbdOtxiPNIl1tkv9kV5aE5Aj2gJm4MeB9uXYhS\r\nOuksboBc0wyUGrl9+XZJ1+NlZOf7IjVi86CieK8= generated-by-azure\r\n\",\"path\":\"/home/azureuser/.ssh/authorized_keys\"}],\"publisher\":\"Canonical\",\"resourceGroupName\":\"sourabh-telemetry-sdk\",\"resourceId\":\"/subscriptions/8fba6d4f-7c37-4d13-9063-fd58ad2b86e2/resourceGroups/sourabh-telemetry-sdk/providers/Microsoft.Compute/virtualMachines/sourabh-testing\",\"securityProfile\":{\"secureBootEnabled\":\"false\",\"virtualTpmEnabled\":\"false\"},\"sku\":\"18.04-LTS\",\"storageProfile\":{\"dataDisks\":[],\"imageReference\":{\"id\":\"\",\"offer\":\"UbuntuServer\",\"publisher\":\"Canonical\",\"sku\":\"18.04-LTS\",\"version\":\"latest\"},\"osDisk\":{\"caching\":\"ReadWrite\",\"createOption\":\"FromImage\",\"diffDiskSettings\":{\"option\":\"\"},\"diskSizeGB\":\"30\",\"encryptionSettings\":{\"enabled\":\"false\"},\"image\":{\"uri\":\"\"},\"managedDisk\":{\"id\":\"/subscriptions/8fba6d4f-7c37-4d13-9063-fd58ad2b86e2/resourceGroups/sourabh-telemetry-sdk/providers/Microsoft.Compute/disks/sourabh-testing_OsDisk_1_9a54abfc5ba149c6a106bd9e5b558c2a\",\"storageAccountType\":\"Premium_LRS\"},\"name\":\"sourabh-testing_OsDisk_1_9a54abfc5ba149c6a106bd9e5b558c2a\",\"osType\":\"Linux\",\"vhd\":{\"uri\":\"\"},\"writeAcceleratorEnabled\":\"false\"}},\"subscriptionId\":\"8fba6d4f-7c37-4d13-9063-fd58ad2b86e2\",\"tags\":\"azsecpack:nonprod;platformsettings.host_environment.service.platform_optedin_for_rootcerts:true\",\"tagsList\":[{\"name\":\"azsecpack\",\"value\":\"nonprod\"},{\"name\":\"platformsettings.host_environment.service.platform_optedin_for_rootcerts\",\"value\":\"true\"}],\"version\":\"18.04.202103250\",\"vmId\":\"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd\",\"vmScaleSetName\":\"\",\"vmSize\":\"Standard_D2s_v3\",\"zone\":\"1\"},\"network\":{\"interface\":[{\"ipv4\":{\"ipAddress\":[{\"privateIpAddress\":\"10.0.7.5\",\"publicIpAddress\":\"\"}],\"subnet\":[{\"address\":\"10.0.7.0\",\"prefix\":\"24\"}]},\"ipv6\":{\"ipAddress\":[]},\"macAddress\":\"000D3A8F8BA0\"}]}}");
string payload = JsonConvert.SerializeObject(jsonObject);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(payload, Encoding.UTF8, "application/json")
};
return Task.FromResult(response);
}

HttpMessageHandler messageHandler = new MockMessageHandler(sendFunc);
CosmosHttpClient cosmoshttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));
HttpMessageHandler messageHandler = new MockMessageHandler(sendFunc);
CosmosHttpClient cosmosHttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));

VmMetadataApiHandler.TryInitialize(cosmoshttpClient);
VmMetadataApiHandler.TryInitialize(cosmosHttpClient);

await Task.Delay(2000);
Assert.AreEqual($"{VmMetadataApiHandler.VmIdPrefix}{"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd"}", VmMetadataApiHandler.GetMachineId());
Assert.AreEqual(VmMetadataApiHandler.GetMachineRegion(), "eastus");
await Task.Delay(2000);

if (isVmMetadataAccessDisabled != null &&
Boolean.TryParse(isVmMetadataAccessDisabled, out bool isVmMetadataAccessDisabledBool) &&
isVmMetadataAccessDisabledBool)
{
Assert.AreNotEqual($"{VmMetadataApiHandler.VmIdPrefix}{"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd"}", VmMetadataApiHandler.GetMachineId());
Assert.IsNull(VmMetadataApiHandler.GetMachineRegion(), VmMetadataApiHandler.GetMachineRegion());
}
else
{
Assert.AreEqual($"{VmMetadataApiHandler.VmIdPrefix}{"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd"}", VmMetadataApiHandler.GetMachineId());
Assert.AreEqual(VmMetadataApiHandler.GetMachineRegion(), "eastus");
}

}
finally
{
Environment.SetEnvironmentVariable("COSMOS_DISABLE_IMDS_ACCESS", null);
}
}

[TestMethod]
Expand All @@ -77,9 +103,9 @@ static Task<HttpResponseMessage> sendFunc(HttpRequestMessage request, Cancellati
}

HttpMessageHandler messageHandler = new MockMessageHandler(sendFunc);
CosmosHttpClient cosmoshttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));
CosmosHttpClient cosmosHttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));

VmMetadataApiHandler.TryInitialize(cosmoshttpClient);
VmMetadataApiHandler.TryInitialize(cosmosHttpClient);

await Task.Delay(2000);
Assert.IsNull(VmMetadataApiHandler.GetMachineInfo());
Expand All @@ -95,9 +121,9 @@ public async Task GetHashedMachineNameAsMachineIdTest()
static Task<HttpResponseMessage> sendFunc(HttpRequestMessage request, CancellationToken cancellationToken) { throw new Exception("error while making API call"); };

HttpMessageHandler messageHandler = new MockMessageHandler(sendFunc);
CosmosHttpClient cosmoshttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));
CosmosHttpClient cosmosHttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));

VmMetadataApiHandler.TryInitialize(cosmoshttpClient);
VmMetadataApiHandler.TryInitialize(cosmosHttpClient);

await Task.Delay(2000);
Assert.AreEqual(expectedMachineId, VmMetadataApiHandler.GetMachineId());
Expand Down Expand Up @@ -139,7 +165,7 @@ public void CatchMetadataApiCallExceptionTest()
static Task<HttpResponseMessage> sendFunc(HttpRequestMessage request, CancellationToken cancellationToken) { throw new Exception("error while making API call"); };

HttpMessageHandler messageHandler = new MockMessageHandler(sendFunc);
CosmosHttpClient cosmoshttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));
CosmosHttpClient cosmosHttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler));

string expectedMsg = "Azure Environment metadata information not available.";
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
Expand All @@ -155,7 +181,7 @@ void TraceHandler(string message)
DefaultTrace.TraceSource.Listeners.Add(new TestTraceListener { Callback = TraceHandler });
DefaultTrace.InitEventListener();

VmMetadataApiHandler.TryInitialize(cosmoshttpClient);
VmMetadataApiHandler.TryInitialize(cosmosHttpClient);

int timeout = 30000;
Assert.IsTrue(manualResetEvent.WaitOne(timeout));
Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,7 @@ Below is a list of any know issues affecting the [recommended minimum version](#
| --- | --- | --- | --- |
| `FeedIterator` enters an infinite loop after a physical partition split occurs in a container using hierarchical partition keys. | Queries using prefix partition keys. | Rather than having the PK included in the query request options, filtering on top level hierarchical Pks should be done through where clauses. | [#4326](https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4326) |
| Single partition queries (queries explicitly targetted to single partition or any queries on collection that had single physical partition) that resume using continuation token after partition split can observe failure on SDK v3.38 and beyond. | Explicit query exeuction using continuation token will fail query execution if these conditions are met. | Turn off Optimistic Direct Execution during query execution either by setting EnableOptimisticDirectExecution to false in query request options or by setting environment variable AZURE_COSMOS_OPTIMISTIC_DIRECT_EXECUTION_ENABLED to false. | [#4432](https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4432) |
| An [Azure API](https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service?tabs=linux) call is made to get the VM information. This call fails if cutomer is on non-Azure VM. | Although this call is made only once, during client initialization but this failure would come up into monitoring tool (e.g AppInsights, Datadog etc.) which leads to a confusion for a developer.| Turn off this call by setting environment variable COSMOS_DISABLE_IMDS_ACCESS to true. |[#4187](https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4187) |

## Release & Retirement dates

Expand Down

0 comments on commit 6d36c36

Please sign in to comment.