Made the file config poller use IHostedService, bit more generic, not… (#507)

* Made the file config poller use IHostedService, bit more generic, not just need to provide the correct implementations of the repo services and it will poll anything..this means we can open up redis for #458

* removed comments
This commit is contained in:
Tom Pallister 2018-07-29 18:23:49 +01:00 committed by GitHub
parent 1817564ea5
commit 0f2cf2d188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 798 additions and 763 deletions

View File

@ -1,84 +1,112 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Microsoft.Extensions.Hosting;
using Ocelot.Configuration.File; using Newtonsoft.Json;
using Ocelot.Configuration.Setter; using Ocelot.Configuration.Creator;
using Ocelot.Logging; using Ocelot.Configuration.File;
using Ocelot.Configuration.Setter;
namespace Ocelot.Configuration.Repository using Ocelot.Logging;
{
public class ConsulFileConfigurationPoller : IDisposable namespace Ocelot.Configuration.Repository
{ {
private readonly IOcelotLogger _logger; public class FileConfigurationPoller : IHostedService, IDisposable
private readonly IFileConfigurationRepository _repo; {
private readonly IFileConfigurationSetter _setter; private readonly IOcelotLogger _logger;
private string _previousAsJson; private readonly IFileConfigurationRepository _repo;
private readonly Timer _timer; private string _previousAsJson;
private bool _polling; private Timer _timer;
private readonly IConsulPollerConfiguration _config; private bool _polling;
private readonly IFileConfigurationPollerOptions _options;
public ConsulFileConfigurationPoller( private readonly IInternalConfigurationRepository _internalConfigRepo;
IOcelotLoggerFactory factory, private readonly IInternalConfigurationCreator _internalConfigCreator;
IFileConfigurationRepository repo,
IFileConfigurationSetter setter, public FileConfigurationPoller(
IConsulPollerConfiguration config) IOcelotLoggerFactory factory,
{ IFileConfigurationRepository repo,
_setter = setter; IFileConfigurationPollerOptions options,
_config = config; IInternalConfigurationRepository internalConfigRepo,
_logger = factory.CreateLogger<ConsulFileConfigurationPoller>(); IInternalConfigurationCreator internalConfigCreator)
_repo = repo; {
_previousAsJson = ""; _internalConfigRepo = internalConfigRepo;
_timer = new Timer(async x => _internalConfigCreator = internalConfigCreator;
{ _options = options;
if(_polling) _logger = factory.CreateLogger<FileConfigurationPoller>();
{ _repo = repo;
return; _previousAsJson = "";
} }
_polling = true; public Task StartAsync(CancellationToken cancellationToken)
await Poll(); {
_polling = false; _logger.LogInformation($"{nameof(FileConfigurationPoller)} is starting.");
}, null, _config.Delay, _config.Delay);
} _timer = new Timer(async x =>
{
private async Task Poll() if(_polling)
{ {
_logger.LogInformation("Started polling consul"); return;
}
var fileConfig = await _repo.Get();
_polling = true;
if(fileConfig.IsError) await Poll();
{ _polling = false;
_logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}"); }, null, _options.Delay, _options.Delay);
return;
} return Task.CompletedTask;
}
var asJson = ToJson(fileConfig.Data);
public Task StopAsync(CancellationToken cancellationToken)
if(!fileConfig.IsError && asJson != _previousAsJson) {
{ _logger.LogInformation($"{nameof(FileConfigurationPoller)} is stopping.");
await _setter.Set(fileConfig.Data);
_previousAsJson = asJson; _timer?.Change(Timeout.Infinite, 0);
}
return Task.CompletedTask;
_logger.LogInformation("Finished polling consul"); }
}
private async Task Poll()
/// <summary> {
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day! _logger.LogInformation("Started polling consul");
/// </summary>
/// <returns>hash of the config</returns> var fileConfig = await _repo.Get();
private string ToJson(FileConfiguration config)
{ if(fileConfig.IsError)
var currentHash = JsonConvert.SerializeObject(config); {
return currentHash; _logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}");
} return;
}
public void Dispose()
{ var asJson = ToJson(fileConfig.Data);
_timer.Dispose();
} 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");
}
/// <summary>
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day!
/// </summary>
/// <returns>hash of the config</returns>
private string ToJson(FileConfiguration config)
{
var currentHash = JsonConvert.SerializeObject(config);
return currentHash;
}
public void Dispose()
{
_timer.Dispose();
}
}
}

View File

@ -1,6 +1,6 @@
namespace Ocelot.Configuration.Repository namespace Ocelot.Configuration.Repository
{ {
public interface IConsulPollerConfiguration public interface IFileConfigurationPollerOptions
{ {
int Delay { get; } int Delay { get; }
} }

View File

@ -1,6 +1,6 @@
namespace Ocelot.Configuration.Repository namespace Ocelot.Configuration.Repository
{ {
public class InMemoryConsulPollerConfiguration : IConsulPollerConfiguration public class InMemoryFileConfigurationPollerOptions : IFileConfigurationPollerOptions
{ {
public int Delay => 1000; public int Delay => 1000;
} }

View File

@ -8,7 +8,7 @@ namespace Ocelot.Configuration.Setter
{ {
public class FileAndInternalConfigurationSetter : IFileConfigurationSetter public class FileAndInternalConfigurationSetter : IFileConfigurationSetter
{ {
private readonly IInternalConfigurationRepository _configRepo; private readonly IInternalConfigurationRepository internalConfigRepo;
private readonly IInternalConfigurationCreator _configCreator; private readonly IInternalConfigurationCreator _configCreator;
private readonly IFileConfigurationRepository _repo; private readonly IFileConfigurationRepository _repo;
@ -17,7 +17,7 @@ namespace Ocelot.Configuration.Setter
IInternalConfigurationCreator configCreator, IInternalConfigurationCreator configCreator,
IFileConfigurationRepository repo) IFileConfigurationRepository repo)
{ {
_configRepo = configRepo; internalConfigRepo = configRepo;
_configCreator = configCreator; _configCreator = configCreator;
_repo = repo; _repo = repo;
} }
@ -35,7 +35,7 @@ namespace Ocelot.Configuration.Setter
if(!config.IsError) if(!config.IsError)
{ {
_configRepo.AddOrReplace(config.Data); internalConfigRepo.AddOrReplace(config.Data);
} }
return new ErrorResponse(config.Errors); return new ErrorResponse(config.Errors);

View File

@ -160,7 +160,7 @@ namespace Ocelot.DependencyInjection
// We add this here so that we can always inject something into the factory for IoC.. // We add this here so that we can always inject something into the factory for IoC..
_services.AddSingleton<IServiceTracer, FakeServiceTracer>(); _services.AddSingleton<IServiceTracer, FakeServiceTracer>();
_services.TryAddSingleton<IConsulPollerConfiguration, InMemoryConsulPollerConfiguration>(); _services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
_services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>(); _services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
_services.TryAddSingleton<IPlaceholders, Placeholders>(); _services.TryAddSingleton<IPlaceholders, Placeholders>();
_services.TryAddSingleton<IConsulClientFactory, ConsulClientFactory>(); _services.TryAddSingleton<IConsulClientFactory, ConsulClientFactory>();
@ -245,7 +245,7 @@ namespace Ocelot.DependencyInjection
public IOcelotBuilder AddStoreOcelotConfigurationInConsul() public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
{ {
_services.AddSingleton<ConsulFileConfigurationPoller>(); _services.AddHostedService<FileConfigurationPoller>();
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>(); _services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
return this; return this;
} }

View File

@ -1,266 +1,263 @@
namespace Ocelot.Middleware namespace Ocelot.Middleware
{ {
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Diagnostics; using System.Diagnostics;
using DependencyInjection; using DependencyInjection;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository; using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter; using Ocelot.Configuration.Setter;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Logging; using Ocelot.Logging;
using Rafty.Concensus; using Rafty.Concensus;
using Rafty.Infrastructure; using Rafty.Infrastructure;
using Ocelot.Middleware.Pipeline; using Ocelot.Middleware.Pipeline;
using Pivotal.Discovery.Client; using Pivotal.Discovery.Client;
using Rafty.Concensus.Node; using Rafty.Concensus.Node;
public static class OcelotMiddlewareExtensions public static class OcelotMiddlewareExtensions
{ {
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder) public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder)
{ {
await builder.UseOcelot(new OcelotPipelineConfiguration()); await builder.UseOcelot(new OcelotPipelineConfiguration());
return builder; return builder;
} }
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, Action<OcelotPipelineConfiguration> pipelineConfiguration) public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, Action<OcelotPipelineConfiguration> pipelineConfiguration)
{ {
var config = new OcelotPipelineConfiguration(); var config = new OcelotPipelineConfiguration();
pipelineConfiguration?.Invoke(config); pipelineConfiguration?.Invoke(config);
return await builder.UseOcelot(config); return await builder.UseOcelot(config);
} }
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{ {
var configuration = await CreateConfiguration(builder); var configuration = await CreateConfiguration(builder);
CreateAdministrationArea(builder, configuration); CreateAdministrationArea(builder, configuration);
if (UsingRafty(builder)) 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<IInternalConfiguration> 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<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));
// 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. SetUpRafty(builder);
await SetFileConfigInConsul(builder, fileConfigRepo, fileConfig, internalConfigCreator, internalConfigRepo); }
}
else if(AdministrationApiInUse(adminPath)) 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<IInternalConfiguration> 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<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));
// 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 //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. //admin api it works...boy this is getting a spit spags boll.
var fileConfigSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter)); var fileConfigSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter));
await SetFileConfig(fileConfigSetter, fileConfig); await SetFileConfig(fileConfigSetter, fileConfig);
} }
return GetOcelotConfigAndReturn(internalConfigRepo); return GetOcelotConfigAndReturn(internalConfigRepo);
} }
private static bool AdministrationApiInUse(IAdministrationPath adminPath) private static bool AdministrationApiInUse(IAdministrationPath adminPath)
{ {
return adminPath.GetType() != typeof(NullAdministrationPath); return adminPath.GetType() != typeof(NullAdministrationPath);
} }
private static async Task SetFileConfigInConsul(IApplicationBuilder builder, private static async Task SetFileConfigInConsul(IApplicationBuilder builder,
IFileConfigurationRepository fileConfigRepo, IOptions<FileConfiguration> fileConfig, IFileConfigurationRepository fileConfigRepo, IOptions<FileConfiguration> fileConfig,
IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo) IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo)
{ {
// get the config from consul. // get the config from consul.
var fileConfigFromConsul = await fileConfigRepo.Get(); var fileConfigFromConsul = await fileConfigRepo.Get();
if (IsError(fileConfigFromConsul)) if (IsError(fileConfigFromConsul))
{ {
ThrowToStopOcelotStarting(fileConfigFromConsul); ThrowToStopOcelotStarting(fileConfigFromConsul);
} }
else if (ConfigNotStoredInConsul(fileConfigFromConsul)) else if (ConfigNotStoredInConsul(fileConfigFromConsul))
{ {
//there was no config in consul set the file in config in consul //there was no config in consul set the file in config in consul
await fileConfigRepo.Set(fileConfig.Value); await fileConfigRepo.Set(fileConfig.Value);
} }
else else
{ {
// create the internal config from consul data // create the internal config from consul data
var internalConfig = await internalConfigCreator.Create(fileConfigFromConsul.Data); var internalConfig = await internalConfigCreator.Create(fileConfigFromConsul.Data);
if (IsError(internalConfig)) if (IsError(internalConfig))
{ {
ThrowToStopOcelotStarting(internalConfig); ThrowToStopOcelotStarting(internalConfig);
} }
else else
{ {
// add the internal config to the internal repo // add the internal config to the internal repo
var response = internalConfigRepo.AddOrReplace(internalConfig.Data); var response = internalConfigRepo.AddOrReplace(internalConfig.Data);
if (IsError(response)) if (IsError(response))
{ {
ThrowToStopOcelotStarting(response); ThrowToStopOcelotStarting(response);
} }
} }
if (IsError(internalConfig)) if (IsError(internalConfig))
{ {
ThrowToStopOcelotStarting(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<FileConfiguration> fileConfig)
} {
var response = await fileConfigSetter.Set(fileConfig.Value);
private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptions<FileConfiguration> fileConfig)
{ if (IsError(response))
var response = await fileConfigSetter.Set(fileConfig.Value); {
ThrowToStopOcelotStarting(response);
if (IsError(response)) }
{ }
ThrowToStopOcelotStarting(response);
} private static bool ConfigNotStoredInConsul(Responses.Response<FileConfiguration> fileConfigFromConsul)
} {
return fileConfigFromConsul.Data == null;
private static bool ConfigNotStoredInConsul(Responses.Response<FileConfiguration> fileConfigFromConsul) }
{
return fileConfigFromConsul.Data == null; private static bool IsError(Response response)
} {
return response == null || response.IsError;
private static bool IsError(Response response) }
{
return response == null || response.IsError; private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigurationRepository provider)
} {
var ocelotConfiguration = provider.Get();
private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigurationRepository provider)
{ if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError)
var ocelotConfiguration = provider.Get(); {
ThrowToStopOcelotStarting(ocelotConfiguration);
if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError) }
{
ThrowToStopOcelotStarting(ocelotConfiguration); return ocelotConfiguration.Data;
} }
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 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 bool UsingConsul(IFileConfigurationRepository fileConfigRepo) }
{
return fileConfigRepo.GetType() == typeof(ConsulFileConfigurationRepository); private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration)
} {
if (!string.IsNullOrEmpty(configuration.AdministrationPath))
private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration) {
{ builder.Map(configuration.AdministrationPath, app =>
if (!string.IsNullOrEmpty(configuration.AdministrationPath)) {
{ //todo - hack so we know that we are using internal identity server
builder.Map(configuration.AdministrationPath, app => var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
{ if (identityServerConfiguration != null)
//todo - hack so we know that we are using internal identity server {
var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration)); app.UseIdentityServer();
if (identityServerConfiguration != null) }
{
app.UseIdentityServer(); app.UseAuthentication();
} app.UseMvc();
});
app.UseAuthentication(); }
app.UseMvc(); }
});
} private static void ConfigureDiagnosticListener(IApplicationBuilder builder)
} {
var env = (IHostingEnvironment)builder.ApplicationServices.GetService(typeof(IHostingEnvironment));
private static void ConfigureDiagnosticListener(IApplicationBuilder builder) var listener = (OcelotDiagnosticListener)builder.ApplicationServices.GetService(typeof(OcelotDiagnosticListener));
{ var diagnosticListener = (DiagnosticListener)builder.ApplicationServices.GetService(typeof(DiagnosticListener));
var env = (IHostingEnvironment)builder.ApplicationServices.GetService(typeof(IHostingEnvironment)); diagnosticListener.SubscribeWithAdapter(listener);
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));
private static void OnShutdown(IApplicationBuilder app) node.Stop();
{ }
var node = (INode)app.ApplicationServices.GetService(typeof(INode)); }
node.Stop(); }
}
}
}

