Skip to content

Commit

Permalink
Provide sink validation for StatsD (#997)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkerkhove authored Apr 30, 2020
1 parent 135b49d commit 58aca6d
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/Promitor.Agents.ResourceDiscovery/Graph/Authentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public static async Task<ServiceClientCredentials> GetServiceClientCredentialsAs

AuthenticationResult authResult = await authContext.AcquireTokenAsync(resource, new ClientCredential(clientId, clientSecret));

ServiceClientCredentials serviceClientCreds = new TokenCredentials(authResult.AccessToken);
ServiceClientCredentials serviceClientCredentials = new TokenCredentials(authResult.AccessToken);

return serviceClientCreds;
return serviceClientCredentials;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ public AzureResourceGraphHealthCheck(AzureResourceGraph azureResourceGraph, IOpt
}

var healthProbeResults = await ProbeSubscriptionsAsync(query);
var healtCheckMetadata = GenerateMetadata(healthProbeResults);
var healthCheckMetadata = GenerateMetadata(healthProbeResults);

if (healthProbeResults.Any(probeKeyValuePair => probeKeyValuePair.IsSuccessful == false))
{
return HealthCheckResult.Unhealthy("One or more subscriptions are unhealthy.", data: healtCheckMetadata);
return HealthCheckResult.Unhealthy("One or more subscriptions are unhealthy.", data: healthCheckMetadata);
}

return HealthCheckResult.Healthy("Successfully queried all subscriptions", data: healtCheckMetadata);
return HealthCheckResult.Healthy("Successfully queried all subscriptions", data: healthCheckMetadata);
}

private static Dictionary<string, object> GenerateMetadata(List<HealthProbeResult> healthProbeResults)
Expand Down
4 changes: 2 additions & 2 deletions src/Promitor.Agents.Scraper/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public static IHostBuilder BuildWebHost(string[] args)
})
.UseConfiguration(configuration)
.UseUrls(endpointUrl)
.UseStartup<Startup>()
.UseSerilog((hostingContext, loggerConfiguration) => ConfigureSerilog(configuration, loggerConfiguration));
.UseSerilog((hostingContext, loggerConfiguration) => ConfigureSerilog(configuration, loggerConfiguration))
.UseStartup<Startup>();
});
}

Expand Down
16 changes: 9 additions & 7 deletions src/Promitor.Agents.Scraper/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
}

ValidateRuntimeConfiguration(app);


app.UsePrometheusScraper(_prometheusBaseUriPath)
.ExposeOpenApiUi()
.UseSerilogRequestLogging()
Expand All @@ -53,15 +51,19 @@ public void ConfigureServices(IServiceCollection services)
.DefineDependencies()
.ConfigureYamlConfiguration(Configuration)
.UseOpenApiSpecifications(_prometheusBaseUriPath, 1)
.UseMetricSinks(Configuration)
.UseHealthChecks()
.UseHealthChecks();

ValidateRuntimeConfiguration(services);

services.UseMetricSinks(Configuration)
.ScheduleMetricScraping()
.UseWebApi();
}

