diff --git a/changelog/content/experimental/unreleased.md b/changelog/content/experimental/unreleased.md
index 927a807b3..3da923a6e 100644
--- a/changelog/content/experimental/unreleased.md
+++ b/changelog/content/experimental/unreleased.md
@@ -15,6 +15,7 @@ version:
- {{% tag added %}} Support for scraping Azure Express Route circuits ([docs](https://promitor.io/configuration/v2.x/metrics/express-route-circuit) | [#1251](https://github.com/tomkerkhove/promitor/issues/1251) | Contributed by [@bluepixbe](https://github.com/bluepixbe) 🎉).
- {{% tag added %}} Support for scraping Azure Application Gateway ([docs](https://promitor.io/configuration/v2.x/metrics/application-gateway) | [#1251](https://github.com/tomkerkhove/promitor/issues/313) | Contributed by [@bluepixbe](https://github.com/bluepixbe) 🎉).
- {{% tag added %}} Support for scraping Azure Network Gateway ([docs](https://promitor.io/configuration/v2.x/metrics/network-gateway) | [#1264](https://github.com/tomkerkhove/promitor/issues/1264) | Contributed by [@bluepixbe](https://github.com/bluepixbe) 🎉).
+- {{% tag added %}} Support for scraping Azure Kubernetes Service ([docs](https://promitor.io/configuration/v2.x/metrics/kubernetes) | [#333](https://github.com/tomkerkhove/promitor/issues/333) | Contributed by [@jkataja](https://github.com/jkataja) 🎉).
- {{% tag added %}} New validation rule to ensure at least one resource or resource collection is configured to scrape
- {{% tag added %}} Provide suggestions when unknown fields are found in the metrics config. ([#1105](https://github.com/tomkerkhove/promitor/issues/1105) | Contributed by [@adamconnelly](https://github.com/adamconnelly) 🎉).
- {{% tag added %}} Add validation to ensure the scraping schedule is a valid Cron expression. ([#1103](https://github.com/tomkerkhove/promitor/issues/1103) | Contributed by [@adamconnelly](https://github.com/adamconnelly) 🎉).
diff --git a/docs/configuration/v2.x/metrics/index.md b/docs/configuration/v2.x/metrics/index.md
index af3c0b444..edd10d296 100644
--- a/docs/configuration/v2.x/metrics/index.md
+++ b/docs/configuration/v2.x/metrics/index.md
@@ -134,6 +134,7 @@ We also provide a simplified way to scrape the following Azure resources:
- [Azure IoT Hub](iot-hub)
- [Azure IoT Hub Device Provisioning Service (DPS)](iot-hub-device-provisioning-service)
- [Azure Key Vault](key-vault)
+- [Azure Kubernetes Service](kubernetes)
- [Azure Logic Apps](logic-apps)
- [Azure Network Gateway](network-gateway)
- [Azure Network Interface](network-interface)
diff --git a/docs/configuration/v2.x/metrics/kubernetes.md b/docs/configuration/v2.x/metrics/kubernetes.md
new file mode 100644
index 000000000..38e82c933
--- /dev/null
+++ b/docs/configuration/v2.x/metrics/kubernetes.md
@@ -0,0 +1,36 @@
+---
+layout: default
+title: Azure Kubernetes Service Declaration
+---
+
+## Azure Kubernetes Service
+
+![Availability Badge](https://img.shields.io/badge/Available%20Starting-v2.0-green.svg)![Resource Discovery Support Badge](https://img.shields.io/badge/Support%20for%20Resource%20Discovery-Yes-green.svg)
+
+You can declare to scrape an Azure Kubernetes Service (AKS)
+via the `KubernetesService` resource type.
+
+The following fields need to be provided:
+
+- `clusterName` - The name of the Azure Kubernetes Service
+
+All supported metrics are documented in the official [Azure Monitor documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-supported#microsoftcontainerservicemanagedclusters).
+
+Example:
+
+```yaml
+name: azure_kubernetes_available_cpu_cores
+description: "Available CPU cores in cluster"
+resourceType: KubernetesService
+azureMetricConfiguration:
+ metricName: kube_node_status_allocatable_cpu_cores
+ aggregation:
+ type: Average
+resources:
+- clusterName: promitor-aks
+```
+
+
+[← back to metrics declarations](/configuration/v2.x/metrics)
+[← back to introduction](/)
+
diff --git a/docs/configuration/v2.x/resource-discovery.md b/docs/configuration/v2.x/resource-discovery.md
index 3680bb2ff..3f981061b 100644
--- a/docs/configuration/v2.x/resource-discovery.md
+++ b/docs/configuration/v2.x/resource-discovery.md
@@ -101,6 +101,7 @@ Dynamic resource discovery is supported for the following scrapers:
- [Azure IoT Hub](metrics/iot-hub)
- [Azure IoT Hub Device Provisioning Service (DPS)](metrics/iot-hub-device-provisioning-service)
- [Azure Key Vault](metrics/key-vault)
+- [Azure Kubernetes Service](metrics/kubernetes)
- [Azure Logic Apps](metrics/logic-apps)
- [Azure Network Gateway](metrics/network-gateway)
- [Azure Network Interface](metrics/network-interface)
diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs
index 78a83a5cd..6a1fa5022 100644
--- a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs
+++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs
@@ -34,6 +34,8 @@ public static ResourceDiscoveryQuery UseResourceDiscoveryFor(ResourceType resour
return new IoTHubDiscoveryQuery();
case ResourceType.KeyVault:
return new KeyVaultDiscoveryQuery();
+ case ResourceType.KubernetesService:
+ return new KubernetesServiceDiscoveryQuery();
case ResourceType.LogicApp:
return new LogicAppDiscoveryQuery();
case ResourceType.NetworkGateway:
diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/KubernetesServiceDiscoveryQuery.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/KubernetesServiceDiscoveryQuery.cs
new file mode 100644
index 000000000..56d20984a
--- /dev/null
+++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/KubernetesServiceDiscoveryQuery.cs
@@ -0,0 +1,21 @@
+using GuardNet;
+using Newtonsoft.Json.Linq;
+using Promitor.Core.Contracts;
+using Promitor.Core.Contracts.ResourceTypes;
+
+namespace Promitor.Agents.ResourceDiscovery.Graph.ResourceTypes
+{
+ public class KubernetesServiceDiscoveryQuery : ResourceDiscoveryQuery
+ {
+ public override string[] ResourceTypes => new[] { "Microsoft.ContainerService/managedClusters" };
+ public override string[] ProjectedFieldNames => new[] { "subscriptionId", "resourceGroup", "type", "name" };
+
+ public override AzureResourceDefinition ParseResults(JToken resultRowEntry)
+ {
+ Guard.NotNull(resultRowEntry, nameof(resultRowEntry));
+
+ var resource = new KubernetesServiceResourceDefinition(resultRowEntry[0]?.ToString(), resultRowEntry[1]?.ToString(), resultRowEntry[3]?.ToString());
+ return resource;
+ }
+ }
+}
diff --git a/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs b/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs
index 65cba4146..3f25508c3 100644
--- a/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs
+++ b/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs
@@ -41,6 +41,8 @@ internal static IMetricValidator GetValidatorFor(ResourceType resourceType)
return new IoTHubMetricValidator();
case ResourceType.KeyVault:
return new KeyVaultMetricValidator();
+ case ResourceType.KubernetesService:
+ return new KubernetesServiceMetricValidator();
case ResourceType.LogicApp:
return new LogicAppMetricValidator();
case ResourceType.NetworkGateway:
diff --git a/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/KubernetesServiceMetricValidator.cs b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/KubernetesServiceMetricValidator.cs
new file mode 100644
index 000000000..da96b90e7
--- /dev/null
+++ b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/KubernetesServiceMetricValidator.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Linq;
+using GuardNet;
+using Promitor.Core.Scraping.Configuration.Model.Metrics;
+using Promitor.Agents.Scraper.Validation.MetricDefinitions.Interfaces;
+using Promitor.Core.Contracts.ResourceTypes;
+
+namespace Promitor.Agents.Scraper.Validation.MetricDefinitions.ResourceTypes
+{
+ internal class KubernetesServiceMetricValidator : IMetricValidator
+ {
+ public IEnumerable Validate(MetricDefinition metricDefinition)
+ {
+ Guard.NotNull(metricDefinition, nameof(metricDefinition));
+
+ foreach (var resourceDefinition in metricDefinition.Resources.Cast())
+ {
+ if (string.IsNullOrWhiteSpace(resourceDefinition.ClusterName))
+ {
+ yield return "No Azure Kubernetes Service cluster name is configured";
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Promitor.Core.Contracts/ResourceType.cs b/src/Promitor.Core.Contracts/ResourceType.cs
index 1b84d5383..950400757 100644
--- a/src/Promitor.Core.Contracts/ResourceType.cs
+++ b/src/Promitor.Core.Contracts/ResourceType.cs
@@ -31,6 +31,7 @@ public enum ResourceType
EventHubs = 26,
ExpressRouteCircuit = 27,
ApplicationGateway = 28,
- NetworkGateway = 29
+ NetworkGateway = 29,
+ KubernetesService = 30
}
}
\ No newline at end of file
diff --git a/src/Promitor.Core.Contracts/ResourceTypes/KubernetesServiceResourceDefinition.cs b/src/Promitor.Core.Contracts/ResourceTypes/KubernetesServiceResourceDefinition.cs
new file mode 100644
index 000000000..3c04b533e
--- /dev/null
+++ b/src/Promitor.Core.Contracts/ResourceTypes/KubernetesServiceResourceDefinition.cs
@@ -0,0 +1,13 @@
+namespace Promitor.Core.Contracts.ResourceTypes
+{
+ public class KubernetesServiceResourceDefinition : AzureResourceDefinition
+ {
+ public KubernetesServiceResourceDefinition(string subscriptionId, string resourceGroupName, string clusterName)
+ : base(ResourceType.KubernetesService, subscriptionId, resourceGroupName, clusterName)
+ {
+ ClusterName = clusterName;
+ }
+
+ public string ClusterName { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs
index 78a420b57..3123bda1f 100644
--- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs
+++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs
@@ -66,6 +66,9 @@ public IDeserializer GetDeserializerFor(ResourceType
case ResourceType.KeyVault:
var keyVaultLogger = _loggerFactory.CreateLogger();
return new KeyVaultDeserializer(keyVaultLogger);
+ case ResourceType.KubernetesService:
+ var kubernetesServiceLogger = _loggerFactory.CreateLogger();
+ return new KubernetesServiceDeserializer(kubernetesServiceLogger);
case ResourceType.LogicApp:
var logicAppLogger = _loggerFactory.CreateLogger();
return new LogicAppDeserializer(logicAppLogger);
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 9e45d04d2..cfbc962b3 100644
--- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs
+++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs
@@ -38,6 +38,7 @@ public V1MappingProfile()
CreateMap();
CreateMap();
CreateMap();
+ CreateMap();
CreateMap();
CreateMap();
CreateMap();
@@ -74,6 +75,7 @@ public V1MappingProfile()
.Include()
.Include()
.Include()
+ .Include()
.Include()
.Include()
.Include()
diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/KubernetesServiceResourceV1.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/KubernetesServiceResourceV1.cs
new file mode 100644
index 000000000..d66019ea8
--- /dev/null
+++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/KubernetesServiceResourceV1.cs
@@ -0,0 +1,13 @@
+namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes
+{
+ ///
+ /// Contains the configuration required to scrape an Azure Kubernetes Service.
+ ///
+ public class KubernetesServiceResourceV1 : AzureResourceDefinitionV1
+ {
+ ///
+ /// The name of the Azure Kubernetes Service to get metrics for.
+ ///
+ public string ClusterName { get; set; }
+ }
+}
diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/KubernetesServiceDeserializer.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/KubernetesServiceDeserializer.cs
new file mode 100644
index 000000000..dc62faf7c
--- /dev/null
+++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/KubernetesServiceDeserializer.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.Logging;
+using Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes;
+
+namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Providers
+{
+ public class KubernetesServiceDeserializer : ResourceDeserializer
+ {
+ public KubernetesServiceDeserializer(ILogger logger) : base(logger)
+ {
+ Map(resource => resource.ClusterName)
+ .IsRequired();
+ }
+ }
+}
diff --git a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs
index bfb0fcf36..d62c83763 100644
--- a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs
+++ b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs
@@ -62,6 +62,8 @@ public IScraper CreateScraper(ResourceType metricDefin
return new IoTHubScraper(scraperConfiguration);
case ResourceType.KeyVault:
return new KeyVaultScraper(scraperConfiguration);
+ case ResourceType.KubernetesService:
+ return new KubernetesServiceScraper(scraperConfiguration);
case ResourceType.NetworkGateway:
return new NetworkGatewayScraper(scraperConfiguration);
case ResourceType.NetworkInterface:
@@ -91,7 +93,7 @@ public IScraper CreateScraper(ResourceType metricDefin
case ResourceType.WebApp:
return new WebAppScraper(scraperConfiguration);
default:
- throw new ArgumentOutOfRangeException();
+ throw new ArgumentOutOfRangeException("metricDefinitionResourceType", metricDefinitionResourceType, "Matching scraper not found");
}
}
}
diff --git a/src/Promitor.Core.Scraping/ResourceTypes/KubernetesServiceScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/KubernetesServiceScraper.cs
new file mode 100644
index 000000000..fc98e629c
--- /dev/null
+++ b/src/Promitor.Core.Scraping/ResourceTypes/KubernetesServiceScraper.cs
@@ -0,0 +1,22 @@
+using Promitor.Core.Contracts;
+using Promitor.Core.Contracts.ResourceTypes;
+using Promitor.Core.Scraping.Configuration.Model.Metrics;
+
+namespace Promitor.Core.Scraping.ResourceTypes
+{
+ public class KubernetesServiceScraper : AzureMonitorScraper
+ {
+ private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ContainerService/managedClusters/{2}";
+
+ public KubernetesServiceScraper(ScraperConfiguration scraperConfiguration) :
+ base(scraperConfiguration)
+ {
+ }
+
+ ///
+ protected override string BuildResourceUri(string subscriptionId, ScrapeDefinition scrapeDefinition, KubernetesServiceResourceDefinition resource)
+ {
+ return string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.ClusterName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs b/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs
index 7fc9d5452..5b9bea311 100644
--- a/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs
+++ b/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs
@@ -608,6 +608,23 @@ public MetricsDeclarationBuilder WithKeyVaultMetric(string metricName = "promito
return this;
}
+ public MetricsDeclarationBuilder WithKubernetesServiceMetric(string metricName = "promitor-aks",
+ string metricDescription = "Description for a metric",
+ string clusterName = "promitor-aks",
+ string azureMetricName = "kube_node_status_condition",
+ string resourceDiscoveryGroupName = "",
+ bool omitResource = false)
+ {
+ var resource = new KubernetesServiceResourceV1
+ {
+ ClusterName = clusterName
+ };
+
+ CreateAndAddMetricDefinition(ResourceType.KubernetesService, metricName, metricDescription, resourceDiscoveryGroupName, omitResource, azureMetricName, resource);
+
+ return this;
+ }
+
private void CreateAndAddMetricDefinition(ResourceType resourceType, string metricName, string metricDescription, string resourceDiscoveryGroupName, bool omitResource, string azureMetricName, AzureResourceDefinitionV1 resource, string metricDimension = null)
{
CreateAndAddMetricDefinition(resourceType, metricName, metricDescription, resourceDiscoveryGroupName, omitResource, azureMetricName, new List {resource}, metricDimension);
diff --git a/src/Promitor.Tests.Unit/Serialization/v1/Providers/KubernetesServiceDeserializerTests.cs b/src/Promitor.Tests.Unit/Serialization/v1/Providers/KubernetesServiceDeserializerTests.cs
new file mode 100644
index 000000000..8f9f25038
--- /dev/null
+++ b/src/Promitor.Tests.Unit/Serialization/v1/Providers/KubernetesServiceDeserializerTests.cs
@@ -0,0 +1,45 @@
+using Promitor.Core.Scraping.Configuration.Serialization;
+using Promitor.Core.Scraping.Configuration.Serialization.v1.Model;
+using Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes;
+using Promitor.Core.Scraping.Configuration.Serialization.v1.Providers;
+using System.ComponentModel;
+using Xunit;
+
+namespace Promitor.Tests.Unit.Serialization.v1.Providers
+{
+ [Category("Unit")]
+ public class KubernetesServiceDeserializerTests : ResourceDeserializerTest
+ {
+ private readonly KubernetesServiceDeserializer _deserializer;
+
+ public KubernetesServiceDeserializerTests()
+ {
+ _deserializer = new KubernetesServiceDeserializer(Logger);
+ }
+
+ [Fact]
+ public void Deserialize_ClusterNameSupplied_SetsClusterName()
+ {
+ const string clusterName = "promitor-aks";
+ YamlAssert.PropertySet(
+ _deserializer,
+ $"clusterName: {clusterName}",
+ clusterName,
+ r => r.ClusterName);
+ }
+
+ [Fact]
+ public void Deserialize_ClusterNameNotSupplied_Null()
+ {
+ YamlAssert.PropertyNull(
+ _deserializer,
+ "resourceGroupName: promitor-group",
+ r => r.ClusterName);
+ }
+
+ protected override IDeserializer CreateDeserializer()
+ {
+ return new KubernetesServiceDeserializer(Logger);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/KubernetesServiceMetricsDeclarationValidationStepsTests.cs b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/KubernetesServiceMetricsDeclarationValidationStepsTests.cs
new file mode 100644
index 000000000..d593057d9
--- /dev/null
+++ b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/KubernetesServiceMetricsDeclarationValidationStepsTests.cs
@@ -0,0 +1,132 @@
+using System.ComponentModel;
+using Microsoft.Extensions.Logging.Abstractions;
+using Promitor.Agents.Scraper.Validation.Steps;
+using Promitor.Tests.Unit.Builders.Metrics.v1;
+using Promitor.Tests.Unit.Stubs;
+using Xunit;
+
+namespace Promitor.Tests.Unit.Validation.Scraper.Metrics.ResourceTypes
+{
+ [Category("Unit")]
+ public class KubernetesServiceMetricsDeclarationValidationStepsTests : MetricsDeclarationValidationStepsTests
+ {
+ [Fact]
+ public void KubernetesServiceMetricsDeclaration_DeclarationWithoutAzureMetricName_Fails()
+ {
+ // Arrange
+ var rawDeclaration = MetricsDeclarationBuilder.WithMetadata()
+ .WithKubernetesServiceMetric(azureMetricName: string.Empty)
+ .Build(Mapper);
+ var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration, Mapper);
+
+ // Act
+ var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance);
+ var validationResult = scrapingScheduleValidationStep.Run();
+
+ // Assert
+ PromitorAssert.ValidationFailed(validationResult);
+ }
+
+ [Fact]
+ public void KubernetesServiceMetricsDeclaration_DeclarationWithoutMetricDescription_Succeeded()
+ {
+ // Arrange
+ var rawDeclaration = MetricsDeclarationBuilder.WithMetadata()
+ .WithKubernetesServiceMetric(metricDescription: string.Empty)
+ .Build(Mapper);
+ var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration, Mapper);
+
+ // Act
+ var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance);
+ var validationResult = scrapingScheduleValidationStep.Run();
+
+ // Assert
+ PromitorAssert.ValidationIsSuccessful(validationResult);
+ }
+
+ [Fact]
+ public void KubernetesServiceMetricsDeclaration_DeclarationWithoutMetricName_Fails()
+ {
+ // Arrange
+ var rawDeclaration = MetricsDeclarationBuilder.WithMetadata()
+ .WithKubernetesServiceMetric(string.Empty)
+ .Build(Mapper);
+ var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration, Mapper);
+
+ // Act
+ var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance);
+ var validationResult = scrapingScheduleValidationStep.Run();
+
+ // Assert
+ PromitorAssert.ValidationFailed(validationResult);
+ }
+
+ [Fact]
+ public void KubernetesServiceMetricsDeclaration_DeclarationWithoutClusterName_Fails()
+ {
+ // Arrange
+ var rawDeclaration = MetricsDeclarationBuilder.WithMetadata()
+ .WithKubernetesServiceMetric(clusterName: string.Empty)
+ .Build(Mapper);
+ var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration, Mapper);
+
+ // Act
+ var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance);
+ var validationResult = scrapingScheduleValidationStep.Run();
+
+ // Assert
+ PromitorAssert.ValidationFailed(validationResult);
+ }
+
+ [Fact]
+ public void KubernetesServiceMetricsDeclaration_DeclarationWithoutResourceAndResourceDiscoveryGroupInfo_Fails()
+ {
+ // Arrange
+ var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata()
+ .WithKubernetesServiceMetric(omitResource: true)
+ .Build(Mapper);
+ var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawMetricsDeclaration, Mapper);
+
+ // Act
+ var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance);
+ var validationResult = scrapingScheduleValidationStep.Run();
+
+ // Assert
+ Assert.False(validationResult.IsSuccessful, "Validation was not successful");
+ }
+
+ [Fact]
+ public void KubernetesServiceMetricsDeclaration_DeclarationWithoutResourceButWithResourceDiscoveryGroupInfo_Succeeds()
+ {
+ // Arrange
+ var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata()
+ .WithKubernetesServiceMetric(omitResource: true, resourceDiscoveryGroupName:"sample-collection")
+ .Build(Mapper);
+ var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawMetricsDeclaration, Mapper);
+
+ // Act
+ var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance);
+ var validationResult = scrapingScheduleValidationStep.Run();
+
+ // Assert
+ PromitorAssert.ValidationIsSuccessful(validationResult);
+ }
+
+ [Fact]
+ public void KubernetesServiceMetricsDeclaration_ValidDeclaration_Succeeds()
+ {
+ // Arrange
+ var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata()
+ .WithKubernetesServiceMetric()
+ .Build(Mapper);
+ var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawMetricsDeclaration, Mapper);
+
+ // Act
+ var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance);
+ var validationResult = scrapingScheduleValidationStep.Run();
+
+ // Assert
+ PromitorAssert.ValidationIsSuccessful(validationResult);
+ }
+ }
+}