View File

@ -1,390 +1,391 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using static Ocelot.Infrastructure.Wait; using static Ocelot.Infrastructure.Wait;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
public class ConfigurationInConsulTests : IDisposable public class ConfigurationInConsulTests : IDisposable
{ {
private IWebHost _builder; private IWebHost _builder;
private readonly Steps _steps; private readonly Steps _steps;
private IWebHost _fakeConsulBuilder; private IWebHost _fakeConsulBuilder;
private FileConfiguration _config; private FileConfiguration _config;
public ConfigurationInConsulTests() public ConfigurationInConsulTests()
{ {
_steps = new Steps(); _steps = new Steps();
} }
[Fact] [Fact]
public void should_return_response_200_with_simple_url() public void should_return_response_200_with_simple_url()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51779, Port = 51779,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
}, },
GlobalConfiguration = new FileGlobalConfiguration() GlobalConfiguration = new FileGlobalConfiguration()
{ {
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{ {
Host = "localhost", Host = "localhost",
Port = 9500 Port = 9500
} }
} }
}; };
var fakeConsulServiceDiscoveryUrl = "http://localhost:9500"; var fakeConsulServiceDiscoveryUrl = "http://localhost:9500";
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51779, Port = 51779,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
}, },
GlobalConfiguration = new FileGlobalConfiguration() GlobalConfiguration = new FileGlobalConfiguration()
{ {
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{ {
Host = "localhost", Host = "localhost",
Port = 9502 Port = 9502
} }
} }
}; };
var fakeConsulServiceDiscoveryUrl = "http://localhost:9502"; var fakeConsulServiceDiscoveryUrl = "http://localhost:9502";
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_load_configuration_out_of_consul() public void should_load_configuration_out_of_consul()
{ {
var consulPort = 8500; var consulPort = 8500;
var configuration = new FileConfiguration
{ var configuration = new FileConfiguration
GlobalConfiguration = new FileGlobalConfiguration() {
{ GlobalConfiguration = new FileGlobalConfiguration()
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() {
{ ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
Host = "localhost", {
Port = consulPort Host = "localhost",
} Port = consulPort
} }
}; }
};
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var consulConfig = new FileConfiguration
{ var consulConfig = new FileConfiguration
ReRoutes = new List<FileReRoute> {
{ ReRoutes = new List<FileReRoute>
new FileReRoute {
{ new FileReRoute
DownstreamPathTemplate = "/status", {
DownstreamScheme = "http", DownstreamPathTemplate = "/status",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamScheme = "http",
{ DownstreamHostAndPorts = new List<FileHostAndPort>
new FileHostAndPort {
{ new FileHostAndPort
Host = "localhost", {
Port = 51779, Host = "localhost",
} Port = 51779,
}, }
UpstreamPathTemplate = "/cs/status", },
UpstreamHttpMethod = new List<string> {"Get"} UpstreamPathTemplate = "/cs/status",
} UpstreamHttpMethod = new List<string> {"Get"}
}, }
GlobalConfiguration = new FileGlobalConfiguration() },
{ GlobalConfiguration = new FileGlobalConfiguration()
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() {
{ ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
Host = "localhost", {
Port = consulPort Host = "localhost",
} Port = consulPort
} }
}; }
};
this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura")) .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura"))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy(); .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
} .BDDfy();
}
[Fact]
public void should_load_configuration_out_of_consul_if_it_is_changed() [Fact]
{ public void should_load_configuration_out_of_consul_if_it_is_changed()
var consulPort = 8506; {
var configuration = new FileConfiguration var consulPort = 8506;
{ var configuration = new FileConfiguration
GlobalConfiguration = new FileGlobalConfiguration() {
{ GlobalConfiguration = new FileGlobalConfiguration()
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() {
{ ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
Host = "localhost", {
Port = consulPort Host = "localhost",
} Port = consulPort
} }
}; }
};
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var consulConfig = new FileConfiguration
{ var consulConfig = new FileConfiguration
ReRoutes = new List<FileReRoute> {
{ ReRoutes = new List<FileReRoute>
new FileReRoute {
{ new FileReRoute
DownstreamPathTemplate = "/status", {
DownstreamScheme = "http", DownstreamPathTemplate = "/status",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamScheme = "http",
{ DownstreamHostAndPorts = new List<FileHostAndPort>
new FileHostAndPort {
{ new FileHostAndPort
Host = "localhost", {
Port = 51780, Host = "localhost",
} Port = 51780,
}, }
UpstreamPathTemplate = "/cs/status", },
UpstreamHttpMethod = new List<string> {"Get"} UpstreamPathTemplate = "/cs/status",
} UpstreamHttpMethod = new List<string> {"Get"}
}, }
GlobalConfiguration = new FileGlobalConfiguration() },
{ GlobalConfiguration = new FileGlobalConfiguration()
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() {
{ ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
Host = "localhost", {
Port = consulPort Host = "localhost",
} Port = consulPort
} }
}; }
};
var secondConsulConfig = new FileConfiguration
{ var secondConsulConfig = new FileConfiguration
ReRoutes = new List<FileReRoute> {
{ ReRoutes = new List<FileReRoute>
new FileReRoute {
{ new FileReRoute
DownstreamPathTemplate = "/status", {
DownstreamScheme = "http", DownstreamPathTemplate = "/status",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamScheme = "http",
{ DownstreamHostAndPorts = new List<FileHostAndPort>
new FileHostAndPort {
{ new FileHostAndPort
Host = "localhost", {
Port = 51780, Host = "localhost",
} Port = 51780,
}, }
UpstreamPathTemplate = "/cs/status/awesome", },
UpstreamHttpMethod = new List<string> {"Get"} UpstreamPathTemplate = "/cs/status/awesome",
} UpstreamHttpMethod = new List<string> {"Get"}
}, }
GlobalConfiguration = new FileGlobalConfiguration() },
{ GlobalConfiguration = new FileGlobalConfiguration()
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() {
{ ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
Host = "localhost", {
Port = consulPort Host = "localhost",
} Port = consulPort
} }
}; }
};
this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura")) .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura"))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.When(x => GivenTheConsulConfigurationIs(secondConsulConfig)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Then(x => ThenTheConfigIsUpdatedInOcelot()) .When(x => GivenTheConsulConfigurationIs(secondConsulConfig))
.BDDfy(); .Then(x => ThenTheConfigIsUpdatedInOcelot())
} .BDDfy();
}
private void ThenTheConfigIsUpdatedInOcelot()
{ private void ThenTheConfigIsUpdatedInOcelot()
var result = WaitFor(20000).Until(() => { {
try var result = WaitFor(20000).Until(() => {
{ try
_steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome"); {
_steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK); _steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome");
_steps.ThenTheResponseBodyShouldBe("Hello from Laura"); _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK);
return true; _steps.ThenTheResponseBodyShouldBe("Hello from Laura");
} return true;
catch (Exception) }
{ catch (Exception)
return false; {
} return false;
}); }
result.ShouldBeTrue(); });
} result.ShouldBeTrue();
}
private void GivenTheConsulConfigurationIs(FileConfiguration config)
{ private void GivenTheConsulConfigurationIs(FileConfiguration config)
_config = config; {
} _config = config;
}
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url)
{ private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url)
_fakeConsulBuilder = new WebHostBuilder() {
.UseUrls(url) _fakeConsulBuilder = new WebHostBuilder()
.UseKestrel() .UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel()
.UseIISIntegration() .UseContentRoot(Directory.GetCurrentDirectory())
.UseUrls(url) .UseIISIntegration()
.Configure(app => .UseUrls(url)
{ .Configure(app =>
app.Run(async context => {
{ app.Run(async context =>
if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") {
{ if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration")
var json = JsonConvert.SerializeObject(_config); {
var json = JsonConvert.SerializeObject(_config);
var bytes = Encoding.UTF8.GetBytes(json);
var bytes = Encoding.UTF8.GetBytes(json);
var base64 = Convert.ToBase64String(bytes);
var base64 = Convert.ToBase64String(bytes);
var kvp = new FakeConsulGetResponse(base64);
var kvp = new FakeConsulGetResponse(base64);
await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp });
} await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp });
else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") }
{ else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration")
try {
{ try
var reader = new StreamReader(context.Request.Body); {
var reader = new StreamReader(context.Request.Body);
var json = reader.ReadToEnd();
var json = reader.ReadToEnd();
_config = JsonConvert.DeserializeObject<FileConfiguration>(json);
_config = JsonConvert.DeserializeObject<FileConfiguration>(json);
var response = JsonConvert.SerializeObject(true);
var response = JsonConvert.SerializeObject(true);
await context.Response.WriteAsync(response);
} await context.Response.WriteAsync(response);
catch (Exception e) }
{ catch (Exception e)
Console.WriteLine(e); {
throw; Console.WriteLine(e);
} throw;
} }
}); }
}) });
.Build(); })
.Build();
_fakeConsulBuilder.Start();
} _fakeConsulBuilder.Start();
}
public class FakeConsulGetResponse
{ public class FakeConsulGetResponse
public FakeConsulGetResponse(string value) {
{ public FakeConsulGetResponse(string value)
Value = value; {
} Value = value;
}
public int CreateIndex => 100;
public int ModifyIndex => 200; public int CreateIndex => 100;
public int LockIndex => 200; public int ModifyIndex => 200;
public string Key => "InternalConfiguration"; public int LockIndex => 200;
public int Flags => 0; public string Key => "InternalConfiguration";
public string Value { get; private set; } public int Flags => 0;
public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; 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)
{ private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody)
_builder = new WebHostBuilder() {
.UseUrls(url) _builder = new WebHostBuilder()
.UseKestrel() .UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel()
.UseIISIntegration() .UseContentRoot(Directory.GetCurrentDirectory())
.UseUrls(url) .UseIISIntegration()
.Configure(app => .UseUrls(url)
{ .Configure(app =>
app.UsePathBase(basePath); {
app.UsePathBase(basePath);
app.Run(async context =>
{ app.Run(async context =>
context.Response.StatusCode = statusCode; {
await context.Response.WriteAsync(responseBody); context.Response.StatusCode = statusCode;
}); await context.Response.WriteAsync(responseBody);
}) });
.Build(); })
.Build();
_builder.Start();
} _builder.Start();
}
public void Dispose()
{ public void Dispose()
_builder?.Dispose(); {
_steps.Dispose(); _builder?.Dispose();
} _steps.Dispose();
} }
} }
}

