Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide configuration & validation for using resource collection #1071

Merged
merged 7 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog/content/experimental/unreleased.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "(2018-09-15)"
date: 2018-09-02T20:46:47+02:00
weight: 1
version:
---

- {{% tag added %}} New validation rule to ensure at least one resource or resource collection is configured to scrape
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ azureLandscape:
- 0329dd2a-59dc-4493-aa54-cb01cb027dc2
- 0f9d7fea-99e8-4768-8672-06a28514f77e
resourceCollections:
- name: service-bus
- name: service-bus-landscape
type: microsoft.servicebus/namespaces
criteria:
subscriptions:
Expand Down
11 changes: 11 additions & 0 deletions config/promitor/scraper/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ metrics:
resources:
# filter is deliberately omitted given filter is optional
- resourceUri: Microsoft.ServiceBus/namespaces/promitor-messaging
- name: promitor_demo_servicebusqueue_queue_size_discovered
description: "Amount of active messages of the Service Bus queues"
resourceType: ServiceBusQueue
azureMetricConfiguration:
metricName: ActiveMessages
aggregation:
type: Average
# Optionally override the default aggregation interval (metricDefaults.aggregation.interval)
interval: 00:15:00
resourceCollections:
- name: orders service-bus-landscape
- name: promitor_demo_servicebusqueue_queue_size
description: "Amount of active messages of the 'orders' queue (determined with ServiceBusQueue provider)"
resourceType: ServiceBusQueue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,31 +58,39 @@ public static IServiceCollection ScheduleMetricScraping(this IServiceCollection

foreach (var metric in metrics.Metrics)
{
foreach (var resource in metric.Resources)
if (metric.ResourceCollections?.Any() == true)
{
var resourceSubscriptionId = string.IsNullOrWhiteSpace(resource.SubscriptionId) ? metrics.AzureMetadata.SubscriptionId : resource.SubscriptionId;
var azureMonitorClient = azureMonitorClientFactory.CreateIfNotExists(metrics.AzureMetadata.Cloud, metrics.AzureMetadata.TenantId, resourceSubscriptionId, metricSinkWriter, runtimeMetricCollector, configuration, azureMonitorLoggingConfiguration, loggerFactory);
var scrapeDefinition = metric.CreateScrapeDefinition(resource, metrics.AzureMetadata);
var jobName = GenerateJobName(scrapeDefinition, resource);
Console.WriteLine("Resource collections are not scraped yet.");
}

services.AddScheduler(builder =>
if (metric.Resources != null)
{
foreach (var resource in metric.Resources)
{
builder.AddJob(jobServices =>
{
return new MetricScrapingJob(jobName, scrapeDefinition,
metricSinkWriter,
jobServices.GetService<IPrometheusMetricWriter>(),
jobServices.GetService<MetricScraperFactory>(),
azureMonitorClient,
jobServices.GetService<ILogger<MetricScrapingJob>>());
}, schedulerOptions =>
var resourceSubscriptionId = string.IsNullOrWhiteSpace(resource.SubscriptionId) ? metrics.AzureMetadata.SubscriptionId : resource.SubscriptionId;
var azureMonitorClient = azureMonitorClientFactory.CreateIfNotExists(metrics.AzureMetadata.Cloud, metrics.AzureMetadata.TenantId, resourceSubscriptionId, metricSinkWriter, runtimeMetricCollector, configuration, azureMonitorLoggingConfiguration, loggerFactory);
var scrapeDefinition = metric.CreateScrapeDefinition(resource, metrics.AzureMetadata);
var jobName = GenerateJobName(scrapeDefinition, resource);

services.AddScheduler(builder =>
{
schedulerOptions.CronSchedule = scrapeDefinition.Scraping.Schedule;
schedulerOptions.RunImmediately = true;
},
jobName: jobName);
builder.UnobservedTaskExceptionHandler = (sender, exceptionEventArgs) => UnobservedJobHandlerHandler(sender, exceptionEventArgs, services);
});
builder.AddJob(jobServices =>
{
return new MetricScrapingJob(jobName, scrapeDefinition,
metricSinkWriter,
jobServices.GetService<IPrometheusMetricWriter>(),
jobServices.GetService<MetricScraperFactory>(),
azureMonitorClient,
jobServices.GetService<ILogger<MetricScrapingJob>>());
}, schedulerOptions =>
{
schedulerOptions.CronSchedule = scrapeDefinition.Scraping.Schedule;
schedulerOptions.RunImmediately = true;
},
jobName: jobName);
builder.UnobservedTaskExceptionHandler = (sender, exceptionEventArgs) => UnobservedJobHandlerHandler(sender, exceptionEventArgs, services);
});
}
}
}

Expand Down Expand Up @@ -129,6 +137,7 @@ public static IServiceCollection DefineDependencies(this IServiceCollection serv
services.AddSingleton<IDeserializer<MetricDimensionV1>, MetricDimensionDeserializer>();
services.AddSingleton<IDeserializer<ScrapingV1>, ScrapingDeserializer>();
services.AddSingleton<IDeserializer<AzureMetricConfigurationV1>, AzureMetricConfigurationDeserializer>();
services.AddSingleton<IDeserializer<AzureResourceCollectionDefinitionV1>, AzureResourceCollectionDeserializer>();
services.AddSingleton<IAzureResourceDeserializerFactory, AzureResourceDeserializerFactory>();
services.AddSingleton<IDeserializer<MetricAggregationV1>, MetricAggregationDeserializer>();
services.AddSingleton<IDeserializer<SecretV1>, SecretDeserializer>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,18 @@ private IEnumerable<string> Validate(MetricDefinition metric)
errorMessages.Add("No metric name is configured");
}

