From 9ff1adfb09ed49fe84cc20302b96ec39b671761c Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Tue, 28 Jul 2020 08:32:24 +0200 Subject: [PATCH 1/3] Provide extension to get azure environment info --- src/Promitor.Agents.Scraper/Docs/Open-Api.xml | 8 ++ .../v1/Core/AzureMetadataDeserializer.cs | 23 +++--- .../Serialization/v1/Model/AzureCloudsV1.cs | 10 --- .../Extensions/AzureCloudExtensions.cs | 31 ++++++++ .../Serialization/Enum/AzureCloud.cs | 11 +++ .../Azure/AzureCloudUnitTests.cs | 79 +++++++++++++++++++ src/Promitor.Tests.Unit/PromitorAssert.cs | 23 ++++++ .../v1/Core/AzureMetadataDeserializerTests.cs | 24 +++++- 8 files changed, 184 insertions(+), 25 deletions(-) delete mode 100644 src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/AzureCloudsV1.cs create mode 100644 src/Promitor.Core/Extensions/AzureCloudExtensions.cs create mode 100644 src/Promitor.Core/Serialization/Enum/AzureCloud.cs create mode 100644 src/Promitor.Tests.Unit/Azure/AzureCloudUnitTests.cs create mode 100644 src/Promitor.Tests.Unit/PromitorAssert.cs diff --git a/src/Promitor.Agents.Scraper/Docs/Open-Api.xml b/src/Promitor.Agents.Scraper/Docs/Open-Api.xml index 6093d73e3..6dfe99c83 100644 --- a/src/Promitor.Agents.Scraper/Docs/Open-Api.xml +++ b/src/Promitor.Agents.Scraper/Docs/Open-Api.xml @@ -72,6 +72,14 @@ + + + Add health check for integration with Promitor Resource Discovery + + Builder for adding health checks + Configuration of Promitor + + Defines the dependencies that Promitor requires diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs index 038727976..e90c4f6af 100644 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Microsoft.Azure.Management.ResourceManager.Fluent; using Microsoft.Extensions.Logging; +using Promitor.Core.Extensions; using Promitor.Core.Scraping.Configuration.Serialization.v1.Model; +using Promitor.Core.Serialization.Enum; using YamlDotNet.RepresentationModel; namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core @@ -23,18 +26,16 @@ public AzureMetadataDeserializer(ILogger logger) : ba private object DetermineAzureCloud(string rawAzureCloud, KeyValuePair nodePair, IErrorReporter errorReporter) { - if (System.Enum.TryParse(rawAzureCloud, out var azureCloud)) + if (System.Enum.TryParse(rawAzureCloud, out var azureCloud)) { - switch (azureCloud) + try { - case AzureCloudsV1.Global: - return AzureEnvironment.AzureGlobalCloud; - case AzureCloudsV1.China: - return AzureEnvironment.AzureChinaCloud; - case AzureCloudsV1.Germany: - return AzureEnvironment.AzureGermanCloud; - case AzureCloudsV1.UsGov: - return AzureEnvironment.AzureUSGovernment; + var azureEnvironment = azureCloud.GetAzureEnvironment(); + return azureEnvironment; + } + catch (ArgumentOutOfRangeException) + { + errorReporter.ReportError(nodePair.Value, $"'{rawAzureCloud}' is not a supported value for 'cloud'."); } } else diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/AzureCloudsV1.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/AzureCloudsV1.cs deleted file mode 100644 index 7c386249c..000000000 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/AzureCloudsV1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Model -{ - public enum AzureCloudsV1 - { - Global, - China, - UsGov, - Germany - } -} \ No newline at end of file diff --git a/src/Promitor.Core/Extensions/AzureCloudExtensions.cs b/src/Promitor.Core/Extensions/AzureCloudExtensions.cs new file mode 100644 index 000000000..6a359c417 --- /dev/null +++ b/src/Promitor.Core/Extensions/AzureCloudExtensions.cs @@ -0,0 +1,31 @@ +using System; +using Microsoft.Azure.Management.ResourceManager.Fluent; +using Promitor.Core.Serialization.Enum; + +namespace Promitor.Core.Extensions +{ + public static class AzureCloudExtensions + { + /// + /// Get Azure environment information + /// + /// Microsoft Azure cloud + /// Azure environment information for specified cloud + public static AzureEnvironment GetAzureEnvironment(this AzureCloud azureCloud) + { + switch (azureCloud) + { + case AzureCloud.Global: + return AzureEnvironment.AzureGlobalCloud; + case AzureCloud.China: + return AzureEnvironment.AzureChinaCloud; + case AzureCloud.Germany: + return AzureEnvironment.AzureGermanCloud; + case AzureCloud.UsGov: + return AzureEnvironment.AzureUSGovernment; + default: + throw new ArgumentOutOfRangeException(nameof(azureCloud), "No Azure environment is known for"); + } + } + } +} \ No newline at end of file diff --git a/src/Promitor.Core/Serialization/Enum/AzureCloud.cs b/src/Promitor.Core/Serialization/Enum/AzureCloud.cs new file mode 100644 index 000000000..32a663e2b --- /dev/null +++ b/src/Promitor.Core/Serialization/Enum/AzureCloud.cs @@ -0,0 +1,11 @@ +namespace Promitor.Core.Serialization.Enum +{ + public enum AzureCloud + { + Unspecified, + Global, + China, + UsGov, + Germany + } +} \ No newline at end of file diff --git a/src/Promitor.Tests.Unit/Azure/AzureCloudUnitTests.cs b/src/Promitor.Tests.Unit/Azure/AzureCloudUnitTests.cs new file mode 100644 index 000000000..b34e6a736 --- /dev/null +++ b/src/Promitor.Tests.Unit/Azure/AzureCloudUnitTests.cs @@ -0,0 +1,79 @@ +using System; +using System.ComponentModel; +using Microsoft.Azure.Management.ResourceManager.Fluent; +using Promitor.Core.Extensions; +using Promitor.Core.Serialization.Enum; +using Xunit; + +namespace Promitor.Tests.Unit.Azure +{ + [Category("Unit")] + public class AzureCloudUnitTests + { + [Fact] + public void GetAzureEnvironment_ForAzureGlobalCloud_ProvidesCorrectEnvironmentInfo() + { + // Arrange + var azureCloud = AzureCloud.Global; + var expectedEnvironment = AzureEnvironment.AzureGlobalCloud; + + // Act + var azureEnvironment = azureCloud.GetAzureEnvironment(); + + // Assert + PromitorAssert.ContainsSameAzureEnvironmentInfo(expectedEnvironment, azureEnvironment); + } + + [Fact] + public void GetAzureEnvironment_ForAzureChinaCloud_ProvidesCorrectEnvironmentInfo() + { + // Arrange + var azureCloud = AzureCloud.China; + var expectedEnvironment = AzureEnvironment.AzureChinaCloud; + + // Act + var azureEnvironment = azureCloud.GetAzureEnvironment(); + + // Assert + PromitorAssert.ContainsSameAzureEnvironmentInfo(expectedEnvironment, azureEnvironment); + } + + [Fact] + public void GetAzureEnvironment_ForAzureGermanCloud_ProvidesCorrectEnvironmentInfo() + { + // Arrange + var azureCloud = AzureCloud.Germany; + var expectedEnvironment = AzureEnvironment.AzureGermanCloud; + + // Act + var azureEnvironment = azureCloud.GetAzureEnvironment(); + + // Assert + PromitorAssert.ContainsSameAzureEnvironmentInfo(expectedEnvironment, azureEnvironment); + } + + [Fact] + public void GetAzureEnvironment_ForAzureUSGovernmentCloud_ProvidesCorrectEnvironmentInfo() + { + // Arrange + var azureCloud = AzureCloud.UsGov; + var expectedEnvironment = AzureEnvironment.AzureUSGovernment; + + // Act + var azureEnvironment = azureCloud.GetAzureEnvironment(); + + // Assert + PromitorAssert.ContainsSameAzureEnvironmentInfo(expectedEnvironment, azureEnvironment); + } + + [Fact] + public void GetAzureEnvironment_ForUnspecifiedAzureCloud_ThrowsException() + { + // Arrange + var azureCloud = AzureCloud.Unspecified; + + // Act & Assert + Assert.Throws(()=> azureCloud.GetAzureEnvironment()); + } + } +} diff --git a/src/Promitor.Tests.Unit/PromitorAssert.cs b/src/Promitor.Tests.Unit/PromitorAssert.cs new file mode 100644 index 000000000..21c8adcf6 --- /dev/null +++ b/src/Promitor.Tests.Unit/PromitorAssert.cs @@ -0,0 +1,23 @@ +using Microsoft.Azure.Management.ResourceManager.Fluent; +using Xunit; + +namespace Promitor.Tests.Unit +{ + internal class PromitorAssert : Assert + { + /// + /// Verifies if Azure environment information are equal + /// + public static void ContainsSameAzureEnvironmentInfo(AzureEnvironment expectedEnvironment, AzureEnvironment azureEnvironment) + { + NotNull(azureEnvironment); + Equal(expectedEnvironment.Name, azureEnvironment.Name); + Equal(expectedEnvironment.AuthenticationEndpoint, azureEnvironment.AuthenticationEndpoint); + Equal(expectedEnvironment.GraphEndpoint, azureEnvironment.GraphEndpoint); + Equal(expectedEnvironment.KeyVaultSuffix, azureEnvironment.KeyVaultSuffix); + Equal(expectedEnvironment.ManagementEndpoint, azureEnvironment.ManagementEndpoint); + Equal(expectedEnvironment.ResourceManagerEndpoint, azureEnvironment.ResourceManagerEndpoint); + Equal(expectedEnvironment.StorageEndpointSuffix, azureEnvironment.StorageEndpointSuffix); + } + } +} \ No newline at end of file diff --git a/src/Promitor.Tests.Unit/Serialization/v1/Core/AzureMetadataDeserializerTests.cs b/src/Promitor.Tests.Unit/Serialization/v1/Core/AzureMetadataDeserializerTests.cs index e3f199485..ed9d8b0f4 100644 --- a/src/Promitor.Tests.Unit/Serialization/v1/Core/AzureMetadataDeserializerTests.cs +++ b/src/Promitor.Tests.Unit/Serialization/v1/Core/AzureMetadataDeserializerTests.cs @@ -3,7 +3,7 @@ using Microsoft.Azure.Management.ResourceManager.Fluent; using Microsoft.Extensions.Logging.Abstractions; using Promitor.Core.Scraping.Configuration.Serialization.v1.Core; -using Promitor.Core.Scraping.Configuration.Serialization.v1.Model; +using Promitor.Core.Serialization.Enum; using Xunit; using YamlDotNet.RepresentationModel; @@ -20,13 +20,29 @@ public AzureMetadataDeserializerTests() } [Fact] - public void Deserialize_AzureCloudSupplied_SetsAzureCloud() + public void Deserialize_AzureCloudSuppliedWithUnspecified_SetsAzureChinaCloud() + { + var yamlNode = YamlUtils.CreateYamlNode( + $@"azureMetadata: + cloud: '{AzureCloud.Unspecified}'"); + var azureMetadataNode = (YamlMappingNode)yamlNode.Children["azureMetadata"]; + var errorNode = azureMetadataNode.Children["cloud"]; + + YamlAssert.ReportsError( + _deserializer, + azureMetadataNode, + errorNode, + "'Unspecified' is not a supported value for 'cloud'."); + } + + [Fact] + public void Deserialize_AzureCloudSupplied_SetsAzureChinaCloud() { AzureEnvironment azureCloud = AzureEnvironment.AzureChinaCloud; var yamlText = $@"azureMetadata: - cloud: '{AzureCloudsV1.China}'"; + cloud: '{AzureCloud.China}'"; YamlAssert.PropertySet( _deserializer, @@ -57,7 +73,7 @@ public void Deserialize_AzureCloudNotSupplied_SetsGlobalAzureCloud() public void Deserialize_InvalidAzureCloudSupplied_ReportsError() { var yamlNode = YamlUtils.CreateYamlNode( -@"azureMetadata: + @"azureMetadata: cloud: invalid"); var azureMetadataNode = (YamlMappingNode)yamlNode.Children["azureMetadata"]; var errorNode = azureMetadataNode.Children["cloud"]; From d3c66a232d844c71314377a047fcbeae7357aaa9 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Tue, 28 Jul 2020 08:39:44 +0200 Subject: [PATCH 2/3] Provide capability to specify the cloud to use Signed-off-by: Tom Kerkhove --- .../Configuration/AzureLandscape.cs | 2 ++ .../Graph/AzureResourceGraph.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Promitor.Agents.ResourceDiscovery/Configuration/AzureLandscape.cs b/src/Promitor.Agents.ResourceDiscovery/Configuration/AzureLandscape.cs index c2b000389..31b8d4db4 100644 --- a/src/Promitor.Agents.ResourceDiscovery/Configuration/AzureLandscape.cs +++ b/src/Promitor.Agents.ResourceDiscovery/Configuration/AzureLandscape.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Promitor.Core.Serialization.Enum; namespace Promitor.Agents.ResourceDiscovery.Configuration { @@ -6,5 +7,6 @@ public class AzureLandscape { public string TenantId { get; set; } public List Subscriptions { get; set; } + public AzureCloud Cloud { get; set; } = AzureCloud.Global; } } diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs index c2d60a51d..4dca3a5e6 100644 --- a/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs @@ -14,6 +14,7 @@ using Promitor.Agents.ResourceDiscovery.Configuration; using Promitor.Agents.ResourceDiscovery.Graph.Exceptions; using Promitor.Agents.ResourceDiscovery.Graph.Model; +using Promitor.Core.Extensions; namespace Promitor.Agents.ResourceDiscovery.Graph { @@ -210,7 +211,8 @@ private async Task GetOrCreateClient() private async Task CreateClientAsync() { - var credentials = await Authentication.GetServiceClientCredentialsAsync("https://management.core.windows.net", QueryApplicationId, _queryApplicationSecret, TenantId); + var azureEnvironment = _resourceDeclarationMonitor.CurrentValue.AzureLandscape.Cloud.GetAzureEnvironment(); + var credentials = await Authentication.GetServiceClientCredentialsAsync(azureEnvironment.ManagementEndpoint, QueryApplicationId, _queryApplicationSecret, TenantId); return new ResourceGraphClient(credentials); } } From d8db442a94fad072ecc40db8491c17e4e712f369 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Tue, 28 Jul 2020 10:45:01 +0200 Subject: [PATCH 3/3] Reduce redundant qualifier --- .../Serialization/v1/Core/AzureMetadataDeserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs index e90c4f6af..5aa830e04 100644 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureMetadataDeserializer.cs @@ -26,7 +26,7 @@ public AzureMetadataDeserializer(ILogger logger) : ba private object DetermineAzureCloud(string rawAzureCloud, KeyValuePair nodePair, IErrorReporter errorReporter) { - if (System.Enum.TryParse(rawAzureCloud, out var azureCloud)) + if (Enum.TryParse(rawAzureCloud, out var azureCloud)) { try {