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

Convert top level deserializers #2

Merged
merged 6 commits into from
Feb 12, 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
2 changes: 1 addition & 1 deletion docs/configuration/v1.x/metrics/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ Every metric that is being declared needs to define the following fields:
- `description` - Description for the metric that will be exposed in the scrape
endpoint for Prometheus.
- `resourceType` - Defines what type of resource needs to be queried.
- `labels` - Defines a set of custom labels to include for a given metric.
- `azureMetricConfiguration.metricName` - The name of the metric in Azure Monitor
to query
- `azureMetricConfiguration.aggregation.type` - The aggregation that needs to be
Expand All @@ -57,6 +56,7 @@ Additionally, the following fields are optional:
- ☝ *Promitor simply acts as a proxy and will not validate if it's supported or
not, we recommend verifying that the dimension is supported in the
[official documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-supported)*
- `labels` - Defines a set of custom labels to include for a given metric.
- `scraping.schedule` - A scraping schedule for the individual metric; overrides
the the one specified in `metricDefaults`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,42 @@ public List<TObject> Deserialize(YamlSequenceNode nodes, IErrorReporter errorRep
return deserializedObjects;
}

public object DeserializeObject(YamlMappingNode node, IErrorReporter errorReporter)
{
return Deserialize(node, errorReporter);
}

protected void MapRequired<TReturn>(Expression<Func<TObject, TReturn>> accessorExpression, Func<string, KeyValuePair<YamlNode, YamlNode>, IErrorReporter, object> customMapperFunc = null)
{
var memberExpression = (MemberExpression)accessorExpression.Body;
_fields[GetName(memberExpression)] = new FieldContext(memberExpression.Member as PropertyInfo, true, default(TReturn), customMapperFunc);
}

protected void MapRequired<TReturn>(
Expression<Func<TObject, TReturn>> accessorExpression, IDeserializer deserializer)
where TReturn: new()
{
var memberExpression = (MemberExpression)accessorExpression.Body;
_fields[GetName(memberExpression)] = new FieldContext(
memberExpression.Member as PropertyInfo, true, default(TReturn), null, deserializer);
}

protected void MapOptional<TReturn>(Expression<Func<TObject, TReturn>> accessorExpression, TReturn defaultValue = default, Func<string, KeyValuePair<YamlNode, YamlNode>, IErrorReporter, object> customMapperFunc = null)
protected void MapOptional<TReturn>(
Expression<Func<TObject, TReturn>> accessorExpression, TReturn defaultValue = default, Func<string, KeyValuePair<YamlNode, YamlNode>, IErrorReporter, object> customMapperFunc = null)
{
var memberExpression = (MemberExpression)accessorExpression.Body;
_fields[GetName(memberExpression)] = new FieldContext(memberExpression.Member as PropertyInfo, false, defaultValue, customMapperFunc);
}

protected void MapOptional<TReturn>(
Expression<Func<TObject, TReturn>> accessorExpression, IDeserializer deserializer)
where TReturn: new()
{
var memberExpression = (MemberExpression)accessorExpression.Body;
_fields[GetName(memberExpression)] = new FieldContext(
memberExpression.Member as PropertyInfo, false, default(TReturn), null, deserializer);
}

