Skip to content

Commit 0126350

Browse files
authored
chore: Add cloud.resource_id for Azure App Services. (#3152)
1 parent 2a58163 commit 0126350

12 files changed

Lines changed: 238 additions & 3 deletions

File tree

src/Agent/NewRelic/Agent/Core/Config/Configuration.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright 2020 New Relic, Inc. All rights reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
43
// ------------------------------------------------------------------------------
54
// <auto-generated>
65
// Generated by Xsd2Code. Version 3.6.0.0
@@ -4059,7 +4058,7 @@ public enum RemoteParentSampledBehaviorType
40594058
{
40604059

40614060
/// <summary>
4062-
/// Default behavior. The agent will use existing sampling logic.
4061+
/// Default behavior. The agent will use New Relic adaptive sampling logic.
40634062
/// </summary>
40644063
@default,
40654064

@@ -5909,6 +5908,8 @@ public partial class configurationUtilization
59095908

59105909
private bool detectAzureFunctionField;
59115910

5911+
private bool detectAzureAppServiceField;
5912+
59125913
private System.Nullable<int> logicalProcessorsField;
59135914

59145915
private System.Nullable<int> totalRamMibField;
@@ -5927,6 +5928,7 @@ public configurationUtilization()
59275928
this.detectDockerField = true;
59285929
this.detectKubernetesField = true;
59295930
this.detectAzureFunctionField = true;
5931+
this.detectAzureAppServiceField = true;
59305932
}
59315933

59325934
[System.Xml.Serialization.XmlAttributeAttribute()]
@@ -6027,6 +6029,20 @@ public bool detectAzureFunction
60276029
}
60286030
}
60296031

