Skip to content

Commit

Permalink
Exit gracefully if a validation error occurs
Browse files Browse the repository at this point in the history
I've tweaked the way that the startup process for Promitor works so that it runs the validation in the `Main()` method. This gives us the opportunity to exit gracefully if validation fails instead of throwing an exception.

Also:

- Added a new enum to track the possible exit statuses, and made sure that unhandled exceptions continue to use an exit code of `1`.
- Updated the unhandled exception message to point people to raising an issue.
- Altered the check to make sure the config folder is set so that it exits gracefully instead of ending up in the unhandled exception block.
- Moved the logging about whether or not the configuration is valid from RuntimeValidator into the main method. It seemed more appropriate for the logging to be there since the main method now has logic for exiting if the config is invalid.

Fixes tomkerkhove#1113
  • Loading branch information
adamconnelly committed Jun 28, 2020
1 parent 8b3e730 commit eb1bb85
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 41 deletions.
35 changes: 35 additions & 0 deletions src/Promitor.Agents.Scraper/Docs/Open-Api.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions src/Promitor.Agents.Scraper/ExitStatus.cs
@@ -0,0 +1,29 @@
namespace Promitor.Agents.Scraper
{
/// <summary>
/// The different statuses that the agent scraper can exit with.
/// </summary>
public enum ExitStatus
{
/// <summary>
/// The application has run successfully.
/// </summary>
Success = 0,

/// <summary>
/// An unhandled exception was thrown during host startup. This probably
/// indicates a bug in Promitor that should be reported.
/// </summary>
UnhandledException = 1,

/// <summary>
/// Validation failed, so Promitor can't start.
/// </summary>
ValidationFailed = 2,

/// <summary>
/// The configuration folder environment variable has not been set.
/// </summary>
ConfigurationFolderNotSpecified = 3
}
}
47 changes: 32 additions & 15 deletions src/Promitor.Agents.Scraper/Program.cs
@@ -1,9 +1,12 @@
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Promitor.Agents.Core;
using Promitor.Agents.Core.Configuration.Server;
using Promitor.Agents.Scraper.Validation;
using Promitor.Core;
using Serilog;

Expand All @@ -20,16 +23,36 @@ public static int Main(string[] args)
// Let's hook in a logger for start-up purposes.
ConfigureStartupLogging();

CreateHostBuilder(args)
.Build()
.Run();
var configurationFolder = Environment.GetEnvironmentVariable(EnvironmentVariables.Configuration.Folder);
if (string.IsNullOrWhiteSpace(configurationFolder))
{
Log.Logger.Fatal($"Unable to determine the configuration folder. Please ensure that the '{EnvironmentVariables.Configuration.Folder}' environment variable is set");
return (int)ExitStatus.ConfigurationFolderNotSpecified;
}

return 0;
var host = CreateHostBuilder(args, configurationFolder)
.Build();

using (var scope = host.Services.CreateScope())
{
var validator = scope.ServiceProvider.GetRequiredService<RuntimeValidator>();
if (!validator.Validate())
{
Log.Logger.Fatal("Promitor is not configured correctly. Please fix validation issues and re-run.");
return (int)ExitStatus.ValidationFailed;
}

Log.Logger.Information("Promitor configuration is valid, we are good to go.");
}

host.Run();

return (int)ExitStatus.Success;
}
catch (Exception exception)
{
Log.Fatal(exception, "Host terminated unexpectedly");
return 1;
Log.Fatal(exception, "Promitor has encountered an unexpected error. Please open an issue at https://github.com/tomkerkhove/promitor/issues to let us know about it.");
return (int)ExitStatus.UnhandledException;
}
finally
{
Expand All @@ -42,9 +65,9 @@ private static void Welcome()
Console.WriteLine(Constants.Texts.Welcome);
}

