diff --git a/src/Config/FileSystemRuntimeConfigLoader.cs b/src/Config/FileSystemRuntimeConfigLoader.cs index 447ee4be57..bcab135b6d 100644 --- a/src/Config/FileSystemRuntimeConfigLoader.cs +++ b/src/Config/FileSystemRuntimeConfigLoader.cs @@ -162,9 +162,20 @@ public bool TryLoadConfig( } config = RuntimeConfig; + + if (lastValidRuntimeConfig is null) + { + lastValidRuntimeConfig = RuntimeConfig; + } + return true; } + if (lastValidRuntimeConfig is not null) + { + RuntimeConfig = lastValidRuntimeConfig; + } + config = null; return false; } @@ -202,7 +213,16 @@ public override bool TryLoadKnownConfig([NotNullWhen(true)] out RuntimeConfig? c public void HotReloadConfig(string defaultDataSourceName, ILogger? logger = null) { logger?.LogInformation(message: "Starting hot-reload process for config: {ConfigFilePath}", ConfigFilePath); - TryLoadConfig(ConfigFilePath, out _, replaceEnvVar: true, defaultDataSourceName: defaultDataSourceName); + if (!TryLoadConfig(ConfigFilePath, out _, replaceEnvVar: true, defaultDataSourceName: defaultDataSourceName)) + { + throw new DataApiBuilderException( + message: "Deserialization of the configuration file failed.", + statusCode: HttpStatusCode.ServiceUnavailable, + subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); + } + + isNewConfigDetected = true; + isNewConfigValidated = false; SendEventNotification(); } diff --git a/src/Config/RuntimeConfigLoader.cs b/src/Config/RuntimeConfigLoader.cs index fda5dc3b16..aae4238a47 100644 --- a/src/Config/RuntimeConfigLoader.cs +++ b/src/Config/RuntimeConfigLoader.cs @@ -31,6 +31,12 @@ public abstract class RuntimeConfigLoader // state in place of using out params. public RuntimeConfig? RuntimeConfig; + public RuntimeConfig? lastValidRuntimeConfig; + + public bool isNewConfigDetected; + + public bool isNewConfigValidated = true; + public RuntimeConfigLoader(HotReloadEventHandler? handler = null, string? connectionString = null) { _handler = handler; diff --git a/src/Core/Configurations/RuntimeConfigProvider.cs b/src/Core/Configurations/RuntimeConfigProvider.cs index 79f63d5b7f..7364480e19 100644 --- a/src/Core/Configurations/RuntimeConfigProvider.cs +++ b/src/Core/Configurations/RuntimeConfigProvider.cs @@ -3,12 +3,14 @@ using System.Data.Common; using System.Diagnostics.CodeAnalysis; +using System.IO.Abstractions; using System.Net; using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Config.Converters; using Azure.DataApiBuilder.Config.NamingPolicies; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Service.Exceptions; +using Microsoft.Extensions.Logging; namespace Azure.DataApiBuilder.Core.Configurations; @@ -74,6 +76,34 @@ public RuntimeConfigProvider(RuntimeConfigLoader runtimeConfigLoader) /// Thrown when the loader is unable to load an instance of the config from its known location. public RuntimeConfig GetConfig() { + // Only used in hot reload to validate the configuration file + if (_configLoader.isNewConfigDetected && !_configLoader.isNewConfigValidated) + { + IFileSystem fileSystem = new FileSystem(); + ILoggerFactory loggerFactory = new LoggerFactory(); + ILogger logger = loggerFactory.CreateLogger(); + RuntimeConfigValidator runtimeConfigValidator = new(this, fileSystem, logger, true); + + _configLoader.isNewConfigDetected = false; + _configLoader.isNewConfigValidated = runtimeConfigValidator.TryValidateConfig(ConfigFilePath, loggerFactory).Result; + + // Saves the lastValidRuntimeConfig as the new RuntimeConfig if it is validated for hot reload + if (_configLoader.isNewConfigValidated) + { + _configLoader.lastValidRuntimeConfig = _configLoader.RuntimeConfig; + } + else + { + _configLoader.isNewConfigValidated = true; + _configLoader.RuntimeConfig = _configLoader.lastValidRuntimeConfig; + + throw new DataApiBuilderException( + message: "Failed validation of configuration file.", + statusCode: HttpStatusCode.ServiceUnavailable, + subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); + } + } + if (_configLoader.RuntimeConfig is not null) { return _configLoader.RuntimeConfig;