var metricDefinitionValidationErrors = MetricValidatorFactory
.GetValidatorFor(metric.ResourceType)
.Validate(metric);
if (metric.Resources?.Any() == false && metric.ResourceCollections?.Any() == false)
{
errorMessages.Add("No resource or resource collection is configured to be scraped");
}
else
{
var metricDefinitionValidationErrors = MetricValidatorFactory
.GetValidatorFor(metric.ResourceType)
.Validate(metric);

errorMessages.AddRange(metricDefinitionValidationErrors);
errorMessages.AddRange(metricDefinitionValidationErrors);
}

var metricAggregationValidator = new AzureMetricConfigurationValidator(_metricDefaults);
var metricsConfigurationErrorMessages = metricAggregationValidator.Validate(metric.AzureMetricConfiguration);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Promitor.Core.Scraping.Configuration.Model.Metrics
{
public class AzureResourceCollection
{
public string Name { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public MetricDefinition(PrometheusMetricDefinition prometheusMetricDefinition,
/// </summary>
public ResourceType ResourceType { get; set; }

/// <summary>
/// Gets or sets the list of resource collections to discover resources with.
/// </summary>
public List<AzureResourceCollection> ResourceCollections { get; set; }

/// <summary>
/// Gets or sets the list of resources to scrape.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.Extensions.Logging;
using Promitor.Core.Scraping.Configuration.Serialization.v1.Model;

namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core
{
public class AzureResourceCollectionDeserializer : Deserializer<AzureResourceCollectionDefinitionV1>
{
public AzureResourceCollectionDeserializer(ILogger<AzureResourceCollectionDeserializer> logger) : base(logger)
{
MapRequired(metadata => metadata.Name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@ namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core
public class MetricDefinitionDeserializer : Deserializer<MetricDefinitionV1>
{
private const string ResourcesTag = "resources";
private const string ResourceCollectionsTag = "resourceCollections";
private readonly IDeserializer<AzureResourceCollectionDefinitionV1> _azureResourceCollectionDeserializer;
private readonly IAzureResourceDeserializerFactory _azureResourceDeserializerFactory;

public MetricDefinitionDeserializer(IDeserializer<AzureMetricConfigurationV1> azureMetricConfigurationDeserializer,
IDeserializer<ScrapingV1> scrapingDeserializer,
IDeserializer<AzureResourceCollectionDefinitionV1> azureResourceCollectionDeserializer,
IAzureResourceDeserializerFactory azureResourceDeserializerFactory,
ILogger<MetricDefinitionDeserializer> logger) : base(logger)
{
_azureResourceCollectionDeserializer = azureResourceCollectionDeserializer;
_azureResourceDeserializerFactory = azureResourceDeserializerFactory;

MapRequired(definition => definition.Name);
MapRequired(definition => definition.Description);
MapRequired(definition => definition.ResourceType);
MapOptional(definition => definition.Labels);
MapRequired(definition => definition.AzureMetricConfiguration, azureMetricConfigurationDeserializer);
MapOptional(definition => definition.Labels);
MapOptional(definition => definition.Scraping, scrapingDeserializer);
IgnoreField(ResourceCollectionsTag);
IgnoreField(ResourcesTag);
}

Expand All @@ -49,6 +54,11 @@ private void DeserializeMetrics(YamlMappingNode node, MetricDefinitionV1 metricD
return;
}

if (node.Children.TryGetValue(ResourceCollectionsTag, out var resourceCollectionNode))
{
metricDefinition.ResourceCollections = _azureResourceCollectionDeserializer.Deserialize((YamlSequenceNode)resourceCollectionNode, errorReporter);
}

if (node.Children.TryGetValue(ResourcesTag, out var metricsNode))
{
var resourceDeserializer = _azureResourceDeserializerFactory.GetDeserializerFor(metricDefinition.ResourceType.Value);
Expand All @@ -61,10 +71,6 @@ private void DeserializeMetrics(YamlMappingNode node, MetricDefinitionV1 metricD
errorReporter.ReportError(resourceTypeNode, $"Could not find a deserializer for resource type '{metricDefinition.ResourceType}'.");
}
}
else
{
errorReporter.ReportError(node, "'resources' is a required field but was not found.");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public V1MappingProfile()
CreateMap<ScrapingV1, Configuration.Model.Scraping>();
CreateMap<AzureMetricConfigurationV1, AzureMetricConfiguration>();
CreateMap<MetricAggregationV1, MetricAggregation>();
CreateMap<AzureResourceCollectionDefinitionV1, AzureResourceCollection>();
CreateMap<SecretV1, Secret>();

CreateMap<ContainerInstanceResourceV1, ContainerInstanceResourceDefinition>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Model
{
/// <summary>
/// Represents a resource collection that represent a collection of Azure resource that are automatically discovered and will be scraped.
/// </summary>
public class AzureResourceCollectionDefinitionV1
{
public string Name { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public class MetricDefinitionV1
/// </summary>
public ScrapingV1 Scraping { get; set; }

/// <summary>
/// The resource collections to be scraped.
/// </summary>
public IReadOnlyCollection<AzureResourceCollectionDefinitionV1> ResourceCollections { get; set; }

/// <summary>
/// The resources to be scraped.
/// </summary>
Expand Down
Loading