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

Metric deserialization refactor #439

Merged
9 changes: 5 additions & 4 deletions adding-a-new-scraper.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ This guide walks you through the process of adding a new scraper type.
-------------------------

## Configuration
1. Add your new scraping type to `ResourceType`
2. Describe what your configuration by creating `<New-Type>MetricDefinition`and inherit from `MetricDefinition`
3. Update the deserialization to support your new type in `MetricsDeserializer`
4. Provide a unit test that tests the deserialization based on our sample
1. Add your new scraping type to the `Promitor.Core.Scraping.Configuration.Model.ResourceType`
2. Describe the resource for which you're creating scraping metrics by creating `<New-Type>MetricDefinition`and inherit from `Promitor.Core.Scraping.Configuration.Model.Metrics.MetricDefinition` - this class should go in `.\src\Promitor.Core.Scraping\Configuration\Model\Metrics\ResourceTypes`
3. Create a new Deserializer in `.\src\Promitor.Core.Scraping\Configuration\Serialization\Deserializers`. This must inherit from `AzureMetricDeserializer`.
3. Update `Promitor.Core.Scraping.Factories.MetricDeserializerFactory` to handle your new resource type by returning a new instance of the Deserializer you created in the previous step.
4. Provide a unit test in `.\src\Promitor.Scraper.Tests.Unit\Serialization\MetricsDeclaration\` that tests the deserialization based on our sample

Going forward in this guide, `TMetricDefinition` will refer to your newly created configuration type.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Promitor.Core.Scraping.Configuration.Model.Metrics
{
public class MetricDefinition
public abstract class MetricDefinition
{
/// <summary>
/// Configuration about the Azure Monitor metric to scrape
Expand All @@ -20,6 +20,6 @@ public class MetricDefinition
/// <summary>
/// Type of resource that is configured
/// </summary>
public virtual ResourceType ResourceType { get; set; }
public abstract ResourceType ResourceType { get; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes
{
public class GenericMetricDefinition : MetricDefinition
public class GenericAzureMetricDefinition : MetricDefinition
{
public string Filter { get; set; }
public override ResourceType ResourceType { get; set; } = ResourceType.Generic;
public override ResourceType ResourceType { get; } = ResourceType.Generic;
public string ResourceUri { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ public class ServiceBusQueueMetricDefinition : MetricDefinition
{
public string Namespace { get; set; }
public string QueueName { get; set; }
public override ResourceType ResourceType { get; set; } = ResourceType.ServiceBusQueue;
public override ResourceType ResourceType { get; } = ResourceType.ServiceBusQueue;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
namespace Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes
{
public class StorageQueueMetricDefinition: MetricDefinition
public class StorageQueueMetricDefinition : MetricDefinition
{
public string AccountName { get; set; }
public string QueueName { get; set; }
public string SasToken { get; set; }
public override ResourceType ResourceType { get; set; } = ResourceType.StorageQueue;
public override ResourceType ResourceType { get; } = ResourceType.StorageQueue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using GuardNet;
using Microsoft.Extensions.Logging;
using Promitor.Core.Scraping.Configuration.Model.Metrics;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization.Core
{
internal abstract class MetricDeserializer
{
protected ILogger Logger { get; private set; }

internal MetricDeserializer WithLogger(ILogger logger)
{
Guard.NotNull(logger, nameof(logger));

Logger = logger;
return this;
}

internal abstract MetricDefinition Deserialize(YamlMappingNode metricNode);

protected virtual TMetricDefinition DeserializeMetricDefinition<TMetricDefinition>(YamlMappingNode metricNode)
where TMetricDefinition : MetricDefinition, new()
{
Guard.NotNull(metricNode, nameof(metricNode));

var name = metricNode.Children[new YamlScalarNode("name")];
var description = metricNode.Children[new YamlScalarNode("description")];
var azureMetricConfigurationNode = (YamlMappingNode)metricNode.Children[new YamlScalarNode("azureMetricConfiguration")];

var azureMetricConfigurationDeserializer = new AzureMetricConfigurationDeserializer(Logger);
var azureMetricConfiguration = azureMetricConfigurationDeserializer.Deserialize(azureMetricConfigurationNode);

var metricDefinition = new TMetricDefinition
{
Name = name?.ToString(),
Description = description?.ToString(),
AzureMetricConfiguration = azureMetricConfiguration
};

return metricDefinition;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using Microsoft.Extensions.Logging;
using Promitor.Core.Scraping.Configuration.Model;
using Promitor.Core.Scraping.Configuration.Model.Metrics;
using Promitor.Core.Scraping.Factories;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization
{
internal class MetricsDeserializer : Deserializer<MetricDefinition>
{
internal MetricsDeserializer(ILogger logger) : base(logger)
{
}

internal override MetricDefinition Deserialize(YamlMappingNode node)
{
var rawResourceType = node.Children[new YamlScalarNode("resourceType")];

if (Enum.TryParse<ResourceType>(rawResourceType.ToString(), out var resourceType))
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved
{
return MetricDeserializerFactory
.GetDeserializerFor(resourceType)
.WithLogger(Logger)
.Deserialize(node);
}
else
{
throw new ArgumentException($@"Unknown 'resourceType' value in metric configuration: {rawResourceType.ToString()}");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Promitor.Core.Scraping.Configuration.Model.Metrics;
using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes;
using Promitor.Core.Scraping.Configuration.Serialization.Core;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization.Deserializers
{
internal class GenericAzureMetricDeserializer : MetricDeserializer
{
/// <summary>Deserializes the specified Generic Azure metric node from the YAML configuration file.</summary>
/// <param name="metricNode">The metric node containing 'filter' and 'resourceUri' parameters pointing to an arbitrary Azure resource</param>
/// <returns>A new <see cref="MetricDefinition"/> object (strongly typed as a <see cref="GenericAzureMetricDefinition"/>) </returns>
internal override MetricDefinition Deserialize(YamlMappingNode metricNode)
{
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved
var metricDefinition = base.DeserializeMetricDefinition<GenericAzureMetricDefinition>(metricNode);

if (metricNode.Children.TryGetValue(new YamlScalarNode("filter"), out var filterNode))
{
metricDefinition.Filter = filterNode?.ToString();
}

var resourceUri = metricNode.Children[new YamlScalarNode("resourceUri")];

metricDefinition.ResourceUri = resourceUri?.ToString();

return metricDefinition;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Promitor.Core.Scraping.Configuration.Model.Metrics;
using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization.Deserializers
{
internal class ServiceBusQueueMetricDeserializer : GenericAzureMetricDeserializer
{
/// <summary>Deserializes the specified Service Bus Queue metric node from the YAML configuration file.</summary>
/// <param name="metricNode">The metric node containing 'queueName' and 'namespace' parameters pointing to an instance of a Service Bus queue</param>
/// <returns>A new <see cref="MetricDefinition"/> object (strongly typed as a <see cref="ServiceBusQueueMetricDefinition"/>) </returns>
internal override MetricDefinition Deserialize(YamlMappingNode metricNode)
{
var metricDefinition = base.DeserializeMetricDefinition<ServiceBusQueueMetricDefinition>(metricNode);

var queueName = metricNode.Children[new YamlScalarNode("queueName")];
var namespaceName = metricNode.Children[new YamlScalarNode("namespace")];

metricDefinition.QueueName = queueName?.ToString();
metricDefinition.Namespace = namespaceName?.ToString();

return metricDefinition;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Promitor.Core.Scraping.Configuration.Model.Metrics;
using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization.Deserializers
{
internal class StorageQueueMetricDeserializer : GenericAzureMetricDeserializer
{
/// <summary>Deserializes the specified Storage Queue metric node from the YAML configuration file.</summary>
/// <param name="metricNode">The metric node containing 'accountName', 'queueName', and 'sasToken' parameters pointing to an instance of a Storage queue</param>
/// <returns>A new <see cref="MetricDefinition"/> object (strongly typed as a <see cref="StorageQueueMetricDefinition"/>) </returns>
internal override MetricDefinition Deserialize(YamlMappingNode metricNode)
{
var metricDefinition = base.DeserializeMetricDefinition<StorageQueueMetricDefinition>(metricNode);
var accountName = metricNode.Children[new YamlScalarNode("accountName")];
var queueName = metricNode.Children[new YamlScalarNode("queueName")];
var sasToken = metricNode.Children[new YamlScalarNode("sasToken")];

metricDefinition.AccountName = accountName?.ToString();
metricDefinition.QueueName = queueName?.ToString();
metricDefinition.SasToken = sasToken?.ToString();

return metricDefinition;
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using Promitor.Core.Scraping.Configuration.Serialization.Deserializers;

namespace Promitor.Core.Scraping.Factories
{
internal static class MetricDeserializerFactory
{
internal static GenericAzureMetricDeserializer GetDeserializerFor(Configuration.Model.ResourceType resource)
{
switch (resource)
{
case Configuration.Model.ResourceType.Generic:
return new GenericAzureMetricDeserializer();
case Configuration.Model.ResourceType.ServiceBusQueue:
return new ServiceBusQueueMetricDeserializer();
case Configuration.Model.ResourceType.StorageQueue:
return new StorageQueueMetricDeserializer();
}

throw new ArgumentOutOfRangeException($@"Resource Type {resource} not supported.");
}
}
}
4 changes: 2 additions & 2 deletions src/Promitor.Core.Scraping/ResourceTypes/GenericScraper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Promitor.Core.Scraping.ResourceTypes
{
internal class GenericScraper : Scraper<GenericMetricDefinition>
internal class GenericScraper : Scraper<GenericAzureMetricDefinition>
{
private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/{2}";

Expand All @@ -18,7 +18,7 @@ public GenericScraper(AzureMetadata azureMetadata, MetricDefaults metricDefaults
{
}

protected override async Task<double> ScrapeResourceAsync(GenericMetricDefinition metricDefinition, AggregationType aggregationType, TimeSpan aggregationInterval)
protected override async Task<double> ScrapeResourceAsync(GenericAzureMetricDefinition metricDefinition, AggregationType aggregationType, TimeSpan aggregationInterval)
{
var resourceUri = string.Format(ResourceUriTemplate, AzureMetadata.SubscriptionId, AzureMetadata.ResourceGroupName, metricDefinition.ResourceUri);
var metricName = metricDefinition.AzureMetricConfiguration.MetricName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public List<string> Validate(MetricDefinition metric)
break;
case ResourceType.Generic:
var genericMetricDefinition = new GenericMetricValidator();
metricDefinitionValidationErrors = genericMetricDefinition.Validate(metric as GenericMetricDefinition);
metricDefinitionValidationErrors = genericMetricDefinition.Validate(metric as GenericAzureMetricDefinition);
break;
case ResourceType.StorageQueue:
var azureStorageQueueMetricValidator = new StorageQueueMetricValidator();
Expand Down
Loading