-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[META 388] Add support for Azure App Service Cloud metadata (#1083)
This commit adds support for retrieving cloud metadata about an Azure App Service from the environment variables.
- Loading branch information
Showing
5 changed files
with
236 additions
and
4 deletions.
There are no files selected for viewing
120 changes: 120 additions & 0 deletions
120
src/Elastic.Apm/Cloud/AzureAppServiceMetadataProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Licensed to Elasticsearch B.V under | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System.Collections; | ||
using System.Threading.Tasks; | ||
using Elastic.Apm.Api; | ||
using Elastic.Apm.Logging; | ||
|
||
namespace Elastic.Apm.Cloud | ||
{ | ||
/// <summary> | ||
/// Provides cloud metadata for Microsoft Azure App Services | ||
/// </summary> | ||
public class AzureAppServiceMetadataProvider : ICloudMetadataProvider | ||
{ | ||
internal const string Name = "azure-app-service"; | ||
|
||
private readonly IApmLogger _logger; | ||
private readonly IDictionary _environmentVariables; | ||
|
||
/// <summary> | ||
/// Value of the form {subscription id}+{app service plan resource group}-{region}webspace | ||
/// </summary> | ||
/// <example> | ||
/// f5940f10-2e30-3e4d-a259-63451ba6dae4+elastic-apm-AustraliaEastwebspace | ||
/// </example> | ||
internal static readonly string WebsiteOwnerName = "WEBSITE_OWNER_NAME"; | ||
|
||
internal static readonly string WebsiteResourceGroup = "WEBSITE_RESOURCE_GROUP"; | ||
|
||
internal static readonly string WebsiteSiteName = "WEBSITE_SITE_NAME"; | ||
|
||
internal static readonly string WebsiteInstanceId = "WEBSITE_INSTANCE_ID"; | ||
|
||
private static readonly string Webspace = "webspace"; | ||
|
||
public AzureAppServiceMetadataProvider(IApmLogger logger, IDictionary environmentVariables) | ||
{ | ||
_logger = logger; | ||
_environmentVariables = environmentVariables; | ||
} | ||
|
||
public string Provider { get; } = Name; | ||
|
||
public Task<Api.Cloud> GetMetadataAsync() | ||
{ | ||
if (_environmentVariables is null) | ||
{ | ||
_logger.Trace()?.Log("Unable to get {Provider} cloud metadata as no environment variables available", Provider); | ||
return Task.FromResult<Api.Cloud>(null); | ||
} | ||
|
||
var websiteOwnerName = GetEnvironmentVariable(WebsiteOwnerName); | ||
var websiteResourceGroup = GetEnvironmentVariable(WebsiteResourceGroup); | ||
var websiteSiteName = GetEnvironmentVariable(WebsiteSiteName); | ||
var websiteInstanceId = GetEnvironmentVariable(WebsiteInstanceId); | ||
|
||
bool NullOrEmptyVariable(string key, string value) | ||
{ | ||
if (!string.IsNullOrEmpty(value)) return false; | ||
|
||
_logger.Trace()?.Log( | ||
"Unable to get {Provider} cloud metadata as no {EnvironmentVariable} environment variable", | ||
Provider, | ||
key); | ||
|
||
return true; | ||
} | ||
|
||
if (NullOrEmptyVariable(WebsiteOwnerName, websiteOwnerName) || | ||
NullOrEmptyVariable(WebsiteResourceGroup, websiteResourceGroup) || | ||
NullOrEmptyVariable(WebsiteSiteName, websiteSiteName) || | ||
NullOrEmptyVariable(WebsiteInstanceId, websiteInstanceId)) | ||
return Task.FromResult<Api.Cloud>(null); | ||
|
||
var websiteOwnerNameParts = websiteOwnerName.Split('+'); | ||
if (websiteOwnerNameParts.Length != 2) | ||
{ | ||
_logger.Trace()?.Log( | ||
"Unable to get {Provider} cloud metadata as {EnvironmentVariable} does not contain expected format", | ||
Provider, | ||
WebsiteOwnerName); | ||
return Task.FromResult<Api.Cloud>(null); | ||
} | ||
|
||
var subscriptionId = websiteOwnerNameParts[0]; | ||
var lastHyphenIndex = websiteOwnerNameParts[1].LastIndexOf('-'); | ||
if (lastHyphenIndex == -1) | ||
{ | ||
_logger.Trace()?.Log( | ||
"Unable to get {Provider} cloud metadata as {EnvironmentVariable} does not contain expected format", | ||
Provider, | ||
WebsiteOwnerName); | ||
return Task.FromResult<Api.Cloud>(null); | ||
} | ||
|
||
var index = lastHyphenIndex + 1; | ||
|
||
var region = websiteOwnerNameParts[1].EndsWith(Webspace) | ||
? websiteOwnerNameParts[1].Substring(index, websiteOwnerNameParts[1].Length - (index + Webspace.Length)) | ||
: websiteOwnerNameParts[1].Substring(index); | ||
|
||
return Task.FromResult(new Api.Cloud | ||
{ | ||
Account = new CloudAccount { Id = subscriptionId }, | ||
Instance = new CloudInstance { Id = websiteInstanceId, Name = websiteSiteName }, | ||
Project = new CloudProject { Name = websiteResourceGroup }, | ||
Provider = "azure", | ||
Region = region | ||
}); | ||
} | ||
|
||
private string GetEnvironmentVariable(string key) => | ||
_environmentVariables.Contains(key) | ||
? _environmentVariables[key]?.ToString() | ||
: null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Licensed to Elasticsearch B.V under | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System; | ||
using System.Collections; | ||
using Elastic.Apm.Logging; | ||
|
||
namespace Elastic.Apm.Helpers | ||
{ | ||
/// <summary> | ||
/// Gets Environment variables, catching and logging any exception that may be thrown. | ||
/// </summary> | ||
internal static class EnvironmentHelper | ||
{ | ||
public static IDictionary GetEnvironmentVariables(IApmLogger logger) | ||
{ | ||
try | ||
{ | ||
return Environment.GetEnvironmentVariables(); | ||
} | ||
catch (Exception e) | ||
{ | ||
logger.Debug()?.LogException(e, "Error while getting environment variables"); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
test/Elastic.Apm.Tests/Cloud/AzureAppServiceMetadataProviderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Licensed to Elasticsearch B.V under | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System.Collections; | ||
using System.Threading.Tasks; | ||
using Elastic.Apm.Cloud; | ||
using Elastic.Apm.Tests.Mocks; | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace Elastic.Apm.Tests.Cloud | ||
{ | ||
public class AzureAppServiceMetadataProviderTests | ||
{ | ||
[Fact] | ||
public async Task GetMetadataAsync_Returns_Expected_Cloud_Metadata() | ||
{ | ||
var environmentVariables = new Hashtable | ||
{ | ||
{ AzureAppServiceMetadataProvider.WebsiteInstanceId, "instance_id" }, | ||
{ AzureAppServiceMetadataProvider.WebsiteOwnerName, "f5940f10-2e30-3e4d-a259-63451ba6dae4+elastic-apm-AustraliaEastwebspace" }, | ||
{ AzureAppServiceMetadataProvider.WebsiteSiteName, "site_name" }, | ||
{ AzureAppServiceMetadataProvider.WebsiteResourceGroup, "resource_group" }, | ||
}; | ||
|
||
var provider = new AzureAppServiceMetadataProvider(new NoopLogger(), environmentVariables); | ||
var metadata = await provider.GetMetadataAsync(); | ||
|
||
metadata.Should().NotBeNull(); | ||
metadata.Account.Should().NotBeNull(); | ||
metadata.Account.Id.Should().Be("f5940f10-2e30-3e4d-a259-63451ba6dae4"); | ||
metadata.Provider.Should().Be("azure"); | ||
metadata.Instance.Should().NotBeNull(); | ||
metadata.Instance.Id.Should().Be("instance_id"); | ||
metadata.Instance.Name.Should().Be("site_name"); | ||
metadata.Project.Should().NotBeNull(); | ||
metadata.Project.Name.Should().Be("resource_group"); | ||
metadata.Region.Should().Be("AustraliaEast"); | ||
} | ||
|
||
[Theory] | ||
[InlineData(null, "f5940f10-2e30-3e4d-a259-63451ba6dae4+elastic-apm-AustraliaEastwebspace", "site_name", "resource_group")] | ||
[InlineData("instance_id", null, "site_name", "resource_group")] | ||
[InlineData("instance_id", "f5940f10-2e30-3e4d-a259-63451ba6dae4+elastic-apm-AustraliaEastwebspace", null, "resource_group")] | ||
[InlineData("instance_id", "f5940f10-2e30-3e4d-a259-63451ba6dae4+elastic-apm-AustraliaEastwebspace", "site_name", null)] | ||
public async Task GetMetadataAsync_Returns_Null_When_Expected_EnvironmentVariable_Is_Missing( | ||
string instanceId, string ownerName, string siteName, string resourceGroup) | ||
{ | ||
var environmentVariables = new Hashtable | ||
{ | ||
{ AzureAppServiceMetadataProvider.WebsiteInstanceId, instanceId }, | ||
{ AzureAppServiceMetadataProvider.WebsiteOwnerName, ownerName }, | ||
{ AzureAppServiceMetadataProvider.WebsiteSiteName, siteName }, | ||
{ AzureAppServiceMetadataProvider.WebsiteResourceGroup, resourceGroup }, | ||
}; | ||
|
||
var provider = new AzureAppServiceMetadataProvider(new NoopLogger(), environmentVariables); | ||
var metadata = await provider.GetMetadataAsync(); | ||
|
||
metadata.Should().BeNull(); | ||
} | ||
|
||
[Fact] | ||
public async Task GetMetadataAsync_Returns_Null_When_EnvironmentVariables_Is_Null() | ||
{ | ||
var provider = new AzureAppServiceMetadataProvider(new NoopLogger(), null); | ||
var metadata = await provider.GetMetadataAsync(); | ||
|
||
metadata.Should().BeNull(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters