From ab1c555682e7dfd7c9db8a9115e1e10a64104765 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 21 Jul 2021 09:38:11 +0200 Subject: [PATCH 1/8] Provide integration test to verify metrics are reported in Prometheus Signed-off-by: Tom Kerkhove --- .../Clients/ScraperClient.cs | 31 ++++++++++++++++++- .../Promitor.Tests.Integration.csproj | 2 ++ .../PrometheusScrapeMetricSinkTests.cs} | 26 ++++++++++++++-- 3 files changed, 55 insertions(+), 4 deletions(-) rename src/Promitor.Tests.Integration/Services/Scraper/{MetricSinksTests.cs => MetricSinks/PrometheusScrapeMetricSinkTests.cs} (53%) diff --git a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs index 4e833bdd7..9877b3e96 100644 --- a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs +++ b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs @@ -1,7 +1,12 @@ -using System.Net.Http; +using System; +using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Polly; +using Promitor.Parsers.Prometheus.Core.Models; +using Promitor.Parsers.Prometheus.Core.Models.Interfaces; namespace Promitor.Tests.Integration.Clients { @@ -27,5 +32,29 @@ public async Task ScrapeAsync() var scrapeUri = Configuration["Agents:Scraper:Prometheus:ScrapeUri"]; return await GetAsync($"/{scrapeUri}"); } + + public async Task WaitForPrometheusMetricAsync(string expectedMetricName) + { + // Create retry to poll for metric to show up + const int MaxRetries = 5; + var pollPolicy = Policy.HandleResult>(x => x == null || x.Find(x => x.Name == expectedMetricName) == null) + .WaitAndRetryAsync(5, + retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), + (exception, timeSpan, retryCount, context) => + { + Logger.LogInformation($"Metric {expectedMetricName} was not found, retrying ({retryCount}/{MaxRetries})."); + }); + + // Poll + var foundMetrics = await pollPolicy.ExecuteAsync(async () => + { + var scrapeResponse = await ScrapeAsync(); + return await scrapeResponse.ReadAsPrometheusMetricsAsync(); + }); + + // Interpret results + var matchingMetric = foundMetrics.Find(x => x.Name == expectedMetricName); + return matchingMetric != null ? (Gauge)matchingMetric : null; + } } } \ No newline at end of file diff --git a/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj b/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj index 0110f5b0f..59643628a 100644 --- a/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj +++ b/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj @@ -20,6 +20,8 @@ + + all diff --git a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinksTests.cs b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs similarity index 53% rename from src/Promitor.Tests.Integration/Services/Scraper/MetricSinksTests.cs rename to src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs index 56160b28b..4502879c0 100644 --- a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinksTests.cs +++ b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Net.Http; using System.Threading.Tasks; using Promitor.Agents.Core; using Promitor.Tests.Integration.Clients; @@ -6,11 +7,11 @@ using Xunit; using Xunit.Abstractions; -namespace Promitor.Tests.Integration.Services.Scraper +namespace Promitor.Tests.Integration.Services.Scraper.MetricSinks { - public class MetricSinksTests : ScraperIntegrationTest + public class PrometheusScrapeMetricSinkTests : ScraperIntegrationTest { - public MetricSinksTests(ITestOutputHelper testOutput) + public PrometheusScrapeMetricSinkTests(ITestOutputHelper testOutput) : base(testOutput) { } @@ -28,6 +29,25 @@ public async Task Prometheus_Scrape_ReturnsOk() Assert.Equal(HttpStatusCode.OK, response.StatusCode); } + [Theory] + [InlineData("promitor_ratelimit_arm")] + [InlineData("promitor_scrape_success")] + [InlineData("promitor_scrape_error")] + public async Task Prometheus_Scrape_ReturnsArmRateLimitingHeader(string expectedMetricName) + { + // Arrange + var scraperClient = new ScraperClient(Configuration, Logger); + + // Act + var gaugeMetric = await scraperClient.WaitForPrometheusMetricAsync(expectedMetricName); + + // Assert + Assert.NotNull(gaugeMetric); + Assert.Equal(expectedMetricName, gaugeMetric.Name); + Assert.NotNull(gaugeMetric.Measurements); + Assert.False(gaugeMetric.Measurements.Count < 1); + } + [Fact] public async Task Prometheus_Scrape_Get_ReturnsVersionHeader() { From 372e05d64e52e1b72f8f2a06b4461404b5300dc6 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 21 Jul 2021 10:06:40 +0200 Subject: [PATCH 2/8] Bump Promitor.Parsers.Prometheus.Http to 0.1.0-rc.3 Signed-off-by: Tom Kerkhove --- .../Promitor.Tests.Integration.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj b/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj index 59643628a..5e7d4bb90 100644 --- a/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj +++ b/src/Promitor.Tests.Integration/Promitor.Tests.Integration.csproj @@ -21,7 +21,7 @@ - + all From 90fc2dc1bbf1b7d8e4795d4cb0780b8d252dbe73 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 21 Jul 2021 10:12:18 +0200 Subject: [PATCH 3/8] Code cleanup Signed-off-by: Tom Kerkhove --- src/Promitor.Tests.Integration/Clients/ScraperClient.cs | 8 ++++---- .../MetricSinks/PrometheusScrapeMetricSinkTests.cs | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs index 9877b3e96..5b52e2d0a 100644 --- a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs +++ b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs @@ -36,13 +36,13 @@ public async Task ScrapeAsync() public async Task WaitForPrometheusMetricAsync(string expectedMetricName) { // Create retry to poll for metric to show up - const int MaxRetries = 5; - var pollPolicy = Policy.HandleResult>(x => x == null || x.Find(x => x.Name == expectedMetricName) == null) + const int maxRetries = 5; + var pollPolicy = Policy.HandleResult>(metrics => metrics?.Find(metric => metric.Name == expectedMetricName) == null) .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, retryCount, context) => { - Logger.LogInformation($"Metric {expectedMetricName} was not found, retrying ({retryCount}/{MaxRetries})."); + Logger.LogInformation($"Metric {expectedMetricName} was not found, retrying ({retryCount}/{maxRetries})."); }); // Poll @@ -54,7 +54,7 @@ public async Task WaitForPrometheusMetricAsync(string expectedMetricName) // Interpret results var matchingMetric = foundMetrics.Find(x => x.Name == expectedMetricName); - return matchingMetric != null ? (Gauge)matchingMetric : null; + return (Gauge) matchingMetric; } } } \ No newline at end of file diff --git a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs index 4502879c0..13b1f6250 100644 --- a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs +++ b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Net.Http; using System.Threading.Tasks; using Promitor.Agents.Core; using Promitor.Tests.Integration.Clients; @@ -33,7 +32,7 @@ public async Task Prometheus_Scrape_ReturnsOk() [InlineData("promitor_ratelimit_arm")] [InlineData("promitor_scrape_success")] [InlineData("promitor_scrape_error")] - public async Task Prometheus_Scrape_ReturnsArmRateLimitingHeader(string expectedMetricName) + public async Task Prometheus_Scrape_ExpectedMetricIsAvailable(string expectedMetricName) { // Arrange var scraperClient = new ScraperClient(Configuration, Logger); From 8bc3c6096c68b8b1ce05d0935dca2b7aeeecd63f Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Wed, 21 Jul 2021 17:09:39 +0200 Subject: [PATCH 4/8] WIP Signed-off-by: Tom Kerkhove --- .../Controllers/v1/ConfigurationController.cs | 17 ++++- .../AzureResourceDefinition.cs | 6 +- .../Model/Metrics/MetricDefinition.cs | 4 +- .../v1/Mapping/V1MappingProfile.cs | 2 +- .../Clients/AgentClient.cs | 3 +- .../Clients/ScraperClient.cs | 46 ++++++++++-- .../Services/Scraper/ConfigurationTests.cs | 8 +- .../PrometheusScrapeMetricSinkTests.cs | 73 ++++++++++++++++++- .../Model/Metrics/MetricDefinitionTests.cs | 10 +-- 9 files changed, 143 insertions(+), 26 deletions(-) diff --git a/src/Promitor.Agents.Scraper/Controllers/v1/ConfigurationController.cs b/src/Promitor.Agents.Scraper/Controllers/v1/ConfigurationController.cs index d12bb9656..3f266e7c9 100644 --- a/src/Promitor.Agents.Scraper/Controllers/v1/ConfigurationController.cs +++ b/src/Promitor.Agents.Scraper/Controllers/v1/ConfigurationController.cs @@ -2,6 +2,8 @@ using System.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Promitor.Agents.Scraper.Configuration; using Promitor.Core.Scraping.Configuration.Model.Metrics; using Promitor.Core.Scraping.Configuration.Providers.Interfaces; @@ -13,6 +15,7 @@ namespace Promitor.Agents.Scraper.Controllers.v1 [Route("api/v1/configuration")] public class ConfigurationController : Controller { + private readonly JsonSerializerSettings _serializerSettings; private readonly IMetricsDeclarationProvider _metricsDeclarationProvider; private readonly IOptionsMonitor _runtimeConfiguration; @@ -20,6 +23,13 @@ public ConfigurationController(IOptionsMonitor runt { _runtimeConfiguration = runtimeConfiguration; _metricsDeclarationProvider = metricsDeclarationProvider; + + _serializerSettings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + TypeNameHandling = TypeNameHandling.Objects + }; + _serializerSettings.Converters.Add(new StringEnumConverter()); } /// @@ -35,7 +45,12 @@ public ConfigurationController(IOptionsMonitor runt public IActionResult GetMetricDeclaration() { var scrapeConfiguration = _metricsDeclarationProvider.Get(true); - return Ok(scrapeConfiguration.Metrics); + + var serializedResources = JsonConvert.SerializeObject(scrapeConfiguration.Metrics, _serializerSettings); + + var response= Content(serializedResources, "application/json"); + response.StatusCode = (int) HttpStatusCode.OK; + return response; } /// diff --git a/src/Promitor.Core.Contracts/AzureResourceDefinition.cs b/src/Promitor.Core.Contracts/AzureResourceDefinition.cs index 7c3e92be7..8b75d0c2f 100644 --- a/src/Promitor.Core.Contracts/AzureResourceDefinition.cs +++ b/src/Promitor.Core.Contracts/AzureResourceDefinition.cs @@ -8,6 +8,10 @@ namespace Promitor.Core.Contracts /// public class AzureResourceDefinition : IAzureResourceDefinition { + public AzureResourceDefinition() + { + } + /// /// Constructor /// @@ -16,7 +20,7 @@ public class AzureResourceDefinition : IAzureResourceDefinition /// Specify a resource group to scrape that defers from the default resource group. /// Name of the main resource public AzureResourceDefinition(ResourceType resourceType, string subscriptionId, string resourceGroupName, string resourceName) - : this(resourceType, subscriptionId, resourceGroupName, resourceName, resourceName) + : this(resourceType, subscriptionId, resourceGroupName, resourceName, resourceName) { } diff --git a/src/Promitor.Core.Scraping/Configuration/Model/Metrics/MetricDefinition.cs b/src/Promitor.Core.Scraping/Configuration/Model/Metrics/MetricDefinition.cs index cdca59fcc..5d76a42e0 100644 --- a/src/Promitor.Core.Scraping/Configuration/Model/Metrics/MetricDefinition.cs +++ b/src/Promitor.Core.Scraping/Configuration/Model/Metrics/MetricDefinition.cs @@ -13,7 +13,7 @@ public MetricDefinition(PrometheusMetricDefinition prometheusMetricDefinition, Scraping scraping, AzureMetricConfiguration azureMetricConfiguration, ResourceType resourceType, - List resources) + List resources) { AzureMetricConfiguration = azureMetricConfiguration; PrometheusMetricDefinition = prometheusMetricDefinition; @@ -50,7 +50,7 @@ public MetricDefinition(PrometheusMetricDefinition prometheusMetricDefinition, /// /// Gets or sets the list of resources to scrape. /// - public List Resources { get; set; } + public List Resources { get; set; } /// /// Creates a object for the specified resource. diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs index 60175ce44..79f221772 100644 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs @@ -66,7 +66,7 @@ public V1MappingProfile() CreateMap() .ForMember(m => m.PrometheusMetricDefinition, o => o.MapFrom(v1 => v1)); - CreateMap() + CreateMap() .Include() .Include() .Include() diff --git a/src/Promitor.Tests.Integration/Clients/AgentClient.cs b/src/Promitor.Tests.Integration/Clients/AgentClient.cs index 203ae3ad3..8278b0078 100644 --- a/src/Promitor.Tests.Integration/Clients/AgentClient.cs +++ b/src/Promitor.Tests.Integration/Clients/AgentClient.cs @@ -55,7 +55,8 @@ protected async Task GetAsync(string uri) try { var rawResponse = await response.Content.ReadAsStringAsync(); - context.Add("Body", rawResponse); + // TODO: Uncomment for troubleshooting + // context.Add("Body", rawResponse); } finally { diff --git a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs index 5b52e2d0a..62aee8c26 100644 --- a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs +++ b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Polly; +using Promitor.Core.Scraping.Configuration.Model; +using Promitor.Core.Scraping.Configuration.Model.Metrics; using Promitor.Parsers.Prometheus.Core.Models; using Promitor.Parsers.Prometheus.Core.Models.Interfaces; @@ -13,47 +17,73 @@ namespace Promitor.Tests.Integration.Clients public class ScraperClient : AgentClient { public ScraperClient(IConfiguration configuration, ILogger logger) - :base("Scraper", "Agents:Scraper:BaseUrl", configuration,logger) + : base("Scraper", "Agents:Scraper:BaseUrl", configuration, logger) { } - public async Task GetRuntimeConfigurationAsync() + public async Task GetRuntimeConfigurationWithResponseAsync() { return await GetAsync("/api/v1/configuration/runtime"); } - public async Task GetMetricDeclarationAsync() + public async Task GetMetricDeclarationWithResponseAsync() { return await GetAsync("/api/v1/configuration/metric-declaration"); } - public async Task ScrapeAsync() + public async Task> GetMetricDeclarationAsync() + { + var response = await GetMetricDeclarationWithResponseAsync(); + var rawResponse = await response.Content.ReadAsStringAsync(); + var jsonSerializerSettings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + NullValueHandling = NullValueHandling.Ignore, + MetadataPropertyHandling = MetadataPropertyHandling.Ignore + }; + return JsonConvert.DeserializeObject>(rawResponse, jsonSerializerSettings); + } + + public async Task ScrapeWithResponseAsync() { var scrapeUri = Configuration["Agents:Scraper:Prometheus:ScrapeUri"]; return await GetAsync($"/{scrapeUri}"); } public async Task WaitForPrometheusMetricAsync(string expectedMetricName) + { + return await WaitForPrometheusMetricAsync(x => x.Name.Equals(expectedMetricName, StringComparison.InvariantCultureIgnoreCase)); + } + + public async Task WaitForPrometheusMetricAsync(string expectedMetricName, string expectedLabelName, string expectedLabelValue) + { + Func, bool> labelFilter = label => label.Key.Equals(expectedLabelName, StringComparison.InvariantCultureIgnoreCase) + && label.Value.Equals(expectedLabelValue, StringComparison.InvariantCultureIgnoreCase); + return await WaitForPrometheusMetricAsync(x => x.Name.Equals(expectedMetricName, StringComparison.InvariantCultureIgnoreCase) + && x.Measurements?.Any(measurement => measurement.Labels?.Any(labelFilter) == true) == true); + } + + private async Task WaitForPrometheusMetricAsync(Predicate filter) { // Create retry to poll for metric to show up const int maxRetries = 5; - var pollPolicy = Policy.HandleResult>(metrics => metrics?.Find(metric => metric.Name == expectedMetricName) == null) + var pollPolicy = Policy.HandleResult>(metrics => metrics?.Find(x => filter((Gauge) x)) == null) .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, retryCount, context) => { - Logger.LogInformation($"Metric {expectedMetricName} was not found, retrying ({retryCount}/{maxRetries})."); + Logger.LogInformation($"Metric was not found, retrying ({retryCount}/{maxRetries})."); }); // Poll var foundMetrics = await pollPolicy.ExecuteAsync(async () => { - var scrapeResponse = await ScrapeAsync(); + var scrapeResponse = await ScrapeWithResponseAsync(); return await scrapeResponse.ReadAsPrometheusMetricsAsync(); }); // Interpret results - var matchingMetric = foundMetrics.Find(x => x.Name == expectedMetricName); + var matchingMetric = foundMetrics.Find(x => filter((Gauge) x)); return (Gauge) matchingMetric; } } diff --git a/src/Promitor.Tests.Integration/Services/Scraper/ConfigurationTests.cs b/src/Promitor.Tests.Integration/Services/Scraper/ConfigurationTests.cs index e5c9d2eed..787eb1ae0 100644 --- a/src/Promitor.Tests.Integration/Services/Scraper/ConfigurationTests.cs +++ b/src/Promitor.Tests.Integration/Services/Scraper/ConfigurationTests.cs @@ -24,7 +24,7 @@ public async Task RuntimeConfiguration_Get_ReturnsOk() var scraperClient = new ScraperClient(Configuration, Logger); // Act - var response = await scraperClient.GetRuntimeConfigurationAsync(); + var response = await scraperClient.GetRuntimeConfigurationWithResponseAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -41,7 +41,7 @@ public async Task RuntimeConfiguration_Get_ReturnsVersionHeader() var scraperClient = new ScraperClient(Configuration, Logger); // Act - var response = await scraperClient.GetRuntimeConfigurationAsync(); + var response = await scraperClient.GetRuntimeConfigurationWithResponseAsync(); // Assert Assert.True(response.Headers.Contains(HttpHeaders.AgentVersion)); @@ -55,7 +55,7 @@ public async Task MetricDeclaration_Get_ReturnsOk() var scraperClient = new ScraperClient(Configuration, Logger); // Act - var response = await scraperClient.GetMetricDeclarationAsync(); + var response = await scraperClient.GetMetricDeclarationWithResponseAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -68,7 +68,7 @@ public async Task MetricDeclaration_Get_ReturnsVersionHeader() var scraperClient = new ScraperClient(Configuration, Logger); // Act - var response = await scraperClient.GetMetricDeclarationAsync(); + var response = await scraperClient.GetMetricDeclarationWithResponseAsync(); // Assert Assert.True(response.Headers.Contains(HttpHeaders.AgentVersion)); diff --git a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs index 13b1f6250..3abbf6075 100644 --- a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs +++ b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs @@ -1,5 +1,11 @@ -using System.Net; +using System.Collections; +using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Promitor.Agents.Core; using Promitor.Tests.Integration.Clients; using Promitor.Tests.Integration.Extensions; @@ -15,6 +21,8 @@ public PrometheusScrapeMetricSinkTests(ITestOutputHelper testOutput) { } + public static IEnumerable ConfiguredMetricNames { get; set; } = new List(); + [Fact] public async Task Prometheus_Scrape_ReturnsOk() { @@ -22,7 +30,7 @@ public async Task Prometheus_Scrape_ReturnsOk() var scraperClient = new ScraperClient(Configuration, Logger); // Act - var response = await scraperClient.ScrapeAsync(); + var response = await scraperClient.ScrapeWithResponseAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -32,6 +40,7 @@ public async Task Prometheus_Scrape_ReturnsOk() [InlineData("promitor_ratelimit_arm")] [InlineData("promitor_scrape_success")] [InlineData("promitor_scrape_error")] + [ClassData(typeof(TestDataGenerator))] public async Task Prometheus_Scrape_ExpectedMetricIsAvailable(string expectedMetricName) { // Arrange @@ -47,6 +56,36 @@ public async Task Prometheus_Scrape_ExpectedMetricIsAvailable(string expectedMet Assert.False(gaugeMetric.Measurements.Count < 1); } + [Theory] + [ClassData(typeof(TestDataGenerator))] + public async Task Prometheus_Scrape_EveryMetricHasAnErrorMetric(string expectedMetricName) + { + // Arrange + const string errorMetricName = "promitor_scrape_error"; + var scraperClient = new ScraperClient(Configuration, Logger); + + // Act + var gaugeMetric = await scraperClient.WaitForPrometheusMetricAsync(errorMetricName, "metric_name", expectedMetricName); + + // Assert + Assert.NotNull(gaugeMetric); + } + + [Theory] + [ClassData(typeof(TestDataGenerator))] + public async Task Prometheus_Scrape_EveryMetricHasAnSuccessMetric(string expectedMetricName) + { + // Arrange + const string errorMetricName = "promitor_scrape_success"; + var scraperClient = new ScraperClient(Configuration, Logger); + + // Act + var gaugeMetric = await scraperClient.WaitForPrometheusMetricAsync(errorMetricName, "metric_name", expectedMetricName); + + // Assert + Assert.NotNull(gaugeMetric); + } + [Fact] public async Task Prometheus_Scrape_Get_ReturnsVersionHeader() { @@ -54,11 +93,39 @@ public async Task Prometheus_Scrape_Get_ReturnsVersionHeader() var scraperClient = new ScraperClient(Configuration, Logger); // Act - var response = await scraperClient.ScrapeAsync(); + var response = await scraperClient.ScrapeWithResponseAsync(); // Assert Assert.True(response.Headers.Contains(HttpHeaders.AgentVersion)); Assert.Equal(ExpectedVersion, response.Headers.GetFirstOrDefaultHeaderValue(HttpHeaders.AgentVersion)); } } + + public class TestDataGenerator : IEnumerable + { + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + + IEnumerator IEnumerable.GetEnumerator() + { + var scraperClient = new ScraperClient(_configuration, _logger); + var declaration = scraperClient.GetMetricDeclarationAsync().Result; + return declaration.Select(x => new object[] { x.PrometheusMetricDefinition.Name }).GetEnumerator(); + } + + public IEnumerator GetEnumerator() => GetEnumerator(); + + public TestDataGenerator() + { + _logger = NullLogger.Instance; + + // TODO: Move to central place and re-use for regular tests + // The appsettings.local.json allows users to override (gitignored) settings locally for testing purposes + _configuration = new ConfigurationBuilder() + .AddJsonFile(path: "appsettings.json") + .AddJsonFile(path: "appsettings.local.json", optional: true) + .AddEnvironmentVariables() + .Build(); + } + } } diff --git a/src/Promitor.Tests.Unit/Core/Scraping/Configuration/Model/Metrics/MetricDefinitionTests.cs b/src/Promitor.Tests.Unit/Core/Scraping/Configuration/Model/Metrics/MetricDefinitionTests.cs index f7c9db6a9..040f0fc7c 100644 --- a/src/Promitor.Tests.Unit/Core/Scraping/Configuration/Model/Metrics/MetricDefinitionTests.cs +++ b/src/Promitor.Tests.Unit/Core/Scraping/Configuration/Model/Metrics/MetricDefinitionTests.cs @@ -21,7 +21,7 @@ public void CreateScrapeDefinition_ResourceOverridesResourceGroupName_UsesOverri { // Arrange var resource = new ContainerInstanceResourceDefinition(null, "containerInstanceResourceGroup", "containerGroup"); - var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); + var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); // Act var scrapeDefinition = definition.CreateScrapeDefinition(resource, _azureMetadata); @@ -35,7 +35,7 @@ public void CreateScrapeDefinition_ResourceDoesNotSpecifyResourceGroupName_UsesG { // Arrange var resource = new ContainerInstanceResourceDefinition("subscription", null, "containerGroup"); - var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); + var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); // Act var scrapeDefinition = definition.CreateScrapeDefinition(resource, _azureMetadata); @@ -48,7 +48,7 @@ public void CreateScrapeDefinition_ResourceOverridesSubscription_UsesOverriddenN { // Arrange var resource = new ContainerInstanceResourceDefinition("subscription", "containerInstanceResourceGroup", "containerGroup"); - var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); + var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); // Act var scrapeDefinition = definition.CreateScrapeDefinition(resource, _azureMetadata); @@ -62,7 +62,7 @@ public void CreateScrapeDefinition_ResourceDoesNotSpecifySubscription_UsesGlobal { // Arrange var resource = new ContainerInstanceResourceDefinition(null, "containerInstanceResourceGroup", "containerGroup"); - var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); + var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); // Act var scrapeDefinition = definition.CreateScrapeDefinition(resource, _azureMetadata); @@ -76,7 +76,7 @@ public void CreateScrapeDefinition_ResourceHasEmptyResourceGroupName_UsesGlobalN { // Arrange var resource = new ContainerInstanceResourceDefinition("subscription", string.Empty, "containerGroup"); - var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); + var definition = new MetricDefinition(_prometheusMetricDefinition, new Promitor.Core.Scraping.Configuration.Model.Scraping(), new AzureMetricConfiguration(), ResourceType.ContainerInstance, new List { resource }); // Act var scrapeDefinition = definition.CreateScrapeDefinition(resource, _azureMetadata); From 42a902b4e4e50eba945fc5bb8b7c3915475319c4 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Sun, 25 Jul 2021 21:45:03 +0200 Subject: [PATCH 5/8] Cleanup Signed-off-by: Tom Kerkhove --- .../Clients/AgentClient.cs | 2 +- .../Clients/ScraperClient.cs | 1 - .../AvailableMetricsTestInputGenerator.cs | 32 +++++++++++++ .../Infrastructure/ConfigurationFactory.cs | 17 +++++++ .../IntegrationTest.cs | 7 +-- .../PrometheusScrapeMetricSinkTests.cs | 45 +++---------------- 6 files changed, 57 insertions(+), 47 deletions(-) create mode 100644 src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs create mode 100644 src/Promitor.Tests.Integration/Infrastructure/ConfigurationFactory.cs diff --git a/src/Promitor.Tests.Integration/Clients/AgentClient.cs b/src/Promitor.Tests.Integration/Clients/AgentClient.cs index 8278b0078..f708409e6 100644 --- a/src/Promitor.Tests.Integration/Clients/AgentClient.cs +++ b/src/Promitor.Tests.Integration/Clients/AgentClient.cs @@ -54,7 +54,7 @@ protected async Task GetAsync(string uri) var context = new Dictionary(); try { - var rawResponse = await response.Content.ReadAsStringAsync(); + await response.Content.ReadAsStringAsync(); // TODO: Uncomment for troubleshooting // context.Add("Body", rawResponse); } diff --git a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs index 62aee8c26..e0b49cc32 100644 --- a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs +++ b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Polly; -using Promitor.Core.Scraping.Configuration.Model; using Promitor.Core.Scraping.Configuration.Model.Metrics; using Promitor.Parsers.Prometheus.Core.Models; using Promitor.Parsers.Prometheus.Core.Models.Interfaces; diff --git a/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs b/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs new file mode 100644 index 000000000..877e219ad --- /dev/null +++ b/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs @@ -0,0 +1,32 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Promitor.Tests.Integration.Clients; +using Promitor.Tests.Integration.Infrastructure; + +namespace Promitor.Tests.Integration.Data +{ + public class AvailableMetricsTestInputGenerator : IEnumerable + { + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + + IEnumerator IEnumerable.GetEnumerator() + { + var scraperClient = new ScraperClient(_configuration, _logger); + var declaration = scraperClient.GetMetricDeclarationAsync().Result; + return declaration.Select(x => new object[] { x.PrometheusMetricDefinition.Name }).GetEnumerator(); + } + + public IEnumerator GetEnumerator() => GetEnumerator(); + + public AvailableMetricsTestInputGenerator() + { + _logger = NullLogger.Instance; + _configuration = ConfigurationFactory.Create(); + } + } +} diff --git a/src/Promitor.Tests.Integration/Infrastructure/ConfigurationFactory.cs b/src/Promitor.Tests.Integration/Infrastructure/ConfigurationFactory.cs new file mode 100644 index 000000000..3f86e2ccd --- /dev/null +++ b/src/Promitor.Tests.Integration/Infrastructure/ConfigurationFactory.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Configuration; + +namespace Promitor.Tests.Integration.Infrastructure +{ + public class ConfigurationFactory + { + public static IConfiguration Create() + { + // The appsettings.local.json allows users to override (gitignored) settings locally for testing purposes + return new ConfigurationBuilder() + .AddJsonFile(path: "appsettings.json") + .AddJsonFile(path: "appsettings.local.json", optional: true) + .AddEnvironmentVariables() + .Build(); + } + } +} diff --git a/src/Promitor.Tests.Integration/IntegrationTest.cs b/src/Promitor.Tests.Integration/IntegrationTest.cs index 31631d7e7..e0cf52958 100644 --- a/src/Promitor.Tests.Integration/IntegrationTest.cs +++ b/src/Promitor.Tests.Integration/IntegrationTest.cs @@ -1,5 +1,6 @@ using Arcus.Testing.Logging; using Microsoft.Extensions.Configuration; +using Promitor.Tests.Integration.Infrastructure; using Xunit; using Xunit.Abstractions; @@ -16,11 +17,7 @@ public IntegrationTest(ITestOutputHelper testOutput) Logger = new XunitTestLogger(testOutput); // The appsettings.local.json allows users to override (gitignored) settings locally for testing purposes - Configuration = new ConfigurationBuilder() - .AddJsonFile(path: "appsettings.json") - .AddJsonFile(path: "appsettings.local.json", optional: true) - .AddEnvironmentVariables() - .Build(); + Configuration = ConfigurationFactory.Create(); } } } diff --git a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs index 3abbf6075..cd47cde3e 100644 --- a/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs +++ b/src/Promitor.Tests.Integration/Services/Scraper/MetricSinks/PrometheusScrapeMetricSinkTests.cs @@ -1,13 +1,8 @@ -using System.Collections; -using System.Collections.Generic; -using System.Net; +using System.Net; using System.Threading.Tasks; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Promitor.Agents.Core; using Promitor.Tests.Integration.Clients; +using Promitor.Tests.Integration.Data; using Promitor.Tests.Integration.Extensions; using Xunit; using Xunit.Abstractions; @@ -21,8 +16,6 @@ public PrometheusScrapeMetricSinkTests(ITestOutputHelper testOutput) { } - public static IEnumerable ConfiguredMetricNames { get; set; } = new List(); - [Fact] public async Task Prometheus_Scrape_ReturnsOk() { @@ -40,7 +33,7 @@ public async Task Prometheus_Scrape_ReturnsOk() [InlineData("promitor_ratelimit_arm")] [InlineData("promitor_scrape_success")] [InlineData("promitor_scrape_error")] - [ClassData(typeof(TestDataGenerator))] + [ClassData(typeof(AvailableMetricsTestInputGenerator))] public async Task Prometheus_Scrape_ExpectedMetricIsAvailable(string expectedMetricName) { // Arrange @@ -57,7 +50,7 @@ public async Task Prometheus_Scrape_ExpectedMetricIsAvailable(string expectedMet } [Theory] - [ClassData(typeof(TestDataGenerator))] + [ClassData(typeof(AvailableMetricsTestInputGenerator))] public async Task Prometheus_Scrape_EveryMetricHasAnErrorMetric(string expectedMetricName) { // Arrange @@ -72,7 +65,7 @@ public async Task Prometheus_Scrape_EveryMetricHasAnErrorMetric(string expectedM } [Theory] - [ClassData(typeof(TestDataGenerator))] + [ClassData(typeof(AvailableMetricsTestInputGenerator))] public async Task Prometheus_Scrape_EveryMetricHasAnSuccessMetric(string expectedMetricName) { // Arrange @@ -100,32 +93,4 @@ public async Task Prometheus_Scrape_Get_ReturnsVersionHeader() Assert.Equal(ExpectedVersion, response.Headers.GetFirstOrDefaultHeaderValue(HttpHeaders.AgentVersion)); } } - - public class TestDataGenerator : IEnumerable - { - private readonly IConfiguration _configuration; - private readonly ILogger _logger; - - IEnumerator IEnumerable.GetEnumerator() - { - var scraperClient = new ScraperClient(_configuration, _logger); - var declaration = scraperClient.GetMetricDeclarationAsync().Result; - return declaration.Select(x => new object[] { x.PrometheusMetricDefinition.Name }).GetEnumerator(); - } - - public IEnumerator GetEnumerator() => GetEnumerator(); - - public TestDataGenerator() - { - _logger = NullLogger.Instance; - - // TODO: Move to central place and re-use for regular tests - // The appsettings.local.json allows users to override (gitignored) settings locally for testing purposes - _configuration = new ConfigurationBuilder() - .AddJsonFile(path: "appsettings.json") - .AddJsonFile(path: "appsettings.local.json", optional: true) - .AddEnvironmentVariables() - .Build(); - } - } } From ee86eff87ec9f3f9aaa6d861b786b9691d9bc263 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Sun, 1 Aug 2021 15:25:37 +0200 Subject: [PATCH 6/8] Filter out metrics relying on discovery groups without matching Azure resources Signed-off-by: Tom Kerkhove --- config/promitor/scraper/metrics.yaml | 49 --------------- .../Clients/AgentClient.cs | 22 ++++++- .../Clients/ResourceDiscoveryClient.cs | 18 ++++-- .../Clients/ScraperClient.cs | 24 +++++--- .../AvailableMetricsTestInputGenerator.cs | 61 +++++++++++++++---- .../ResourceCollectionTests.cs | 4 +- .../ResourceDiscoveryTests.cs | 24 ++++---- 7 files changed, 112 insertions(+), 90 deletions(-) diff --git a/config/promitor/scraper/metrics.yaml b/config/promitor/scraper/metrics.yaml index 56528664f..10ab884a1 100644 --- a/config/promitor/scraper/metrics.yaml +++ b/config/promitor/scraper/metrics.yaml @@ -39,18 +39,6 @@ metrics: resources: - autoscaleSettingsName: app-service-autoscaling-rules resourceGroupName: demo -- name: promitor_demo_appplan_percentage_cpu - description: "Average percentage of memory usage on an Azure App Plan" - resourceType: AppPlan - labels: - app: promitor - azureMetricConfiguration: - metricName: MemoryPercentage - aggregation: - type: Average - resources: - - appPlanName: promitor-app-plan - resourceGroupName: promitor-sources - name: azure_container_registry_total_pull_count_discovered description: "Amount of images that were pulled from the container registry" resourceType: ContainerRegistry @@ -175,29 +163,6 @@ metrics: type: Average resources: - namespace: promitor-messaging -- name: promitor_demo_app_insights_dependency_duration - description: "Average dependency duration per dependency type" - resourceType: Generic - azureMetricConfiguration: - metricName: dependencies/duration - dimension: - name: dependency/type - aggregation: - type: Average - resources: - - resourceUri: Microsoft.Insights/Components/docker-hub-metrics - resourceGroupName: docker-hub-metrics -- name: promitor_demo_app_insights_dependency_duration_200_OK - description: "Average dependency duration per dependency type" - resourceType: Generic - azureMetricConfiguration: - metricName: dependencies/duration - aggregation: - type: Average - resources: - - resourceUri: Microsoft.Insights/Components/docker-hub-metrics - resourceGroupName: docker-hub-metrics - filter: dependency/resultCode eq '200' - name: promitor_demo_automation_job_count description: "Amount of jobs per Azure Automation account" resourceType: AutomationAccount @@ -234,20 +199,6 @@ metrics: resources: - resourceGroupName: promitor-sources accountName: promitor-sandbox -- name: promitor_demo_frontdoor_backend_health_per_backend - description: "Health percentage for a backed in Azure Front Door" - resourceType: FrontDoor - labels: - app: promitor - azureMetricConfiguration: - metricName: BackendHealthPercentage - dimension: - name: Backend - aggregation: - type: Average - resources: - - name: promitor-landscape - resourceGroupName: promitor-landscape - name: promitor_demo_frontdoor_backend_health_per_backend_pool description: "Health percentage for a backed in Azure Front Door" resourceType: FrontDoor diff --git a/src/Promitor.Tests.Integration/Clients/AgentClient.cs b/src/Promitor.Tests.Integration/Clients/AgentClient.cs index f708409e6..fb8d866eb 100644 --- a/src/Promitor.Tests.Integration/Clients/AgentClient.cs +++ b/src/Promitor.Tests.Integration/Clients/AgentClient.cs @@ -6,6 +6,7 @@ using GuardNet; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; namespace Promitor.Tests.Integration.Clients { @@ -55,8 +56,8 @@ protected async Task GetAsync(string uri) try { await response.Content.ReadAsStringAsync(); - // TODO: Uncomment for troubleshooting - // context.Add("Body", rawResponse); + // TODO: Uncomment for full payload during troubleshooting + //context.Add("Body", rawResponse); } finally { @@ -65,5 +66,22 @@ protected async Task GetAsync(string uri) return response; } + + protected JsonSerializerSettings GetJsonSerializerSettings() + { + var jsonSerializerSettings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + NullValueHandling = NullValueHandling.Ignore, + MetadataPropertyHandling = MetadataPropertyHandling.Ignore + }; + return jsonSerializerSettings; + } + + protected TResponse GetDeserializedResponse(string rawResponse) + { + var jsonSerializerSettings = GetJsonSerializerSettings(); + return JsonConvert.DeserializeObject(rawResponse, jsonSerializerSettings); + } } } \ No newline at end of file diff --git a/src/Promitor.Tests.Integration/Clients/ResourceDiscoveryClient.cs b/src/Promitor.Tests.Integration/Clients/ResourceDiscoveryClient.cs index dbb0ee38b..ee5023835 100644 --- a/src/Promitor.Tests.Integration/Clients/ResourceDiscoveryClient.cs +++ b/src/Promitor.Tests.Integration/Clients/ResourceDiscoveryClient.cs @@ -1,25 +1,35 @@ -using System.Net.Http; +using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Promitor.Core.Contracts; namespace Promitor.Tests.Integration.Clients { - public class ResourceDiscoveryClient:AgentClient + public class ResourceDiscoveryClient : AgentClient { public ResourceDiscoveryClient(IConfiguration configuration, ILogger logger) : base("Resource Discovery", "Agents:ResourceDiscovery:BaseUrl", configuration, logger) { } - public async Task GetResourceDiscoveryGroupsAsync() + public async Task GetResourceDiscoveryGroupsWithResponseAsync() { return await GetAsync("/api/v1/resources/groups"); } - public async Task GetDiscoveredResourcesAsync(string resourceDiscoveryGroupName) + public async Task GetDiscoveredResourcesWithResponseAsync(string resourceDiscoveryGroupName) { return await GetAsync($"/api/v1/resources/groups/{resourceDiscoveryGroupName}/discover"); } + + public async Task> GetDiscoveredResourcesAsync(string resourceDiscoveryGroupName) + { + var response = await GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); + var rawResponse = await response.Content.ReadAsStringAsync(); + + return GetDeserializedResponse>(rawResponse); + } } } \ No newline at end of file diff --git a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs index f1930e1a5..4a74d909e 100644 --- a/src/Promitor.Tests.Integration/Clients/ScraperClient.cs +++ b/src/Promitor.Tests.Integration/Clients/ScraperClient.cs @@ -5,10 +5,8 @@ using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using Polly; using Promitor.Core.Scraping.Configuration.Model.Metrics; -using Polly; using Promitor.Parsers.Prometheus.Core.Models; using Promitor.Parsers.Prometheus.Core.Models.Interfaces; @@ -35,13 +33,8 @@ public async Task> GetMetricDeclarationAsync() { var response = await GetMetricDeclarationWithResponseAsync(); var rawResponse = await response.Content.ReadAsStringAsync(); - var jsonSerializerSettings = new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.Objects, - NullValueHandling = NullValueHandling.Ignore, - MetadataPropertyHandling = MetadataPropertyHandling.Ignore - }; - return JsonConvert.DeserializeObject>(rawResponse, jsonSerializerSettings); + + return GetDeserializedResponse>(rawResponse); } public async Task ScrapeWithResponseAsync() @@ -84,7 +77,18 @@ private async Task WaitForPrometheusMetricAsync(Predicate filter) // Interpret results var matchingMetric = foundMetrics.Find(x => filter((Gauge) x)); - return (Gauge) matchingMetric; + var gauge = (Gauge)matchingMetric; + + if (gauge == null) + { + Logger.LogInformation("No matching gauge was found"); + } + else + { + Logger.LogInformation("Found matching gauge {Name} with {Measurements} measurements", gauge.Name, gauge.Measurements.Count); + } + + return gauge; } } } \ No newline at end of file diff --git a/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs b/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs index 877e219ad..9a97e27f3 100644 --- a/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs +++ b/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs @@ -1,9 +1,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Promitor.Core.Scraping.Configuration.Model.Metrics; using Promitor.Tests.Integration.Clients; using Promitor.Tests.Integration.Infrastructure; @@ -11,22 +10,62 @@ namespace Promitor.Tests.Integration.Data { public class AvailableMetricsTestInputGenerator : IEnumerable { - private readonly IConfiguration _configuration; - private readonly ILogger _logger; + private readonly ResourceDiscoveryClient _resourceDiscoveryClient; + private readonly ScraperClient _scraperClient; + + public AvailableMetricsTestInputGenerator() + { + var logger = NullLogger.Instance; + var configuration = ConfigurationFactory.Create(); + + _scraperClient = new ScraperClient(configuration, logger); + _resourceDiscoveryClient = new ResourceDiscoveryClient(configuration, logger); + } IEnumerator IEnumerable.GetEnumerator() { - var scraperClient = new ScraperClient(_configuration, _logger); - var declaration = scraperClient.GetMetricDeclarationAsync().Result; - return declaration.Select(x => new object[] { x.PrometheusMetricDefinition.Name }).GetEnumerator(); + var configuredMetrics = GetConfiguredMetrics(); + + // We need to filter out configured metrics that only have resource discovery groups, but don't have any matching resources + // Otherwise, we'll expect metrics to show up while they will never be reported which is by design. + var foundMetricNames = FilterResourceGroupsWithoutResources(configuredMetrics); + + return foundMetricNames.Select(x => new object[] { x }).GetEnumerator(); } - public IEnumerator GetEnumerator() => GetEnumerator(); + private List FilterResourceGroupsWithoutResources(List configuredMetrics) + { + List foundMetricNames = new List(); + foreach (var configuredMetric in configuredMetrics) + { + bool resourceFound = true; + if (configuredMetric.Resources?.Any() == false) + { + foreach (var discoveryGroup in configuredMetric.ResourceDiscoveryGroups) + { + var discoveredResources = _resourceDiscoveryClient.GetDiscoveredResourcesAsync(discoveryGroup.Name).Result; + if (discoveredResources?.Any() != true) + { + resourceFound = false; + break; + } + } + } - public AvailableMetricsTestInputGenerator() + if (resourceFound) + { + foundMetricNames.Add(configuredMetric.PrometheusMetricDefinition.Name); + } + } + + return foundMetricNames; + } + + private List GetConfiguredMetrics() { - _logger = NullLogger.Instance; - _configuration = ConfigurationFactory.Create(); + return _scraperClient.GetMetricDeclarationAsync().Result; } + + public IEnumerator GetEnumerator() => GetEnumerator(); } } diff --git a/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceCollectionTests.cs b/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceCollectionTests.cs index c217109b4..4d1ac474f 100644 --- a/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceCollectionTests.cs +++ b/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceCollectionTests.cs @@ -25,7 +25,7 @@ public async Task ResourceDiscoveryGroup_GetAll_ReturnsValidList() var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetResourceDiscoveryGroupsAsync(); + var response = await resourceDiscoveryClient.GetResourceDiscoveryGroupsWithResponseAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -43,7 +43,7 @@ public async Task ResourceDiscoveryGroup_SuccessfulCall_ReturnsVersionHeader() var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetResourceDiscoveryGroupsAsync(); + var response = await resourceDiscoveryClient.GetResourceDiscoveryGroupsWithResponseAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs b/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs index 9dd38c79e..5463455e6 100644 --- a/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs +++ b/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs @@ -29,7 +29,7 @@ public async Task ResourceDiscovery_GetForUnexistingResourceDiscoveryGroup_Retur var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); @@ -45,7 +45,7 @@ public async Task ResourceDiscovery_SuccessfulCall_ReturnsVersionHeader() var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -62,7 +62,7 @@ public async Task ResourceDiscovery_GetAllPerResourceTypeWithoutFilters_ReturnsE var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -82,7 +82,7 @@ public async Task ResourceDiscovery_GetWithFilterOnOneResourceGroup_ReturnsExpec var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -102,7 +102,7 @@ public async Task ResourceDiscovery_GetWithFilterOnOneResourceGroupWithDifferent var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -122,7 +122,7 @@ public async Task ResourceDiscovery_GetWithFilterOnTwoResourceGroups_ReturnsExpe var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -142,7 +142,7 @@ public async Task ResourceDiscovery_GetWithFilterOnOneSubscription_ReturnsExpect var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -162,7 +162,7 @@ public async Task ResourceDiscovery_GetWithFilterOnTwoSubscriptions_ReturnsExpec var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -182,7 +182,7 @@ public async Task ResourceDiscovery_GetWithFilterOnAppTag_ReturnsExpectedAmount( var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -202,7 +202,7 @@ public async Task ResourceDiscovery_GetWithFilterOnAppAndRegionTag_ReturnsExpect var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -222,7 +222,7 @@ public async Task ResourceDiscovery_GetWithFilterOnOneRegion_ReturnsExpectedAmou var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -242,7 +242,7 @@ public async Task ResourceDiscovery_GetWithFilterOnTwoRegions_ReturnsExpectedAmo var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act - var response = await resourceDiscoveryClient.GetDiscoveredResourcesAsync(resourceDiscoveryGroupName); + var response = await resourceDiscoveryClient.GetDiscoveredResourcesWithResponseAsync(resourceDiscoveryGroupName); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); From b743f6593d0f2f2d623154725b069705c6a0a75c Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Sun, 1 Aug 2021 15:34:15 +0200 Subject: [PATCH 7/8] Fix dicsovery variables in CI Signed-off-by: Tom Kerkhove --- build/azure-devops/agents-ci-scraper.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-devops/agents-ci-scraper.yml b/build/azure-devops/agents-ci-scraper.yml index 7bf2df127..6dc01e575 100644 --- a/build/azure-devops/agents-ci-scraper.yml +++ b/build/azure-devops/agents-ci-scraper.yml @@ -99,9 +99,9 @@ stages: - name: Agent.Scraper.BaseUrl value: http://localhost:$(Container.Scraper.Port) - name: Agent.ResourceDiscovery.BaseUrl - value: NOTUSED + value: http://localhost:$(Container.ResourceDiscovery.Port) - name: Agent.ResourceDiscovery.Version - value: NOTUSED + value: $(App.Version) - name: Container.Scraper.Name value: 'promitor.scraper.agent' - name: Container.ResourceDiscovery.Name From a32919aa34fa27f0998d8860b7d6a942bcd0fb51 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Sun, 1 Aug 2021 16:04:28 +0200 Subject: [PATCH 8/8] Code quality --- .../Data/AvailableMetricsTestInputGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs b/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs index 9a97e27f3..d71ec584a 100644 --- a/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs +++ b/src/Promitor.Tests.Integration/Data/AvailableMetricsTestInputGenerator.cs @@ -66,6 +66,7 @@ private List GetConfiguredMetrics() return _scraperClient.GetMetricDeclarationAsync().Result; } + // ReSharper disable once FunctionRecursiveOnAllPaths public IEnumerator GetEnumerator() => GetEnumerator(); } }