diff --git a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs similarity index 55% rename from src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs rename to src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs index 8dabeaf2..74bbf002 100644 --- a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs +++ b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs @@ -1,84 +1,112 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Setter; -using Ocelot.Logging; - -namespace Ocelot.Configuration.Repository -{ - public class ConsulFileConfigurationPoller : IDisposable - { - private readonly IOcelotLogger _logger; - private readonly IFileConfigurationRepository _repo; - private readonly IFileConfigurationSetter _setter; - private string _previousAsJson; - private readonly Timer _timer; - private bool _polling; - private readonly IConsulPollerConfiguration _config; - - public ConsulFileConfigurationPoller( - IOcelotLoggerFactory factory, - IFileConfigurationRepository repo, - IFileConfigurationSetter setter, - IConsulPollerConfiguration config) - { - _setter = setter; - _config = config; - _logger = factory.CreateLogger(); - _repo = repo; - _previousAsJson = ""; - _timer = new Timer(async x => - { - if(_polling) - { - return; - } - - _polling = true; - await Poll(); - _polling = false; - }, null, _config.Delay, _config.Delay); - } - - private async Task Poll() - { - _logger.LogInformation("Started polling consul"); - - var fileConfig = await _repo.Get(); - - if(fileConfig.IsError) - { - _logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}"); - return; - } - - var asJson = ToJson(fileConfig.Data); - - if(!fileConfig.IsError && asJson != _previousAsJson) - { - await _setter.Set(fileConfig.Data); - _previousAsJson = asJson; - } - - _logger.LogInformation("Finished polling consul"); - } - - /// - /// We could do object comparison here but performance isnt really a problem. This might be an issue one day! - /// - /// hash of the config - private string ToJson(FileConfiguration config) - { - var currentHash = JsonConvert.SerializeObject(config); - return currentHash; - } - - public void Dispose() - { - _timer.Dispose(); - } - } -} +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Ocelot.Configuration.Setter; +using Ocelot.Logging; + +namespace Ocelot.Configuration.Repository +{ + public class FileConfigurationPoller : IHostedService, IDisposable + { + private readonly IOcelotLogger _logger; + private readonly IFileConfigurationRepository _repo; + private string _previousAsJson; + private Timer _timer; + private bool _polling; + private readonly IFileConfigurationPollerOptions _options; + private readonly IInternalConfigurationRepository _internalConfigRepo; + private readonly IInternalConfigurationCreator _internalConfigCreator; + + public FileConfigurationPoller( + IOcelotLoggerFactory factory, + IFileConfigurationRepository repo, + IFileConfigurationPollerOptions options, + IInternalConfigurationRepository internalConfigRepo, + IInternalConfigurationCreator internalConfigCreator) + { + _internalConfigRepo = internalConfigRepo; + _internalConfigCreator = internalConfigCreator; + _options = options; + _logger = factory.CreateLogger(); + _repo = repo; + _previousAsJson = ""; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation($"{nameof(FileConfigurationPoller)} is starting."); + + _timer = new Timer(async x => + { + if(_polling) + { + return; + } + + _polling = true; + await Poll(); + _polling = false; + }, null, _options.Delay, _options.Delay); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _logger.LogInformation($"{nameof(FileConfigurationPoller)} is stopping."); + + _timer?.Change(Timeout.Infinite, 0); + + return Task.CompletedTask; + } + + private async Task Poll() + { + _logger.LogInformation("Started polling consul"); + + var fileConfig = await _repo.Get(); + + if(fileConfig.IsError) + { + _logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}"); + return; + } + + var asJson = ToJson(fileConfig.Data); + + if(!fileConfig.IsError && asJson != _previousAsJson) + { + var config = await _internalConfigCreator.Create(fileConfig.Data); + + if(!config.IsError) + { + _internalConfigRepo.AddOrReplace(config.Data); + } + + _previousAsJson = asJson; + } + + _logger.LogInformation("Finished polling consul"); + } + + /// + /// We could do object comparison here but performance isnt really a problem. This might be an issue one day! + /// + /// hash of the config + private string ToJson(FileConfiguration config) + { + var currentHash = JsonConvert.SerializeObject(config); + return currentHash; + } + + public void Dispose() + { + _timer.Dispose(); + } + } +} diff --git a/src/Ocelot/Configuration/Repository/IConsulPollerConfiguration.cs b/src/Ocelot/Configuration/Repository/IConsulPollerConfiguration.cs index d1f1430d..70fc9bd4 100644 --- a/src/Ocelot/Configuration/Repository/IConsulPollerConfiguration.cs +++ b/src/Ocelot/Configuration/Repository/IConsulPollerConfiguration.cs @@ -1,6 +1,6 @@ namespace Ocelot.Configuration.Repository { - public interface IConsulPollerConfiguration + public interface IFileConfigurationPollerOptions { int Delay { get; } } diff --git a/src/Ocelot/Configuration/Repository/InMemoryConsulPollerConfiguration.cs b/src/Ocelot/Configuration/Repository/InMemoryFileConfigurationPollerOptions.cs similarity index 50% rename from src/Ocelot/Configuration/Repository/InMemoryConsulPollerConfiguration.cs rename to src/Ocelot/Configuration/Repository/InMemoryFileConfigurationPollerOptions.cs index 9e411f76..f5ebe186 100644 --- a/src/Ocelot/Configuration/Repository/InMemoryConsulPollerConfiguration.cs +++ b/src/Ocelot/Configuration/Repository/InMemoryFileConfigurationPollerOptions.cs @@ -1,6 +1,6 @@ namespace Ocelot.Configuration.Repository { - public class InMemoryConsulPollerConfiguration : IConsulPollerConfiguration + public class InMemoryFileConfigurationPollerOptions : IFileConfigurationPollerOptions { public int Delay => 1000; } diff --git a/src/Ocelot/Configuration/Setter/FileAndInternalConfigurationSetter.cs b/src/Ocelot/Configuration/Setter/FileAndInternalConfigurationSetter.cs index 6821553e..a5549b29 100644 --- a/src/Ocelot/Configuration/Setter/FileAndInternalConfigurationSetter.cs +++ b/src/Ocelot/Configuration/Setter/FileAndInternalConfigurationSetter.cs @@ -8,7 +8,7 @@ namespace Ocelot.Configuration.Setter { public class FileAndInternalConfigurationSetter : IFileConfigurationSetter { - private readonly IInternalConfigurationRepository _configRepo; + private readonly IInternalConfigurationRepository internalConfigRepo; private readonly IInternalConfigurationCreator _configCreator; private readonly IFileConfigurationRepository _repo; @@ -17,7 +17,7 @@ namespace Ocelot.Configuration.Setter IInternalConfigurationCreator configCreator, IFileConfigurationRepository repo) { - _configRepo = configRepo; + internalConfigRepo = configRepo; _configCreator = configCreator; _repo = repo; } @@ -35,7 +35,7 @@ namespace Ocelot.Configuration.Setter if(!config.IsError) { - _configRepo.AddOrReplace(config.Data); + internalConfigRepo.AddOrReplace(config.Data); } return new ErrorResponse(config.Errors); diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 72064a9d..07e62358 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -160,7 +160,7 @@ namespace Ocelot.DependencyInjection // We add this here so that we can always inject something into the factory for IoC.. _services.AddSingleton(); - _services.TryAddSingleton(); + _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); @@ -245,7 +245,7 @@ namespace Ocelot.DependencyInjection public IOcelotBuilder AddStoreOcelotConfigurationInConsul() { - _services.AddSingleton(); + _services.AddHostedService(); _services.AddSingleton(); return this; } diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index fecf749f..d5421b12 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -1,266 +1,263 @@ -namespace Ocelot.Middleware -{ - using System; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Options; - using System.Diagnostics; - using DependencyInjection; - using Microsoft.AspNetCore.Builder; - using Ocelot.Configuration; - using Ocelot.Configuration.Creator; - using Ocelot.Configuration.File; - using Ocelot.Configuration.Repository; - using Ocelot.Configuration.Setter; - using Ocelot.Responses; - using Ocelot.Logging; - using Rafty.Concensus; - using Rafty.Infrastructure; - using Ocelot.Middleware.Pipeline; - using Pivotal.Discovery.Client; - using Rafty.Concensus.Node; - - public static class OcelotMiddlewareExtensions - { - public static async Task UseOcelot(this IApplicationBuilder builder) - { - await builder.UseOcelot(new OcelotPipelineConfiguration()); - - return builder; - } - - public static async Task UseOcelot(this IApplicationBuilder builder, Action pipelineConfiguration) - { - var config = new OcelotPipelineConfiguration(); - pipelineConfiguration?.Invoke(config); - return await builder.UseOcelot(config); - } - public static async Task UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) - { - var configuration = await CreateConfiguration(builder); - - CreateAdministrationArea(builder, configuration); - - if (UsingRafty(builder)) - { - SetUpRafty(builder); - } - - if (UsingEurekaServiceDiscoveryProvider(configuration)) - { - builder.UseDiscoveryClient(); - } - - ConfigureDiagnosticListener(builder); - - var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices); - - pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration); - - var firstDelegate = pipelineBuilder.Build(); - - /* - inject first delegate into first piece of asp.net middleware..maybe not like this - then because we are updating the http context in ocelot it comes out correct for - rest of asp.net.. - */ - - builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware"; - - builder.Use(async (context, task) => - { - var downstreamContext = new DownstreamContext(context); - await firstDelegate.Invoke(downstreamContext); - }); - - return builder; - } - - private static bool UsingEurekaServiceDiscoveryProvider(IInternalConfiguration configuration) - { - return configuration?.ServiceProviderConfiguration != null && configuration.ServiceProviderConfiguration.Type?.ToLower() == "eureka"; - } - - private static bool UsingRafty(IApplicationBuilder builder) - { - var possible = builder.ApplicationServices.GetService(typeof(INode)) as INode; - if (possible != null) - { - return true; - } - - return false; - } - - private static void SetUpRafty(IApplicationBuilder builder) - { - var applicationLifetime = (IApplicationLifetime)builder.ApplicationServices.GetService(typeof(IApplicationLifetime)); - applicationLifetime.ApplicationStopping.Register(() => OnShutdown(builder)); - var node = (INode)builder.ApplicationServices.GetService(typeof(INode)); - var nodeId = (NodeId)builder.ApplicationServices.GetService(typeof(NodeId)); - node.Start(nodeId); - } - - private static async Task CreateConfiguration(IApplicationBuilder builder) - { - // 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 = (IOptions)builder.ApplicationServices.GetService(typeof(IOptions)); - - // now create the config - var internalConfigCreator = (IInternalConfigurationCreator)builder.ApplicationServices.GetService(typeof(IInternalConfigurationCreator)); - var internalConfig = await internalConfigCreator.Create(fileConfig.Value); - - // now save it in memory - var internalConfigRepo = (IInternalConfigurationRepository)builder.ApplicationServices.GetService(typeof(IInternalConfigurationRepository)); - internalConfigRepo.AddOrReplace(internalConfig.Data); - - var fileConfigRepo = (IFileConfigurationRepository)builder.ApplicationServices.GetService(typeof(IFileConfigurationRepository)); - - var adminPath = (IAdministrationPath)builder.ApplicationServices.GetService(typeof(IAdministrationPath)); - - if (UsingConsul(fileConfigRepo)) +namespace Ocelot.Middleware +{ + using System; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Options; + using System.Diagnostics; + using DependencyInjection; + using Microsoft.AspNetCore.Builder; + using Ocelot.Configuration; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Ocelot.Configuration.Repository; + using Ocelot.Configuration.Setter; + using Ocelot.Responses; + using Ocelot.Logging; + using Rafty.Concensus; + using Rafty.Infrastructure; + using Ocelot.Middleware.Pipeline; + using Pivotal.Discovery.Client; + using Rafty.Concensus.Node; + + public static class OcelotMiddlewareExtensions + { + public static async Task UseOcelot(this IApplicationBuilder builder) + { + await builder.UseOcelot(new OcelotPipelineConfiguration()); + + return builder; + } + + public static async Task UseOcelot(this IApplicationBuilder builder, Action pipelineConfiguration) + { + var config = new OcelotPipelineConfiguration(); + pipelineConfiguration?.Invoke(config); + return await builder.UseOcelot(config); + } + public static async Task UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) + { + var configuration = await CreateConfiguration(builder); + + CreateAdministrationArea(builder, configuration); + + if (UsingRafty(builder)) { - //Lots of jazz happens in here..check it out if you are using consul to store your config. - await SetFileConfigInConsul(builder, fileConfigRepo, fileConfig, internalConfigCreator, internalConfigRepo); - } - else if(AdministrationApiInUse(adminPath)) + SetUpRafty(builder); + } + + if (UsingEurekaServiceDiscoveryProvider(configuration)) + { + builder.UseDiscoveryClient(); + } + + ConfigureDiagnosticListener(builder); + + var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices); + + pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration); + + var firstDelegate = pipelineBuilder.Build(); + + /* + inject first delegate into first piece of asp.net middleware..maybe not like this + then because we are updating the http context in ocelot it comes out correct for + rest of asp.net.. + */ + + builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware"; + + builder.Use(async (context, task) => + { + var downstreamContext = new DownstreamContext(context); + await firstDelegate.Invoke(downstreamContext); + }); + + return builder; + } + + private static bool UsingEurekaServiceDiscoveryProvider(IInternalConfiguration configuration) + { + return configuration?.ServiceProviderConfiguration != null && configuration.ServiceProviderConfiguration.Type?.ToLower() == "eureka"; + } + + private static bool UsingRafty(IApplicationBuilder builder) + { + var possible = builder.ApplicationServices.GetService(typeof(INode)) as INode; + if (possible != null) + { + return true; + } + + return false; + } + + private static void SetUpRafty(IApplicationBuilder builder) + { + var applicationLifetime = (IApplicationLifetime)builder.ApplicationServices.GetService(typeof(IApplicationLifetime)); + applicationLifetime.ApplicationStopping.Register(() => OnShutdown(builder)); + var node = (INode)builder.ApplicationServices.GetService(typeof(INode)); + var nodeId = (NodeId)builder.ApplicationServices.GetService(typeof(NodeId)); + node.Start(nodeId); + } + + private static async Task CreateConfiguration(IApplicationBuilder builder) + { + // 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 = (IOptions)builder.ApplicationServices.GetService(typeof(IOptions)); + + // now create the config + var internalConfigCreator = (IInternalConfigurationCreator)builder.ApplicationServices.GetService(typeof(IInternalConfigurationCreator)); + var internalConfig = await internalConfigCreator.Create(fileConfig.Value); + + // now save it in memory + var internalConfigRepo = (IInternalConfigurationRepository)builder.ApplicationServices.GetService(typeof(IInternalConfigurationRepository)); + internalConfigRepo.AddOrReplace(internalConfig.Data); + + var fileConfigRepo = (IFileConfigurationRepository)builder.ApplicationServices.GetService(typeof(IFileConfigurationRepository)); + + var adminPath = (IAdministrationPath)builder.ApplicationServices.GetService(typeof(IAdministrationPath)); + + if (UsingConsul(fileConfigRepo)) + { + //Lots of jazz happens in here..check it out if you are using consul to store your config. + await SetFileConfigInConsul(builder, fileConfigRepo, fileConfig, internalConfigCreator, internalConfigRepo); + } + else if(AdministrationApiInUse(adminPath)) { //We have to make sure the file config is set for the ocelot.env.json and ocelot.json so that if we pull it from the - //admin api it works...boy this is getting a spit spags boll. - var fileConfigSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter)); - - await SetFileConfig(fileConfigSetter, fileConfig); - } - - return GetOcelotConfigAndReturn(internalConfigRepo); - } - - private static bool AdministrationApiInUse(IAdministrationPath adminPath) - { - return adminPath.GetType() != typeof(NullAdministrationPath); - } - - private static async Task SetFileConfigInConsul(IApplicationBuilder builder, - IFileConfigurationRepository fileConfigRepo, IOptions fileConfig, - IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo) - { - // get the config from consul. - var fileConfigFromConsul = await fileConfigRepo.Get(); - - if (IsError(fileConfigFromConsul)) - { - ThrowToStopOcelotStarting(fileConfigFromConsul); - } - else if (ConfigNotStoredInConsul(fileConfigFromConsul)) - { - //there was no config in consul set the file in config in consul - await fileConfigRepo.Set(fileConfig.Value); - } - else - { - // create the internal config from consul data - var internalConfig = await internalConfigCreator.Create(fileConfigFromConsul.Data); - - if (IsError(internalConfig)) - { - ThrowToStopOcelotStarting(internalConfig); - } - else - { - // add the internal config to the internal repo - var response = internalConfigRepo.AddOrReplace(internalConfig.Data); - - if (IsError(response)) - { - ThrowToStopOcelotStarting(response); - } - } - - if (IsError(internalConfig)) - { - ThrowToStopOcelotStarting(internalConfig); - } - } - - //todo - this starts the poller if it has been registered...please this is so bad. - var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller)); - } - - private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptions fileConfig) - { - var response = await fileConfigSetter.Set(fileConfig.Value); - - if (IsError(response)) - { - ThrowToStopOcelotStarting(response); - } - } - - private static bool ConfigNotStoredInConsul(Responses.Response fileConfigFromConsul) - { - return fileConfigFromConsul.Data == null; - } - - private static bool IsError(Response response) - { - return response == null || response.IsError; - } - - private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigurationRepository provider) - { - var ocelotConfiguration = provider.Get(); - - if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError) - { - ThrowToStopOcelotStarting(ocelotConfiguration); - } - - return ocelotConfiguration.Data; - } - - private static void ThrowToStopOcelotStarting(Response config) - { - throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}"); - } - - private static bool UsingConsul(IFileConfigurationRepository fileConfigRepo) - { - return fileConfigRepo.GetType() == typeof(ConsulFileConfigurationRepository); - } - - private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration) - { - if (!string.IsNullOrEmpty(configuration.AdministrationPath)) - { - builder.Map(configuration.AdministrationPath, app => - { - //todo - hack so we know that we are using internal identity server - var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration)); - if (identityServerConfiguration != null) - { - app.UseIdentityServer(); - } - - app.UseAuthentication(); - app.UseMvc(); - }); - } - } - - private static void ConfigureDiagnosticListener(IApplicationBuilder builder) - { - var env = (IHostingEnvironment)builder.ApplicationServices.GetService(typeof(IHostingEnvironment)); - var listener = (OcelotDiagnosticListener)builder.ApplicationServices.GetService(typeof(OcelotDiagnosticListener)); - var diagnosticListener = (DiagnosticListener)builder.ApplicationServices.GetService(typeof(DiagnosticListener)); - diagnosticListener.SubscribeWithAdapter(listener); - } - - private static void OnShutdown(IApplicationBuilder app) - { - var node = (INode)app.ApplicationServices.GetService(typeof(INode)); - node.Stop(); - } - } -} + //admin api it works...boy this is getting a spit spags boll. + var fileConfigSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter)); + + await SetFileConfig(fileConfigSetter, fileConfig); + } + + return GetOcelotConfigAndReturn(internalConfigRepo); + } + + private static bool AdministrationApiInUse(IAdministrationPath adminPath) + { + return adminPath.GetType() != typeof(NullAdministrationPath); + } + + private static async Task SetFileConfigInConsul(IApplicationBuilder builder, + IFileConfigurationRepository fileConfigRepo, IOptions fileConfig, + IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo) + { + // get the config from consul. + var fileConfigFromConsul = await fileConfigRepo.Get(); + + if (IsError(fileConfigFromConsul)) + { + ThrowToStopOcelotStarting(fileConfigFromConsul); + } + else if (ConfigNotStoredInConsul(fileConfigFromConsul)) + { + //there was no config in consul set the file in config in consul + await fileConfigRepo.Set(fileConfig.Value); + } + else + { + // create the internal config from consul data + var internalConfig = await internalConfigCreator.Create(fileConfigFromConsul.Data); + + if (IsError(internalConfig)) + { + ThrowToStopOcelotStarting(internalConfig); + } + else + { + // add the internal config to the internal repo + var response = internalConfigRepo.AddOrReplace(internalConfig.Data); + + if (IsError(response)) + { + ThrowToStopOcelotStarting(response); + } + } + + if (IsError(internalConfig)) + { + ThrowToStopOcelotStarting(internalConfig); + } + } + } + + private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptions fileConfig) + { + var response = await fileConfigSetter.Set(fileConfig.Value); + + if (IsError(response)) + { + ThrowToStopOcelotStarting(response); + } + } + + private static bool ConfigNotStoredInConsul(Responses.Response fileConfigFromConsul) + { + return fileConfigFromConsul.Data == null; + } + + private static bool IsError(Response response) + { + return response == null || response.IsError; + } + + private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigurationRepository provider) + { + var ocelotConfiguration = provider.Get(); + + if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError) + { + ThrowToStopOcelotStarting(ocelotConfiguration); + } + + return ocelotConfiguration.Data; + } + + private static void ThrowToStopOcelotStarting(Response config) + { + throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}"); + } + + private static bool UsingConsul(IFileConfigurationRepository fileConfigRepo) + { + return fileConfigRepo.GetType() == typeof(ConsulFileConfigurationRepository); + } + + private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration) + { + if (!string.IsNullOrEmpty(configuration.AdministrationPath)) + { + builder.Map(configuration.AdministrationPath, app => + { + //todo - hack so we know that we are using internal identity server + var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration)); + if (identityServerConfiguration != null) + { + app.UseIdentityServer(); + } + + app.UseAuthentication(); + app.UseMvc(); + }); + } + } + + private static void ConfigureDiagnosticListener(IApplicationBuilder builder) + { + var env = (IHostingEnvironment)builder.ApplicationServices.GetService(typeof(IHostingEnvironment)); + var listener = (OcelotDiagnosticListener)builder.ApplicationServices.GetService(typeof(OcelotDiagnosticListener)); + var diagnosticListener = (DiagnosticListener)builder.ApplicationServices.GetService(typeof(DiagnosticListener)); + diagnosticListener.SubscribeWithAdapter(listener); + } + + private static void OnShutdown(IApplicationBuilder app) + { + var node = (INode)app.ApplicationServices.GetService(typeof(INode)); + node.Stop(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs index 5a5cce82..af99888d 100644 --- a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs @@ -1,390 +1,391 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Text; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; -using Ocelot.Configuration.File; -using Shouldly; -using TestStack.BDDfy; -using Xunit; -using static Ocelot.Infrastructure.Wait; - -namespace Ocelot.AcceptanceTests -{ - public class ConfigurationInConsulTests : IDisposable - { - private IWebHost _builder; - private readonly Steps _steps; - private IWebHost _fakeConsulBuilder; - private FileConfiguration _config; - - public ConfigurationInConsulTests() - { - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_with_simple_url() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51779, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = 9500 - } - } - }; - - var fakeConsulServiceDiscoveryUrl = "http://localhost:9500"; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51779, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = 9502 - } - } - }; - - var fakeConsulServiceDiscoveryUrl = "http://localhost:9502"; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_load_configuration_out_of_consul() - { - var consulPort = 8500; - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = consulPort - } - } - }; - - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - var consulConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/status", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51779, - } - }, - UpstreamPathTemplate = "/cs/status", - UpstreamHttpMethod = new List {"Get"} - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) - .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_load_configuration_out_of_consul_if_it_is_changed() - { - var consulPort = 8506; - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = consulPort - } - } - }; - - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - var consulConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/status", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51780, - } - }, - UpstreamPathTemplate = "/cs/status", - UpstreamHttpMethod = new List {"Get"} - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = consulPort - } - } - }; - - var secondConsulConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/status", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51780, - } - }, - UpstreamPathTemplate = "/cs/status/awesome", - UpstreamHttpMethod = new List {"Get"} - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) - .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) - .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .When(x => GivenTheConsulConfigurationIs(secondConsulConfig)) - .Then(x => ThenTheConfigIsUpdatedInOcelot()) - .BDDfy(); - } - - private void ThenTheConfigIsUpdatedInOcelot() - { - var result = WaitFor(20000).Until(() => { - try - { - _steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome"); - _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK); - _steps.ThenTheResponseBodyShouldBe("Hello from Laura"); - return true; - } - catch (Exception) - { - return false; - } - }); - result.ShouldBeTrue(); - } - - private void GivenTheConsulConfigurationIs(FileConfiguration config) - { - _config = config; - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url) - { - _fakeConsulBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") - { - var json = JsonConvert.SerializeObject(_config); - - var bytes = Encoding.UTF8.GetBytes(json); - - var base64 = Convert.ToBase64String(bytes); - - var kvp = new FakeConsulGetResponse(base64); - - await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp }); - } - else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") - { - try - { - var reader = new StreamReader(context.Request.Body); - - var json = reader.ReadToEnd(); - - _config = JsonConvert.DeserializeObject(json); - - var response = JsonConvert.SerializeObject(true); - - await context.Response.WriteAsync(response); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - }); - }) - .Build(); - - _fakeConsulBuilder.Start(); - } - - public class FakeConsulGetResponse - { - public FakeConsulGetResponse(string value) - { - Value = value; - } - - public int CreateIndex => 100; - public int ModifyIndex => 200; - public int LockIndex => 200; - public string Key => "InternalConfiguration"; - public int Flags => 0; - public string Value { get; private set; } - public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; - } - - private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) - { - _builder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.UsePathBase(basePath); - - app.Run(async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }) - .Build(); - - _builder.Start(); - } - - public void Dispose() - { - _builder?.Dispose(); - _steps.Dispose(); - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Text; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Ocelot.Configuration.File; +using Shouldly; +using TestStack.BDDfy; +using Xunit; +using static Ocelot.Infrastructure.Wait; + +namespace Ocelot.AcceptanceTests +{ + public class ConfigurationInConsulTests : IDisposable + { + private IWebHost _builder; + private readonly Steps _steps; + private IWebHost _fakeConsulBuilder; + private FileConfiguration _config; + + public ConfigurationInConsulTests() + { + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_with_simple_url() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51779, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = 9500 + } + } + }; + + var fakeConsulServiceDiscoveryUrl = "http://localhost:9500"; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51779, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = 9502 + } + } + }; + + var fakeConsulServiceDiscoveryUrl = "http://localhost:9502"; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_load_configuration_out_of_consul() + { + var consulPort = 8500; + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = consulPort + } + } + }; + + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + + var consulConfig = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/status", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51779, + } + }, + UpstreamPathTemplate = "/cs/status", + UpstreamHttpMethod = new List {"Get"} + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) + .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_load_configuration_out_of_consul_if_it_is_changed() + { + var consulPort = 8506; + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = consulPort + } + } + }; + + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + + var consulConfig = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/status", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51780, + } + }, + UpstreamPathTemplate = "/cs/status", + UpstreamHttpMethod = new List {"Get"} + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = consulPort + } + } + }; + + var secondConsulConfig = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/status", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51780, + } + }, + UpstreamPathTemplate = "/cs/status/awesome", + UpstreamHttpMethod = new List {"Get"} + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) + .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) + .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .When(x => GivenTheConsulConfigurationIs(secondConsulConfig)) + .Then(x => ThenTheConfigIsUpdatedInOcelot()) + .BDDfy(); + } + + private void ThenTheConfigIsUpdatedInOcelot() + { + var result = WaitFor(20000).Until(() => { + try + { + _steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome"); + _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK); + _steps.ThenTheResponseBodyShouldBe("Hello from Laura"); + return true; + } + catch (Exception) + { + return false; + } + }); + result.ShouldBeTrue(); + } + + private void GivenTheConsulConfigurationIs(FileConfiguration config) + { + _config = config; + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url) + { + _fakeConsulBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + { + var json = JsonConvert.SerializeObject(_config); + + var bytes = Encoding.UTF8.GetBytes(json); + + var base64 = Convert.ToBase64String(bytes); + + var kvp = new FakeConsulGetResponse(base64); + + await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp }); + } + else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + { + try + { + var reader = new StreamReader(context.Request.Body); + + var json = reader.ReadToEnd(); + + _config = JsonConvert.DeserializeObject(json); + + var response = JsonConvert.SerializeObject(true); + + await context.Response.WriteAsync(response); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + }); + }) + .Build(); + + _fakeConsulBuilder.Start(); + } + + public class FakeConsulGetResponse + { + public FakeConsulGetResponse(string value) + { + Value = value; + } + + public int CreateIndex => 100; + public int ModifyIndex => 200; + public int LockIndex => 200; + public string Key => "InternalConfiguration"; + public int Flags => 0; + public string Value { get; private set; } + public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; + } + + private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) + { + _builder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.UsePathBase(basePath); + + app.Run(async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _builder.Start(); + } + + public void Dispose() + { + _builder?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ConsulFileConfigurationPollerTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs similarity index 73% rename from test/Ocelot.UnitTests/Configuration/ConsulFileConfigurationPollerTests.cs rename to test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs index 17fc604d..18e8ddb6 100644 --- a/test/Ocelot.UnitTests/Configuration/ConsulFileConfigurationPollerTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs @@ -12,37 +12,39 @@ using TestStack.BDDfy; using Xunit; using Shouldly; using static Ocelot.Infrastructure.Wait; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration; namespace Ocelot.UnitTests.Configuration { - public class ConsulFileConfigurationPollerTests : IDisposable + public class FileConfigurationPollerTests : IDisposable { - private readonly ConsulFileConfigurationPoller _poller; + private readonly FileConfigurationPoller _poller; private Mock _factory; private readonly Mock _repo; - private readonly Mock _setter; private readonly FileConfiguration _fileConfig; - private Mock _config; + private Mock _config; + private readonly Mock _internalConfigRepo; + private readonly Mock _internalConfigCreator; + private IInternalConfiguration _internalConfig; - public ConsulFileConfigurationPollerTests() + public FileConfigurationPollerTests() { var logger = new Mock(); _factory = new Mock(); - _factory.Setup(x => x.CreateLogger()).Returns(logger.Object); + _factory.Setup(x => x.CreateLogger()).Returns(logger.Object); _repo = new Mock(); - _setter = new Mock(); _fileConfig = new FileConfiguration(); - _config = new Mock(); + _config = new Mock(); _repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse(_fileConfig)); _config.Setup(x => x.Delay).Returns(100); - _poller = new ConsulFileConfigurationPoller(_factory.Object, _repo.Object, _setter.Object, _config.Object); + _internalConfigRepo = new Mock(); + _internalConfigCreator = new Mock(); + _internalConfigCreator.Setup(x => x.Create(It.IsAny())).ReturnsAsync(new OkResponse(_internalConfig)); + _poller = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object); + _poller.StartAsync(new CancellationToken()); } - - public void Dispose() - { - _poller.Dispose(); - } - + [Fact] public void should_start() { @@ -141,10 +143,11 @@ namespace Ocelot.UnitTests.Configuration private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times) { - var result = WaitFor(2000).Until(() => { + var result = WaitFor(4000).Until(() => { try { - _setter.Verify(x => x.Set(fileConfig), Times.Exactly(times)); + _internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.Exactly(times)); + _internalConfigCreator.Verify(x => x.Create(fileConfig), Times.Exactly(times)); return true; } catch(Exception) @@ -157,10 +160,11 @@ namespace Ocelot.UnitTests.Configuration private void ThenTheSetterIsCalledAtLeast(FileConfiguration fileConfig, int times) { - var result = WaitFor(2000).Until(() => { + var result = WaitFor(4000).Until(() => { try { - _setter.Verify(x => x.Set(fileConfig), Times.AtLeast(times)); + _internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.AtLeast(times)); + _internalConfigCreator.Verify(x => x.Create(fileConfig), Times.AtLeast(times)); return true; } catch(Exception) @@ -170,5 +174,10 @@ namespace Ocelot.UnitTests.Configuration }); result.ShouldBeTrue(); } + + public void Dispose() + { + _poller.Dispose(); + } } }