Skip to content

Commit

Permalink
Support for Azure Database for PostgreSQL resource type (#596)
Browse files Browse the repository at this point in the history
* add configuration for PostgreSql scraper

* add validation for PostgreSQL scraper

* add scraper definition for PostgreSql

* add documentation for PostgreSQL scraper
  • Loading branch information
jcorioland authored and tomkerkhove committed Jun 21, 2019
1 parent cdeeb08 commit 07cb659
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/configuration/metrics/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ 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 Database for PostgreSQL](postgresql)
- [Azure Network Interface](network-interface)
- [Azure Service Bus Queue](service-bus-queue)
- [Azure Storage Queue](storage-queue)
Expand Down
30 changes: 30 additions & 0 deletions docs/configuration/metrics/postgresql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
layout: default
title: Azure Database for PostgreSQL
---

## Azure Database for PostgreSQL - ![Availability Badge](https://img.shields.io/badge/Available%20Starting-v1.0.0-green.svg)
You can declare to scrape an Azure Database for PostgreSQL server via the `PostgreSql` resource type.

The following fields need to be provided:
- `serverName` - The name of the PostgreSQL server

All supported metrics are documented in the official [Azure Monitor documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-supported#microsoftdbforpostgresqlservers).

Example:
```yaml
name: postgre_sql_cpu_percent
description: "The CPU percentage on the server"
resourceType: PostgreSql
serverName: Promitor
scraping:
schedule: "0 */2 * ? * *"
azureMetricConfiguration:
metricName: cpu_percent
aggregation:
type: Average
interval: 00:01:00
```
[&larr; back to metrics declarations](/configuration/metrics)<br />
[&larr; back to introduction](/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes
{
public class PostgreSqlMetricDefinition : MetricDefinition
{
public string ServerName { get; set; }
public override ResourceType ResourceType { get; } = ResourceType.PostgreSql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public enum ResourceType
NetworkInterface = 7,
CosmosDb = 8,
RedisCache = 9,
PostgreSql = 10,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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
{
/// <summary>
/// Defines a deserializer for the PostgreSQL Server resource type
/// </summary>
internal class PostgreSqlMetricDeserializer : GenericAzureMetricDeserializer
{
/// <summary>
/// Deserializes the specified PostgreSQL Server metric node from the YAML configuration file.
/// </summary>
/// <param name="metricNode">The metric node to deserialize to PostgreSQL Server configuration</param>
/// <returns>A new <see cref="MetricDefinition"/> object (strongly typed as a <see cref="PostgreSqlMetricDefinition"/>) </returns>
internal override MetricDefinition Deserialize(YamlMappingNode metricNode)
{
var metricDefinition = base.DeserializeMetricDefinition<PostgreSqlMetricDefinition>(metricNode);

var serverName = metricNode.Children[new YamlScalarNode("serverName")];
metricDefinition.ServerName = serverName?.ToString();

return metricDefinition;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ internal static GenericAzureMetricDeserializer GetDeserializerFor(Configuration.
case Configuration.Model.ResourceType.CosmosDb:
return new CosmosDbMetricDeserializer();
case Configuration.Model.ResourceType.RedisCache:
return new RedisCacheMetricDeserializer();
return new RedisCacheMetricDeserializer();
case Configuration.Model.ResourceType.PostgreSql:
return new PostgreSqlMetricDeserializer();
}

throw new ArgumentOutOfRangeException($@"Resource Type {resource} not supported.");
Expand Down
4 changes: 3 additions & 1 deletion src/Promitor.Core.Scraping/Factories/MetricScraperFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ public static IScraper<MetricDefinition> CreateScraper(ResourceType metricDefini
case ResourceType.CosmosDb:
return new CosmosDbScraper(azureMetadata, azureMonitorClient, logger, exceptionTracker);
case ResourceType.RedisCache:
return new RedisCacheScraper(azureMetadata, azureMonitorClient, logger, exceptionTracker);
return new RedisCacheScraper(azureMetadata, azureMonitorClient, logger, exceptionTracker);
case ResourceType.PostgreSql:
return new PostgreSqlScraper(azureMetadata, azureMonitorClient, logger, exceptionTracker);
default:
throw new ArgumentOutOfRangeException();
}
Expand Down
31 changes: 31 additions & 0 deletions src/Promitor.Core.Scraping/ResourceTypes/PostgreSqlScraper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.Azure.Management.Monitor.Fluent.Models;
using Microsoft.Extensions.Logging;
using Promitor.Core.Scraping.Configuration.Model;
using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes;
using Promitor.Core.Telemetry.Interfaces;
using Promitor.Integrations.AzureMonitor;
using System;
using System.Threading.Tasks;

namespace Promitor.Core.Scraping.ResourceTypes
{
public class PostgreSqlScraper : Scraper<PostgreSqlMetricDefinition>
{
private const string ResourceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DBforPostgreSQL/servers/{2}";

public PostgreSqlScraper(AzureMetadata azureMetadata, AzureMonitorClient azureMonitorClient, ILogger logger, IExceptionTracker exceptionTracker)
: base(azureMetadata, azureMonitorClient, logger, exceptionTracker)
{
}

protected override async Task<ScrapeResult> ScrapeResourceAsync(string subscriptionId, string resourceGroupName, PostgreSqlMetricDefinition metricDefinition, AggregationType aggregationType, TimeSpan aggregationInterval)
{
var resourceUri = string.Format(ResourceUriTemplate, subscriptionId, resourceGroupName, metricDefinition.ServerName);

var metricName = metricDefinition.AzureMetricConfiguration.MetricName;
var foundMetricValue = await AzureMonitorClient.QueryMetricAsync(metricName, aggregationType, aggregationInterval, resourceUri);

return new ScrapeResult(resourceUri, foundMetricValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ internal static IMetricValidator GetValidatorFor(ResourceType resourceType)
case ResourceType.CosmosDb:
return new CosmosDbMetricValidator();
case ResourceType.RedisCache:
return new RedisCacheMetricValidator();
return new RedisCacheMetricValidator();
case ResourceType.PostgreSql:
return new PostgreSqlMetricValidator();
}

throw new ArgumentOutOfRangeException(nameof(resourceType), $"No validation rules are defined for metric type '{resourceType}'");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using GuardNet;
using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes;
using System.Collections.Generic;

namespace Promitor.Scraper.Host.Validation.MetricDefinitions.ResourceTypes
{
internal class PostgreSqlMetricValidator : MetricValidator<PostgreSqlMetricDefinition>
{
protected override IEnumerable<string> Validate(PostgreSqlMetricDefinition postgreSqlMetricDefinition)
{
Guard.NotNull(postgreSqlMetricDefinition, nameof(postgreSqlMetricDefinition));

if (string.IsNullOrWhiteSpace(postgreSqlMetricDefinition.ServerName))
{
yield return "No server name is configured";
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,20 @@ private AzureMetricConfiguration CreateAzureMetricConfiguration(string azureMetr

return this;
}

public MetricsDeclarationBuilder WithPostgreSqlMetric(string metricName = "promitor-postgresql", string metricDescription = "Description for a metric", string serverName = "promitor-postgresql", string azureMetricName = "cpu_percent")
{
var azureMetricConfiguration = CreateAzureMetricConfiguration(azureMetricName);
var metric = new PostgreSqlMetricDefinition
{
Name = metricName,
Description = metricDescription,
ServerName = serverName,
AzureMetricConfiguration = azureMetricConfiguration
};
_metrics.Add(metric);

return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Bogus;
using Microsoft.Extensions.Logging.Abstractions;
using Promitor.Core.Scraping.Configuration.Model.Metrics.ResourceTypes;
using Promitor.Core.Scraping.Configuration.Serialization.Core;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Xunit;
using MetricDefinition = Promitor.Core.Scraping.Configuration.Model.Metrics.MetricDefinition;

namespace Promitor.Scraper.Tests.Unit.Serialization.MetricsDeclaration
{
[Category("Unit")]
public class MetricsDeclarationWithPostgreSqlYamlSerializationTests : YamlSerializationTests<PostgreSqlMetricDefinition>
{
[Theory]
[InlineData("promitor1", @"* */1 * * * *", @"* */2 * * * *")]
[InlineData(null, null, null)]
public void YamlSerialization_SerializeAndDeserializeConfigForPostgreSql_SucceedsWithIdenticalOutput(string resourceGroupName, string defaultScrapingInterval, string metricScrapingInterval)
{
// Arrange
var azureMetadata = GenerateBogusAzureMetadata();
var postgreSqlMetricDefinition = GenerateBogusPostgreSqlMetricDefinition(resourceGroupName, metricScrapingInterval);
var metricDefaults = GenerateBogusMetricDefaults(defaultScrapingInterval);
var scrapingConfiguration = new Core.Scraping.Configuration.Model.MetricsDeclaration()
{
AzureMetadata = azureMetadata,
MetricDefaults = metricDefaults,
Metrics = new List<MetricDefinition>()
{
postgreSqlMetricDefinition
}
};
var configurationSerializer = new ConfigurationSerializer(NullLogger.Instance);

// Act
var serializedConfiguration = configurationSerializer.Serialize(scrapingConfiguration);
var deserializedConfiguration = configurationSerializer.Deserialize(serializedConfiguration);

// Assert
Assert.NotNull(deserializedConfiguration);
AssertAzureMetadata(deserializedConfiguration, azureMetadata);
AssertMetricDefaults(deserializedConfiguration, metricDefaults);
Assert.NotNull(deserializedConfiguration.Metrics);
Assert.Single(deserializedConfiguration.Metrics);
var deserializedMetricDefinition = deserializedConfiguration.Metrics.FirstOrDefault();
AssertMetricDefinition(deserializedMetricDefinition, postgreSqlMetricDefinition);
var deserializedPostgreSqlMetricDefinition = deserializedMetricDefinition as PostgreSqlMetricDefinition;
AssertPostgreSqlMetricDefinition(deserializedPostgreSqlMetricDefinition, postgreSqlMetricDefinition);
}

private static void AssertPostgreSqlMetricDefinition(PostgreSqlMetricDefinition deserializedPostgreSqlMetricDefinition, PostgreSqlMetricDefinition postgreSqlMetricDefinition)
{
Assert.NotNull(deserializedPostgreSqlMetricDefinition);
Assert.Equal(postgreSqlMetricDefinition.ServerName, deserializedPostgreSqlMetricDefinition.ServerName);
}

private PostgreSqlMetricDefinition GenerateBogusPostgreSqlMetricDefinition(string resourceGroupName, string metricScrapingInterval)
{
var bogusScrapingInterval = GenerateBogusScrapingInterval(metricScrapingInterval);
var bogusAzureMetricConfiguration = GenerateBogusAzureMetricConfiguration();

var bogusGenerator = new Faker<PostgreSqlMetricDefinition>()
.StrictMode(ensureRulesForAllProperties: true)
.RuleFor(metricDefinition => metricDefinition.Name, faker => faker.Lorem.Word())
.RuleFor(metricDefinition => metricDefinition.Description, faker => faker.Lorem.Sentence(wordCount: 6))
.RuleFor(metricDefinition => metricDefinition.ResourceType, faker => Core.Scraping.Configuration.Model.ResourceType.PostgreSql)
.RuleFor(metricDefinition => metricDefinition.ServerName, faker => faker.Lorem.Word())
.RuleFor(metricDefinition => metricDefinition.AzureMetricConfiguration, faker => bogusAzureMetricConfiguration)
.RuleFor(metricDefinition => metricDefinition.ResourceGroupName, faker => resourceGroupName)
.RuleFor(metricDefinition => metricDefinition.Scraping, faker => bogusScrapingInterval)
.Ignore(metricDefinition => metricDefinition.ResourceGroupName);

return bogusGenerator.Generate();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Promitor.Scraper.Host.Validation.Steps;
using Promitor.Scraper.Tests.Unit.Builders;
using Promitor.Scraper.Tests.Unit.Stubs;
using System.ComponentModel;
using Xunit;

namespace Promitor.Scraper.Tests.Unit.Validation.Metrics.ResourceTypes
{
[Category("Unit")]
public class PostgreSqlMetricsDeclarationValidationStepTests
{
[Fact]
public void PostgreSqlMetricsDeclaration_DeclarationWithoutAzureMetricName_Fails()
{
// Arrange
var rawDeclaration = MetricsDeclarationBuilder.WithMetadata()
.WithPostgreSqlMetric(azureMetricName: string.Empty)
.Build();
var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration);

// Act
var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider);
var validationResult = scrapingScheduleValidationStep.Run();

// Assert
Assert.False(validationResult.IsSuccessful, "Validation is not successful");
}

[Fact]
public void PostgreSqlMetricsDeclaration_DeclarationWithoutAzureMetricDescription_Succeeds()
{
// Arrange
var rawDeclaration = MetricsDeclarationBuilder.WithMetadata()
.WithPostgreSqlMetric(metricDescription: string.Empty)
.Build();
var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration);

// Act
var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider);
var validationResult = scrapingScheduleValidationStep.Run();

// Assert
Assert.True(validationResult.IsSuccessful, "Validation is successful");
}

[Fact]
public void PostgreSqlMetricsDeclaration_DeclarationWithoutServerName_Fails()
{
// Arrange
var rawDeclaration = MetricsDeclarationBuilder.WithMetadata()
.WithPostgreSqlMetric(serverName: string.Empty)
.Build();
var metricsDeclarationProvider = new MetricsDeclarationProviderStub(rawDeclaration);

// Act
var scrapingScheduleValidationStep = new MetricsDeclarationValidationStep(metricsDeclarationProvider);
var validationResult = scrapingScheduleValidationStep.Run();

// Assert
Assert.False(validationResult.IsSuccessful, "Validation is not successful");
}
}
}

0 comments on commit 07cb659

Please sign in to comment.