public static IHostBuilder CreateHostBuilder(string[] args)
public static IHostBuilder CreateHostBuilder(string[] args, string configurationFolder)
{
IConfiguration configuration = BuildConfiguration();
IConfiguration configuration = BuildConfiguration(configurationFolder);
ServerConfiguration serverConfiguration = GetServerConfiguration(configuration);
IHostBuilder webHostBuilder = CreatePromitorWebHost<Startup>(args, configuration, serverConfiguration);

Expand All @@ -57,14 +80,8 @@ private static ServerConfiguration GetServerConfiguration(IConfiguration configu
return serverConfiguration;
}

private static IConfigurationRoot BuildConfiguration()
private static IConfigurationRoot BuildConfiguration(string configurationFolder)
{
var configurationFolder = Environment.GetEnvironmentVariable(EnvironmentVariables.Configuration.Folder);
if (string.IsNullOrWhiteSpace(configurationFolder))
{
throw new Exception("Unable to determine the configuration folder");
}

var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddYamlFile($"{configurationFolder}/runtime.yaml", optional: false, reloadOnChange: true)
Expand Down
10 changes: 0 additions & 10 deletions src/Promitor.Agents.Scraper/Startup.cs
Expand Up @@ -12,7 +12,6 @@
using Promitor.Agents.Scraper.Configuration.Sinks;
using Promitor.Agents.Scraper.Extensions;
using Promitor.Agents.Scraper.Health;
using Promitor.Agents.Scraper.Validation;
using Promitor.Core.Scraping.Configuration.Serialization.v1.Mapping;
using Promitor.Integrations.AzureMonitor.Logging;
using Serilog;
Expand Down Expand Up @@ -55,8 +54,6 @@ public void ConfigureServices(IServiceCollection services)
healthCheckBuilder.AddCheck<ResourceDiscoveryHealthCheck>("Promitor Resource Discovery", HealthStatus.Degraded);
}

ValidateRuntimeConfiguration(services);

services.UseMetricSinks(Configuration)
.ScheduleMetricScraping();
}
Expand All @@ -80,13 +77,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
UseSerilog(ComponentName, app.ApplicationServices);
}

private void ValidateRuntimeConfiguration(IServiceCollection services)
{
var serviceProvider = services.BuildServiceProvider();
var runtimeValidator = serviceProvider.GetService<RuntimeValidator>();
runtimeValidator.Run();
}

protected override LoggerConfiguration FilterTelemetry(LoggerConfiguration loggerConfiguration)
{
var standardConfiguration = base.FilterTelemetry(loggerConfiguration);
Expand Down
25 changes: 9 additions & 16 deletions src/Promitor.Agents.Scraper/Validation/RuntimeValidator.cs
Expand Up @@ -5,7 +5,6 @@
using Microsoft.Extensions.Options;
using Promitor.Agents.Scraper.Configuration;
using Promitor.Core.Scraping.Configuration.Providers.Interfaces;
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;
Expand Down Expand Up @@ -39,26 +38,20 @@ public class RuntimeValidator
};
}

public void Run()
/// <summary>
/// Checks whether Promitor's configuration is valid so that the application
/// can start running successfully.
/// </summary>
/// <returns>
/// true if the configuration is valid, false otherwise.
/// </returns>
public bool Validate()
{
_validationLogger.LogInformation("Starting validation of Promitor setup");

var validationResults = RunValidationSteps();
ProcessValidationResults(validationResults);
}

private void ProcessValidationResults(List<ValidationResult> validationResults)
{
var failedValidationResults = validationResults.Where(result => result.IsSuccessful == false).ToList();

var validationFailed = failedValidationResults.Any();
if (validationFailed)
{
_validationLogger.LogCritical("Promitor is not configured correctly. Please fix validation issues and re-run.");
throw new ValidationFailedException(failedValidationResults);
}

_validationLogger.LogInformation("Promitor configuration is valid, we are good to go.");
return validationResults.All(result => result.IsSuccessful);
}

private List<ValidationResult> RunValidationSteps()
Expand Down

0 comments on commit eb1bb85

Please sign in to comment.