diff --git a/changelog/content/experimental/unreleased.md b/changelog/content/experimental/unreleased.md index ab9eee97a..46e17ace0 100644 --- a/changelog/content/experimental/unreleased.md +++ b/changelog/content/experimental/unreleased.md @@ -6,6 +6,10 @@ version: #### Scraper +- {{% tag added %}} Provide scraper for Azure Data Factory ([docs](https://promitor.io/configuration/v2.x/metrics/data-factory) + | [#359](https://github.com/tomkerkhove/promitor/issues/359)) +- {{% tag added %}} Provide scraper for Azure Data Share ([docs](https://promitor.io/configuration/v2.x/metrics/data-share) + | [#1678](https://github.com/tomkerkhove/promitor/issues/1678)) - {{% tag added %}} Provide system metrics related to agent performance & resources ([docs](https://promitor.io/operations/#performance) | [#341](https://github.com/tomkerkhove/promitor/issues/341)) - {{% tag added %}} Provide system metrics indicating ARM throttling status ([docs](https://promitor.io/operations/#azure-resource-manager-api---consumption--throttling) diff --git a/config/promitor/resource-discovery/resource-discovery-declaration.yaml b/config/promitor/resource-discovery/resource-discovery-declaration.yaml index 1e43dd8e5..e68ae245b 100644 --- a/config/promitor/resource-discovery/resource-discovery-declaration.yaml +++ b/config/promitor/resource-discovery/resource-discovery-declaration.yaml @@ -20,6 +20,10 @@ resourceDiscoveryGroups: type: ContainerRegistry - name: cosmos-accounts type: CosmosDb +- name: data-factory-landscape + type: DataFactory +- name: data-share-landscape + type: DataShare - name: dps type: DeviceProvisioningService - name: event-hubs-landscape diff --git a/config/promitor/scraper/metrics.yaml b/config/promitor/scraper/metrics.yaml index 10ab884a1..c0a3763d6 100644 --- a/config/promitor/scraper/metrics.yaml +++ b/config/promitor/scraper/metrics.yaml @@ -14,250 +14,298 @@ metricDefaults: # Every minute schedule: "0 * * ? * *" metrics: -- name: promitor_demo_appplan_autoscale_instances_current_discovered - description: "Average amount of current instances for an Azure App Plan with Azure Monitor Autoscale" - resourceType: MonitorAutoscale - labels: - app: promitor - azureMetricConfiguration: - metricName: ObservedCapacity - aggregation: - type: Average - resourceDiscoveryGroups: - - name: autoscaling-rules -- name: promitor_demo_appplan_autoscale_observed_capacity - description: "Average amount of current instances for an Azure App Plan with Azure Monitor Autoscale" - resourceType: MonitorAutoscale - labels: - app: promitor - azureMetricConfiguration: - metricName: MetricThreshold - dimension: - name: MetricTriggerRule - aggregation: - type: Average - resources: - - autoscaleSettingsName: app-service-autoscaling-rules - resourceGroupName: demo -- 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 - labels: - geo: europe - app: promitor - azureMetricConfiguration: - metricName: ActiveMessages - aggregation: - type: Average - resources: - - namespace: promitor-messaging -- name: promitor_demo_servicebus_messagecount_limited - description: "Average percentage of memory usage on an Azure App Plan" - resourceType: ServiceBusNamespace - labels: - geo: europe - app: promitor - azureMetricConfiguration: - limit: 5 - metricName: ActiveMessages - aggregation: - type: Average - resources: - - namespace: promitor-messaging -- 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_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 -- name: promitor_demo_synapse_apache_spark_apps_ended - description: "Amount of apps ended running on Apache Spark pool in Azure Synapse" - resourceType: SynapseApacheSparkPool - azureMetricConfiguration: - metricName: BigDataPoolApplicationsEnded - aggregation: - type: Total - resourceDiscoveryGroups: - - name: synapse-apache-spark-pools -- name: promitor_demo_synapse_sql_pool_dwu_limit - description: "Amount of DWUs defined as limit for SQL pool in Azure Synapse" - resourceType: SynapseSqlPool - azureMetricConfiguration: - metricName: DWULimit - aggregation: - type: Maximum - resourceDiscoveryGroups: - - name: synapse-sql-pools -- name: promitor_demo_synapse_workspace_builtin_sql_processed_bytes - description: "Amount of bytes processed in Azure Synapse workspace" - resourceType: SynapseWorkspace - azureMetricConfiguration: - metricName: BuiltinSqlPoolDataProcessedBytes - aggregation: - type: Total - resourceDiscoveryGroups: - - name: synapse-workspaces + - name: promitor_demo_appplan_autoscale_instances_current_discovered + description: "Average amount of current instances for an Azure App Plan with Azure Monitor Autoscale" + resourceType: MonitorAutoscale + labels: + app: promitor + azureMetricConfiguration: + metricName: ObservedCapacity + aggregation: + type: Average + resourceDiscoveryGroups: + - name: autoscaling-rules + - name: promitor_demo_appplan_autoscale_observed_capacity + description: "Average amount of current instances for an Azure App Plan with Azure Monitor Autoscale" + resourceType: MonitorAutoscale + labels: + app: promitor + azureMetricConfiguration: + metricName: MetricThreshold + dimension: + name: MetricTriggerRule + aggregation: + type: Average + resources: + - autoscaleSettingsName: app-service-autoscaling-rules + resourceGroupName: demo + - 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 + labels: + geo: europe + app: promitor + azureMetricConfiguration: + metricName: ActiveMessages + aggregation: + type: Average + resources: + - namespace: promitor-messaging + - name: promitor_demo_servicebus_messagecount_limited + description: "Average percentage of memory usage on an Azure App Plan" + resourceType: ServiceBusNamespace + labels: + geo: europe + app: promitor + azureMetricConfiguration: + limit: 5 + metricName: ActiveMessages + aggregation: + type: Average + resources: + - namespace: promitor-messaging + - 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_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 + - name: promitor_demo_synapse_apache_spark_apps_ended + description: "Amount of apps ended running on Apache Spark pool in Azure Synapse" + resourceType: SynapseApacheSparkPool + azureMetricConfiguration: + metricName: BigDataPoolApplicationsEnded + aggregation: + type: Total + resourceDiscoveryGroups: + - name: synapse-apache-spark-pools + - name: promitor_demo_synapse_sql_pool_dwu_limit + description: "Amount of DWUs defined as limit for SQL pool in Azure Synapse" + resourceType: SynapseSqlPool + azureMetricConfiguration: + metricName: DWULimit + aggregation: + type: Maximum + resourceDiscoveryGroups: + - name: synapse-sql-pools + - name: promitor_demo_synapse_workspace_builtin_sql_processed_bytes + description: "Amount of bytes processed in Azure Synapse workspace" + resourceType: SynapseWorkspace + azureMetricConfiguration: + metricName: BuiltinSqlPoolDataProcessedBytes + aggregation: + type: Total + resourceDiscoveryGroups: + - name: synapse-workspaces + - name: promitor_demo_data_factory_pipeline_run_successful_declared + description: "Amount of successful pipeline runs in Azure Data Factory" + resourceType: DataFactory + azureMetricConfiguration: + metricName: PipelineSucceededRuns + aggregation: + type: Total + resources: + - resourceGroupName: promitor-sources + factoryName: promitor-data-factory + pipelineName: metric-generator + - name: promitor_demo_data_factory_pipeline_run_successful_discovered + description: "Amount of successful runs for 'metric generator' pipline in Azure Data Factory" + resourceType: DataFactory + azureMetricConfiguration: + metricName: PipelineSucceededRuns + aggregation: + type: Total + resourceDiscoveryGroups: + - name: data-factory-landscape + - name: promitor_demo_data_share_received_declared + description: "Amount of shares received from other parties per Azure Data Share account" + resourceType: DataShare + azureMetricConfiguration: + metricName: ShareSubscriptionCount + aggregation: + type: Maximum + resources: + - resourceGroupName: promitor-sources + accountName: promitor-data-share + - name: promitor_demo_data_share_received_discovered + description: "Amount of shares received from other parties per Azure Data Share account" + resourceType: DataShare + azureMetricConfiguration: + metricName: ShareSubscriptionCount + aggregation: + type: Maximum + resourceDiscoveryGroups: + - name: data-share-landscape + - name: promitor_demo_data_share_sent_discovered + description: "Amount of shares sent from other parties per Azure Data Share account" + resourceType: DataShare + azureMetricConfiguration: + metricName: ShareCount + aggregation: + type: Maximum + resourceDiscoveryGroups: + - name: data-share-landscape diff --git a/docs/configuration/v2.x/metrics/data-factory.md b/docs/configuration/v2.x/metrics/data-factory.md new file mode 100644 index 000000000..8413777f5 --- /dev/null +++ b/docs/configuration/v2.x/metrics/data-factory.md @@ -0,0 +1,44 @@ +--- +layout: default +title: Azure Data Factory Declaration +--- + +## Azure Data Factory + +![Availability Badge](https://img.shields.io/badge/Available%20Starting-v2.5-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 Data Factory resource via the `DataFactory` resource +type. + +When using declared resources, the following fields need to be provided: + +- `factoryName` - The name of the Azure Data Factory resource +- `pipelineName` - The name of the data pipeline *(optional)* + +All supported metrics are documented in the official [Azure Monitor documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/essentials/metrics-supported#microsoftdatafactoryfactories). + +The following scraper-specific metric label will be added: + +- `pipeline_name` - Name of the data pipeline. + +Example: + +```yaml +- name: azure_data_factory_pipeline_run_successful + description: "Amount of successful runs for 'data-pipeline-example' pipline in Azure Data Factory" + resourceType: DataFactory + azureMetricConfiguration: + metricName: PipelineSucceededRuns + aggregation: + type: Total + resources: # Optional, required when no resource discovery is configured + - factoryName: promitor-data-factory + pipelineName: data-pipeline-example + resourceDiscoveryGroups: # Optional, requires Promitor Resource Discovery agent (https://promitor.io/concepts/how-it-works#using-resource-discovery) + - name: data-factory-landscape +``` + + +[← back to metrics declarations](/configuration/v2.x/metrics)
+[← back to introduction](/) + diff --git a/docs/configuration/v2.x/metrics/data-share.md b/docs/configuration/v2.x/metrics/data-share.md new file mode 100644 index 000000000..15893b283 --- /dev/null +++ b/docs/configuration/v2.x/metrics/data-share.md @@ -0,0 +1,44 @@ +--- +layout: default +title: Azure Data Share Declaration +--- + +## Azure Data Share + +![Availability Badge](https://img.shields.io/badge/Available%20Starting-v2.5-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 Data Share resource via the `DataShare` resource +type. + +When using declared resources, the following fields need to be provided: + +- `accountName` - The name of the Azure Data Share account +- `shareName` - The name of the share *(optional)* + +All supported metrics are documented in the official [Azure Monitor documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/essentials/metrics-supported#microsoftdatashareaccounts). + +The following scraper-specific metric label will be added: + +- `share_name` - Name of the share. + +Example: + +```yaml +- name: promitor_demo_data_share_received + description: "Amount of shares received from other parties per Azure Data Share account" + resourceType: DataShare + azureMetricConfiguration: + metricName: ShareSubscriptionCount + aggregation: + type: Maximum + resources: # Optional, required when no resource discovery is configured + - accountName: promitor-data-share + shareName: Promitor + resourceDiscoveryGroups: # Optional, requires Promitor Resource Discovery agent (https://promitor.io/concepts/how-it-works#using-resource-discovery) + - name: data-share-landscape +``` + + +[← back to metrics declarations](/configuration/v2.x/metrics)
+[← back to introduction](/) + diff --git a/docs/configuration/v2.x/metrics/index.md b/docs/configuration/v2.x/metrics/index.md index 750778a7e..0fdcf163d 100644 --- a/docs/configuration/v2.x/metrics/index.md +++ b/docs/configuration/v2.x/metrics/index.md @@ -25,6 +25,8 @@ We also provide a simplified way to scrape the following Azure resources: - [Azure Container Instances](container-instances) - [Azure Container Registry](container-registry) - [Azure Cosmos DB](cosmos-db) +- [Azure Data Factory](data-factory) +- [Azure Data Share](data-share) - [Azure Database for PostgreSQL](postgresql) - [Azure Event Hubs](event-hubs) - [Azure Express Route Circuit](express-route-circuit) diff --git a/docs/configuration/v2.x/resource-discovery.md b/docs/configuration/v2.x/resource-discovery.md index 90dae0a44..589ad6013 100644 --- a/docs/configuration/v2.x/resource-discovery.md +++ b/docs/configuration/v2.x/resource-discovery.md @@ -95,6 +95,8 @@ Dynamic resource discovery is supported for the following scrapers: - [Azure Container Instances](metrics/container-instances) - [Azure Container Registry](metrics/container-registry) - [Azure Cosmos DB](metrics/cosmos-db) +- [Azure Data Factory](metrics/data-factory) +- [Azure Data Share](metrics/data-share) - [Azure Database for PostgreSQL](metrics/postgresql) - [Azure Event Hubs](metrics/event-hubs) - [Azure Express Route Circuit](metrics/express-route-circuit) diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs index 4f6bbc52c..18a2cc8ca 100644 --- a/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/AzureResourceGraph.cs @@ -100,7 +100,6 @@ private async Task QueryAsync(string queryName, string query, Lis return response; } - // TODO: Clean up private async Task InteractWithAzureResourceGraphAsync(string queryName, string query, Func> interactionFunc, List targetSubscriptions = null) { Guard.NotNullOrWhitespace(query, nameof(query)); diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/Repositories/AzureResourceRepository.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/Repositories/AzureResourceRepository.cs index a0ddd8595..da7f7c004 100644 --- a/src/Promitor.Agents.ResourceDiscovery/Graph/Repositories/AzureResourceRepository.cs +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/Repositories/AzureResourceRepository.cs @@ -70,7 +70,6 @@ public virtual async Task> GetResourcesAsync(strin return foundResources; } - // TODO: Unit test public async Task> DiscoverAzureSubscriptionsAsync() { var query = @"ResourceContainers @@ -90,7 +89,6 @@ public async Task> DiscoverAzureSubscriptions }); } - // TODO: Unit test public async Task> DiscoverAzureResourceGroupsAsync() { var query = @"ResourceContainers diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs index cca408587..b82dac829 100644 --- a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceDiscoveryFactory.cs @@ -24,6 +24,10 @@ public static ResourceDiscoveryQuery UseResourceDiscoveryFor(ResourceType resour return new ContainerRegistryDiscoveryQuery(); case ResourceType.CosmosDb: return new CosmosDbDiscoveryQuery(); + case ResourceType.DataFactory: + return new DataFactoryDiscoveryQuery(); + case ResourceType.DataShare: + return new DataShareDiscoveryQuery(); case ResourceType.DeviceProvisioningService: return new DeviceProvisioningServiceDiscoveryQuery(); case ResourceType.EventHubs: diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/DataFactoryDiscoveryQuery.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/DataFactoryDiscoveryQuery.cs new file mode 100644 index 000000000..757811033 --- /dev/null +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/DataFactoryDiscoveryQuery.cs @@ -0,0 +1,22 @@ +using GuardNet; +using Newtonsoft.Json.Linq; +using Promitor.Core.Contracts; +using Promitor.Core.Contracts.ResourceTypes; + +namespace Promitor.Agents.ResourceDiscovery.Graph.ResourceTypes +{ + public class DataFactoryDiscoveryQuery : ResourceDiscoveryQuery + { + public override string[] ResourceTypes => new[] { "microsoft.datafactory/factories" }; + public override string[] ProjectedFieldNames => new[] { "subscriptionId", "resourceGroup", "type", "name", "id" }; + + public override AzureResourceDefinition ParseResults(JToken resultRowEntry) + { + Guard.NotNull(resultRowEntry, nameof(resultRowEntry)); + + var pipelineName = string.Empty; // We don't use pipeline name since we want all info + var resource = new DataFactoryResourceDefinition(resultRowEntry[0]?.ToString(), resultRowEntry[1]?.ToString(), resultRowEntry[3]?.ToString(), pipelineName); + return resource; + } + } +} diff --git a/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/DataShareDiscoveryQuery.cs b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/DataShareDiscoveryQuery.cs new file mode 100644 index 000000000..20e10c578 --- /dev/null +++ b/src/Promitor.Agents.ResourceDiscovery/Graph/ResourceTypes/DataShareDiscoveryQuery.cs @@ -0,0 +1,22 @@ +using GuardNet; +using Newtonsoft.Json.Linq; +using Promitor.Core.Contracts; +using Promitor.Core.Contracts.ResourceTypes; + +namespace Promitor.Agents.ResourceDiscovery.Graph.ResourceTypes +{ + public class DataShareDiscoveryQuery : ResourceDiscoveryQuery + { + public override string[] ResourceTypes => new[] { "microsoft.datashare/accounts" }; + public override string[] ProjectedFieldNames => new[] { "subscriptionId", "resourceGroup", "type", "name", "id" }; + + public override AzureResourceDefinition ParseResults(JToken resultRowEntry) + { + Guard.NotNull(resultRowEntry, nameof(resultRowEntry)); + + var shareName = string.Empty; // We don't use share name since we want all info + var resource = new DataShareResourceDefinition(resultRowEntry[0]?.ToString(), resultRowEntry[1]?.ToString(), resultRowEntry[3]?.ToString(), shareName); + return resource; + } + } +} diff --git a/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs b/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs index 426356198..2b34f146e 100644 --- a/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs +++ b/src/Promitor.Agents.Scraper/Validation/Factories/MetricValidatorFactory.cs @@ -27,6 +27,10 @@ internal static IMetricValidator GetValidatorFor(ResourceType resourceType) return new ContainerRegistryMetricValidator(); case ResourceType.CosmosDb: return new CosmosDbMetricValidator(); + case ResourceType.DataFactory: + return new DataFactoryMetricValidator(); + case ResourceType.DataShare: + return new DataShareMetricValidator(); case ResourceType.DeviceProvisioningService: return new DeviceProvisioningServiceMetricValidator(); case ResourceType.EventHubs: diff --git a/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/DataFactoryMetricValidator.cs b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/DataFactoryMetricValidator.cs new file mode 100644 index 000000000..0ec1f9487 --- /dev/null +++ b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/DataFactoryMetricValidator.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 DataFactoryMetricValidator : IMetricValidator + { + public IEnumerable Validate(MetricDefinition metricDefinition) + { + Guard.NotNull(metricDefinition, nameof(metricDefinition)); + + foreach (var resourceDefinition in metricDefinition.Resources.Cast()) + { + if (string.IsNullOrWhiteSpace(resourceDefinition.FactoryName)) + { + yield return "No Azure Data Factory name is configured"; + } + } + } + } +} \ No newline at end of file diff --git a/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/DataShareMetricValidator.cs b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/DataShareMetricValidator.cs new file mode 100644 index 000000000..9ebefbd90 --- /dev/null +++ b/src/Promitor.Agents.Scraper/Validation/MetricDefinitions/ResourceTypes/DataShareMetricValidator.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 DataShareMetricValidator : IMetricValidator + { + public IEnumerable Validate(MetricDefinition metricDefinition) + { + Guard.NotNull(metricDefinition, nameof(metricDefinition)); + + foreach (var resourceDefinition in metricDefinition.Resources.Cast()) + { + if (string.IsNullOrWhiteSpace(resourceDefinition.AccountName)) + { + yield return "No Azure Data Share account 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 8c5c866ba..80e339f8d 100644 --- a/src/Promitor.Core.Contracts/ResourceType.cs +++ b/src/Promitor.Core.Contracts/ResourceType.cs @@ -39,6 +39,8 @@ public enum ResourceType SynapseApacheSparkPool = 34, SynapseSqlPool = 35, SynapseWorkspace = 36, - MonitorAutoscale = 37 + MonitorAutoscale = 37, + DataFactory = 38, + DataShare = 39 } } \ No newline at end of file diff --git a/src/Promitor.Core.Contracts/ResourceTypes/DataFactoryResourceDefinition.cs b/src/Promitor.Core.Contracts/ResourceTypes/DataFactoryResourceDefinition.cs new file mode 100644 index 000000000..4f152aaf5 --- /dev/null +++ b/src/Promitor.Core.Contracts/ResourceTypes/DataFactoryResourceDefinition.cs @@ -0,0 +1,16 @@ +namespace Promitor.Core.Contracts.ResourceTypes +{ + public class DataFactoryResourceDefinition : AzureResourceDefinition + { + public DataFactoryResourceDefinition(string subscriptionId, string resourceGroupName, string factoryName, string pipelineName) + : base(ResourceType.DataFactory, subscriptionId, resourceGroupName, $"{factoryName}-{pipelineName}") + { + FactoryName = factoryName; + PipelineName = pipelineName; + } + + public string FactoryName { get; } + + public string PipelineName { get; } + } +} \ No newline at end of file diff --git a/src/Promitor.Core.Contracts/ResourceTypes/DataShareResourceDefinition.cs b/src/Promitor.Core.Contracts/ResourceTypes/DataShareResourceDefinition.cs new file mode 100644 index 000000000..894815f60 --- /dev/null +++ b/src/Promitor.Core.Contracts/ResourceTypes/DataShareResourceDefinition.cs @@ -0,0 +1,15 @@ +namespace Promitor.Core.Contracts.ResourceTypes +{ + public class DataShareResourceDefinition : AzureResourceDefinition + { + public DataShareResourceDefinition(string subscriptionId, string resourceGroupName, string accountName, string shareName) + : base(ResourceType.DataShare, subscriptionId, resourceGroupName, $"{accountName}-{shareName}") + { + AccountName = accountName; + ShareName = shareName; + } + + public string AccountName { get; } + public string ShareName { get; } + } +} \ No newline at end of file diff --git a/src/Promitor.Core.Scraping/AzureMonitorScraper.cs b/src/Promitor.Core.Scraping/AzureMonitorScraper.cs index f00001821..a7ff55496 100644 --- a/src/Promitor.Core.Scraping/AzureMonitorScraper.cs +++ b/src/Promitor.Core.Scraping/AzureMonitorScraper.cs @@ -39,13 +39,13 @@ protected override async Task ScrapeResourceAsync(string subscript var resourceUri = BuildResourceUri(subscriptionId, scrapeDefinition, resourceDefinition); // Determine the metric filter to use, if any - var metricFilter = DetermineMetricFilter(resourceDefinition); + var metricFilter = DetermineMetricFilter(metricName, resourceDefinition); // Determine the metric limit to use, if any var metricLimit = DetermineMetricLimit(scrapeDefinition); // Determine the metric dimension to use, if any - var dimensionName = DetermineMetricDimension(resourceDefinition, scrapeDefinition.AzureMetricConfiguration?.Dimension); + var dimensionName = DetermineMetricDimension(metricName, resourceDefinition, scrapeDefinition.AzureMetricConfiguration?.Dimension); List measuredMetrics = new List(); try @@ -95,8 +95,9 @@ protected virtual List EnrichMeasuredMetrics(TResourceDefinition /// /// Determines the metric filter to use /// + /// Name of the metric being queried /// Contains the resource cast to the specific resource type. - protected virtual string DetermineMetricFilter(TResourceDefinition resourceDefinition) + protected virtual string DetermineMetricFilter(string metricName, TResourceDefinition resourceDefinition) { return null; } @@ -104,9 +105,10 @@ protected virtual string DetermineMetricFilter(TResourceDefinition resourceDefin /// /// Determines the dimension for a metric to use /// + /// Name of the metric being queried /// Contains the resource cast to the specific resource type. /// Provides information concerning the configured metric dimension. - protected virtual string DetermineMetricDimension(TResourceDefinition resourceDefinition, MetricDimension dimension) + protected virtual string DetermineMetricDimension(string metricName, TResourceDefinition resourceDefinition, MetricDimension dimension) { return dimension?.Name; } 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 9984eb206..65ad4fbd3 100644 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Core/AzureResourceDeserializerFactory.cs @@ -45,6 +45,12 @@ public IDeserializer GetDeserializerFor(ResourceType case ResourceType.CosmosDb: var cosmosDbLogger = _loggerFactory.CreateLogger(); return new CosmosDbDeserializer(cosmosDbLogger); + case ResourceType.DataFactory: + var dataFactoryDeserializer = _loggerFactory.CreateLogger(); + return new DataFactoryDeserializer(dataFactoryDeserializer); + case ResourceType.DataShare: + var dataShareLogger = _loggerFactory.CreateLogger(); + return new DataShareDeserializer(dataShareLogger); case ResourceType.DeviceProvisioningService: var deviceProvisioningServiceLogger = _loggerFactory.CreateLogger(); return new DeviceProvisioningServiceDeserializer(deviceProvisioningServiceLogger); 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 79f221772..9ace8d7c5 100644 --- a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Mapping/V1MappingProfile.cs @@ -30,6 +30,8 @@ public V1MappingProfile() CreateMap(); CreateMap(); CreateMap(); + CreateMap(); + CreateMap(); CreateMap(); CreateMap(); CreateMap(); @@ -75,6 +77,8 @@ public V1MappingProfile() .Include() .Include() .Include() + .Include() + .Include() .Include() .Include() .Include() diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/DataFactoryResourceV1.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/DataFactoryResourceV1.cs new file mode 100644 index 000000000..cae37699a --- /dev/null +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/DataFactoryResourceV1.cs @@ -0,0 +1,18 @@ +namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes +{ + /// + /// Contains the configuration required to scrape a Data Factory instance. + /// + public class DataFactoryResourceV1 : AzureResourceDefinitionV1 + { + /// + /// The data factory name. + /// + public string FactoryName { get; set; } + + /// + /// The data pipeline name. + /// + public string PipelineName { get; set; } + } +} diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/DataShareResourceV1.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/DataShareResourceV1.cs new file mode 100644 index 000000000..8c68266f6 --- /dev/null +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Model/ResourceTypes/DataShareResourceV1.cs @@ -0,0 +1,18 @@ +namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes +{ + /// + /// Contains the configuration required to scrape a Data Share instance. + /// + public class DataShareResourceV1 : AzureResourceDefinitionV1 + { + /// + /// The data share account name. + /// + public string AccountName { get; set; } + + /// + /// The data share name. + /// + public string ShareName { get; set; } + } +} diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/DataFactoryDeserializer.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/DataFactoryDeserializer.cs new file mode 100644 index 000000000..84821cbc4 --- /dev/null +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/DataFactoryDeserializer.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; +using Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes; + +namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Providers +{ + public class DataFactoryDeserializer : ResourceDeserializer + { + public DataFactoryDeserializer(ILogger logger) : base(logger) + { + Map(resource => resource.FactoryName) + .IsRequired(); + Map(resource => resource.PipelineName); + } + } +} diff --git a/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/DataShareDeserializer.cs b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/DataShareDeserializer.cs new file mode 100644 index 000000000..cc49a6450 --- /dev/null +++ b/src/Promitor.Core.Scraping/Configuration/Serialization/v1/Providers/DataShareDeserializer.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; +using Promitor.Core.Scraping.Configuration.Serialization.v1.Model.ResourceTypes; + +namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Providers +{ + public class DataShareDeserializer : ResourceDeserializer + { + public DataShareDeserializer(ILogger logger) : base(logger) + { + Map(resource => resource.AccountName) + .IsRequired(); + Map(resource => resource.ShareName); + } + } +} diff --git a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs index 0b7ede68e..6c1999f64 100644 --- a/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs +++ b/src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs @@ -50,6 +50,10 @@ public IScraper CreateScraper(ResourceType metricDefin return new ContainerRegistryScraper(scraperConfiguration); case ResourceType.CosmosDb: return new CosmosDbScraper(scraperConfiguration); + case ResourceType.DataFactory: + return new DataFactoryScraper(scraperConfiguration); + case ResourceType.DataShare: + return new DataShareScraper(scraperConfiguration); case ResourceType.DeviceProvisioningService: return new DeviceProvisioningServiceScraper(scraperConfiguration); case ResourceType.EventHubs: diff --git a/src/Promitor.Core.Scraping/ResourceTypes/ApiManagementScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/ApiManagementScraper.cs index 4d7893b0a..660924d74 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/ApiManagementScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/ApiManagementScraper.cs @@ -22,11 +22,11 @@ protected override string BuildResourceUri(string subscriptionId, ScrapeDefiniti return string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.InstanceName); } - protected override string DetermineMetricFilter(ApiManagementResourceDefinition resourceDefinition) + protected override string DetermineMetricFilter(string metricName, ApiManagementResourceDefinition resourceDefinition) { if (string.IsNullOrWhiteSpace(resourceDefinition.LocationName)) { - return base.DetermineMetricFilter(resourceDefinition); + return base.DetermineMetricFilter(metricName, resourceDefinition); } return $"Location eq '{resourceDefinition.LocationName}'"; diff --git a/src/Promitor.Core.Scraping/ResourceTypes/AutomationAccountScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/AutomationAccountScraper.cs index 11deb09d3..a1cda843d 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/AutomationAccountScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/AutomationAccountScraper.cs @@ -22,11 +22,11 @@ protected override string BuildResourceUri(string subscriptionId, ScrapeDefiniti return string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.AccountName); } - protected override string DetermineMetricFilter(AutomationAccountResourceDefinition resourceDefinition) + protected override string DetermineMetricFilter(string metricName, AutomationAccountResourceDefinition resourceDefinition) { if (string.IsNullOrWhiteSpace(resourceDefinition.RunbookName)) { - return base.DetermineMetricFilter(resourceDefinition); + return base.DetermineMetricFilter(metricName, resourceDefinition); } return $"Runbook eq '{resourceDefinition.RunbookName}'"; diff --git a/src/Promitor.Core.Scraping/ResourceTypes/AzureMessagingScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/AzureMessagingScraper.cs index 704d90317..80db3ad01 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/AzureMessagingScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/AzureMessagingScraper.cs @@ -40,11 +40,11 @@ protected override Dictionary DetermineMetricLabels(TResourceDef return metricLabels; } - protected override string DetermineMetricDimension(TResourceDefinition resourceDefinition, MetricDimension dimension) + protected override string DetermineMetricDimension(string metricName, TResourceDefinition resourceDefinition, MetricDimension dimension) { if (IsEntityDeclared(resourceDefinition)) { - return base.DetermineMetricDimension(resourceDefinition, dimension); + return base.DetermineMetricDimension(metricName, resourceDefinition, dimension); } Logger.LogTrace("Using 'EntityName' dimension since no topic was configured."); @@ -52,7 +52,7 @@ protected override string DetermineMetricDimension(TResourceDefinition resourceD return "EntityName"; } - protected override string DetermineMetricFilter(TResourceDefinition resourceDefinition) + protected override string DetermineMetricFilter(string metricName, TResourceDefinition resourceDefinition) { var entityName = "*"; diff --git a/src/Promitor.Core.Scraping/ResourceTypes/DataFactoryScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/DataFactoryScraper.cs new file mode 100644 index 000000000..a684b5058 --- /dev/null +++ b/src/Promitor.Core.Scraping/ResourceTypes/DataFactoryScraper.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Promitor.Core.Contracts; +using Promitor.Core.Contracts.ResourceTypes; +using Promitor.Core.Scraping.Configuration.Model; +using Promitor.Core.Scraping.Configuration.Model.Metrics; + +namespace Promitor.Core.Scraping.ResourceTypes +{ + public class DataFactoryScraper : AzureMonitorScraper + { + private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DataFactory/factories/{2}"; + + public DataFactoryScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) + { + } + + protected override string BuildResourceUri(string subscriptionId, ScrapeDefinition scrapeDefinition, DataFactoryResourceDefinition resource) + { + return string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.FactoryName); + } + + protected override string DetermineMetricFilter(string metricName, DataFactoryResourceDefinition resourceDefinition) + { + var fieldName = GetMetricFilterFieldName(metricName); + + var entityName = "*"; + + if (IsPipelineNameConfigured(resourceDefinition)) + { + entityName = resourceDefinition.PipelineName; + } + + return $"{fieldName} eq '{entityName}'"; + } + + protected override Dictionary DetermineMetricLabels(DataFactoryResourceDefinition resourceDefinition) + { + var metricLabels = base.DetermineMetricLabels(resourceDefinition); + + if (IsPipelineNameConfigured(resourceDefinition)) + { + metricLabels.Add("pipeline_name", resourceDefinition.PipelineName); + } + + return metricLabels; + } + + protected override string DetermineMetricDimension(string metricName, DataFactoryResourceDefinition resourceDefinition, MetricDimension dimension) + { + if (IsPipelineNameConfigured(resourceDefinition)) + { + return base.DetermineMetricDimension(metricName, resourceDefinition, dimension); + } + + var dimensionName = GetMetricFilterFieldName(metricName); + Logger.LogTrace($"Using '{dimensionName}' dimension since no pipeline name was configured."); + + return dimensionName; + } + + private static bool IsPipelineNameConfigured(DataFactoryResourceDefinition resourceDefinition) + { + return string.IsNullOrWhiteSpace(resourceDefinition.PipelineName) == false; + } + + private static string GetMetricFilterFieldName(string metricName) + { + var fieldName = "Name"; + + // We need to switch field names when querying activities + if (metricName.Equals("ActivitySucceededRuns", StringComparison.InvariantCultureIgnoreCase) + || metricName.Equals("ActivityFailedRuns", StringComparison.InvariantCultureIgnoreCase) + || metricName.Equals("ActivityCancelledRuns", StringComparison.InvariantCultureIgnoreCase)) + { + fieldName = "PipelineName"; + } + + return fieldName; + } + } +} \ No newline at end of file diff --git a/src/Promitor.Core.Scraping/ResourceTypes/DataShareScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/DataShareScraper.cs new file mode 100644 index 000000000..5ac73c34b --- /dev/null +++ b/src/Promitor.Core.Scraping/ResourceTypes/DataShareScraper.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; +using Promitor.Core.Contracts; +using Promitor.Core.Contracts.ResourceTypes; +using Promitor.Core.Metrics; +using Promitor.Core.Scraping.Configuration.Model; +using Promitor.Core.Scraping.Configuration.Model.Metrics; + +namespace Promitor.Core.Scraping.ResourceTypes +{ + public class DataShareScraper : AzureMonitorScraper + { + private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DataShare/accounts/{2}"; + + public DataShareScraper(ScraperConfiguration scraperConfiguration) + : base(scraperConfiguration) + { + } + + protected override string BuildResourceUri(string subscriptionId, ScrapeDefinition scrapeDefinition, DataShareResourceDefinition resource) + { + return string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.AccountName); + } + + protected override string DetermineMetricFilter(string metricName, DataShareResourceDefinition resourceDefinition) + { + var fieldName = GetMetricFilterFieldName(metricName); + + var entityName = "*"; + + if (IsShareNameConfigured(resourceDefinition)) + { + entityName = resourceDefinition.ShareName; + } + + return $"{fieldName} eq '{entityName}'"; + } + + protected override string DetermineMetricDimension(string metricName, DataShareResourceDefinition resourceDefinition, MetricDimension dimension) + { + if (IsShareNameConfigured(resourceDefinition)) + { + return base.DetermineMetricDimension(metricName, resourceDefinition, dimension); + } + + var dimensionName = GetMetricFilterFieldName(metricName); + Logger.LogTrace($"Using '{dimensionName}' dimension since no share name was configured."); + + return dimensionName; + } + + protected override List EnrichMeasuredMetrics(DataShareResourceDefinition resourceDefinition, string dimensionName, List metricValues) + { + // Change Azure Monitor dimension name to more representable value + foreach (var measuredMetric in metricValues.Where(metricValue => metricValue.DimensionName == "ShareName" + || metricValue.DimensionName == "ShareSubscriptionName")) + { + measuredMetric.DimensionName = "share_name"; + } + + return metricValues; + } + + private static string GetMetricFilterFieldName(string metricName) + { + var fieldName = "ShareName"; + + // We need to switch field names when querying activities + if (metricName.Equals("ShareSubscriptionCount", StringComparison.InvariantCultureIgnoreCase)) + { + fieldName = "ShareSubscriptionName"; + } + + return fieldName; + } + + protected override Dictionary DetermineMetricLabels(DataShareResourceDefinition resourceDefinition) + { + var metricLabels = base.DetermineMetricLabels(resourceDefinition); + + if (IsShareNameConfigured(resourceDefinition)) + { + metricLabels.Add("share_name", resourceDefinition.ShareName); + } + + return metricLabels; + } + + private static bool IsShareNameConfigured(DataShareResourceDefinition resourceDefinition) + { + return string.IsNullOrWhiteSpace(resourceDefinition.ShareName) == false; + } + } +} \ No newline at end of file diff --git a/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs b/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs index ce97b902d..c0c690a35 100644 --- a/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs +++ b/src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs @@ -18,7 +18,7 @@ protected override string BuildResourceUri(string subscriptionId, ScrapeDefiniti return string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.ResourceUri); } - protected override string DetermineMetricFilter(GenericAzureResourceDefinition resourceDefinition) + protected override string DetermineMetricFilter(string metricName, GenericAzureResourceDefinition resourceDefinition) { return resourceDefinition.Filter; } diff --git a/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs b/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs index 4f7c9c7f2..13bdf8596 100644 --- a/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs +++ b/src/Promitor.Tests.Unit/Builders/Metrics/v1/MetricsDeclarationBuilder.cs @@ -148,6 +148,46 @@ public MetricsDeclarationBuilder WithContainerRegistryMetric(string metricName = return this; } + public MetricsDeclarationBuilder WithDataShareMetric(string metricName = "promitor-data-share", + string metricDescription = "Description for a metric", + string accountName = "promitor-data-share-account", + string shareName = "promitor-data-share", + string azureMetricName = "TotalRequests", + string resourceDiscoveryGroupName = "", + int? azureMetricLimit = null, + bool omitResource = false) + { + var resource = new DataShareResourceV1 + { + AccountName = accountName, + ShareName = shareName + }; + + CreateAndAddMetricDefinition(ResourceType.DataShare, metricName, metricDescription, resourceDiscoveryGroupName, omitResource, azureMetricName, azureMetricLimit, resource); + + return this; + } + + public MetricsDeclarationBuilder WithDataFactoryMetric(string metricName = "promitor-data-factory", + string metricDescription = "Description for a metric", + string factoryName = "promitor-data-factory", + string pipelineName = "promitor-data-pipeline", + string azureMetricName = "TotalRequests", + string resourceDiscoveryGroupName = "", + int? azureMetricLimit = null, + bool omitResource = false) + { + var resource = new DataFactoryResourceV1 + { + FactoryName = factoryName, + PipelineName = pipelineName + }; + + CreateAndAddMetricDefinition(ResourceType.DataFactory, metricName, metricDescription, resourceDiscoveryGroupName, omitResource, azureMetricName, azureMetricLimit, resource); + + return this; + } + public MetricsDeclarationBuilder WithEventHubsMetric(string metricName = "promitor-event-hubs", string metricDescription = "Description for a metric", string metricDimension = "", diff --git a/src/Promitor.Tests.Unit/Serialization/v1/Providers/DataFactoryDeserializerTests.cs b/src/Promitor.Tests.Unit/Serialization/v1/Providers/DataFactoryDeserializerTests.cs new file mode 100644 index 000000000..1260eab35 --- /dev/null +++ b/src/Promitor.Tests.Unit/Serialization/v1/Providers/DataFactoryDeserializerTests.cs @@ -0,0 +1,76 @@ +using System.ComponentModel; +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 DataFactoryDeserializerTests : ResourceDeserializerTest + { + private readonly DataFactoryDeserializer _deserializer; + + public DataFactoryDeserializerTests() + { + _deserializer = new DataFactoryDeserializer(Logger); + } + + protected override IDeserializer CreateDeserializer() + { + return new DataFactoryDeserializer(Logger); + } + + [Fact] + public void Deserialize_FactoryNameSupplied_SetsFactoryName() + { + YamlAssert.PropertySet( + _deserializer, + "factoryName: promitor-group", + "promitor-group", + c => c.FactoryName); + } + + [Fact] + public void Deserialize_PipelineNameSupplied_SetsPipelineName() + { + YamlAssert.PropertySet( + _deserializer, + "pipelineName: staging", + "staging", + r => r.PipelineName); + } + + [Fact] + public void Deserialize_PipelineNameNotSupplied_Null() + { + YamlAssert.PropertyNull( + _deserializer, + "resourceGroupName: promitor-group", + r => r.PipelineName); + } + + [Fact] + public void Deserialize_FactoryNameNotSupplied_Null() + { + YamlAssert.PropertyNull( + _deserializer, + "resourceGroupName: promitor-resource-group", + c => c.FactoryName); + } + + [Fact] + public void Deserialize_FactoryNameNotSupplied_ReportsError() + { + // Arrange + var node = YamlUtils.CreateYamlNode("resourceGroupName: promitor-resource-group"); + + // Act / Assert + YamlAssert.ReportsErrorForProperty( + _deserializer, + node, + "factoryName"); + } + } +} diff --git a/src/Promitor.Tests.Unit/Serialization/v1/Providers/DataShareDeserializerTests.cs b/src/Promitor.Tests.Unit/Serialization/v1/Providers/DataShareDeserializerTests.cs new file mode 100644 index 000000000..207b3fa42 --- /dev/null +++ b/src/Promitor.Tests.Unit/Serialization/v1/Providers/DataShareDeserializerTests.cs @@ -0,0 +1,76 @@ +using System.ComponentModel; +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 DataShareDeserializerTests : ResourceDeserializerTest + { + private readonly DataShareDeserializer _deserializer; + + public DataShareDeserializerTests() + { + _deserializer = new DataShareDeserializer(Logger); + } + + protected override IDeserializer CreateDeserializer() + { + return new DataShareDeserializer(Logger); + } + + [Fact] + public void Deserialize_AccountNameSupplied_SetsAccountName() + { + YamlAssert.PropertySet( + _deserializer, + "accountName: promitor-group", + "promitor-group", + c => c.AccountName); + } + + [Fact] + public void Deserialize_AccountNameNotSupplied_Null() + { + YamlAssert.PropertyNull( + _deserializer, + "resourceGroupName: promitor-resource-group", + c => c.AccountName); + } + + [Fact] + public void Deserialize_ShareNameSupplied_SetsShareName() + { + YamlAssert.PropertySet( + _deserializer, + "shareName: staging", + "staging", + r => r.ShareName); + } + + [Fact] + public void Deserialize_ShareNameNotSupplied_Null() + { + YamlAssert.PropertyNull( + _deserializer, + "resourceGroupName: promitor-group", + r => r.ShareName); + } + + [Fact] + public void Deserialize_AccountNameNotSupplied_ReportsError() + { + // Arrange + var node = YamlUtils.CreateYamlNode("resourceGroupName: promitor-resource-group"); + + // Act / Assert + YamlAssert.ReportsErrorForProperty( + _deserializer, + node, + "accountName"); + } + } +} diff --git a/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/DataFactoryMetricsDeclarationValidationStepTests.cs b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/DataFactoryMetricsDeclarationValidationStepTests.cs new file mode 100644 index 000000000..7791b7db1 --- /dev/null +++ b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/DataFactoryMetricsDeclarationValidationStepTests.cs @@ -0,0 +1,169 @@ +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 DataFactoryMetricsDeclarationValidationStepTests : MetricsDeclarationValidationStepsTests + { + [Fact] + public void DataFactoryMetricsDeclaration_DeclarationWithoutAzureMetricName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(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); + } + + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(10001)] + public void DataFactoryMetricsDeclaration_DeclarationWithInvalidMetricLimit_Fails(int metricLimit) + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(azureMetricLimit: metricLimit) + .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 DataFactoryMetricsDeclaration_DeclarationWithoutMetricDescription_Succeeded() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(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 DataFactoryMetricsDeclaration_DeclarationWithoutMetricName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(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 DataFactoryMetricsDeclaration_DeclarationWithoutFactoryName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(factoryName: 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 DataFactoryMetricsDeclaration_DeclarationWithoutPipelineName_Succeeds() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(pipelineName: 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 DataFactoryMetricsDeclaration_DeclarationWithoutResourceAndResourceDiscoveryGroupInfo_Fails() + { + // Arrange + var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(omitResource: true) + .Build(Mapper); + var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawMetricsDeclaration, Mapper); + + // Act + var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance); + var validationResult = scrapingScheduleValidationStep.Run(); + + // Assert + PromitorAssert.ValidationFailed(validationResult); + } + + [Fact] + public void DataFactoryMetricsDeclaration_DeclarationWithoutResourceButWithResourceDiscoveryGroupInfo_Succeeds() + { + // Arrange + var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric(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 DataFactoryMetricsDeclaration_ValidDeclaration_Succeeds() + { + // Arrange + var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataFactoryMetric() + .Build(Mapper); + var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawMetricsDeclaration, Mapper); + + // Act + var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance); + var validationResult = scrapingScheduleValidationStep.Run(); + + // Assert + PromitorAssert.ValidationIsSuccessful(validationResult); + } + } +} \ No newline at end of file diff --git a/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/DataShareMetricsDeclarationValidationStepTests.cs b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/DataShareMetricsDeclarationValidationStepTests.cs new file mode 100644 index 000000000..8aafd4304 --- /dev/null +++ b/src/Promitor.Tests.Unit/Validation/Scraper/Metrics/ResourceTypes/DataShareMetricsDeclarationValidationStepTests.cs @@ -0,0 +1,169 @@ +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 DataShareMetricsDeclarationValidationStepTests : MetricsDeclarationValidationStepsTests + { + [Fact] + public void DataShareMetricsDeclaration_DeclarationWithoutAzureMetricName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(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); + } + + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(10001)] + public void DataShareMetricsDeclaration_DeclarationWithInvalidMetricLimit_Fails(int metricLimit) + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(azureMetricLimit: metricLimit) + .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 DataShareMetricsDeclaration_DeclarationWithoutMetricDescription_Succeeded() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(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 DataShareMetricsDeclaration_DeclarationWithoutMetricName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(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 DataShareMetricsDeclaration_DeclarationWithoutAccountName_Fails() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(accountName: 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 DataShareMetricsDeclaration_DeclarationWithoutShareName_Succeeds() + { + // Arrange + var rawDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(shareName: 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 DataShareMetricsDeclaration_DeclarationWithoutResourceAndResourceDiscoveryGroupInfo_Fails() + { + // Arrange + var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(omitResource: true) + .Build(Mapper); + var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawMetricsDeclaration, Mapper); + + // Act + var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance); + var validationResult = scrapingScheduleValidationStep.Run(); + + // Assert + PromitorAssert.ValidationFailed(validationResult); + } + + [Fact] + public void DataShareMetricsDeclaration_DeclarationWithoutResourceButWithResourceDiscoveryGroupInfo_Succeeds() + { + // Arrange + var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric(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 DataShareMetricsDeclaration_ValidDeclaration_Succeeds() + { + // Arrange + var rawMetricsDeclaration = MetricsDeclarationBuilder.WithMetadata() + .WithDataShareMetric() + .Build(Mapper); + var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawMetricsDeclaration, Mapper); + + // Act + var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider, NullLogger.Instance); + var validationResult = scrapingScheduleValidationStep.Run(); + + // Assert + PromitorAssert.ValidationIsSuccessful(validationResult); + } + } +} \ No newline at end of file