mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-29 16:11:05 +01:00
Setup rudimentary configuration validation
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Tgstation.Server.Host.Configuration.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration validator for <see cref="GeneralConfiguration"/>.
|
||||
/// </summary>
|
||||
sealed class GeneralConfigValidator : IValidateOptions<GeneralConfiguration>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ValidateOptionsResult Validate(string? name, GeneralConfiguration options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
var metricsValidation = HostingSpecificationConfigValidator.ValidateHostingSpecifications(options.MetricsEndPoints, nameof(options.MetricsEndPoints), false);
|
||||
if (metricsValidation.Failed)
|
||||
return metricsValidation;
|
||||
|
||||
return HostingSpecificationConfigValidator.ValidateHostingSpecifications(options.ApiEndPoints, nameof(GeneralConfiguration.ApiEndPoints), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
|
||||
namespace Tgstation.Server.Host
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration validation for <see cref="HostingSpecification"/>s.
|
||||
/// </summary>
|
||||
static class HostingSpecificationConfigValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Validate given a set of <paramref name="endPoints"/>.
|
||||
/// </summary>
|
||||
/// <param name="endPoints">The <see cref="HostingSpecification"/>s to validate.</param>
|
||||
/// <param name="name">The name of the config subsection.</param>
|
||||
/// <param name="required">If these endpoints are required.</param>
|
||||
/// <returns>The <see cref="ValidateOptionsResult"/> for the <paramref name="endPoints"/>.</returns>
|
||||
public static ValidateOptionsResult ValidateHostingSpecifications(IReadOnlyList<HostingSpecification> endPoints, string name, bool required)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(endPoints);
|
||||
ArgumentNullException.ThrowIfNull(name);
|
||||
|
||||
if (endPoints == null || endPoints.Count == 0)
|
||||
{
|
||||
if (!required)
|
||||
return ValidateOptionsResult.Success;
|
||||
|
||||
return ValidateOptionsResult.Fail($"At least one hosting specification for {name} must be specified");
|
||||
}
|
||||
|
||||
foreach (var endPoint in endPoints)
|
||||
{
|
||||
if (endPoint == null)
|
||||
return ValidateOptionsResult.Fail("A hosting specification cannot be null!");
|
||||
|
||||
if (endPoint.Port == 0)
|
||||
return ValidateOptionsResult.Fail($"Hosting specification port in {name} cannot be 0!");
|
||||
|
||||
if (endPoint.IPAddress != null && !IPAddress.TryParse(endPoint.IPAddress, out _))
|
||||
return ValidateOptionsResult.Fail($"Could not part hosting IP address in {name}: {endPoint.IPAddress}");
|
||||
}
|
||||
|
||||
return ValidateOptionsResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Tgstation.Server.Host.Configuration.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration validator for <see cref="SessionConfiguration"/>.
|
||||
/// </summary>
|
||||
sealed class SessionConfigValidator : IValidateOptions<SessionConfiguration>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ValidateOptionsResult Validate(string? name, SessionConfiguration options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
if (options.BridgePort == 0)
|
||||
return ValidateOptionsResult.Fail($"{nameof(SessionConfiguration.BridgePort)} cannot be zero!");
|
||||
|
||||
return ValidateOptionsResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Tgstation.Server.Host.Configuration.Validators
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration validator for <see cref="SwarmConfiguration"/>.
|
||||
/// </summary>
|
||||
sealed class SwarmConfigValidator : IValidateOptions<SwarmConfiguration>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ValidateOptionsResult Validate(string? name, SwarmConfiguration options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
if (options.PrivateKey == null)
|
||||
return ValidateOptionsResult.Success;
|
||||
|
||||
if (options.UpdateRequiredNodeCount == 0)
|
||||
return ValidateOptionsResult.Fail($"{nameof(SwarmConfiguration.UpdateRequiredNodeCount)} must be greater than 0!");
|
||||
|
||||
if (options.Address == null)
|
||||
return ValidateOptionsResult.Fail($"{nameof(SwarmConfiguration.Address)} must be set to an http endpoint of this swarm service accessible from other servers in the swarm!");
|
||||
|
||||
return HostingSpecificationConfigValidator.ValidateHostingSpecifications(options.EndPoints, nameof(SwarmConfiguration.EndPoints), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -822,6 +822,10 @@ namespace Tgstation.Server.Host.Core
|
||||
protected override void ConfigureHostedService(IServiceCollection services)
|
||||
=> services.AddSingleton<IHostedService>(x => x.GetRequiredService<InstanceManager>());
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UseValidatedConfig<TConfig, TValidator>(IServiceCollection services)
|
||||
=> services.UseValidatedConfig<TConfig, TValidator>(Configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Configure the <paramref name="services"/> for the authentication pipeline.
|
||||
/// </summary>
|
||||
|
||||
@@ -165,6 +165,22 @@ namespace Tgstation.Server.Host.Extensions
|
||||
.BindConfiguration(sectionName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a standard <typeparamref name="TConfig"/> binding.
|
||||
/// </summary>
|
||||
/// <typeparam name="TConfig">The <see langword="class"/> to bind. Must have a <see langword="public"/> const/static <see cref="string"/> field named "Section".</typeparam>
|
||||
/// <typeparam name="TValidator">The <see cref="IValidateOptions{TOptions}"/> <see cref="Type"/> for <typeparamref name="TConfig"/>s.</typeparam>
|
||||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to configure.</param>
|
||||
/// <param name="configuration">The <see cref="IConfiguration"/> containing the <typeparamref name="TConfig"/>.</param>
|
||||
/// <returns><paramref name="serviceCollection"/>.</returns>
|
||||
public static OptionsBuilder<TConfig> UseValidatedConfig<TConfig, TValidator>(this IServiceCollection serviceCollection, IConfiguration configuration)
|
||||
where TConfig : class
|
||||
where TValidator : class, IValidateOptions<TConfig>
|
||||
{
|
||||
serviceCollection.AddSingleton<IValidateOptions<TConfig>, TValidator>();
|
||||
return UseStandardConfig<TConfig>(serviceCollection, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear previous providers and configure logging.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Serilog.Events;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Configuration.Validators;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
@@ -56,14 +59,14 @@ namespace Tgstation.Server.Host.Setup
|
||||
services.AddSingleton<IAsyncDelayer, AsyncDelayer>();
|
||||
|
||||
// these configs are what's injected into PostSetupServices
|
||||
services.UseStandardConfig<GeneralConfiguration>(Configuration);
|
||||
UseValidatedConfig<GeneralConfiguration, GeneralConfigValidator>(services);
|
||||
services.UseStandardConfig<DatabaseConfiguration>(Configuration);
|
||||
services.UseStandardConfig<SecurityConfiguration>(Configuration);
|
||||
services.UseStandardConfig<FileLoggingConfiguration>(Configuration);
|
||||
services.UseStandardConfig<ElasticsearchConfiguration>(Configuration);
|
||||
services.UseStandardConfig<InternalConfiguration>(Configuration);
|
||||
services.UseStandardConfig<SwarmConfiguration>(Configuration);
|
||||
services.UseStandardConfig<SessionConfiguration>(Configuration);
|
||||
UseValidatedConfig<SwarmConfiguration, SwarmConfigValidator>(services);
|
||||
UseValidatedConfig<SessionConfiguration, SessionConfigValidator>(services);
|
||||
|
||||
ConfigureHostedService(services);
|
||||
}
|
||||
@@ -77,5 +80,16 @@ namespace Tgstation.Server.Host.Setup
|
||||
services.AddSingleton<IPostSetupServices, PostSetupServices>();
|
||||
services.AddSingleton<IHostedService, SetupWizard>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the <see cref="IHostedService"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TConfig">The <see langword="class"/> to bind. Must have a <see langword="public"/> const/static <see cref="string"/> field named "Section".</typeparam>
|
||||
/// <typeparam name="TValidator">The <see cref="IValidateOptions{TOptions}"/> <see cref="Type"/> for <typeparamref name="TConfig"/>s.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to configure.</param>
|
||||
protected virtual void UseValidatedConfig<TConfig, TValidator>(IServiceCollection services)
|
||||
where TConfig : class
|
||||
where TValidator : class, IValidateOptions<TConfig>
|
||||
=> services.UseStandardConfig<TConfig>(Configuration); // don't enable config validation during setup
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user