mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Feature/hacking consul file config (#157)
* moving things around to see if I can get consul to store fileconfiguration rather than ocelotconfiguration * more refactoring to see if we can get a test for the feature * acceptance test passing for updating in consul..need to sort object comparison out * fixed the failing tests
This commit is contained in:
parent
d377482013
commit
68242102d8
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Repository;
|
using Ocelot.Configuration.Repository;
|
||||||
@ -16,9 +17,9 @@ namespace Ocelot.Configuration.Provider
|
|||||||
_repo = repo;
|
_repo = repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<FileConfiguration> Get()
|
public async Task<Response<FileConfiguration>> Get()
|
||||||
{
|
{
|
||||||
var fileConfig = _repo.Get();
|
var fileConfig = await _repo.Get();
|
||||||
return new OkResponse<FileConfiguration>(fileConfig.Data);
|
return new OkResponse<FileConfiguration>(fileConfig.Data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
@ -5,6 +6,6 @@ namespace Ocelot.Configuration.Provider
|
|||||||
{
|
{
|
||||||
public interface IFileConfigurationProvider
|
public interface IFileConfigurationProvider
|
||||||
{
|
{
|
||||||
Response<FileConfiguration> Get();
|
Task<Response<FileConfiguration>> Get();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
namespace Ocelot.Configuration.Provider
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Repository;
|
using Ocelot.Configuration.Repository;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
@ -9,16 +10,16 @@ namespace Ocelot.Configuration.Provider
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class OcelotConfigurationProvider : IOcelotConfigurationProvider
|
public class OcelotConfigurationProvider : IOcelotConfigurationProvider
|
||||||
{
|
{
|
||||||
private readonly IOcelotConfigurationRepository _repo;
|
private readonly IOcelotConfigurationRepository _config;
|
||||||
|
|
||||||
public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
|
public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_config = repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<IOcelotConfiguration>> Get()
|
public async Task<Response<IOcelotConfiguration>> Get()
|
||||||
{
|
{
|
||||||
var repoConfig = await _repo.Get();
|
var repoConfig = await _config.Get();
|
||||||
|
|
||||||
if (repoConfig.IsError)
|
if (repoConfig.IsError)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
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 IOcelotLogger _logger;
|
||||||
|
private IFileConfigurationRepository _repo;
|
||||||
|
private IFileConfigurationSetter _setter;
|
||||||
|
private string _previousAsJson;
|
||||||
|
private Timer _timer;
|
||||||
|
private bool _polling;
|
||||||
|
|
||||||
|
public ConsulFileConfigurationPoller(IOcelotLoggerFactory factory, IFileConfigurationRepository repo, IFileConfigurationSetter setter)
|
||||||
|
{
|
||||||
|
_setter = setter;
|
||||||
|
_logger = factory.CreateLogger<ConsulFileConfigurationPoller>();
|
||||||
|
_repo = repo;
|
||||||
|
_previousAsJson = "";
|
||||||
|
_timer = new Timer(async x =>
|
||||||
|
{
|
||||||
|
if(_polling)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_polling = true;
|
||||||
|
await Poll();
|
||||||
|
_polling = false;
|
||||||
|
|
||||||
|
}, null, 0, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Poll()
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Started polling consul");
|
||||||
|
|
||||||
|
var fileConfig = await _repo.Get();
|
||||||
|
|
||||||
|
if(fileConfig.IsError)
|
||||||
|
{
|
||||||
|
_logger.LogDebug($"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.LogDebug("Finished polling consul");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string ToJson(FileConfiguration config)
|
||||||
|
{
|
||||||
|
var currentHash = JsonConvert.SerializeObject(config);
|
||||||
|
return currentHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_timer.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,19 +3,20 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Consul;
|
using Consul;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Repository
|
namespace Ocelot.Configuration.Repository
|
||||||
{
|
{
|
||||||
public class ConsulOcelotConfigurationRepository : IOcelotConfigurationRepository
|
|
||||||
|
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
|
||||||
{
|
{
|
||||||
private readonly ConsulClient _consul;
|
private readonly ConsulClient _consul;
|
||||||
private string _ocelotConfiguration = "OcelotConfiguration";
|
private string _ocelotConfiguration = "OcelotConfiguration";
|
||||||
private readonly Cache.IOcelotCache<IOcelotConfiguration> _cache;
|
private readonly Cache.IOcelotCache<FileConfiguration> _cache;
|
||||||
|
|
||||||
|
public ConsulFileConfigurationRepository(Cache.IOcelotCache<FileConfiguration> cache, ServiceProviderConfiguration serviceProviderConfig)
|
||||||
public ConsulOcelotConfigurationRepository(Cache.IOcelotCache<IOcelotConfiguration> cache, ServiceProviderConfiguration serviceProviderConfig)
|
|
||||||
{
|
{
|
||||||
var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.ServiceProviderHost) ? "localhost" : serviceProviderConfig?.ServiceProviderHost;
|
var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.ServiceProviderHost) ? "localhost" : serviceProviderConfig?.ServiceProviderHost;
|
||||||
var consulPort = serviceProviderConfig?.ServiceProviderPort ?? 8500;
|
var consulPort = serviceProviderConfig?.ServiceProviderPort ?? 8500;
|
||||||
@ -27,32 +28,32 @@ namespace Ocelot.Configuration.Repository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<IOcelotConfiguration>> Get()
|
public async Task<Response<FileConfiguration>> Get()
|
||||||
{
|
{
|
||||||
var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
|
var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
|
||||||
|
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
return new OkResponse<IOcelotConfiguration>(config);
|
return new OkResponse<FileConfiguration>(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryResult = await _consul.KV.Get(_ocelotConfiguration);
|
var queryResult = await _consul.KV.Get(_ocelotConfiguration);
|
||||||
|
|
||||||
if (queryResult.Response == null)
|
if (queryResult.Response == null)
|
||||||
{
|
{
|
||||||
return new OkResponse<IOcelotConfiguration>(null);
|
return new OkResponse<FileConfiguration>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes = queryResult.Response.Value;
|
var bytes = queryResult.Response.Value;
|
||||||
|
|
||||||
var json = Encoding.UTF8.GetString(bytes);
|
var json = Encoding.UTF8.GetString(bytes);
|
||||||
|
|
||||||
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json);
|
var consulConfig = JsonConvert.DeserializeObject<FileConfiguration>(json);
|
||||||
|
|
||||||
return new OkResponse<IOcelotConfiguration>(consulConfig);
|
return new OkResponse<FileConfiguration>(consulConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response> AddOrReplace(IOcelotConfiguration ocelotConfiguration)
|
public async Task<Response> Set(FileConfiguration ocelotConfiguration)
|
||||||
{
|
{
|
||||||
var json = JsonConvert.SerializeObject(ocelotConfiguration);
|
var json = JsonConvert.SerializeObject(ocelotConfiguration);
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ namespace Ocelot.Configuration.Repository
|
|||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ErrorResponse(new UnableToSetConfigInConsulError("Unable to set config in consul"));
|
return new ErrorResponse(new UnableToSetConfigInConsulError($"Unable to set FileConfiguration in consul, response status code from consul was {result.StatusCode}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
@ -8,7 +9,7 @@ namespace Ocelot.Configuration.Repository
|
|||||||
public class FileConfigurationRepository : IFileConfigurationRepository
|
public class FileConfigurationRepository : IFileConfigurationRepository
|
||||||
{
|
{
|
||||||
private static readonly object _lock = new object();
|
private static readonly object _lock = new object();
|
||||||
public Response<FileConfiguration> Get()
|
public async Task<Response<FileConfiguration>> Get()
|
||||||
{
|
{
|
||||||
var configFilePath = $"{AppContext.BaseDirectory}/configuration.json";
|
var configFilePath = $"{AppContext.BaseDirectory}/configuration.json";
|
||||||
string json = string.Empty;
|
string json = string.Empty;
|
||||||
@ -20,7 +21,7 @@ namespace Ocelot.Configuration.Repository
|
|||||||
return new OkResponse<FileConfiguration>(fileConfiguration);
|
return new OkResponse<FileConfiguration>(fileConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response Set(FileConfiguration fileConfiguration)
|
public async Task<Response> Set(FileConfiguration fileConfiguration)
|
||||||
{
|
{
|
||||||
var configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
|
var configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
@ -5,7 +6,7 @@ namespace Ocelot.Configuration.Repository
|
|||||||
{
|
{
|
||||||
public interface IFileConfigurationRepository
|
public interface IFileConfigurationRepository
|
||||||
{
|
{
|
||||||
Response<FileConfiguration> Get();
|
Task<Response<FileConfiguration>> Get();
|
||||||
Response Set(FileConfiguration fileConfiguration);
|
Task<Response> Set(FileConfiguration fileConfiguration);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Repository
|
namespace Ocelot.Configuration.Repository
|
||||||
|
@ -22,7 +22,7 @@ namespace Ocelot.Configuration.Setter
|
|||||||
|
|
||||||
public async Task<Response> Set(FileConfiguration fileConfig)
|
public async Task<Response> Set(FileConfiguration fileConfig)
|
||||||
{
|
{
|
||||||
var response = _repo.Set(fileConfig);
|
var response = await _repo.Set(fileConfig);
|
||||||
|
|
||||||
if(response.IsError)
|
if(response.IsError)
|
||||||
{
|
{
|
||||||
|
@ -21,9 +21,9 @@ namespace Ocelot.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Get()
|
public async Task<IActionResult> Get()
|
||||||
{
|
{
|
||||||
var response = _configGetter.Get();
|
var response = await _configGetter.Get();
|
||||||
|
|
||||||
if(response.IsError)
|
if(response.IsError)
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
services.AddSingleton<ServiceProviderConfiguration>(config);
|
services.AddSingleton<ServiceProviderConfiguration>(config);
|
||||||
services.AddSingleton<IOcelotConfigurationRepository, ConsulOcelotConfigurationRepository>();
|
services.AddSingleton<ConsulFileConfigurationPoller>();
|
||||||
|
services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +91,11 @@ namespace Ocelot.DependencyInjection
|
|||||||
services.TryAddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
|
services.TryAddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
|
||||||
services.TryAddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
|
services.TryAddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
|
||||||
|
|
||||||
|
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings);
|
||||||
|
var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache);
|
||||||
|
services.TryAddSingleton<ICacheManager<FileConfiguration>>(fileConfigCacheManagerOutputCache);
|
||||||
|
services.TryAddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager);
|
||||||
|
|
||||||
services.Configure<FileConfiguration>(configurationRoot);
|
services.Configure<FileConfiguration>(configurationRoot);
|
||||||
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
||||||
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||||
|
@ -35,6 +35,14 @@ namespace Ocelot.Infrastructure.RequestData
|
|||||||
{
|
{
|
||||||
object obj;
|
object obj;
|
||||||
|
|
||||||
|
if(_httpContextAccessor.HttpContext == null || _httpContextAccessor.HttpContext.Items == null)
|
||||||
|
{
|
||||||
|
return new ErrorResponse<T>(new List<Error>
|
||||||
|
{
|
||||||
|
new CannotFindDataError($"Unable to find data for key: {key} because HttpContext or HttpContext.Items is null")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if(_httpContextAccessor.HttpContext.Items.TryGetValue(key, out obj))
|
if(_httpContextAccessor.HttpContext.Items.TryGetValue(key, out obj))
|
||||||
{
|
{
|
||||||
var data = (T) obj;
|
var data = (T) obj;
|
||||||
|
@ -29,10 +29,13 @@ namespace Ocelot.Middleware
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Provider;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
using Ocelot.Configuration.Setter;
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.LoadBalancer.Middleware;
|
using Ocelot.LoadBalancer.Middleware;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
public static class OcelotMiddlewareExtensions
|
public static class OcelotMiddlewareExtensions
|
||||||
{
|
{
|
||||||
@ -56,7 +59,9 @@ namespace Ocelot.Middleware
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
|
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
|
||||||
{
|
{
|
||||||
await CreateAdministrationArea(builder);
|
var configuration = await CreateConfiguration(builder);
|
||||||
|
|
||||||
|
await CreateAdministrationArea(builder, configuration);
|
||||||
|
|
||||||
ConfigureDiagnosticListener(builder);
|
ConfigureDiagnosticListener(builder);
|
||||||
|
|
||||||
@ -155,7 +160,34 @@ namespace Ocelot.Middleware
|
|||||||
|
|
||||||
if (ocelotConfiguration == null || ocelotConfiguration.Data == null || ocelotConfiguration.IsError)
|
if (ocelotConfiguration == null || ocelotConfiguration.Data == null || ocelotConfiguration.IsError)
|
||||||
{
|
{
|
||||||
var config = await configSetter.Set(fileConfig.Value);
|
Response config = null;
|
||||||
|
var fileConfigRepo = builder.ApplicationServices.GetService(typeof(IFileConfigurationRepository));
|
||||||
|
if (fileConfigRepo.GetType() == typeof(ConsulFileConfigurationRepository))
|
||||||
|
{
|
||||||
|
var consulFileConfigRepo = (ConsulFileConfigurationRepository) fileConfigRepo;
|
||||||
|
var ocelotConfigurationRepository =
|
||||||
|
(IOcelotConfigurationRepository) builder.ApplicationServices.GetService(
|
||||||
|
typeof(IOcelotConfigurationRepository));
|
||||||
|
var ocelotConfigurationCreator =
|
||||||
|
(IOcelotConfigurationCreator) builder.ApplicationServices.GetService(
|
||||||
|
typeof(IOcelotConfigurationCreator));
|
||||||
|
|
||||||
|
var fileConfigFromConsul = await consulFileConfigRepo.Get();
|
||||||
|
if (fileConfigFromConsul.Data == null)
|
||||||
|
{
|
||||||
|
config = await configSetter.Set(fileConfig.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var ocelotConfig = await ocelotConfigurationCreator.Create(fileConfigFromConsul.Data);
|
||||||
|
config = await ocelotConfigurationRepository.AddOrReplace(ocelotConfig.Data);
|
||||||
|
var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config = await configSetter.Set(fileConfig.Value);
|
||||||
|
}
|
||||||
|
|
||||||
if (config == null || config.IsError)
|
if (config == null || config.IsError)
|
||||||
{
|
{
|
||||||
@ -173,10 +205,8 @@ namespace Ocelot.Middleware
|
|||||||
return ocelotConfiguration.Data;
|
return ocelotConfiguration.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task CreateAdministrationArea(IApplicationBuilder builder)
|
private static async Task CreateAdministrationArea(IApplicationBuilder builder, IOcelotConfiguration configuration)
|
||||||
{
|
{
|
||||||
var configuration = await CreateConfiguration(builder);
|
|
||||||
|
|
||||||
var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
|
var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
|
||||||
|
|
||||||
if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null)
|
if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@ -20,7 +22,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
private IWebHost _builder;
|
private IWebHost _builder;
|
||||||
private readonly Steps _steps;
|
private readonly Steps _steps;
|
||||||
private IWebHost _fakeConsulBuilder;
|
private IWebHost _fakeConsulBuilder;
|
||||||
private IOcelotConfiguration _config;
|
private FileConfiguration _config;
|
||||||
|
|
||||||
public ConfigurationInConsul()
|
public ConfigurationInConsul()
|
||||||
{
|
{
|
||||||
@ -67,7 +69,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_fix_issue_142()
|
public void should_load_configuration_out_of_consul()
|
||||||
{
|
{
|
||||||
var consulPort = 8500;
|
var consulPort = 8500;
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
@ -84,28 +86,31 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||||
|
|
||||||
var serviceProviderConfig = new ServiceProviderConfigurationBuilder()
|
var consulConfig = new FileConfiguration
|
||||||
.WithServiceDiscoveryProviderHost("localhost")
|
{
|
||||||
.WithServiceDiscoveryProviderPort(consulPort)
|
ReRoutes = new List<FileReRoute>
|
||||||
.Build();
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/status",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 51779,
|
||||||
|
UpstreamPathTemplate = "/cs/status",
|
||||||
|
UpstreamHttpMethod = new List<string> {"Get"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration()
|
||||||
|
{
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = consulPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var reRoute = new ReRouteBuilder()
|
this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
|
||||||
.WithDownstreamPathTemplate("/status")
|
|
||||||
.WithUpstreamTemplatePattern("^(?i)/cs/status/$")
|
|
||||||
.WithDownstreamScheme("http")
|
|
||||||
.WithDownstreamHost("localhost")
|
|
||||||
.WithDownstreamPort(51779)
|
|
||||||
.WithUpstreamPathTemplate("/cs/status")
|
|
||||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
|
||||||
.WithReRouteKey("/cs/status|Get")
|
|
||||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, false))
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var reRoutes = new List<ReRoute> { reRoute };
|
|
||||||
|
|
||||||
var config = new OcelotConfiguration(reRoutes, null, serviceProviderConfig);
|
|
||||||
|
|
||||||
this.Given(x => GivenTheConsulConfigurationIs(config))
|
|
||||||
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
|
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
|
||||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura"))
|
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
@ -116,7 +121,95 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheConsulConfigurationIs(OcelotConfiguration config)
|
|
||||||
|
[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<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/status",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 51779,
|
||||||
|
UpstreamPathTemplate = "/cs/status",
|
||||||
|
UpstreamHttpMethod = new List<string> {"Get"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration()
|
||||||
|
{
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = consulPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var secondConsulConfig = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/status",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 51779,
|
||||||
|
UpstreamPathTemplate = "/cs/status/awesome",
|
||||||
|
UpstreamHttpMethod = new List<string> {"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())
|
||||||
|
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))
|
||||||
|
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.And(x => GivenTheConsulConfigurationIs(secondConsulConfig))
|
||||||
|
.And(x => GivenIWaitForTheConfigToReplicateToOcelot())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenIWaitForTheConfigToReplicateToOcelot()
|
||||||
|
{
|
||||||
|
Thread.Sleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheConsulConfigurationIs(FileConfiguration config)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
}
|
}
|
||||||
@ -154,7 +247,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
var json = reader.ReadToEnd();
|
var json = reader.ReadToEnd();
|
||||||
|
|
||||||
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json);
|
_config = JsonConvert.DeserializeObject<FileConfiguration>(json);
|
||||||
|
|
||||||
var response = JsonConvert.SerializeObject(true);
|
var response = JsonConvert.SerializeObject(true);
|
||||||
|
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Ocelot.Configuration.Setter;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
using Shouldly;
|
||||||
|
using static Ocelot.UnitTests.Wait;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Configuration
|
||||||
|
{
|
||||||
|
public class ConsulFileConfigurationPollerTests : IDisposable
|
||||||
|
{
|
||||||
|
private ConsulFileConfigurationPoller _poller;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private Mock<IFileConfigurationRepository> _repo;
|
||||||
|
private Mock<IFileConfigurationSetter> _setter;
|
||||||
|
private FileConfiguration _fileConfig;
|
||||||
|
|
||||||
|
public ConsulFileConfigurationPollerTests()
|
||||||
|
{
|
||||||
|
var logger = new Mock<IOcelotLogger>();
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<ConsulFileConfigurationPoller>()).Returns(logger.Object);
|
||||||
|
_repo = new Mock<IFileConfigurationRepository>();
|
||||||
|
_setter = new Mock<IFileConfigurationSetter>();
|
||||||
|
_fileConfig = new FileConfiguration();
|
||||||
|
_repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse<FileConfiguration>(_fileConfig));
|
||||||
|
_poller = new ConsulFileConfigurationPoller(_factory.Object, _repo.Object, _setter.Object);
|
||||||
|
}
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_poller.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_start()
|
||||||
|
{
|
||||||
|
this.Given(x => ThenTheSetterIsCalled(_fileConfig))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_setter_when_gets_new_config()
|
||||||
|
{
|
||||||
|
|
||||||
|
var newConfig = new FileConfiguration {
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamHost = "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => WhenTheConfigIsChangedInConsul(newConfig))
|
||||||
|
.Then(x => ThenTheSetterIsCalled(newConfig))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenTheConfigIsChangedInConsul(FileConfiguration newConfig)
|
||||||
|
{
|
||||||
|
_repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse<FileConfiguration>(newConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheSetterIsCalled(FileConfiguration fileConfig)
|
||||||
|
{
|
||||||
|
var result = WaitFor(2000).Until(() => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_setter.Verify(x => x.Set(fileConfig), Times.Once);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,12 +45,12 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
_fileConfiguration = fileConfiguration;
|
_fileConfiguration = fileConfiguration;
|
||||||
_repo
|
_repo
|
||||||
.Setup(x => x.Get())
|
.Setup(x => x.Get())
|
||||||
.Returns(new OkResponse<FileConfiguration>(fileConfiguration));
|
.ReturnsAsync(new OkResponse<FileConfiguration>(fileConfiguration));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenIGetTheReRoutes()
|
private void WhenIGetTheReRoutes()
|
||||||
{
|
{
|
||||||
_result = _provider.Get().Data;
|
_result = _provider.Get().Result.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheRepoIsCalledCorrectly()
|
private void ThenTheRepoIsCalledCorrectly()
|
||||||
|
@ -100,7 +100,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
private void WhenISetTheConfiguration()
|
private void WhenISetTheConfiguration()
|
||||||
{
|
{
|
||||||
_repo.Set(_fileConfiguration);
|
_repo.Set(_fileConfiguration);
|
||||||
_result = _repo.Get().Data;
|
_result = _repo.Get().Result.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheConfigurationIsStoredAs(FileConfiguration expected)
|
private void ThenTheConfigurationIsStoredAs(FileConfiguration expected)
|
||||||
@ -135,7 +135,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
private void WhenIGetTheReRoutes()
|
private void WhenIGetTheReRoutes()
|
||||||
{
|
{
|
||||||
_result = _repo.Get().Data;
|
_result = _repo.Get().Result.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheFollowingIsReturned(FileConfiguration expected)
|
private void ThenTheFollowingIsReturned(FileConfiguration expected)
|
||||||
|
@ -78,7 +78,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
_repo
|
_repo
|
||||||
.Setup(x => x.Set(It.IsAny<FileConfiguration>()))
|
.Setup(x => x.Set(It.IsAny<FileConfiguration>()))
|
||||||
.Returns(response);
|
.ReturnsAsync(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenAnErrorResponseIsReturned()
|
private void ThenAnErrorResponseIsReturned()
|
||||||
|
@ -107,12 +107,12 @@ namespace Ocelot.UnitTests.Controllers
|
|||||||
{
|
{
|
||||||
_configGetter
|
_configGetter
|
||||||
.Setup(x => x.Get())
|
.Setup(x => x.Get())
|
||||||
.Returns(fileConfiguration);
|
.ReturnsAsync(fileConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenIGetTheFileConfiguration()
|
private void WhenIGetTheFileConfiguration()
|
||||||
{
|
{
|
||||||
_result = _controller.Get();
|
_result = _controller.Get().Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TheTheGetFileConfigurationIsCalledCorrectly()
|
private void TheTheGetFileConfigurationIsCalledCorrectly()
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Ocelot.DependencyInjection;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.DependencyInjection
|
||||||
|
{
|
||||||
|
public class ServiceCollectionExtensionTests
|
||||||
|
{
|
||||||
|
private Exception _ex;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_set_up_services()
|
||||||
|
{
|
||||||
|
this.When(x => WhenISetUpOcelotServices())
|
||||||
|
.Then(x => ThenAnExceptionIsntThrown())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenISetUpOcelotServices()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IWebHostBuilder builder = new WebHostBuilder();
|
||||||
|
IConfigurationRoot configRoot = new ConfigurationRoot(new List<IConfigurationProvider>());
|
||||||
|
IServiceCollection services = new ServiceCollection();
|
||||||
|
services.AddSingleton(builder);
|
||||||
|
services.AddOcelot(configRoot);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_ex = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenAnExceptionIsntThrown()
|
||||||
|
{
|
||||||
|
_ex.ShouldBeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
test/Ocelot.UnitTests/Waiter.cs
Normal file
55
test/Ocelot.UnitTests/Waiter.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests
|
||||||
|
{
|
||||||
|
public class Wait
|
||||||
|
{
|
||||||
|
public static Waiter WaitFor(int milliSeconds)
|
||||||
|
{
|
||||||
|
return new Waiter(milliSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Waiter
|
||||||
|
{
|
||||||
|
private readonly int _milliSeconds;
|
||||||
|
|
||||||
|
public Waiter(int milliSeconds)
|
||||||
|
{
|
||||||
|
_milliSeconds = milliSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Until(Func<bool> condition)
|
||||||
|
{
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
var passed = false;
|
||||||
|
while (stopwatch.ElapsedMilliseconds < _milliSeconds)
|
||||||
|
{
|
||||||
|
if (condition.Invoke())
|
||||||
|
{
|
||||||
|
passed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Until<T>(Func<bool> condition)
|
||||||
|
{
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
var passed = false;
|
||||||
|
while (stopwatch.ElapsedMilliseconds < _milliSeconds)
|
||||||
|
{
|
||||||
|
if (condition.Invoke())
|
||||||
|
{
|
||||||
|
passed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user