From ad8025df1ba7b38d7821971ec83815519babeb10 Mon Sep 17 00:00:00 2001 From: Marcelo Castagna Date: Sat, 4 Aug 2018 04:17:31 -0300 Subject: [PATCH] Reload config on change (#527) * Reload config on change. Added test case. * added testing for adding the json with reloadOnChange = false --- .../Middleware/OcelotMiddlewareExtensions.cs | 20 ++++-- .../ConfigurationReloadTests.cs | 68 +++++++++++++++++++ test/Ocelot.AcceptanceTests/Steps.cs | 41 +++++++++++ 3 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 76a23001..9f5b9a20 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -113,12 +113,12 @@ { // make configuration from file system? // earlier user needed to add ocelot files in startup configuration stuff, asp.net will map it to this - var fileConfig = builder.ApplicationServices.GetService>(); + var fileConfig = builder.ApplicationServices.GetService>(); // now create the config var internalConfigCreator = builder.ApplicationServices.GetService(); - var internalConfig = await internalConfigCreator.Create(fileConfig.Value); - //Configuration error, throw error message + var internalConfig = await internalConfigCreator.Create(fileConfig.CurrentValue); + //Configuration error, throw error message if (internalConfig.IsError) { ThrowToStopOcelotStarting(internalConfig); @@ -128,6 +128,12 @@ var internalConfigRepo = builder.ApplicationServices.GetService(); internalConfigRepo.AddOrReplace(internalConfig.Data); + fileConfig.OnChange(async (config) => + { + var newInternalConfig = await internalConfigCreator.Create(config); + internalConfigRepo.AddOrReplace(newInternalConfig.Data); + }); + var fileConfigRepo = builder.ApplicationServices.GetService(); var adminPath = builder.ApplicationServices.GetService(); @@ -155,7 +161,7 @@ } private static async Task SetFileConfigInConsul(IApplicationBuilder builder, - IFileConfigurationRepository fileConfigRepo, IOptions fileConfig, + IFileConfigurationRepository fileConfigRepo, IOptionsMonitor fileConfig, IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo) { // get the config from consul. @@ -168,7 +174,7 @@ else if (ConfigNotStoredInConsul(fileConfigFromConsul)) { //there was no config in consul set the file in config in consul - await fileConfigRepo.Set(fileConfig.Value); + await fileConfigRepo.Set(fileConfig.CurrentValue); } else { @@ -197,9 +203,9 @@ } } - private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptions fileConfig) + private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptionsMonitor fileConfig) { - var response = await fileConfigSetter.Set(fileConfig.Value); + var response = await fileConfigSetter.Set(fileConfig.CurrentValue); if (IsError(response)) { diff --git a/test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs new file mode 100644 index 00000000..f5a23938 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs @@ -0,0 +1,68 @@ +using Ocelot.Configuration.File; +using Ocelot.Configuration.Setter; +using Ocelot.Middleware; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Text; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + public class ConfigurationReloadTests : IDisposable + { + private FileConfiguration _initialConfig; + private FileConfiguration _anotherConfig; + private Steps _steps; + + public ConfigurationReloadTests() + { + _steps = new Steps(); + + _initialConfig = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + RequestIdKey = "initialKey" + } + }; + + _anotherConfig = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + RequestIdKey = "someOtherKey" + } + }; + } + + [Fact] + public void should_reload_config_on_change() + { + this.Given(x => _steps.GivenThereIsAConfiguration(_initialConfig)) + .And(x => _steps.GivenOcelotIsRunningReloadingConfig(true)) + .And(x => _steps.GivenThereIsAConfiguration(_anotherConfig)) + .And(x => _steps.GivenIWait(2500)) + .And(x => _steps.ThenConfigShouldBe(_anotherConfig)) + .BDDfy(); + } + + [Fact] + public void should_not_reload_config_on_change() + { + + this.Given(x => _steps.GivenThereIsAConfiguration(_initialConfig)) + .And(x => _steps.GivenOcelotIsRunningReloadingConfig(false)) + .And(x => _steps.GivenThereIsAConfiguration(_anotherConfig)) + .And(x => _steps.GivenIWait(2500)) + .And(x => _steps.ThenConfigShouldBe(_initialConfig)) + .BDDfy(); + } + + public void Dispose() + { + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 9dcb8bc5..63d3a540 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -34,6 +34,7 @@ namespace Ocelot.AcceptanceTests using Butterfly; using Configuration.Repository; using Microsoft.Net.Http.Headers; + using Ocelot.Configuration.Creator; using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; public class Steps : IDisposable @@ -55,6 +56,18 @@ namespace Ocelot.AcceptanceTests _random = new Random(); } + public async Task ThenConfigShouldBe(FileConfiguration fileConfig) + { + var internalConfigCreator = _ocelotServer.Host.Services.GetService(); + + var internalConfigRepo = _ocelotServer.Host.Services.GetService(); + var internalConfig = internalConfigRepo.Get(); + + var config = await internalConfigCreator.Create(fileConfig); + + internalConfig.Data.RequestId.ShouldBe(config.Data.RequestId); + } + public async Task StartFakeOcelotWithWebSockets() { _ocelotBuilder = new WebHostBuilder(); @@ -116,6 +129,34 @@ namespace Ocelot.AcceptanceTests File.WriteAllText(configurationPath, jsonConfiguration); } + public void GivenOcelotIsRunningReloadingConfig(bool shouldReload) + { + _webHostBuilder = new WebHostBuilder(); + + _webHostBuilder + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: shouldReload); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(s => + { + s.AddOcelot(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + /// /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. ///