View File

@ -12,37 +12,39 @@ using TestStack.BDDfy;
using Xunit; using Xunit;
using Shouldly; using Shouldly;
using static Ocelot.Infrastructure.Wait; using static Ocelot.Infrastructure.Wait;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration;
namespace Ocelot.UnitTests.Configuration namespace Ocelot.UnitTests.Configuration
{ {
public class ConsulFileConfigurationPollerTests : IDisposable public class FileConfigurationPollerTests : IDisposable
{ {
private readonly ConsulFileConfigurationPoller _poller; private readonly FileConfigurationPoller _poller;
private Mock<IOcelotLoggerFactory> _factory; private Mock<IOcelotLoggerFactory> _factory;
private readonly Mock<IFileConfigurationRepository> _repo; private readonly Mock<IFileConfigurationRepository> _repo;
private readonly Mock<IFileConfigurationSetter> _setter;
private readonly FileConfiguration _fileConfig; private readonly FileConfiguration _fileConfig;
private Mock<IConsulPollerConfiguration> _config; private Mock<IFileConfigurationPollerOptions> _config;
private readonly Mock<IInternalConfigurationRepository> _internalConfigRepo;
private readonly Mock<IInternalConfigurationCreator> _internalConfigCreator;
private IInternalConfiguration _internalConfig;
public ConsulFileConfigurationPollerTests() public FileConfigurationPollerTests()
{ {
var logger = new Mock<IOcelotLogger>(); var logger = new Mock<IOcelotLogger>();
_factory = new Mock<IOcelotLoggerFactory>(); _factory = new Mock<IOcelotLoggerFactory>();
_factory.Setup(x => x.CreateLogger<ConsulFileConfigurationPoller>()).Returns(logger.Object); _factory.Setup(x => x.CreateLogger<FileConfigurationPoller>()).Returns(logger.Object);
_repo = new Mock<IFileConfigurationRepository>(); _repo = new Mock<IFileConfigurationRepository>();
_setter = new Mock<IFileConfigurationSetter>();
_fileConfig = new FileConfiguration(); _fileConfig = new FileConfiguration();
_config = new Mock<IConsulPollerConfiguration>(); _config = new Mock<IFileConfigurationPollerOptions>();
_repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse<FileConfiguration>(_fileConfig)); _repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse<FileConfiguration>(_fileConfig));
_config.Setup(x => x.Delay).Returns(100); _config.Setup(x => x.Delay).Returns(100);
_poller = new ConsulFileConfigurationPoller(_factory.Object, _repo.Object, _setter.Object, _config.Object); _internalConfigRepo = new Mock<IInternalConfigurationRepository>();
_internalConfigCreator = new Mock<IInternalConfigurationCreator>();
_internalConfigCreator.Setup(x => x.Create(It.IsAny<FileConfiguration>())).ReturnsAsync(new OkResponse<IInternalConfiguration>(_internalConfig));
_poller = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object);
_poller.StartAsync(new CancellationToken());
} }
public void Dispose()
{
_poller.Dispose();
}
[Fact] [Fact]
public void should_start() public void should_start()
{ {
@ -141,10 +143,11 @@ namespace Ocelot.UnitTests.Configuration
private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times) private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times)
{ {
var result = WaitFor(2000).Until(() => { var result = WaitFor(4000).Until(() => {
try 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; return true;
} }
catch(Exception) catch(Exception)
@ -157,10 +160,11 @@ namespace Ocelot.UnitTests.Configuration
private void ThenTheSetterIsCalledAtLeast(FileConfiguration fileConfig, int times) private void ThenTheSetterIsCalledAtLeast(FileConfiguration fileConfig, int times)
{ {
var result = WaitFor(2000).Until(() => { var result = WaitFor(4000).Until(() => {
try 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; return true;
} }
catch(Exception) catch(Exception)
@ -170,5 +174,10 @@ namespace Ocelot.UnitTests.Configuration
}); });
result.ShouldBeTrue(); result.ShouldBeTrue();
} }
public void Dispose()
{
_poller.Dispose();
}
} }
} }