From f63349c203329b6bb6ea8f337c195b9caadd70d5 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Fri, 12 Feb 2021 21:57:04 +0100 Subject: [PATCH] feat: Provide support for scraping metrics for Azure SQL Elastic Pool (#1509) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP Signed-off-by: Tom Kerkhove * Ship it 🐿 Signed-off-by: Tom Kerkhove --- changelog/content/experimental/unreleased.md | 4 + .../resource-discovery-declaration.yaml | 2 + config/promitor/scraper/metrics.yaml | 456 +++++++++--------- docs/configuration/v2.x/metrics/index.md | 1 + .../v2.x/metrics/sql-elastic-pool.md | 46 ++ docs/configuration/v2.x/resource-discovery.md | 1 + .../Graph/ResourceDiscoveryFactory.cs | 2 + .../SqlDatabaseDiscoveryQuery.cs | 20 +- .../Graph/ResourceTypes/SqlDiscoveryQuery.cs | 23 + .../SqlElasticPoolDiscoveryQuery.cs | 29 ++ src/Promitor.Agents.Scraper/Docs/Open-Api.xml | 8 + .../Factories/MetricValidatorFactory.cs | 2 + .../SqlElasticPoolMetricValidator.cs | 34 ++ src/Promitor.Core.Contracts/ResourceType.cs | 3 +- .../SqlElasticPoolResourceDefinition.cs | 32 ++ .../Core/AzureResourceDeserializerFactory.cs | 3 + .../v1/Mapping/V1MappingProfile.cs | 2 + .../ResourceTypes/SqlElasticPoolResourceV1.cs | 22 + .../Providers/SqlElasticPoolDeserializer.cs | 23 + .../Factories/MetricScraperFactory.cs | 2 + .../ResourceTypes/SqlElasticPoolScraper.cs | 35 ++ .../ResourceDiscoveryTests.cs | 6 +- .../Metrics/v1/MetricsDeclarationBuilder.cs | 20 + .../SqlElasticPoolDeserializerTests.cs | 85 ++++ ...olMetricsDeclarationValidationStepTests.cs | 132 +++++ 25 files changed, 755 insertions(+), 238 deletions(-) create mode 100644 docs/configuration/v2.x/metrics/sql-elastic-pool.md create mode 100644 src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDiscoveryQuery.cs create mode 100644 src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlElasticPoolDiscoveryQuery.cs create mode 100644 src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/SqlElasticPoolMetricValidator.cs create mode 100644 src/Promitor.Core.Contracts/ResourceTypes/SqlElasticPoolResourceDefinition.cs create mode 100644 src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/SqlElasticPoolResourceV1.cs create mode 100644 src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/SqlElasticPoolDeserializer.cs create mode 100644 src/Promitor.Core.Scraping/ResourceTypes/SqlElasticPoolScraper.cs create mode 100644 src/Promitor.Tests.Unit/Serialization/v1/Providers/SqlElasticPoolDeserializerTests.cs create mode 100644 src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/SqlElasticPoolMetricsDeclarationValidationStepTests.cs diff --git a/changelog/content/experimental/unreleased.md b/changelog/content/experimental/unreleased.md index 9693b553d..f76b2d7a5 100644 --- a/changelog/content/experimental/unreleased.md +++ b/changelog/content/experimental/unreleased.md @@ -10,6 +10,8 @@ version: | [#352](https://github.com/tomkerkhove/promitor/issues/352)) - {{% tag added %}} Support for scraping Azure Front Door account ([docs](https://promitor.io/configuration/v2.x/metrics/front-door) | [#343](https://github.com/tomkerkhove/promitor/issues/343)) +- {{% tag added %}} Support for scraping Azure SQL Elastic Pool ([docs](https://promitor.io/configuration/v2.x/metrics/sql-elastic-pool) + | [#319](https://github.com/tomkerkhove/promitor/issues/319)) - {{% tag changed %}} Provide better usability in terms of startup and configuration insights ([#1474](https://github.com/tomkerkhove/promitor/issues/1474)) **Resource Discovery (Container)** @@ -18,5 +20,7 @@ version: | [#352](https://github.com/tomkerkhove/promitor/issues/352)) - {{% tag added %}} Support for discovering Azure Front Door resources ([docs](https://promitor.io/configuration/v2.x/metrics/front-door) | [#343](https://github.com/tomkerkhove/promitor/issues/343)) +- {{% tag added %}} Support for scraping Azure SQL Elastic Pool ([docs](https://promitor.io/configuration/v2.x/metrics/sql-elastic-pool) + | [#319](https://github.com/tomkerkhove/promitor/issues/319)) - {{% tag changed %}} Provide better usability in terms of startup and configuration insights ([#1474](https://github.com/tomkerkhove/promitor/issues/1474)) - {{% tag fixed %}} Provide case-invarient lookups for resource group filtering ([#1492](https://github.com/tomkerkhove/promitor/issues/1492)) diff --git a/config/promitor/resource-discovery/resource-discovery-declaration.yaml b/config/promitor/resource-discovery/resource-discovery-declaration.yaml index 48ffb3569..13ebd3bcc 100644 --- a/config/promitor/resource-discovery/resource-discovery-declaration.yaml +++ b/config/promitor/resource-discovery/resource-discovery-declaration.yaml @@ -40,6 +40,8 @@ resourceDiscoveryGroups: type: SqlDatabase - name: sql-managed-instances type: SqlManagedInstance +- name: sql-elastic-pools + type: SqlElasticPool - name: sql-servers type: SqlServer - name: storage-accounts diff --git a/config/promitor/scraper/metrics.yaml b/config/promitor/scraper/metrics.yaml index 536739544..4e86ee650 100644 --- a/config/promitor/scraper/metrics.yaml +++ b/config/promitor/scraper/metrics.yaml @@ -10,220 +10,242 @@ metricDefaults: # Every minute schedule: "0 * * ? * *" metrics: - - name: promitor_demo_frontdoor_backend_health - description: "Average percentage of memory usage on an Azure App Plan" - resourceType: FrontDoor - labels: - app: promitor - azureMetricConfiguration: - metricName: BackendHealthPercentage - aggregation: - type: Average - resources: - - name: promitor-app-plan - - 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 - azureMetricConfiguration: - metricName: TotalPullCount - aggregation: - type: Average - resourceDiscoveryGroups: - - name: container-registry-landscape - - name: promitor_demo_appplan_percentage_cpu_discovered - description: "Average percentage of memory usage on an Azure App Plan" - resourceType: AppPlan - azureMetricConfiguration: - metricName: MemoryPercentage - aggregation: - type: Average - resourceDiscoveryGroups: - - name: app-plan-landscape - - name: promitor_demo_webapp_cpu_discovery - description: "Amount of CPU time used for an Azure Web App" - resourceType: WebApp - azureMetricConfiguration: - metricName: CpuTime - aggregation: - type: Total - resourceDiscoveryGroups: - - name: web-app-landscape - - name: promitor_demo_function_memory_discovery - description: "Average memory for an Azure Function App" - resourceType: FunctionApp - azureMetricConfiguration: - metricName: MemoryWorkingSet - aggregation: - type: Average - resourceDiscoveryGroups: - - name: function-app-landscape - - name: azure_logic_apps_failed_run - description: "Total amount of failed runs for Azure Logic Apps" - resourceType: LogicApp - azureMetricConfiguration: - metricName: RunsFailed - aggregation: - type: Total - resources: - - workflowName: promitor-automation-github-ci-scraper - - name: azure_logic_apps_failed_run_discovery - description: "Total amount of failed runs for Azure Logic Apps" - resourceType: LogicApp - azureMetricConfiguration: - metricName: RunsFailed - aggregation: - type: Total - resourceDiscoveryGroups: - - name: logic-apps-unfiltered - - name: azure_storage_account_capacity_discovery - description: "The average capacity used in the storage account" - resourceType: StorageAccount - azureMetricConfiguration: - metricName: UsedCapacity - aggregation: - type: Average - resourceDiscoveryGroups: - - name: storage-accounts - - name: azure_sql_database_cpu_percent_discovery - description: "The CPU percentage used by an Azure SQL Database." - resourceType: SqlDatabase - azureMetricConfiguration: - metricName: cpu_percent - aggregation: - type: Average - resourceDiscoveryGroups: - - name: sql-databases - - name: promitor_demo_azuresqlmanagedinstance_nodes_discovery - description: "The amount of nodes for an Azure SQL Managed Instance." - resourceType: SqlManagedInstance - azureMetricConfiguration: - metricName: virtual_core_count - aggregation: - type: Average - resourceDiscoveryGroups: - - name: sql-managed-instances - - name: azure_network_interface_bytes_received_rate_discovery - description: "Number of bytes the Network Interface sent" - resourceType: NetworkInterface - azureMetricConfiguration: - metricName: BytesReceivedRate - aggregation: - type: Average - resourceDiscoveryGroups: - - name: network-interfaces - - name: azure_event_hubs_incoming_messages_discovery - description: "The number of incoming messages on an Azure Event Hub namespace" - resourceType: EventHubs - azureMetricConfiguration: - metricName: IncomingMessages - aggregation: - type: Total - resourceDiscoveryGroups: - - name: event-hubs-landscape - - name: promitor_demo_servicebus_messagecount_discovered - description: "Average percentage of memory usage on an Azure App Plan" - resourceType: ServiceBusNamespace - azureMetricConfiguration: - metricName: ActiveMessages - aggregation: - type: Total - resourceDiscoveryGroups: - - name: service-bus-landscape - - 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 - azureMetricConfiguration: - metricName: TotalJob - aggregation: - type: Total - resourceDiscoveryGroups: - - name: automation-accounts - resources: - - resourceGroupName: promitor-sources - accountName: promitor-sandbox - - name: promitor_demo_automation_update_deployment_runs - description: "Amount of jobs per Azure Automation account" - resourceType: AutomationAccount - azureMetricConfiguration: - metricName: TotalUpdateDeploymentRuns - aggregation: - type: Total - resourceDiscoveryGroups: - - name: automation-accounts - resources: - - resourceGroupName: promitor-sources - accountName: promitor-sandbox - - name: promitor_demo_automation_update_deployment_machine_runs - description: "Amount of jobs per Azure Automation account" - resourceType: AutomationAccount - azureMetricConfiguration: - metricName: TotalUpdateDeploymentMachineRuns - aggregation: - type: Total - resourceDiscoveryGroups: - - name: automation-accounts - 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 - labels: - app: promitor - azureMetricConfiguration: - metricName: BackendHealthPercentage - dimension: - name: BackendPool - aggregation: - type: Average - resourceDiscoveryGroups: - - name: front-door-landscape \ No newline at end of file +- name: promitor_demo_frontdoor_backend_health + description: "Average percentage of memory usage on an Azure App Plan" + resourceType: FrontDoor + labels: + app: promitor + azureMetricConfiguration: + metricName: BackendHealthPercentage + aggregation: + type: Average + resources: + - name: promitor-app-plan +- 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 + azureMetricConfiguration: + metricName: TotalPullCount + aggregation: + type: Average + resourceDiscoveryGroups: + - name: container-registry-landscape +- name: promitor_demo_appplan_percentage_cpu_discovered + description: "Average percentage of memory usage on an Azure App Plan" + resourceType: AppPlan + azureMetricConfiguration: + metricName: MemoryPercentage + aggregation: + type: Average + resourceDiscoveryGroups: + - name: app-plan-landscape +- name: promitor_demo_webapp_cpu_discovery + description: "Amount of CPU time used for an Azure Web App" + resourceType: WebApp + azureMetricConfiguration: + metricName: CpuTime + aggregation: + type: Total + resourceDiscoveryGroups: + - name: web-app-landscape +- name: promitor_demo_function_memory_discovery + description: "Average memory for an Azure Function App" + resourceType: FunctionApp + azureMetricConfiguration: + metricName: MemoryWorkingSet + aggregation: + type: Average + resourceDiscoveryGroups: + - name: function-app-landscape +- name: azure_logic_apps_failed_run + description: "Total amount of failed runs for Azure Logic Apps" + resourceType: LogicApp + azureMetricConfiguration: + metricName: RunsFailed + aggregation: + type: Total + resources: + - workflowName: promitor-automation-github-ci-scraper +- name: azure_logic_apps_failed_run_discovery + description: "Total amount of failed runs for Azure Logic Apps" + resourceType: LogicApp + azureMetricConfiguration: + metricName: RunsFailed + aggregation: + type: Total + resourceDiscoveryGroups: + - name: logic-apps-unfiltered +- name: azure_storage_account_capacity_discovery + description: "The average capacity used in the storage account" + resourceType: StorageAccount + azureMetricConfiguration: + metricName: UsedCapacity + aggregation: + type: Average + resourceDiscoveryGroups: + - name: storage-accounts +- name: azure_sql_database_cpu_percent_discovery + description: "The CPU percentage used by an Azure SQL Database." + resourceType: SqlDatabase + azureMetricConfiguration: + metricName: cpu_percent + aggregation: + type: Average + resourceDiscoveryGroups: + - name: sql-databases +- name: promitor_demo_azuresqlmanagedinstance_nodes_discovery + description: "The amount of nodes for an Azure SQL Managed Instance." + resourceType: SqlManagedInstance + azureMetricConfiguration: + metricName: virtual_core_count + aggregation: + type: Average + resourceDiscoveryGroups: + - name: sql-managed-instances +- name: azure_network_interface_bytes_received_rate_discovery + description: "Number of bytes the Network Interface sent" + resourceType: NetworkInterface + azureMetricConfiguration: + metricName: BytesReceivedRate + aggregation: + type: Average + resourceDiscoveryGroups: + - name: network-interfaces +- name: azure_event_hubs_incoming_messages_discovery + description: "The number of incoming messages on an Azure Event Hub namespace" + resourceType: EventHubs + azureMetricConfiguration: + metricName: IncomingMessages + aggregation: + type: Total + resourceDiscoveryGroups: + - name: event-hubs-landscape +- name: promitor_demo_servicebus_messagecount_discovered + description: "Average percentage of memory usage on an Azure App Plan" + resourceType: ServiceBusNamespace + azureMetricConfiguration: + metricName: ActiveMessages + aggregation: + type: Total + resourceDiscoveryGroups: + - name: service-bus-landscape +- 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 + azureMetricConfiguration: + metricName: TotalJob + aggregation: + type: Total + resourceDiscoveryGroups: + - name: automation-accounts + resources: + - resourceGroupName: promitor-sources + accountName: promitor-sandbox +- name: promitor_demo_automation_update_deployment_runs + description: "Amount of jobs per Azure Automation account" + resourceType: AutomationAccount + azureMetricConfiguration: + metricName: TotalUpdateDeploymentRuns + aggregation: + type: Total + resourceDiscoveryGroups: + - name: automation-accounts + resources: + - resourceGroupName: promitor-sources + accountName: promitor-sandbox +- name: promitor_demo_automation_update_deployment_machine_runs + description: "Amount of jobs per Azure Automation account" + resourceType: AutomationAccount + azureMetricConfiguration: + metricName: TotalUpdateDeploymentMachineRuns + aggregation: + type: Total + resourceDiscoveryGroups: + - name: automation-accounts + 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 + labels: + app: promitor + azureMetricConfiguration: + metricName: BackendHealthPercentage + dimension: + name: BackendPool + aggregation: + type: Average + resourceDiscoveryGroups: + - name: front-door-landscape +- name: promitor_demo_sql_elastic_pool_cpu + description: "CPU percentage used for a Azure SQL Elastic Pool" + resourceType: SqlElasticPool + labels: + app: promitor + azureMetricConfiguration: + metricName: cpu_percent + aggregation: + type: Average + resourceDiscoveryGroups: + - name: sql-elastic-pools +- name: promitor_demo_sql_elastic_pool_allocated_storage + description: "Percentage of allocated storage for a Azure SQL Elastic Pool" + resourceType: SqlElasticPool + labels: + app: promitor + azureMetricConfiguration: + metricName: allocated_data_storage_percent + aggregation: + type: Average + resourceDiscoveryGroups: + - name: sql-elastic-pools \ No newline at end of file diff --git a/docs/configuration/v2.x/metrics/index.md b/docs/configuration/v2.x/metrics/index.md index 6afff74fe..1fe1a6bd6 100644 --- a/docs/configuration/v2.x/metrics/index.md +++ b/docs/configuration/v2.x/metrics/index.md @@ -146,6 +146,7 @@ We also provide a simplified way to scrape the following Azure resources: - [Azure Network Interface](network-interface) - [Azure Service Bus Namespace](service-bus-namespace) - [Azure SQL Database](sql-database) +- [Azure SQL Elastic Pool](sql-elastic-pool) - [Azure SQL Managed Instance](sql-managed-instance) - [Azure SQL Server](sql-server) - [Azure Storage (Account)](storage-account) diff --git a/docs/configuration/v2.x/metrics/sql-elastic-pool.md b/docs/configuration/v2.x/metrics/sql-elastic-pool.md new file mode 100644 index 000000000..0cb2a6c01 --- /dev/null +++ b/docs/configuration/v2.x/metrics/sql-elastic-pool.md @@ -0,0 +1,46 @@ +--- +layout: default +title: Azure SQL Elastic Pool Declaration +--- + +## Azure SQL Elastic Pool + +![Availability Badge](https://img.shields.io/badge/Available%20Starting-v2.1-green.svg)![Resource Discovery Support Badge](https://img.shields.io/badge/Support%20for%20Resource%20Discovery-Yes-green.svg) + +You can scrape an Azure SQL Elastic Pool via the `SqlElasticPool` resource type. + +The following fields need to be provided: + +- `serverName` - The name of the SQL Server instance. +- `poolName` - The name of the elastic pool. + +All supported metrics are documented in the official [Azure Monitor documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-supported#microsoftsqlserverselasticpools). + +The following scraper-specific metric labels will be added: + +- `server` - The name of the SQL Server instance. +- `elastic_pool` - The name of the elastic pool. + +Example: + +```yaml +- name: promitor_demo_sql_elastic_pool_cpu + description: "CPU percentage used for a Azure SQL Elastic Pool" + resourceType: SqlElasticPool + labels: + app: promitor + azureMetricConfiguration: + metricName: cpu_percent + aggregation: + type: Average + resources: + - serverName: promitor-sql-server + poolName: promitor-db + resourceDiscoveryGroups: # Optional, requires Promitor Resource Discovery agent (https://promitor.io/concepts/how-it-works#using-resource-discovery) + - name: sql-elastic-pools +``` + + +[← 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 70180cca1..a58e0969a 100644 --- a/docs/configuration/v2.x/resource-discovery.md +++ b/docs/configuration/v2.x/resource-discovery.md @@ -109,6 +109,7 @@ Dynamic resource discovery is supported for the following scrapers: - [Azure Network Interface](metrics/network-interface) - [Azure Service Bus Namespace](metrics/service-bus-namespace) - [Azure SQL Database](metrics/sql-database) +- [Azure SQL Elastic Pool](metrics/sql-elastic-pool) - [Azure SQL Managed Instance](metrics/sql-managed-instance) - [Azure Storage (Account)](metrics/storage-account) - [Azure Virtual Machine](metrics/virtual-machine) diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs index 97ed55fc1..cf6de2d11 100644 --- a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs @@ -54,6 +54,8 @@ public static ResourceDiscoveryQuery UseResourceDiscoveryFor(ResourceType resour return new ServiceBusNamespaceDiscoveryQuery(); case ResourceType.SqlDatabase: return new SqlDatabaseDiscoveryQuery(); + case ResourceType.SqlElasticPool: + return new SqlElasticPoolDiscoveryQuery(); case ResourceType.SqlManagedInstance: return new SqlManagedInstanceDiscoveryQuery(); case ResourceType.StorageAccount: diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDatabaseDiscoveryQuery.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDatabaseDiscoveryQuery.cs index ea6ce96db..f4199fbf1 100644 --- a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDatabaseDiscoveryQuery.cs +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDatabaseDiscoveryQuery.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using GuardNet; using Newtonsoft.Json.Linq; using Promitor.Core.Contracts; @@ -7,17 +6,15 @@ namespace Promitor.Agents.ResourceDiscovery.Graph.ResourceTypes { - public class SqlDatabaseDiscoveryQuery : ResourceDiscoveryQuery + public class SqlDatabaseDiscoveryQuery : SqlDiscoveryQuery { - private const string ServerSectionInResourceUri = "servers/"; - public override string[] ResourceTypes => new[] { "microsoft.sql/servers/databases" }; public override string[] ProjectedFieldNames => new[] { "subscriptionId", "resourceGroup", "type", "name", "id" }; public override AzureResourceDefinition ParseResults(JToken resultRowEntry) { Guard.NotNull(resultRowEntry, nameof(resultRowEntry)); - + var serverName = GetServerNameFromResourceUri(resultRowEntry[4]); if (string.IsNullOrWhiteSpace(serverName)) @@ -25,19 +22,8 @@ public override AzureResourceDefinition ParseResults(JToken resultRowEntry) throw new Exception($"Unable to determine server name from resource URI '{resultRowEntry[4]}'"); } - var resource = new SqlDatabaseResourceDefinition(resultRowEntry[0]?.ToString(), resultRowEntry[1]?.ToString(), serverName,resultRowEntry[3]?.ToString()); + var resource = new SqlDatabaseResourceDefinition(resultRowEntry[0]?.ToString(), resultRowEntry[1]?.ToString(), serverName, resultRowEntry[3]?.ToString()); return resource; } - - public virtual string GetServerNameFromResourceUri(JToken resourceUri) - { - Guard.NotNull(resourceUri, nameof(resourceUri)); - var rawResourceUri = resourceUri.ToString(); - Guard.For(()=>string.IsNullOrWhiteSpace(rawResourceUri), nameof(resourceUri)); - - var positionOfServersSection = rawResourceUri.LastIndexOf(ServerSectionInResourceUri, StringComparison.InvariantCultureIgnoreCase) + ServerSectionInResourceUri.Length; - var sqlResourceDetailsParts = rawResourceUri.Substring(positionOfServersSection).Split("/"); - return sqlResourceDetailsParts.FirstOrDefault(); - } } } diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDiscoveryQuery.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDiscoveryQuery.cs new file mode 100644 index 000000000..c672ee91e --- /dev/null +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlDiscoveryQuery.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq; +using GuardNet; +using Newtonsoft.Json.Linq; + +namespace Promitor.Agents.ResourceDiscovery.Graph.ResourceTypes +{ + public abstract class SqlDiscoveryQuery : ResourceDiscoveryQuery + { + private const string ServerSectionInResourceUri = "servers/"; + + public virtual string GetServerNameFromResourceUri(JToken resourceUri) + { + Guard.NotNull(resourceUri, nameof(resourceUri)); + var rawResourceUri = resourceUri.ToString(); + Guard.For(() => string.IsNullOrWhiteSpace(rawResourceUri), nameof(resourceUri)); + + var positionOfServersSection = rawResourceUri.LastIndexOf(ServerSectionInResourceUri, StringComparison.InvariantCultureIgnoreCase) + ServerSectionInResourceUri.Length; + var sqlResourceDetailsParts = rawResourceUri.Substring(positionOfServersSection).Split("/"); + return sqlResourceDetailsParts.FirstOrDefault(); + } + } +} diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlElasticPoolDiscoveryQuery.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlElasticPoolDiscoveryQuery.cs new file mode 100644 index 000000000..e43b402f0 --- /dev/null +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/SqlElasticPoolDiscoveryQuery.cs @@ -0,0 +1,29 @@ +using System; +using GuardNet; +using Newtonsoft.Json.Linq; +using Promitor.Core.Contracts; +using Promitor.Core.Contracts.ResourceTypes; + +namespace Promitor.Agents.ResourceDiscovery.Graph.ResourceTypes +{ + public class SqlElasticPoolDiscoveryQuery : SqlDiscoveryQuery + { + public override string[] ResourceTypes => new[] { "microsoft.sql/servers/elasticpools" }; + public override string[] ProjectedFieldNames => new[] { "subscriptionId", "resourceGroup", "type", "name", "id" }; + + public override AzureResourceDefinition ParseResults(JToken resultRowEntry) + { + Guard.NotNull(resultRowEntry, nameof(resultRowEntry)); + + var serverName = GetServerNameFromResourceUri(resultRowEntry[4]); + + if (string.IsNullOrWhiteSpace(serverName)) + { + throw new Exception($"Unable to determine server name from resource URI '{resultRowEntry[4]}'"); + } + + var resource = new SqlElasticPoolResourceDefinition(resultRowEntry[0]?.ToString(), resultRowEntry[1]?.ToString(), serverName, resultRowEntry[3]?.ToString()); + return resource; + } + } +} diff --git a/src/Promitor.Agents.Scraper/Docs/Open-Api.xml b/src/Promitor.Agents.Scraper/Docs/Open-Api.xml index 6ec0d318d..aa38965cb 100644 --- a/src/Promitor.Agents.Scraper/Docs/Open-Api.xml +++ b/src/Promitor.Agents.Scraper/Docs/Open-Api.xml @@ -83,6 +83,14 @@ + + + Validates objects. + + + + + Validates objects. diff --git a/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs b/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs index 5ed6d49d3..68c5f5756 100644 --- a/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs +++ b/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs @@ -61,6 +61,8 @@ internal static IMetricValidator GetValidatorFor(ResourceType resourceType) return new ServiceBusNamespaceMetricValidator(); case ResourceType.SqlDatabase: return new SqlDatabaseMetricValidator(); + case ResourceType.SqlElasticPool: + return new SqlElasticPoolMetricValidator(); case ResourceType.SqlManagedInstance: return new SqlManagedInstanceMetricValidator(); case ResourceType.SqlServer: diff --git a/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/SqlElasticPoolMetricValidator.cs b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/SqlElasticPoolMetricValidator.cs new file mode 100644 index 000000000..6f2217912 --- /dev/null +++ b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/SqlElasticPoolMetricValidator.cs @@ -0,0 +1,34 @@ +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 +{ + /// + /// Validates objects. + /// + public class SqlElasticPoolMetricValidator : IMetricValidator + { + /// + public IEnumerable Validate(MetricDefinition metricDefinition) + { + Guard.NotNull(metricDefinition, nameof(metricDefinition)); + + foreach (var definition in metricDefinition.Resources.Cast()) + { + if (string.IsNullOrWhiteSpace(definition.ServerName)) + { + yield return "No server name is configured"; + } + + if (string.IsNullOrWhiteSpace(definition.PoolName)) + { + yield return "No pool 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 70c93c3c2..d788d86ff 100644 --- a/src/Promitor.Core.Contracts/ResourceType.cs +++ b/src/Promitor.Core.Contracts/ResourceType.cs @@ -34,6 +34,7 @@ public enum ResourceType NetworkGateway = 29, KubernetesService = 30, AutomationAccount = 31, - FrontDoor = 32 + FrontDoor = 32, + SqlElasticPool = 33 } } \ No newline at end of file diff --git a/src/Promitor.Core.Contracts/ResourceTypes/SqlElasticPoolResourceDefinition.cs b/src/Promitor.Core.Contracts/ResourceTypes/SqlElasticPoolResourceDefinition.cs new file mode 100644 index 000000000..2f8e695a7 --- /dev/null +++ b/src/Promitor.Core.Contracts/ResourceTypes/SqlElasticPoolResourceDefinition.cs @@ -0,0 +1,32 @@ +namespace Promitor.Core.Contracts.ResourceTypes +{ + /// + /// Represents an Azure Azure SQL Elastic Pool resource. + /// + public class SqlElasticPoolResourceDefinition : AzureResourceDefinition + { + /// + /// Initializes a new instance of the class. + /// + /// Specify a subscription to scrape that defers from the default subscription. + /// The name of the resource group the server is in. + /// The name of the Azure SQL Server instance. + /// The name of the Azure SQL Elastic Pool instance. + public SqlElasticPoolResourceDefinition(string subscriptionId, string resourceGroupName, string serverName, string poolName) + : base(ResourceType.SqlElasticPool, subscriptionId, resourceGroupName, poolName, $"{serverName}-{poolName}") + { + ServerName = serverName; + PoolName = poolName; + } + + /// + /// The name of the SQL server instance. + /// + public string ServerName { get; } + + /// + /// The name of the Azure SQL Elastic Pool instance. + /// + public string PoolName { 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 856687f60..cb750f58c 100644 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs @@ -96,6 +96,9 @@ public IDeserializer GetDeserializerFor(ResourceType case ResourceType.SqlDatabase: var sqlDatabaseLogger = _loggerFactory.CreateLogger(); return new SqlDatabaseDeserializer(sqlDatabaseLogger); + case ResourceType.SqlElasticPool: + var sqlElasticPoolLogger = _loggerFactory.CreateLogger(); + return new SqlElasticPoolDeserializer(sqlElasticPoolLogger); case ResourceType.SqlManagedInstance: var sqlManagedInstanceLogger = _loggerFactory.CreateLogger(); return new SqlManagedInstanceDeserializer(sqlManagedInstanceLogger); 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 9cfabd3bb..b3156bdb2 100644 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs @@ -48,6 +48,7 @@ public V1MappingProfile() CreateMap(); CreateMap(); CreateMap(); + CreateMap(); CreateMap(); CreateMap(); CreateMap(); @@ -87,6 +88,7 @@ public V1MappingProfile() .Include() .Include() .Include() + .Include() .Include() .Include() .Include() diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/SqlElasticPoolResourceV1.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/SqlElasticPoolResourceV1.cs new file mode 100644 index 000000000..9a351fcdd --- /dev/null +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/SqlElasticPoolResourceV1.cs @@ -0,0 +1,22 @@ +namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes +{ + /// + /// Represents an Azure SQL Elastic Pool to scrape. + /// + public class SqlElasticPoolResourceV1 : SqlServerResourceV1 + { + public SqlElasticPoolResourceV1() + { + } + + public SqlElasticPoolResourceV1(SqlServerResourceV1 sqlServerResource) + : base(sqlServerResource.ServerName, sqlServerResource.ResourceGroupName) + { + } + + /// + /// The name of the elastic pool. + /// + public string PoolName { get; set; } + } +} \ No newline at end of file diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/SqlElasticPoolDeserializer.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/SqlElasticPoolDeserializer.cs new file mode 100644 index 000000000..d44b645cf --- /dev/null +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/SqlElasticPoolDeserializer.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.Logging; +using Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes; + +namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Providers +{ + /// + /// Used to deserialize a resource. + /// + public class SqlElasticPoolDeserializer : ResourceDeserializer + { + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public SqlElasticPoolDeserializer(ILogger logger) : base(logger) + { + Map(resource => resource.ServerName) + .IsRequired(); + Map(resource => resource.PoolName) + .IsRequired(); + } + } +} \ No newline at end of file diff --git a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs index ec96808ab..60111704f 100644 --- a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs +++ b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs @@ -82,6 +82,8 @@ public IScraper CreateScraper(ResourceType metricDefin return new ServiceBusNamespaceScraper(scraperConfiguration); case ResourceType.SqlDatabase: return new SqlDatabaseScraper(scraperConfiguration); + case ResourceType.SqlElasticPool: + return new SqlElasticPoolScraper(scraperConfiguration); case ResourceType.SqlManagedInstance: return new SqlManagedInstanceScraper(scraperConfiguration); case ResourceType.SqlServer: diff --git a/src/Promitor.Core.Scraping/ResourceTypes/SqlElasticPoolScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/SqlElasticPoolScraper.cs new file mode 100644 index 000000000..46811d412 --- /dev/null +++ b/src/Promitor.Core.Scraping/ResourceTypes/SqlElasticPoolScraper.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Promitor.Core.Contracts; +using Promitor.Core.Contracts.ResourceTypes; +using Promitor.Core.Scraping.Configuration.Model.Metrics; + +namespace Promitor.Core.Scraping.ResourceTypes +{ + /// + /// Scrapes an Azure SQL Database. + /// + public class SqlElasticPoolScraper : AzureMonitorScraper + { + private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Sql/servers/{2}/elasticPools/{3}"; + + public SqlElasticPoolScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) + { + } + + protected override string BuildResourceUri(string subscriptionId, ScrapeDefinition scrapeDefinition, SqlElasticPoolResourceDefinition resource) + { + return string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.ServerName, resource.PoolName); + } + + protected override Dictionary DetermineMetricLabels(SqlElasticPoolResourceDefinition resourceDefinition) + { + var metricLabels = base.DetermineMetricLabels(resourceDefinition); + + metricLabels.TryAdd("server", resourceDefinition.ServerName); + metricLabels.TryAdd("elastic_pool", resourceDefinition.PoolName); + + return metricLabels; + } + } +} \ No newline at end of file diff --git a/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs b/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs index 45caa1ab5..618d0d8bf 100644 --- a/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs +++ b/src/Promitor.Tests.Integration/Services/ResourceDiscovery/ResourceDiscoveryTests.cs @@ -58,7 +58,7 @@ public async Task ResourceDiscovery_GetAllPerResourceTypeWithoutFilters_ReturnsE { // Arrange const string resourceDiscoveryGroupName = "logic-apps-unfiltered"; - const int expectedResourceCount = 20; + const int expectedResourceCount = 22; var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act @@ -158,7 +158,7 @@ public async Task ResourceDiscovery_GetWithFilterOnTwoSubscriptions_ReturnsExpec { // Arrange const string resourceDiscoveryGroupName = "two-subscriptions-scenario"; - const int expectedResourceCount = 20; + const int expectedResourceCount = 22; var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act @@ -238,7 +238,7 @@ public async Task ResourceDiscovery_GetWithFilterOnTwoRegions_ReturnsExpectedAmo { // Arrange const string resourceDiscoveryGroupName = "two-region-scenario"; - const int expectedResourceCount = 19; + const int expectedResourceCount = 21; var resourceDiscoveryClient = new ResourceDiscoveryClient(Configuration, Logger); // Act diff --git a/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs b/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs index 1357e6750..cdfa2cdd3 100644 --- a/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs +++ b/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs @@ -559,6 +559,26 @@ public MetricsDeclarationBuilder WithSqlDatabaseMetric( return this; } + public MetricsDeclarationBuilder WithSqlElasticPoolMetric( + string metricName = "promitor-sql-db", + string azureMetricName = "cpu_percent", + string serverName = "promitor-sql-server", + string poolName = "promitor-elastic-pool", + string metricDescription = "Metric description", + string resourceDiscoveryGroupName = "", + bool omitResource = false) + { + var resource = new SqlElasticPoolResourceV1 + { + ServerName = serverName, + PoolName = poolName + }; + + CreateAndAddMetricDefinition(ResourceType.SqlElasticPool, metricName, metricDescription, resourceDiscoveryGroupName, omitResource, azureMetricName, resource); + + return this; + } + public MetricsDeclarationBuilder WithSqlServerMetric( string metricName = "promitor-sql-server", string azureMetricName = "cpu_percent", diff --git a/src/Promitor.Tests.Unit/Serialization/v1/Providers/SqlElasticPoolDeserializerTests.cs b/src/Promitor.Tests.Unit/Serialization/v1/Providers/SqlElasticPoolDeserializerTests.cs new file mode 100644 index 000000000..8902174bc --- /dev/null +++ b/src/Promitor.Tests.Unit/Serialization/v1/Providers/SqlElasticPoolDeserializerTests.cs @@ -0,0 +1,85 @@ +using System.ComponentModel; +using Microsoft.Extensions.Logging.Abstractions; +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 Xunit; + +namespace Promitor.Tests.Unit.Serialization.v1.Providers +{ + [Category("Unit")] + public class SqlElasticPoolDeserializerTests : ResourceDeserializerTest + { + private readonly SqlElasticPoolDeserializer _deserializer = new SqlElasticPoolDeserializer(NullLogger.Instance); + + [Fact] + public void Deserialize_ServerNameSupplied_SetsServerName() + { + YamlAssert.PropertySet( + _deserializer, + "serverName: promitor-sql-server", + "promitor-sql-server", + c => c.ServerName); + } + + [Fact] + public void Deserialize_ServerNameNotSupplied_Null() + { + YamlAssert.PropertyNull( + _deserializer, + "resourceGroupName: promitor-group", + c => c.ServerName); + } + + [Fact] + public void Deserialize_ServerNameNotSupplied_ReportsError() + { + // Arrange + var node = YamlUtils.CreateYamlNode("resourceGroupName: promitor-resource-group"); + + // Act / Assert + YamlAssert.ReportsErrorForProperty( + _deserializer, + node, + "serverName"); + } + + [Fact] + public void Deserialize_PoolNameSupplied_SetsPoolName() + { + YamlAssert.PropertySet( + _deserializer, + "poolName: promitor-elastic-pool", + "promitor-elastic-pool", + c => c.PoolName); + } + + [Fact] + public void Deserialize_PoolNameNotSupplied_Null() + { + YamlAssert.PropertyNull( + _deserializer, + "resourceGroupName: promitor-group", + c => c.PoolName); + } + + [Fact] + public void Deserialize_PoolNameNotSupplied_ReportsError() + { + // Arrange + var node = YamlUtils.CreateYamlNode("resourceGroupName: promitor-resource-group"); + + // Act / Assert + YamlAssert.ReportsErrorForProperty( + _deserializer, + node, + "poolName"); + } + + protected override IDeserializer CreateDeserializer() + { + return new SqlElasticPoolDeserializer(NullLogger.Instance); + } + } +} \ No newline at end of file diff --git a/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/SqlElasticPoolMetricsDeclarationValidationStepTests.cs b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/SqlElasticPoolMetricsDeclarationValidationStepTests.cs new file mode 100644 index 000000000..1180d002f --- /dev/null +++ b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/SqlElasticPoolMetricsDeclarationValidationStepTests.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 SqlElasticPoolMetricsDeclarationValidationStepTests : MetricsDeclarationValidationStepsTests + { + [Fact] + public void SqlElasticPoolMetricsDeclaration_DeclarationWithoutAzureMetricName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithSqlElasticPoolMetric(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 SqlElasticPoolMetricsDeclaration_DeclarationWithoutAzureMetricDescription_Succeeds() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithSqlElasticPoolMetric(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 SqlElasticPoolMetricsDeclaration_DeclarationWithoutServerName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithSqlElasticPoolMetric(serverName: 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 SqlElasticPoolMetricsDeclaration_DeclarationWithoutPoolName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithSqlElasticPoolMetric(poolName: 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 SqlElasticPoolMetricsDeclaration_DeclarationWithoutResourceAndResourceDiscoveryGroupInfo_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithSqlElasticPoolMetric(omitResource: true) + .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 SqlElasticPoolMetricsDeclaration_DeclarationWithoutResourceButWithResourceDiscoveryGroupInfo_Succeeds() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithSqlElasticPoolMetric(omitResource:true, resourceDiscoveryGroupName:"sample-collection") + .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 SqlElasticPoolMetricsDeclaration_ValidDeclaration_Succeeds() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithSqlElasticPoolMetric() + .Build(Mapper); + var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration, Mapper); + + // Act + var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance); + var validationResult = scrapingScheduleValidationStep.Run(); + + // Assert + PromitorAssert.ValidationIsSuccessful(validationResult); + } + } +}