private static string GetName(MemberExpression memberExpression)
{
return Char.ToLowerInvariant(memberExpression.Member.Name[0]) + memberExpression.Member.Name.Substring(1);
Expand All @@ -102,6 +126,12 @@ private static object GetFieldValue(
return fieldContext.CustomMapperFunc(fieldNodePair.Value.ToString(), fieldNodePair, errorReporter);
}


if (fieldContext.Deserializer != null)
{
return fieldContext.Deserializer.DeserializeObject((YamlMappingNode)fieldNodePair.Value, errorReporter);
}

var propertyType = Nullable.GetUnderlyingType(fieldContext.PropertyInfo.PropertyType) ?? fieldContext.PropertyInfo.PropertyType;
if (propertyType.IsEnum)
{
Expand Down Expand Up @@ -146,19 +176,21 @@ private static object GetFieldValue(

private class FieldContext
{
public FieldContext(PropertyInfo propertyInfo, bool isRequired, object defaultValue, Func<string, KeyValuePair<YamlNode, YamlNode>, IErrorReporter, object> customMapperFunc)
public FieldContext(PropertyInfo propertyInfo, bool isRequired, object defaultValue, Func<string, KeyValuePair<YamlNode, YamlNode>, IErrorReporter, object> customMapperFunc, IDeserializer deserializer = null)
{
CustomMapperFunc = customMapperFunc;
PropertyInfo = propertyInfo;
IsRequired = isRequired;
DefaultValue = defaultValue;
Deserializer = deserializer;
}

public bool HasBeenSet { get; private set; }
public PropertyInfo PropertyInfo { get; }
public bool IsRequired { get; }
public object DefaultValue { get; }
public Func<string, KeyValuePair<YamlNode, YamlNode>, IErrorReporter, object> CustomMapperFunc { get; }
public IDeserializer Deserializer { get; }

public void SetValue(TObject target, object value)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,25 @@

namespace Promitor.Core.Scraping.Configuration.Serialization
{
/// <summary>
/// An object that can deserialize a yaml node into an object.
/// </summary>
public interface IDeserializer
{
/// <summary>
/// Deserializes the specified node.
/// </summary>
/// <param name="node">The node to deserialize.</param>
/// <param name="errorReporter">Used to report deserialization errors.</param>
/// <returns>The deserialized object.</returns>
object DeserializeObject(YamlMappingNode node, IErrorReporter errorReporter);
}

/// <summary>
/// An object that can deserialize a yaml node into an object.
/// </summary>
/// <typeparam name="TObject">The type of object that can be deserialized.</typeparam>
public interface IDeserializer<TObject> where TObject: new()
public interface IDeserializer<TObject> : IDeserializer where TObject: new()
{
/// <summary>
/// Deserializes the specified node.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,16 @@
using Microsoft.Extensions.Logging;
using Promitor.Core.Scraping.Configuration.Serialization.v1.Model;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core
{
public class AzureMetricConfigurationDeserializer : Deserializer<AzureMetricConfigurationV1>
{
private const string MetricNameTag = "metricName";
private const string DimensionTag = "dimension";
private const string AggregationTag = "aggregation";
private readonly IDeserializer<MetricDimensionV1> _dimensionDeserializer;
private readonly IDeserializer<MetricAggregationV1> _aggregationDeserializer;

public AzureMetricConfigurationDeserializer(IDeserializer<MetricDimensionV1> dimensionDeserializer, IDeserializer<MetricAggregationV1> aggregationDeserializer, ILogger<AzureMetricConfigurationDeserializer> logger)
: base(logger)
{
_dimensionDeserializer = dimensionDeserializer;
_aggregationDeserializer = aggregationDeserializer;
}

public override AzureMetricConfigurationV1 Deserialize(YamlMappingNode node, IErrorReporter errorReporter)
{
return new AzureMetricConfigurationV1
{
MetricName = node.GetString(MetricNameTag),
Dimension = DeserializeDimension(node, errorReporter),
Aggregation = DeserializeAggregation(node, errorReporter)
};
}

private MetricAggregationV1 DeserializeAggregation(YamlMappingNode node, IErrorReporter errorReporter)
{
if (node.Children.TryGetValue(AggregationTag, out var aggregationNode))
{
return _aggregationDeserializer.Deserialize((YamlMappingNode) aggregationNode, errorReporter);
}

return null;
}

private MetricDimensionV1 DeserializeDimension(YamlMappingNode node, IErrorReporter errorReporter)
{
if (node.Children.TryGetValue(DimensionTag, out var aggregationNode))
{
return _dimensionDeserializer.Deserialize((YamlMappingNode)aggregationNode, errorReporter);
}

return null;
MapRequired(config => config.MetricName);
MapRequired(config => config.Dimension, dimensionDeserializer);
MapRequired(config => config.Aggregation, aggregationDeserializer);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
using Microsoft.Azure.Management.Monitor.Fluent.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using Promitor.Core.Scraping.Configuration.Serialization.v1.Model;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core
{
public class MetricAggregationDeserializer : Deserializer<MetricAggregationV1>
{
private const string TypeTag = "type";
private const string IntervalTag = "interval";

public MetricAggregationDeserializer(ILogger<MetricAggregationDeserializer> logger) : base(logger)
{
}

public override MetricAggregationV1 Deserialize(YamlMappingNode node, IErrorReporter errorReporter)
{
var aggregationType = node.GetEnum<AggregationType>(TypeTag);

var interval = node.GetTimeSpan(IntervalTag);

return new MetricAggregationV1
{
Type = aggregationType,
Interval = interval
};
MapRequired(aggregation => aggregation.Type);
MapOptional(aggregation => aggregation.Interval);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
using Microsoft.Extensions.Logging;
using Promitor.Core.Scraping.Configuration.Serialization.v1.Model;
using YamlDotNet.RepresentationModel;

namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core
{
public class MetricDefaultsDeserializer : Deserializer<MetricDefaultsV1>
{
private const string AggregationTag = "aggregation";
private const string ScrapingTag = "scraping";

private readonly IDeserializer<AggregationV1> _aggregationDeserializer;
private readonly IDeserializer<ScrapingV1> _scrapingDeserializer;

public MetricDefaultsDeserializer(
IDeserializer<AggregationV1> aggregationDeserializer,
IDeserializer<ScrapingV1> scrapingDeserializer,
ILogger<MetricDefaultsDeserializer> logger) : base(logger)
{
_aggregationDeserializer = aggregationDeserializer;
_scrapingDeserializer = scrapingDeserializer;
}

public override MetricDefaultsV1 Deserialize(YamlMappingNode node, IErrorReporter errorReporter)
{
var defaults = new MetricDefaultsV1();

defaults.Aggregation = node.DeserializeChild(AggregationTag, _aggregationDeserializer, errorReporter);
defaults.Scraping = node.DeserializeChild(ScrapingTag, _scrapingDeserializer, errorReporter);

return defaults;
MapOptional(defaults => defaults.Aggregation, aggregationDeserializer);
MapRequired(defaults => defaults.Scraping, scrapingDeserializer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,76 +7,55 @@ namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core
{
public class MetricDefinitionDeserializer : Deserializer<MetricDefinitionV1>
{
private const string NameTag = "name";
private const string DescriptionTag = "description";
private const string ResourceTypeTag = "resourceType";
private const string LabelsTag = "labels";
private const string AzureMetricConfigurationTag = "azureMetricConfiguration";
private const string ScrapingTag = "scraping";
private const string ResourcesTag = "resources";

private readonly IDeserializer<AzureMetricConfigurationV1> _azureMetricConfigurationDeserializer;
private readonly IDeserializer<ScrapingV1> _scrapingDeserializer;
private readonly IAzureResourceDeserializerFactory _azureResourceDeserializerFactory;

public MetricDefinitionDeserializer(IDeserializer<AzureMetricConfigurationV1> azureMetricConfigurationDeserializer,
IDeserializer<ScrapingV1> scrapingDeserializer,
IAzureResourceDeserializerFactory azureResourceDeserializerFactory,
ILogger<MetricDefinitionDeserializer> logger) : base(logger)
{
_azureMetricConfigurationDeserializer = azureMetricConfigurationDeserializer;
_scrapingDeserializer = scrapingDeserializer;
_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.Scraping, scrapingDeserializer);
}

public override MetricDefinitionV1 Deserialize(YamlMappingNode node, IErrorReporter errorReporter)
{
var name = node.GetString(NameTag);
var description = node.GetString(DescriptionTag);
var resourceType = node.GetEnum<ResourceType>(ResourceTypeTag);
var labels = node.GetDictionary(LabelsTag);

var metricDefinition = new MetricDefinitionV1
{
Name = name,
Description = description,
ResourceType = resourceType,
Labels = labels
};
var metricDefinition = base.Deserialize(node, errorReporter);

DeserializeAzureMetricConfiguration(node, metricDefinition, errorReporter);
DeserializeScraping(node, metricDefinition, errorReporter);
DeserializeMetrics(node, metricDefinition, errorReporter);

return metricDefinition;
}

private void DeserializeAzureMetricConfiguration(YamlMappingNode node, MetricDefinitionV1 metricDefinition, IErrorReporter errorReporter)
private void DeserializeMetrics(YamlMappingNode node, MetricDefinitionV1 metricDefinition, IErrorReporter errorReporter)
{
if (node.Children.TryGetValue(AzureMetricConfigurationTag, out var configurationNode))
if (metricDefinition.ResourceType == null)
{
metricDefinition.AzureMetricConfiguration =
_azureMetricConfigurationDeserializer.Deserialize((YamlMappingNode) configurationNode, errorReporter);
return;
}
}

private void DeserializeScraping(YamlMappingNode node, MetricDefinitionV1 metricDefinition, IErrorReporter errorReporter)
{
if (node.Children.TryGetValue(ScrapingTag, out var scrapingNode))
if (metricDefinition.ResourceType == ResourceType.NotSpecified)
{
metricDefinition.Scraping = _scrapingDeserializer.Deserialize((YamlMappingNode)scrapingNode, errorReporter);
errorReporter.ReportError(node.Children["resourceType"], "'resourceType' must not be set to 'NotSpecified'.");
return;
}
}

private void DeserializeMetrics(YamlMappingNode node, MetricDefinitionV1 metricDefinition, IErrorReporter errorReporter)
{
if (metricDefinition.ResourceType != null &&
metricDefinition.ResourceType != ResourceType.NotSpecified &&
node.Children.TryGetValue(ResourcesTag, out var metricsNode))
if (node.Children.TryGetValue(ResourcesTag, out var metricsNode))
{
var resourceDeserializer = _azureResourceDeserializerFactory.GetDeserializerFor(metricDefinition.ResourceType.Value);
metricDefinition.Resources = resourceDeserializer.Deserialize((YamlSequenceNode)metricsNode, errorReporter);
}
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 @@ -6,24 +6,9 @@ namespace Promitor.Core.Scraping.Configuration.Serialization.v1.Core
{
public class ScrapingDeserializer : Deserializer<ScrapingV1>
{
private const string ScheduleTag = "schedule";

public ScrapingDeserializer(ILogger<ScrapingDeserializer> logger) : base(logger)
{
}

public override ScrapingV1 Deserialize(YamlMappingNode node, IErrorReporter errorReporter)
{
var scraping = new ScrapingV1();

scraping.Schedule = node.GetString(ScheduleTag);

if (scraping.Schedule == null)
{
Logger.LogError("No default metric scraping schedule was configured!");
}

return scraping;
MapRequired(scraping => scraping.Schedule);
}
}
}
Loading