private void ValidateRuntimeConfiguration(IApplicationBuilder app)
private void ValidateRuntimeConfiguration(IServiceCollection services)
{
var runtimeValidator = app.ApplicationServices.GetService<RuntimeValidator>();
var serviceProvider = services.BuildServiceProvider();
var runtimeValidator = serviceProvider.GetService<RuntimeValidator>();
runtimeValidator.Run();
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/Promitor.Agents.Scraper/Validation/RuntimeValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Promitor.Agents.Scraper.Validation.Exceptions;
using Promitor.Agents.Scraper.Validation.Interfaces;
using Promitor.Agents.Scraper.Validation.Steps;
using Promitor.Agents.Scraper.Validation.Steps.Sinks;
using Promitor.Core.Configuration.Model;

#pragma warning disable 618

Expand All @@ -19,6 +21,7 @@ public class RuntimeValidator
private readonly List<IValidationStep> _validationSteps;

public RuntimeValidator(
IOptions<RuntimeConfiguration> runtimeConfiguration,
IOptions<MetricsConfiguration> metricsConfiguration,
ILogger<RuntimeValidator> validatorLogger,
IMetricsDeclarationProvider scrapeConfigurationProvider,
Expand All @@ -30,7 +33,8 @@ public RuntimeValidator(
{
new ConfigurationPathValidationStep(metricsConfiguration, _validationLogger),
new AzureAuthenticationValidationStep(configuration, _validationLogger),
new MetricsDeclarationValidationStep(scrapeConfigurationProvider, _validationLogger)
new MetricsDeclarationValidationStep(scrapeConfigurationProvider, _validationLogger),
new StatsDMetricSinkValidationStep(runtimeConfiguration, _validationLogger)
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Promitor.Agents.Scraper.Validation.Interfaces;
using Promitor.Core.Configuration.Model;

namespace Promitor.Agents.Scraper.Validation.Steps.Sinks
{
public class StatsDMetricSinkValidationStep : ValidationStep, IValidationStep
{
private readonly IOptions<RuntimeConfiguration> _runtimeConfiguration;

public StatsDMetricSinkValidationStep(IOptions<RuntimeConfiguration> runtimeConfiguration)
: this(runtimeConfiguration, NullLogger.Instance)
{
}

public StatsDMetricSinkValidationStep(IOptions<RuntimeConfiguration> runtimeConfiguration, ILogger validationLogger)
: base(validationLogger)
{
_runtimeConfiguration = runtimeConfiguration;
}

public string ComponentName { get; } = "StatsD Metric Sink";

public ValidationResult Run()
{
var currentRuntimeConfiguration = _runtimeConfiguration.Value;
var statsDConfiguration = currentRuntimeConfiguration?.MetricSinks?.Statsd;
if (statsDConfiguration == null)
{
return ValidationResult.Successful(ComponentName);
}

if (string.IsNullOrWhiteSpace(statsDConfiguration.Host))
{
var errorMessage = "No host of StatsD server is configured";
return ValidationResult.Failure(ComponentName, errorMessage);
}

if (statsDConfiguration.Port <= 0)
{
var errorMessage = $"StatsD port {statsDConfiguration.Port} is not allowed";
return ValidationResult.Failure(ComponentName, errorMessage);
}

return ValidationResult.Successful(ComponentName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ public async Task RuntimeConfiguration_IsFullyConfigured_UsesCorrectValues()
// Assert
Assert.NotNull(runtimeConfiguration);
Assert.NotNull(runtimeConfiguration.Server);
Assert.NotNull(runtimeConfiguration.MetricSinks);
Assert.NotNull(runtimeConfiguration.MetricSinks.Statsd);
Assert.NotNull(runtimeConfiguration.Telemetry);
Assert.NotNull(runtimeConfiguration.Telemetry.ApplicationInsights);
Assert.NotNull(runtimeConfiguration.Telemetry.ContainerLogs);
Expand All @@ -411,6 +413,9 @@ public async Task RuntimeConfiguration_IsFullyConfigured_UsesCorrectValues()
Assert.Equal(bogusRuntimeConfiguration.Prometheus.MetricUnavailableValue, runtimeConfiguration.Prometheus.MetricUnavailableValue);
Assert.Equal(bogusRuntimeConfiguration.Prometheus.ScrapeEndpoint.BaseUriPath, runtimeConfiguration.Prometheus.ScrapeEndpoint.BaseUriPath);
Assert.Equal(bogusRuntimeConfiguration.MetricsConfiguration.AbsolutePath, runtimeConfiguration.MetricsConfiguration.AbsolutePath);
Assert.Equal(bogusRuntimeConfiguration.MetricSinks.Statsd.Host, runtimeConfiguration.MetricSinks.Statsd.Host);
Assert.Equal(bogusRuntimeConfiguration.MetricSinks.Statsd.Port, runtimeConfiguration.MetricSinks.Statsd.Port);
Assert.Equal(bogusRuntimeConfiguration.MetricSinks.Statsd.MetricPrefix, runtimeConfiguration.MetricSinks.Statsd.MetricPrefix);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Promitor.Core.Configuration.Model.Metrics;
using Promitor.Core.Configuration.Model.Prometheus;
using Promitor.Core.Configuration.Model.Server;
using Promitor.Core.Configuration.Model.Sinks;
using Promitor.Core.Configuration.Model.Telemetry;
using Promitor.Core.Configuration.Model.Telemetry.Sinks;

Expand Down Expand Up @@ -51,13 +52,24 @@ internal static RuntimeConfiguration Generate()
.RuleFor(telemetry => telemetry.ContainerLogs, faker => containerLogConfiguration)
.RuleFor(telemetry => telemetry.ApplicationInsights, faker => applicationInsightsConfiguration)
.Generate();
var statsDConfiguration = new Faker<StatsdSinkConfiguration>()
.StrictMode(true)
.RuleFor(statsdSinkConfiguration => statsdSinkConfiguration.Host, faker => faker.Person.FirstName)
.RuleFor(statsdSinkConfiguration => statsdSinkConfiguration.Port, faker => faker.Random.Int(min: 0))
.RuleFor(statsdSinkConfiguration => statsdSinkConfiguration.MetricPrefix, faker => faker.Person.FirstName)
.Generate();
var metricSinkConfiguration = new Faker<MetricSinkConfiguration>()
.StrictMode(true)
.RuleFor(sinkConfiguration => sinkConfiguration.Statsd, statsDConfiguration)
.Generate();

var runtimeConfiguration = new RuntimeConfiguration
{
Server = serverConfiguration,
MetricsConfiguration = metricsConfiguration,
Prometheus = prometheusConfiguration,
Telemetry = telemetryConfiguration
Telemetry = telemetryConfiguration,
MetricSinks = metricSinkConfiguration
};

return runtimeConfiguration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,18 @@ public async Task<IConfiguration> GenerateAsync()
}
}

if (_runtimeConfiguration?.MetricSinks != null)
{
configurationBuilder.AppendLine("metricSinks:");
if (_runtimeConfiguration?.MetricSinks.Statsd != null)
{
configurationBuilder.AppendLine(" statsD:");
configurationBuilder.AppendLine($" host: {_runtimeConfiguration?.MetricSinks.Statsd.Host}");
configurationBuilder.AppendLine($" port: {_runtimeConfiguration?.MetricSinks.Statsd.Port}");
configurationBuilder.AppendLine($" metricPrefix: {_runtimeConfiguration?.MetricSinks.Statsd.MetricPrefix}");
}
}

if (_runtimeConfiguration?.MetricsConfiguration != null)
{
configurationBuilder.AppendLine("metricsConfiguration:");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System.ComponentModel;
using Microsoft.Extensions.Options;
using Promitor.Agents.Scraper.Validation.Steps.Sinks;
using Promitor.Core.Configuration.Model;
using Promitor.Tests.Unit.Generators.Config;
using Xunit;

namespace Promitor.Tests.Unit.Validation.Metrics.Sinks
{
[Category("Unit")]
public class StatsDMetricSinkValidationStepTests
{
[Fact]
public void Validate_StatsDIsFullyConfigured_Success()
{
// Arrange
var runtimeConfiguration = CreateRuntimeConfiguration();

// Act
var azureAuthenticationValidationStep = new StatsDMetricSinkValidationStep(runtimeConfiguration);
var validationResult = azureAuthenticationValidationStep.Run();

// Assert
Assert.True(validationResult.IsSuccessful);
}

[Fact]
public void Validate_StatsDIsNotConfigured_Success()
{
// Arrange
var runtimeConfiguration = CreateRuntimeConfiguration();
runtimeConfiguration.Value.MetricSinks.Statsd = null;

// Act
var azureAuthenticationValidationStep = new StatsDMetricSinkValidationStep(runtimeConfiguration);
var validationResult = azureAuthenticationValidationStep.Run();

// Assert
Assert.True(validationResult.IsSuccessful);
}
[Fact]
public void Validate_NoSinksConfigured_Success()
{
// Arrange
var runtimeConfiguration = CreateRuntimeConfiguration();
runtimeConfiguration.Value.MetricSinks = null;

// Act
var azureAuthenticationValidationStep = new StatsDMetricSinkValidationStep(runtimeConfiguration);
var validationResult = azureAuthenticationValidationStep.Run();

// Assert
Assert.True(validationResult.IsSuccessful);
}

[Fact]
public void Validate_StatsDWithNegativePort_Fails()
{
// Arrange
var runtimeConfiguration = CreateRuntimeConfiguration();
runtimeConfiguration.Value.MetricSinks.Statsd.Port = -1;

// Act
var azureAuthenticationValidationStep = new StatsDMetricSinkValidationStep(runtimeConfiguration);
var validationResult = azureAuthenticationValidationStep.Run();

// Assert
Assert.False(validationResult.IsSuccessful);
}

[Theory]
[InlineData("")]
[InlineData(null)]
public void Validate_StatsDWithoutHost_Fails(string host)
{
// Arrange
var runtimeConfiguration = CreateRuntimeConfiguration();
runtimeConfiguration.Value.MetricSinks.Statsd.Host = host;

// Act
var azureAuthenticationValidationStep = new StatsDMetricSinkValidationStep(runtimeConfiguration);
var validationResult = azureAuthenticationValidationStep.Run();

// Assert
Assert.False(validationResult.IsSuccessful);
}

[Theory]
[InlineData("")]
[InlineData(null)]
public void Validate_StatsDWithoutMetricPrefix_Succeeds(string metricPrefix)
{
// Arrange
var runtimeConfiguration = CreateRuntimeConfiguration();
runtimeConfiguration.Value.MetricSinks.Statsd.MetricPrefix = metricPrefix;

// Act
var azureAuthenticationValidationStep = new StatsDMetricSinkValidationStep(runtimeConfiguration);
var validationResult = azureAuthenticationValidationStep.Run();

// Assert
Assert.True(validationResult.IsSuccessful);
}

private IOptions<RuntimeConfiguration> CreateRuntimeConfiguration()
{
var bogusRuntimeConfiguration = BogusRuntimeConfigurationGenerator.Generate();

return Options.Create(bogusRuntimeConfiguration);
}
}
}

0 comments on commit 58aca6d

Please sign in to comment.