6032+
[System.Xml.Serialization.XmlAttributeAttribute()]
6033+
[System.ComponentModel.DefaultValueAttribute(true)]
6034+
public bool detectAzureAppService
6035+
{
6036+
get
6037+
{
6038+
return this.detectAzureAppServiceField;
6039+
}
6040+
set
6041+
{
6042+
this.detectAzureAppServiceField = value;
6043+
}
6044+
}
6045+
60306046
[System.Xml.Serialization.XmlAttributeAttribute()]
60316047
public int logicalProcessors
60326048
{

src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- Copyright 2020 New Relic, Inc. All rights reserved. -->
3+
<!-- SPDX-License-Identifier: Apache-2.0 -->
24
<!-- The documentation in here must be kept up to date with that embedded in comments in newrelic.config -->
35
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:tns="urn:newrelic-config" targetNamespace="urn:newrelic-config" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:nr="Configuration.xsd">
46

@@ -2038,6 +2040,14 @@
20382040
</xs:documentation>
20392041
</xs:annotation>
20402042
</xs:attribute>
2043+
2044+
<xs:attribute name="detectAzureAppService" type="xs:boolean" default="true" use="optional">
2045+
<xs:annotation>
2046+
<xs:documentation>
2047+
This attribute controls whether to look for Azure AppService environment variables (optional). The default is true.
2048+
</xs:documentation>
2049+
</xs:annotation>
2050+
</xs:attribute>
20412051
<xs:attribute name="logicalProcessors" type="xs:int" use="optional">
20422052
<xs:annotation>
20432053
<xs:documentation>

src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,8 @@ private RecordSqlConfigurationItem GetServerOverrideOrLocalRecordSqlConfiguratio
17121712

17131713
public bool UtilizationDetectAzureFunction => AzureFunctionModeEnabled && EnvironmentOverrides(_localConfiguration.utilization.detectAzureFunction, "NEW_RELIC_UTILIZATION_DETECT_AZURE_FUNCTION");
17141714

1715+
public bool UtilizationDetectAzureAppService => EnvironmentOverrides(_localConfiguration.utilization.detectAzureAppService, "NEW_RELIC_UTILIZATION_DETECT_AZURE_APPSERVICE");
1716+
17151717
public int? UtilizationLogicalProcessors
17161718
{
17171719
get

src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,9 @@ public ReportedConfiguration(IConfiguration configuration)
547547
[JsonProperty("utilization.detect_azure_function_enabled")]
548548
public bool UtilizationDetectAzureFunction => _configuration.UtilizationDetectAzureFunction;
549549

550+
[JsonProperty("utilization.detect_azure_appservice_enabled")]
551+
public bool UtilizationDetectAzureAppService => _configuration.UtilizationDetectAzureAppService;
552+
550553
[JsonProperty("utilization.logical_processors")]
551554
public int? UtilizationLogicalProcessors => _configuration.UtilizationLogicalProcessors;
552555

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2020 New Relic, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using Newtonsoft.Json;
5+
6+
namespace NewRelic.Agent.Core.Utilization
7+
{
8+
public class AzureAppServiceVendorModel : IVendorModel
9+
{
10+
public string VendorName => "azureappservice";
11+
12+
[JsonProperty("cloud.resource_id", NullValueHandling = NullValueHandling.Ignore)]
13+
public string CloudResourceId {get;}
14+
15+
public AzureAppServiceVendorModel(string cloudResourceId)
16+
{
17+
CloudResourceId = cloudResourceId;
18+
}
19+
}
20+
}

src/Agent/NewRelic/Agent/Core/Utilization/VendorInfo.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public class VendorInfo
5858
private const string GetMethod = "GET";
5959
private const string PutMethod = "PUT";
6060

61+
private const string AzureWebsiteOwnerName = "WEBSITE_OWNER_NAME";
62+
private const string AzureWebsiteResourceGroup = "WEBSITE_RESOURCE_GROUP";
63+
private const string AzureWebsiteSiteName = "WEBSITE_SITE_NAME";
64+
6165
public VendorInfo(IConfiguration configuration, IAgentHealthReporter agentHealthReporter, IEnvironment environment, VendorHttpApiRequestor vendorHttpApiRequestor, IFileWrapper fileWrapper)
6266
{
6367
_configuration = configuration;
@@ -102,6 +106,10 @@ public IDictionary<string, IVendorModel> GetVendors()
102106
{
103107
vendorMethods.Add(GetAzureFunctionVendorInfo);
104108
}
109+
if (_configuration.UtilizationDetectAzureAppService)
110+
{
111+
vendorMethods.Add(GetAzureAppServiceVendorInfo);
112+
}
105113

106114
foreach (var vendorMethod in vendorMethods)
107115
{
@@ -155,6 +163,40 @@ public IVendorModel GetAzureFunctionVendorInfo()
155163
return new AzureFunctionVendorModel(appName, cloudRegion);
156164
}
157165

166+
public IVendorModel GetAzureAppServiceVendorInfo()
167+
{
168+
// WEBSITE_OWNER_NAME should container the subscription ID and the resource group name, separated by a '+' sign.
169+
var candidateSubscriptionId = GetProcessEnvironmentVariable(AzureWebsiteOwnerName);
170+
if (string.IsNullOrWhiteSpace(candidateSubscriptionId) || !candidateSubscriptionId.Contains('+'))
171+
{
172+
return null;
173+
}
174+
175+
var subscriptionId = candidateSubscriptionId.Split('+')[0];
176+
var resourceGroupName = GetProcessEnvironmentVariable(AzureWebsiteResourceGroup);
177+
var siteName = GetProcessEnvironmentVariable(AzureWebsiteSiteName);
178+
179+
if (!IsValidAndLog(subscriptionId, AzureWebsiteOwnerName)
180+
|| !IsValidAndLog(resourceGroupName, AzureWebsiteResourceGroup)
181+
|| !IsValidAndLog(siteName, AzureWebsiteSiteName))
182+
{
183+
return null;
184+
}
185+
186+
return new AzureAppServiceVendorModel($"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{siteName}");
187+
188+
bool IsValidAndLog(string value, string valueSource)
189+
{
190+
if (string.IsNullOrWhiteSpace(value))
191+
{
192+
Log.Finest($"When building Azure App Service resource id, {valueSource} was null.");
193+
return false;
194+
}
195+
196+
return true;
197+
}
198+
}
199+
158200
private IVendorModel GetAwsVendorInfo()
159201
{
160202
var token = _vendorHttpApiRequestor.CallVendorApi(new Uri(AwsTokenUri), PutMethod, AwsName, new List<string> { AwsTokenDurationHeader });

src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ public interface IConfiguration
238238

239239
bool UtilizationDetectAzureFunction { get; }
240240

241+
bool UtilizationDetectAzureAppService { get; }
242+
241243
string AwsAccountId { get; }
242244
bool GCSamplerV2Enabled { get; }
243245

tests/Agent/UnitTests/Core.UnitTest/Configuration/DefaultConfigurationTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3062,6 +3062,31 @@ public void AzureFunctionModeEnabledByDefault()
30623062
{
30633063
Assert.That(_defaultConfig.AzureFunctionModeEnabled, Is.True, "AzureFunctionMode should be enabled by default");
30643064
}
3065+
3066+
[TestCase("true", true, ExpectedResult = true)]
3067+
[TestCase("true", false, ExpectedResult = true)]
3068+
[TestCase("true", null, ExpectedResult = true)]
3069+
[TestCase("false", true, ExpectedResult = false)]
3070+
[TestCase("false", false, ExpectedResult = false)]
3071+
[TestCase("false", null, ExpectedResult = false)]
3072+
[TestCase("invalidEnvVarValue", true, ExpectedResult = true)]
3073+
[TestCase("invalidEnvVarValue", false, ExpectedResult = false)]
3074+
[TestCase("invalidEnvVarValue", null, ExpectedResult = true)]
3075+
[TestCase(null, true, ExpectedResult = true)]
3076+
[TestCase(null, false, ExpectedResult = false)]
3077+
[TestCase(null, null, ExpectedResult = true)] // true by default test
3078+
public bool UtilizationDetectAzureAppServiceConfigurationWorksProperly(string environmentSetting, bool? localSetting)
3079+
{
3080+
Mock.Arrange(() => _environment.GetEnvironmentVariableFromList("NEW_RELIC_UTILIZATION_DETECT_AZURE_APPSERVICE")).Returns(environmentSetting);
3081+
3082+
if (localSetting.HasValue)
3083+
{
3084+
_localConfig.utilization.detectAzureAppService = localSetting.Value;
3085+
}
3086+
3087+
return _defaultConfig.UtilizationDetectAzureAppService;
3088+
}
3089+
30653090
#endregion
30663091

30673092
#region Log Metrics and Events

tests/Agent/UnitTests/Core.UnitTest/DataTransport/AgentSettingsTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ public void serializes_correctly()
289289
"utilization.detect_docker_enabled": true,
290290
"utilization.detect_kubernetes_enabled": true,
291291
"utilization.detect_azure_function_enabled": true,
292+
"utilization.detect_azure_appservice_enabled": true,
293+
292294
"utilization.logical_processors": 22,
293295
"utilization.total_ram_mib": 33,
294296
"utilization.billing_host": "UtilizationBillingHost",

tests/Agent/UnitTests/Core.UnitTest/DataTransport/ConnectModelTests.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public void serializes_correctly()
3333
{ "gcp" , new GcpVendorModel("myId", "myMachineType", "myName", "myZone") },
3434
{ "pcf", new PcfVendorModel("myInstanceGuid", "myInstanceIp", "myMemoryLimit") },
3535
{ "kubernetes", new KubernetesVendorModel("10.96.0.1") },
36-
{ "azurefunction", new AzureFunctionVendorModel("myAppName", "myCloudRegion") }
36+
{ "azurefunction", new AzureFunctionVendorModel("myAppName", "myCloudRegion") },
37+
{ "azureappservice", new AzureAppServiceVendorModel("/subscriptions/b808887b-cb91-49e0-b922-c9188372bdba/resourceGroups/testgroup/providers/Microsoft.Web/sites/testwebsitename")}
3738
};
3839

3940
var fullyPopulatedTestConfiguration = new ExhaustiveTestConfiguration();
@@ -358,6 +359,7 @@ public void serializes_correctly()
358359
"utilization.detect_docker_enabled": true,
359360
"utilization.detect_kubernetes_enabled": true,
360361
"utilization.detect_azure_function_enabled": true,
362+
"utilization.detect_azure_appservice_enabled": true,
361363
"utilization.logical_processors": 22,
362364
"utilization.total_ram_mib": 33,
363365
"utilization.billing_host": "UtilizationBillingHost",
@@ -469,6 +471,9 @@ public void serializes_correctly()
469471
"azurefunction": {
470472
"faas.app_name": "myAppName",
471473
"cloud.region": "myCloudRegion"
474+
},
475+
"azureappservice": {
476+
"cloud.resource_id": "/subscriptions/b808887b-cb91-49e0-b922-c9188372bdba/resourceGroups/testgroup/providers/Microsoft.Web/sites/testwebsitename"
472477
}
473478
}
474479
},

0 commit comments

Comments
 (0)