Merge pull request #31 from TomPallister/develop

mergecode
This commit is contained in:
geffzhang 2017-11-25 21:23:27 +08:00 committed by GitHub
commit b0f928127e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 2208 additions and 1319 deletions

View File

@ -4,9 +4,7 @@
[![Coverage Status](https://coveralls.io/repos/github/TomPallister/Ocelot/badge.svg?branch=develop)](https://coveralls.io/github/TomPallister/Ocelot?branch=develop)
Attempt at a .NET Api Gateway
This project is aimed at people using .NET running
Ocelot is a .NET Api Gateway. This project is aimed at people using .NET running
a micro services / service orientated architecture
that need a unified point of entry into their system.

View File

@ -1,6 +1,6 @@
{
"projects": [ "src", "test" ],
"sdk": {
"version": "2.0.0"
"version": "2.0.2"
}
}

View File

@ -26,13 +26,14 @@ namespace Ocelot.Configuration.Builder
private string _downstreamHost;
private int _downstreamPort;
private string _loadBalancer;
private ServiceProviderConfiguration _serviceProviderConfiguraion;
private bool _useQos;
private QoSOptions _qosOptions;
private HttpHandlerOptions _httpHandlerOptions;
public bool _enableRateLimiting;
public RateLimitOptions _rateLimitOptions;
private string _authenticationProviderKey;
private bool _useServiceDiscovery;
private string _serviceName;
public ReRouteBuilder WithLoadBalancer(string loadBalancer)
{
@ -148,18 +149,12 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
public ReRouteBuilder WithReRouteKey(string loadBalancerKey)
{
_loadBalancerKey = loadBalancerKey;
return this;
}
public ReRouteBuilder WithServiceProviderConfiguraion(ServiceProviderConfiguration serviceProviderConfiguraion)
{
_serviceProviderConfiguraion = serviceProviderConfiguraion;
return this;
}
public ReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions)
{
_authenticationOptions = authenticationOptions;
@ -190,6 +185,18 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery)
{
_useServiceDiscovery = useServiceDiscovery;
return this;
}
public ReRouteBuilder WithServiceName(string serviceName)
{
_serviceName = serviceName;
return this;
}
public ReRoute Build()
{
return new ReRoute(
@ -212,12 +219,13 @@ namespace Ocelot.Configuration.Builder
_downstreamHost,
_downstreamPort,
_loadBalancerKey,
_serviceProviderConfiguraion,
_useQos,
_qosOptions,
_enableRateLimiting,
_rateLimitOptions,
_httpHandlerOptions);
_httpHandlerOptions,
_useServiceDiscovery,
_serviceName);
}
}
}

View File

@ -2,44 +2,9 @@ namespace Ocelot.Configuration.Builder
{
public class ServiceProviderConfigurationBuilder
{
private string _serviceName;
private string _downstreamHost;
private int _downstreamPort;
private bool _userServiceDiscovery;
private string _serviceDiscoveryProvider;
private string _serviceDiscoveryProviderHost;
private int _serviceDiscoveryProviderPort;
public ServiceProviderConfigurationBuilder WithServiceName(string serviceName)
{
_serviceName = serviceName;
return this;
}
public ServiceProviderConfigurationBuilder WithDownstreamHost(string downstreamHost)
{
_downstreamHost = downstreamHost;
return this;
}
public ServiceProviderConfigurationBuilder WithDownstreamPort(int downstreamPort)
{
_downstreamPort = downstreamPort;
return this;
}
public ServiceProviderConfigurationBuilder WithUseServiceDiscovery(bool userServiceDiscovery)
{
_userServiceDiscovery = userServiceDiscovery;
return this;
}
public ServiceProviderConfigurationBuilder WithServiceDiscoveryProvider(string serviceDiscoveryProvider)
{
_serviceDiscoveryProvider = serviceDiscoveryProvider;
return this;
}
public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
{
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
@ -52,11 +17,9 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ServiceProviderConfiguration Build()
{
return new ServiceProviderConfiguration(_serviceName, _downstreamHost, _downstreamPort, _userServiceDiscovery,
_serviceDiscoveryProvider, _serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
return new ServiceProviderConfiguration(_serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
}
}
}

View File

@ -9,11 +9,11 @@ using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Validator;
using Ocelot.LoadBalancer;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using Ocelot.Utilities;
namespace Ocelot.Configuration.Creator
{
@ -25,10 +25,6 @@ namespace Ocelot.Configuration.Creator
private readonly IOptions<FileConfiguration> _options;
private readonly IConfigurationValidator _configurationValidator;
private readonly IOcelotLogger _logger;
private readonly ILoadBalancerFactory _loadBalanceFactory;
private readonly ILoadBalancerHouse _loadBalancerHouse;
private readonly IQoSProviderFactory _qoSProviderFactory;
private readonly IQosProviderHouse _qosProviderHouse;
private readonly IClaimsToThingCreator _claimsToThingCreator;
private readonly IAuthenticationOptionsCreator _authOptionsCreator;
private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
@ -44,10 +40,6 @@ namespace Ocelot.Configuration.Creator
IOptions<FileConfiguration> options,
IConfigurationValidator configurationValidator,
IOcelotLoggerFactory loggerFactory,
ILoadBalancerFactory loadBalancerFactory,
ILoadBalancerHouse loadBalancerHouse,
IQoSProviderFactory qoSProviderFactory,
IQosProviderHouse qosProviderHouse,
IClaimsToThingCreator claimsToThingCreator,
IAuthenticationOptionsCreator authOptionsCreator,
IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator,
@ -65,10 +57,6 @@ namespace Ocelot.Configuration.Creator
_requestIdKeyCreator = requestIdKeyCreator;
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
_authOptionsCreator = authOptionsCreator;
_loadBalanceFactory = loadBalancerFactory;
_loadBalancerHouse = loadBalancerHouse;
_qoSProviderFactory = qoSProviderFactory;
_qosProviderHouse = qosProviderHouse;
_options = options;
_configurationValidator = configurationValidator;
_logger = loggerFactory.CreateLogger<FileOcelotConfigurationCreator>();
@ -79,48 +67,37 @@ namespace Ocelot.Configuration.Creator
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
}
public async Task<Response<IOcelotConfiguration>> Create()
{
var config = await SetUpConfiguration(_options.Value);
return new OkResponse<IOcelotConfiguration>(config);
}
public async Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration)
{
var config = await SetUpConfiguration(fileConfiguration);
return new OkResponse<IOcelotConfiguration>(config);
return config;
}
private async Task<IOcelotConfiguration> SetUpConfiguration(FileConfiguration fileConfiguration)
private async Task<Response<IOcelotConfiguration>> SetUpConfiguration(FileConfiguration fileConfiguration)
{
var response = await _configurationValidator.IsValid(fileConfiguration);
if (response.Data.IsError)
{
var errorBuilder = new StringBuilder();
foreach (var error in response.Errors)
{
errorBuilder.AppendLine(error.Message);
}
throw new Exception($"Unable to start Ocelot..configuration, errors were {errorBuilder}");
return new ErrorResponse<IOcelotConfiguration>(response.Data.Errors);
}
var reRoutes = new List<ReRoute>();
foreach (var reRoute in fileConfiguration.ReRoutes)
{
var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration);
var ocelotReRoute = SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration);
reRoutes.Add(ocelotReRoute);
}
return new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath);
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
var config = new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath, serviceProviderConfiguration);
return new OkResponse<IOcelotConfiguration>(config);
}
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
private ReRoute SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
{
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
@ -130,8 +107,6 @@ namespace Ocelot.Configuration.Creator
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileReRoute, globalConfiguration);
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
@ -167,17 +142,16 @@ namespace Ocelot.Configuration.Creator
.WithLoadBalancer(fileReRoute.LoadBalancer)
.WithDownstreamHost(fileReRoute.DownstreamHost)
.WithDownstreamPort(fileReRoute.DownstreamPort)
.WithLoadBalancerKey(reRouteKey)
.WithServiceProviderConfiguraion(serviceProviderConfiguration)
.WithReRouteKey(reRouteKey)
.WithIsQos(fileReRouteOptions.IsQos)
.WithQosOptions(qosOptions)
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
.WithRateLimitOptions(rateLimitOption)
.WithHttpHandlerOptions(httpHandlerOptions)
.WithServiceName(fileReRoute.ServiceName)
.WithUseServiceDiscovery(fileReRoute.UseServiceDiscovery)
.Build();
await SetupLoadBalancer(reRoute);
SetupQosProvider(reRoute);
return reRoute;
}
@ -187,17 +161,5 @@ namespace Ocelot.Configuration.Creator
var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
return loadBalancerKey;
}
private async Task SetupLoadBalancer(ReRoute reRoute)
{
var loadBalancer = await _loadBalanceFactory.Get(reRoute);
_loadBalancerHouse.Add(reRoute.ReRouteKey, loadBalancer);
}
private void SetupQosProvider(ReRoute reRoute)
{
var loadBalancer = _qoSProviderFactory.Get(reRoute);
_qosProviderHouse.Add(reRoute.ReRouteKey, loadBalancer);
}
}
}

View File

@ -6,7 +6,6 @@ namespace Ocelot.Configuration.Creator
{
public interface IOcelotConfigurationCreator
{
Task<Response<IOcelotConfiguration>> Create();
Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration);
}
}

View File

@ -4,6 +4,6 @@ namespace Ocelot.Configuration.Creator
{
public interface IServiceProviderConfigurationCreator
{
ServiceProviderConfiguration Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration);
ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration);
}
}

View File

@ -5,19 +5,11 @@ namespace Ocelot.Configuration.Creator
{
public class ServiceProviderConfigurationCreator : IServiceProviderConfigurationCreator
{
public ServiceProviderConfiguration Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration)
{
var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName)
&& !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider);
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
return new ServiceProviderConfigurationBuilder()
.WithServiceName(fileReRoute.ServiceName)
.WithDownstreamHost(fileReRoute.DownstreamHost)
.WithDownstreamPort(fileReRoute.DownstreamPort)
.WithUseServiceDiscovery(useServiceDiscovery)
.WithServiceDiscoveryProvider(globalConfiguration?.ServiceDiscoveryProvider?.Provider)
.WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
.WithServiceDiscoveryProviderPort(serviceProviderPort)
.Build();

View File

@ -1,12 +1,11 @@
using System.Collections.Generic;
using Ocelot.Configuration.File;
using Ocelot.Utilities;
namespace Ocelot.Configuration.Creator
{
public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
{
private const string RegExMatchEverything = ".*";
private const string RegExMatchEverything = "[0-9a-zA-Z].*";
private const string RegExMatchEndString = "$";
private const string RegExIgnoreCase = "(?i)";
private const string RegExForwardSlashOnly = "^/$";
@ -15,8 +14,6 @@ namespace Ocelot.Configuration.Creator
{
var upstreamTemplate = reRoute.UpstreamPathTemplate;
upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
var placeholders = new List<string>();
for (var i = 0; i < upstreamTemplate.Length; i++)
@ -40,6 +37,11 @@ namespace Ocelot.Configuration.Creator
return RegExForwardSlashOnly;
}
if(upstreamTemplate.EndsWith("/"))
{
upstreamTemplate = upstreamTemplate.Remove(upstreamTemplate.Length -1, 1) + "(/|)";
}
var route = reRoute.ReRouteIsCaseSensitive
? $"^{upstreamTemplate}{RegExMatchEndString}"
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";

View File

@ -37,5 +37,6 @@ namespace Ocelot.Configuration.File
public FileRateLimitRule RateLimitOptions { get; set; }
public FileAuthenticationOptions AuthenticationOptions { get; set; }
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
public bool UseServiceDiscovery {get;set;}
}
}

View File

@ -2,8 +2,6 @@ namespace Ocelot.Configuration.File
{
public class FileServiceDiscoveryProvider
{
public string Provider {get;set;}
public string Host {get;set;}
public int Port { get; set; }
}

View File

@ -6,5 +6,6 @@ namespace Ocelot.Configuration
{
List<ReRoute> ReRoutes { get; }
string AdministrationPath {get;}
ServiceProviderConfiguration ServiceProviderConfiguration {get;}
}
}

View File

@ -4,13 +4,15 @@ namespace Ocelot.Configuration
{
public class OcelotConfiguration : IOcelotConfiguration
{
public OcelotConfiguration(List<ReRoute> reRoutes, string administrationPath)
public OcelotConfiguration(List<ReRoute> reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration)
{
ReRoutes = reRoutes;
AdministrationPath = administrationPath;
ServiceProviderConfiguration = serviceProviderConfiguration;
}
public List<ReRoute> ReRoutes { get; }
public string AdministrationPath {get;}
public ServiceProviderConfiguration ServiceProviderConfiguration {get;}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository;
@ -16,9 +17,9 @@ namespace Ocelot.Configuration.Provider
_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);
}
}

View File

@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Ocelot.Configuration.File;
using Ocelot.Responses;
@ -5,6 +6,6 @@ namespace Ocelot.Configuration.Provider
{
public interface IFileConfigurationProvider
{
Response<FileConfiguration> Get();
Task<Response<FileConfiguration>> Get();
}
}

View File

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Ocelot.Configuration.File;
using Ocelot.Responses;
namespace Ocelot.Configuration.Provider

View File

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository;
using Ocelot.Responses;
@ -9,16 +10,16 @@ namespace Ocelot.Configuration.Provider
/// </summary>
public class OcelotConfigurationProvider : IOcelotConfigurationProvider
{
private readonly IOcelotConfigurationRepository _repo;
private readonly IOcelotConfigurationRepository _config;
public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
{
_repo = repo;
_config = repo;
}
public async Task<Response<IOcelotConfiguration>> Get()
{
var repoConfig = await _repo.Get();
var repoConfig = await _config.Get();
if (repoConfig.IsError)
{

View File

@ -25,15 +25,17 @@ namespace Ocelot.Configuration
string downstreamHost,
int downstreamPort,
string reRouteKey,
ServiceProviderConfiguration serviceProviderConfiguraion,
bool isQos,
QoSOptions qosOptions,
bool enableEndpointRateLimiting,
RateLimitOptions ratelimitOptions,
HttpHandlerOptions httpHandlerOptions)
HttpHandlerOptions httpHandlerOptions,
bool useServiceDiscovery,
string serviceName)
{
ServiceName = serviceName;
UseServiceDiscovery = useServiceDiscovery;
ReRouteKey = reRouteKey;
ServiceProviderConfiguraion = serviceProviderConfiguraion;
LoadBalancer = loadBalancer;
DownstreamHost = downstreamHost;
DownstreamPort = downstreamPort;
@ -83,9 +85,10 @@ namespace Ocelot.Configuration
public string LoadBalancer {get;private set;}
public string DownstreamHost { get; private set; }
public int DownstreamPort { get; private set; }
public ServiceProviderConfiguration ServiceProviderConfiguraion { get; private set; }
public bool EnableEndpointEndpointRateLimiting { get; private set; }
public RateLimitOptions RateLimitOptions { get; private set; }
public HttpHandlerOptions HttpHandlerOptions { get; private set; }
public bool UseServiceDiscovery {get;private set;}
public string ServiceName {get;private set;}
}
}

View File

@ -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();
}
}
}

View File

@ -3,56 +3,57 @@ using System.Text;
using System.Threading.Tasks;
using Consul;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.Responses;
using Ocelot.ServiceDiscovery;
namespace Ocelot.Configuration.Repository
{
public class ConsulOcelotConfigurationRepository : IOcelotConfigurationRepository
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
{
private readonly ConsulClient _consul;
private ConsulRegistryConfiguration _configuration;
private string _ocelotConfiguration = "OcelotConfiguration";
private Cache.IOcelotCache<IOcelotConfiguration> _cache;
private readonly Cache.IOcelotCache<FileConfiguration> _cache;
public ConsulOcelotConfigurationRepository(ConsulRegistryConfiguration consulRegistryConfiguration, Cache.IOcelotCache<IOcelotConfiguration> cache)
public ConsulFileConfigurationRepository(Cache.IOcelotCache<FileConfiguration> cache, ServiceProviderConfiguration serviceProviderConfig)
{
var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
_configuration = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.ServiceName);
var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.ServiceProviderHost) ? "localhost" : serviceProviderConfig?.ServiceProviderHost;
var consulPort = serviceProviderConfig?.ServiceProviderPort ?? 8500;
var configuration = new ConsulRegistryConfiguration(consulHost, consulPort, _ocelotConfiguration);
_cache = cache;
_consul = new ConsulClient(config =>
_consul = new ConsulClient(c =>
{
config.Address = new Uri($"http://{_configuration.HostName}:{_configuration.Port}");
c.Address = new Uri($"http://{configuration.HostName}:{configuration.Port}");
});
}
public async Task<Response<IOcelotConfiguration>> Get()
public async Task<Response<FileConfiguration>> Get()
{
var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
if (config != null)
{
return new OkResponse<IOcelotConfiguration>(config);
return new OkResponse<FileConfiguration>(config);
}
var queryResult = await _consul.KV.Get(_ocelotConfiguration);
if (queryResult.Response == null)
{
return new OkResponse<IOcelotConfiguration>(null);
return new OkResponse<FileConfiguration>(null);
}
var bytes = queryResult.Response.Value;
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);
@ -72,7 +73,7 @@ namespace Ocelot.Configuration.Repository
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}"));
}
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.Responses;
@ -7,33 +9,41 @@ namespace Ocelot.Configuration.Repository
{
public class FileConfigurationRepository : IFileConfigurationRepository
{
private readonly string _configFilePath;
private static readonly object _lock = new object();
public Response<FileConfiguration> Get()
public FileConfigurationRepository(IHostingEnvironment hostingEnvironment)
{
var configFilePath = $"{AppContext.BaseDirectory}/configuration.json";
string json = string.Empty;
_configFilePath = $"{AppContext.BaseDirectory}/configuration{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
}
public async Task<Response<FileConfiguration>> Get()
{
string jsonConfiguration;
lock(_lock)
{
json = System.IO.File.ReadAllText(configFilePath);
jsonConfiguration = System.IO.File.ReadAllText(_configFilePath);
}
var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(json);
var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(jsonConfiguration);
return new OkResponse<FileConfiguration>(fileConfiguration);
}
public Response Set(FileConfiguration fileConfiguration)
public async Task<Response> Set(FileConfiguration fileConfiguration)
{
var configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
lock(_lock)
{
if (System.IO.File.Exists(configurationPath))
if (System.IO.File.Exists(_configFilePath))
{
System.IO.File.Delete(configurationPath);
System.IO.File.Delete(_configFilePath);
}
System.IO.File.WriteAllText(configurationPath, jsonConfiguration);
System.IO.File.WriteAllText(_configFilePath, jsonConfiguration);
}
return new OkResponse();

View File

@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Ocelot.Configuration.File;
using Ocelot.Responses;
@ -5,7 +6,7 @@ namespace Ocelot.Configuration.Repository
{
public interface IFileConfigurationRepository
{
Response<FileConfiguration> Get();
Response Set(FileConfiguration fileConfiguration);
Task<Response<FileConfiguration>> Get();
Task<Response> Set(FileConfiguration fileConfiguration);
}
}

View File

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Ocelot.Configuration.File;
using Ocelot.Responses;
namespace Ocelot.Configuration.Repository

View File

@ -2,23 +2,12 @@
{
public class ServiceProviderConfiguration
{
public ServiceProviderConfiguration(string serviceName, string downstreamHost,
int downstreamPort, bool useServiceDiscovery, string serviceDiscoveryProvider, string serviceProviderHost, int serviceProviderPort)
public ServiceProviderConfiguration(string serviceProviderHost, int serviceProviderPort)
{
ServiceName = serviceName;
DownstreamHost = downstreamHost;
DownstreamPort = downstreamPort;
UseServiceDiscovery = useServiceDiscovery;
ServiceDiscoveryProvider = serviceDiscoveryProvider;
ServiceProviderHost = serviceProviderHost;
ServiceProviderPort = serviceProviderPort;
}
public string ServiceName { get; }
public string DownstreamHost { get; }
public int DownstreamPort { get; }
public bool UseServiceDiscovery { get; }
public string ServiceDiscoveryProvider { get; }
public string ServiceProviderHost { get; private set; }
public int ServiceProviderPort { get; private set; }
}

View File

@ -22,7 +22,7 @@ namespace Ocelot.Configuration.Setter
public async Task<Response> Set(FileConfiguration fileConfig)
{
var response = _repo.Set(fileConfig);
var response = await _repo.Set(fileConfig);
if(response.IsError)
{

View File

@ -21,9 +21,9 @@ namespace Ocelot.Controllers
}
[HttpGet]
public IActionResult Get()
public async Task<IActionResult> Get()
{
var response = _configGetter.Get();
var response = await _configGetter.Get();
if(response.IsError)
{

View File

@ -0,0 +1,11 @@
using CacheManager.Core;
using System;
namespace Ocelot.DependencyInjection
{
public interface IOcelotBuilder
{
IOcelotBuilder AddStoreOcelotConfigurationInConsul();
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
}
}

View File

@ -0,0 +1,262 @@
using CacheManager.Core;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Authorisation;
using Ocelot.Cache;
using Ocelot.Claims;
using Ocelot.Configuration.Authentication;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
using Ocelot.Configuration.Validator;
using Ocelot.Controllers;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Headers;
using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.QueryStrings;
using Ocelot.RateLimit;
using Ocelot.Request.Builder;
using Ocelot.Request.Mapper;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Ocelot.Responder;
using Ocelot.ServiceDiscovery;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;
namespace Ocelot.DependencyInjection
{
public class OcelotBuilder : IOcelotBuilder
{
private IServiceCollection _services;
private IConfigurationRoot _configurationRoot;
public OcelotBuilder(IServiceCollection services, IConfigurationRoot configurationRoot)
{
_configurationRoot = configurationRoot;
_services = services;
//add default cache settings...
Action<ConfigurationBuilderCachePart> defaultCachingSettings = x =>
{
x.WithDictionaryHandle();
};
AddCacheManager(defaultCachingSettings);
//add ocelot services...
_services.Configure<FileConfiguration>(configurationRoot);
_services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
_services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
_services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>();
_services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
_services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
_services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
_services.TryAddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>();
_services.TryAddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>();
_services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
_services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
_services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
_services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
_services.TryAddSingleton<IRegionCreator, RegionCreator>();
_services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
_services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
_services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
_services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>();
_services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>();
_services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
_services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
_services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
_services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
_services.TryAddSingleton<IUrlBuilder, UrlBuilder>();
_services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
_services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
_services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
_services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
_services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
_services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
_services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
_services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
_services.TryAddSingleton<IClaimsParser, ClaimsParser>();
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
_services.TryAddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
_services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
_services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
_services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
_services.TryAddSingleton<IRequestMapper, RequestMapper>();
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
_services.TryAddScoped<IRequestScopedDataRepository, HttpDataRepository>();
_services.AddMemoryCache();
_services.TryAddSingleton<OcelotDiagnosticListener>();
//add identity server for admin area
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
if (identityServerConfiguration != null)
{
AddIdentityServer(identityServerConfiguration);
}
//add asp.net services..
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
_services.AddMvcCore()
.AddApplicationPart(assembly)
.AddControllersAsServices()
.AddAuthorization()
.AddJsonFormatters();
_services.AddLogging();
_services.AddMiddlewareAnalysis();
_services.AddWebEncoders();
}
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
{
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
var serviceDiscoveryHost = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Host", string.Empty);
var config = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderPort(serviceDiscoveryPort)
.WithServiceDiscoveryProviderHost(serviceDiscoveryHost)
.Build();
_services.AddSingleton<ServiceProviderConfiguration>(config);
_services.AddSingleton<ConsulFileConfigurationPoller>();
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
return this;
}
public IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings)
{
var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
var ocelotOutputCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
_services.RemoveAll(typeof(ICacheManager<HttpResponseMessage>));
_services.RemoveAll(typeof(IOcelotCache<HttpResponseMessage>));
_services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
_services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotOutputCacheManager);
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings);
var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache);
_services.RemoveAll(typeof(ICacheManager<IOcelotConfiguration>));
_services.RemoveAll(typeof(IOcelotCache<IOcelotConfiguration>));
_services.AddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
_services.AddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings);
var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache);
_services.RemoveAll(typeof(ICacheManager<FileConfiguration>));
_services.RemoveAll(typeof(IOcelotCache<FileConfiguration>));
_services.AddSingleton<ICacheManager<FileConfiguration>>(fileConfigCacheManagerOutputCache);
_services.AddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager);
return this;
}
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration)
{
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
_services.TryAddSingleton<IHashMatcher, HashMatcher>();
var identityServerBuilder = _services
.AddIdentityServer(o => {
o.IssuerUri = "Ocelot";
})
.AddInMemoryApiResources(Resources(identityServerConfiguration))
.AddInMemoryClients(Client(identityServerConfiguration))
.AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
//todo - refactor a method so we know why this is happening
var whb = _services.First(x => x.ServiceType == typeof(IWebHostBuilder));
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
var baseSchemeUrlAndPort = urlFinder.Find();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
_services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(o =>
{
var adminPath = _configurationRoot.GetValue("GlobalConfiguration:AdministrationPath", string.Empty);
o.Authority = baseSchemeUrlAndPort + adminPath;
o.ApiName = identityServerConfiguration.ApiName;
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = identityServerConfiguration.ApiSecret;
});
//todo - refactor naming..
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
{
identityServerBuilder.AddDeveloperSigningCredential();
}
else
{
//todo - refactor so calls method?
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
identityServerBuilder.AddSigningCredential(cert);
}
}
private List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
{
return new List<ApiResource>
{
new ApiResource(identityServerConfiguration.ApiName, identityServerConfiguration.ApiName)
{
ApiSecrets = new List<Secret>
{
new Secret
{
Value = identityServerConfiguration.ApiSecret.Sha256()
}
}
}
};
}
private List<Client> Client(IIdentityServerConfiguration identityServerConfiguration)
{
return new List<Client>
{
new Client
{
ClientId = identityServerConfiguration.ApiName,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
AllowedScopes = { identityServerConfiguration.ApiName }
}
};
}
}
}

View File

@ -1,223 +1,16 @@
using CacheManager.Core;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Ocelot.Authorisation;
using Ocelot.Cache;
using Ocelot.Claims;
using Ocelot.Configuration.Authentication;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
using Ocelot.Configuration.Validator;
using Ocelot.Controllers;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Headers;
using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.QueryStrings;
using Ocelot.RateLimit;
using Ocelot.Request.Builder;
using Ocelot.Request.Mapper;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Ocelot.Responder;
using Ocelot.ServiceDiscovery;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
namespace Ocelot.DependencyInjection
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddOcelotStoreConfigurationInConsul(this IServiceCollection services, ConsulRegistryConfiguration consulConfig)
public static IOcelotBuilder AddOcelot(this IServiceCollection services,
IConfigurationRoot configurationRoot)
{
services.AddSingleton<ConsulRegistryConfiguration>(consulConfig);
services.AddSingleton<IOcelotConfigurationRepository, ConsulOcelotConfigurationRepository>();
return services;
}
public static IServiceCollection AddOcelot(this IServiceCollection services, IConfigurationRoot configurationRoot, Action<ConfigurationBuilderCachePart> settings)
{
var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
var ocelotOutputCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
services.TryAddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
services.TryAddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotOutputCacheManager);
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings);
var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache);
services.TryAddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
services.TryAddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
services.Configure<FileConfiguration>(configurationRoot);
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>();
services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
services.TryAddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>();
services.TryAddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>();
services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
services.AddMvcCore()
.AddApplicationPart(assembly)
.AddControllersAsServices()
.AddAuthorization()
.AddJsonFormatters();
services.AddLogging();
services.TryAddSingleton<IRegionCreator, RegionCreator>();
services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>();
services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>();
services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.TryAddSingleton<IUrlBuilder, UrlBuilder>();
services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
services.TryAddSingleton<IClaimsParser, ClaimsParser>();
services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
services.TryAddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
services.TryAddSingleton<IRequestMapper, RequestMapper>();
services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddScoped<IRequestScopedDataRepository, HttpDataRepository>();
services.AddMemoryCache();
//Used to log the the start and ending of middleware
services.TryAddSingleton<OcelotDiagnosticListener>();
services.AddMiddlewareAnalysis();
services.AddWebEncoders();
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
if (identityServerConfiguration != null)
{
services.AddIdentityServer(identityServerConfiguration, configurationRoot);
}
return services;
}
private static void AddIdentityServer(this IServiceCollection services, IIdentityServerConfiguration identityServerConfiguration, IConfigurationRoot configurationRoot)
{
services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
services.TryAddSingleton<IHashMatcher, HashMatcher>();
var identityServerBuilder = services
.AddIdentityServer(o => {
o.IssuerUri = "Ocelot";
})
.AddInMemoryApiResources(Resources(identityServerConfiguration))
.AddInMemoryClients(Client(identityServerConfiguration))
.AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
//todo - refactor a method so we know why this is happening
var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
var baseSchemeUrlAndPort = urlFinder.Find();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(o =>
{
var adminPath = configurationRoot.GetValue("GlobalConfiguration:AdministrationPath", string.Empty);
o.Authority = baseSchemeUrlAndPort + adminPath;
o.ApiName = identityServerConfiguration.ApiName;
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = identityServerConfiguration.ApiSecret;
});
//todo - refactor naming..
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
{
identityServerBuilder.AddDeveloperSigningCredential();
}
else
{
//todo - refactor so calls method?
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
identityServerBuilder.AddSigningCredential(cert);
}
}
private static List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
{
return new List<ApiResource>
{
new ApiResource(identityServerConfiguration.ApiName, identityServerConfiguration.ApiName)
{
ApiSecrets = new List<Secret>
{
new Secret
{
Value = identityServerConfiguration.ApiSecret.Sha256()
}
}
}
};
}
private static List<Client> Client(IIdentityServerConfiguration identityServerConfiguration)
{
return new List<Client>
{
new Client
{
ClientId = identityServerConfiguration.ApiName,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
AllowedScopes = { identityServerConfiguration.ApiName }
}
};
return new OcelotBuilder(services, configurationRoot);
}
}
}

View File

@ -2,34 +2,28 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Responses;
using Ocelot.Utilities;
namespace Ocelot.DownstreamRouteFinder.Finder
{
public class DownstreamRouteFinder : IDownstreamRouteFinder
{
private readonly IOcelotConfigurationProvider _configProvider;
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IUrlPathPlaceholderNameAndValueFinder _urlPathPlaceholderNameAndValueFinder;
public DownstreamRouteFinder(IOcelotConfigurationProvider configProvider, IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
{
_configProvider = configProvider;
_urlMatcher = urlMatcher;
_urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
}
public async Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration)
{
upstreamUrlPath = upstreamUrlPath.SetLastCharacterAs('/');
var configuration = await _configProvider.Get();
var applicableReRoutes = configuration.Data.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));
var applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));
foreach (var reRoute in applicableReRoutes)
{

View File

@ -1,10 +1,11 @@
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.Finder
{
public interface IDownstreamRouteFinder
{
Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod);
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration);
}
}

View File

@ -2,12 +2,12 @@ using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Utilities;
namespace Ocelot.DownstreamRouteFinder.Middleware
{
@ -16,13 +16,17 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
private readonly RequestDelegate _next;
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IOcelotLogger _logger;
private readonly IOcelotConfigurationProvider _configProvider;
public DownstreamRouteFinderMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IDownstreamRouteFinder downstreamRouteFinder,
IRequestScopedDataRepository requestScopedDataRepository)
IRequestScopedDataRepository requestScopedDataRepository,
IOcelotConfigurationProvider configProvider)
:base(requestScopedDataRepository)
{
_configProvider = configProvider;
_next = next;
_downstreamRouteFinder = downstreamRouteFinder;
_logger = loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>();
@ -32,9 +36,19 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
{
var upstreamUrlPath = context.Request.Path.ToString();
//todo make this getting config its own middleware one day?
var configuration = await _configProvider.Get();
if(configuration.IsError)
{
_logger.LogError($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
SetPipelineError(configuration.Errors);
}
SetServiceProviderConfigurationForThisRequest(configuration.Data.ServiceProviderConfiguration);
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
var downstreamRoute = await _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method);
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method, configuration.Data);
if (downstreamRoute.IsError)
{

View File

@ -13,7 +13,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
for (int counterForTemplate = 0; counterForTemplate < upstreamUrlPathTemplate.Length; counterForTemplate++)
{
if (CharactersDontMatch(upstreamUrlPathTemplate[counterForTemplate], upstreamUrlPath[counterForUrl]) && ContinueScanningUrl(counterForUrl,upstreamUrlPath.Length))
if ((upstreamUrlPath.Length > counterForUrl) && CharactersDontMatch(upstreamUrlPathTemplate[counterForTemplate], upstreamUrlPath[counterForUrl]) && ContinueScanningUrl(counterForUrl,upstreamUrlPath.Length))
{
if (IsPlaceholder(upstreamUrlPathTemplate[counterForTemplate]))
{

View File

@ -10,5 +10,10 @@ namespace Ocelot.Errors
public string Message { get; private set; }
public OcelotErrorCode Code { get; private set; }
public override string ToString()
{
return Message;
}
}
}

View File

@ -35,6 +35,14 @@ namespace Ocelot.Infrastructure.RequestData
{
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))
{
var data = (T) obj;

View File

@ -5,6 +5,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{
public interface ILoadBalancerFactory
{
Task<ILoadBalancer> Get(ReRoute reRoute);
Task<ILoadBalancer> Get(ReRoute reRoute, ServiceProviderConfiguration config);
}
}

View File

@ -1,10 +1,11 @@
using Ocelot.Responses;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.LoadBalancer.LoadBalancers
{
public interface ILoadBalancerHouse
{
Response<ILoadBalancer> Get(string key);
Response Add(string key, ILoadBalancer loadBalancer);
Task<Response<ILoadBalancer>> Get(ReRoute reRoute, ServiceProviderConfiguration config);
}
}

View File

@ -8,14 +8,14 @@ using Ocelot.Values;
namespace Ocelot.LoadBalancer.LoadBalancers
{
public class LeastConnectionLoadBalancer : ILoadBalancer
public class LeastConnection : ILoadBalancer
{
private readonly Func<Task<List<Service>>> _services;
private readonly List<Lease> _leases;
private readonly string _serviceName;
private static readonly object _syncLock = new object();
public LeastConnectionLoadBalancer(Func<Task<List<Service>>> services, string serviceName)
public LeastConnection(Func<Task<List<Service>>> services, string serviceName)
{
_services = services;
_serviceName = serviceName;

View File

@ -12,16 +12,16 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_serviceProviderFactory = serviceProviderFactory;
}
public async Task<ILoadBalancer> Get(ReRoute reRoute)
public async Task<ILoadBalancer> Get(ReRoute reRoute, ServiceProviderConfiguration config)
{
var serviceProvider = _serviceProviderFactory.Get(reRoute.ServiceProviderConfiguraion);
var serviceProvider = _serviceProviderFactory.Get(config, reRoute);
switch (reRoute.LoadBalancer)
{
case "RoundRobin":
return new RoundRobinLoadBalancer(async () => await serviceProvider.Get());
return new RoundRobin(async () => await serviceProvider.Get());
case "LeastConnection":
return new LeastConnectionLoadBalancer(async () => await serviceProvider.Get(), reRoute.ServiceProviderConfiguraion.ServiceName);
return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName);
default:
return new NoLoadBalancer(await serviceProvider.Get());
}

View File

@ -1,42 +1,56 @@
using System.Collections.Generic;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.LoadBalancer.LoadBalancers
{
public class LoadBalancerHouse : ILoadBalancerHouse
{
private readonly Dictionary<string, ILoadBalancer> _loadBalancers;
private readonly ILoadBalancerFactory _factory;
private readonly ConcurrentDictionary<string, ILoadBalancer> _loadBalancers;
public LoadBalancerHouse()
public LoadBalancerHouse(ILoadBalancerFactory factory)
{
_loadBalancers = new Dictionary<string, ILoadBalancer>();
_factory = factory;
_loadBalancers = new ConcurrentDictionary<string, ILoadBalancer>();
}
public Response<ILoadBalancer> Get(string key)
public async Task<Response<ILoadBalancer>> Get(ReRoute reRoute, ServiceProviderConfiguration config)
{
ILoadBalancer loadBalancer;
try
{
if(_loadBalancers.TryGetValue(reRoute.ReRouteKey, out var loadBalancer))
{
loadBalancer = _loadBalancers[reRoute.ReRouteKey];
if(_loadBalancers.TryGetValue(key, out loadBalancer))
if(reRoute.LoadBalancer != loadBalancer.GetType().Name)
{
return new OkResponse<ILoadBalancer>(_loadBalancers[key]);
loadBalancer = await _factory.Get(reRoute, config);
AddLoadBalancer(reRoute.ReRouteKey, loadBalancer);
}
return new OkResponse<ILoadBalancer>(loadBalancer);
}
loadBalancer = await _factory.Get(reRoute, config);
AddLoadBalancer(reRoute.ReRouteKey, loadBalancer);
return new OkResponse<ILoadBalancer>(loadBalancer);
}
catch(Exception ex)
{
return new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
{
new UnableToFindLoadBalancerError($"unabe to find load balancer for {key}")
new UnableToFindLoadBalancerError($"unabe to find load balancer for {reRoute.ReRouteKey} exception is {ex}")
});
}
public Response Add(string key, ILoadBalancer loadBalancer)
{
if (!_loadBalancers.ContainsKey(key))
{
_loadBalancers.Add(key, loadBalancer);
}
_loadBalancers.Remove(key);
_loadBalancers.Add(key, loadBalancer);
return new OkResponse();
private void AddLoadBalancer(string key, ILoadBalancer loadBalancer)
{
_loadBalancers.AddOrUpdate(key, loadBalancer, (x, y) => loadBalancer);
}
}
}

View File

@ -6,13 +6,13 @@ using System;
namespace Ocelot.LoadBalancer.LoadBalancers
{
public class RoundRobinLoadBalancer : ILoadBalancer
public class RoundRobin : ILoadBalancer
{
private readonly Func<Task<List<Service>>> _services;
private int _last;
public RoundRobinLoadBalancer(Func<Task<List<Service>>> services)
public RoundRobin(Func<Task<List<Service>>> services)
{
_services = services;
}

View File

@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.Provider;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
@ -28,7 +29,7 @@ namespace Ocelot.LoadBalancer.Middleware
public async Task Invoke(HttpContext context)
{
var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.ReRouteKey);
var loadBalancer = await _loadBalancerHouse.Get(DownstreamRoute.ReRoute, ServiceProviderConfiguration);
if(loadBalancer.IsError)
{
_logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder;
using Ocelot.Errors;
using Ocelot.Infrastructure.RequestData;
@ -30,11 +31,18 @@ namespace Ocelot.Middleware
public HttpResponseMessage HttpResponseMessage => _requestScopedDataRepository.Get<HttpResponseMessage>("HttpResponseMessage").Data;
public ServiceProviderConfiguration ServiceProviderConfiguration => _requestScopedDataRepository.Get<ServiceProviderConfiguration>("ServiceProviderConfiguration").Data;
public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute)
{
_requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute);
}
public void SetServiceProviderConfigurationForThisRequest(ServiceProviderConfiguration serviceProviderConfiguration)
{
_requestScopedDataRepository.Add("ServiceProviderConfiguration", serviceProviderConfiguration);
}
public void SetUpstreamRequestForThisRequest(Request.Request request)
{
_requestScopedDataRepository.Add("Request", request);

View File

@ -23,16 +23,20 @@ using Ocelot.RateLimit.Middleware;
namespace Ocelot.Middleware
{
using System;
using System.Linq;
using System.Threading.Tasks;
using Authorisation.Middleware;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
using Ocelot.LoadBalancer.Middleware;
using Ocelot.Responses;
public static class OcelotMiddlewareExtensions
{
@ -56,7 +60,9 @@ namespace Ocelot.Middleware
/// <returns></returns>
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);
@ -145,38 +151,110 @@ namespace Ocelot.Middleware
private static async Task<IOcelotConfiguration> CreateConfiguration(IApplicationBuilder builder)
{
var fileConfig = (IOptions<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));
var deps = GetDependencies(builder);
var configSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter));
var ocelotConfiguration = await deps.provider.Get();
var configProvider = (IOcelotConfigurationProvider)builder.ApplicationServices.GetService(typeof(IOcelotConfigurationProvider));
var ocelotConfiguration = await configProvider.Get();
if (ocelotConfiguration == null || ocelotConfiguration.Data == null || ocelotConfiguration.IsError)
if (ConfigurationNotSetUp(ocelotConfiguration))
{
var config = await configSetter.Set(fileConfig.Value);
var response = await SetConfig(builder, deps.fileConfiguration, deps.setter, deps.provider, deps.repo);
if (config == null || config.IsError)
if (UnableToSetConfig(response))
{
throw new Exception("Unable to start Ocelot: configuration was not set up correctly.");
ThrowToStopOcelotStarting(response);
}
}
ocelotConfiguration = await configProvider.Get();
return await GetOcelotConfigAndReturn(deps.provider);
}
private static async Task<Response> SetConfig(IApplicationBuilder builder, IOptions<FileConfiguration> fileConfiguration, IFileConfigurationSetter setter, IOcelotConfigurationProvider provider, IFileConfigurationRepository repo)
{
if (UsingConsul(repo))
{
return await SetUpConfigFromConsul(builder, repo, setter, fileConfiguration);
}
return await setter.Set(fileConfiguration.Value);
}
private static bool UnableToSetConfig(Response response)
{
return response == null || response.IsError;
}
private static bool ConfigurationNotSetUp(Response<IOcelotConfiguration> ocelotConfiguration)
{
return ocelotConfiguration == null || ocelotConfiguration.Data == null || ocelotConfiguration.IsError;
}
private static (IOptions<FileConfiguration> fileConfiguration, IFileConfigurationSetter setter, IOcelotConfigurationProvider provider, IFileConfigurationRepository repo) GetDependencies(IApplicationBuilder builder)
{
var fileConfiguration = (IOptions<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));
var setter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter));
var provider = (IOcelotConfigurationProvider)builder.ApplicationServices.GetService(typeof(IOcelotConfigurationProvider));
var repo = (IFileConfigurationRepository)builder.ApplicationServices.GetService(typeof(IFileConfigurationRepository));
return (fileConfiguration, setter, provider, repo);
}
private static async Task<IOcelotConfiguration> GetOcelotConfigAndReturn(IOcelotConfigurationProvider provider)
{
var ocelotConfiguration = await provider.Get();
if(ocelotConfiguration == null || ocelotConfiguration.Data == null || ocelotConfiguration.IsError)
{
throw new Exception("Unable to start Ocelot: ocelot configuration was not returned by provider.");
ThrowToStopOcelotStarting(ocelotConfiguration);
}
return ocelotConfiguration.Data;
}
private static async Task CreateAdministrationArea(IApplicationBuilder builder)
private static void ThrowToStopOcelotStarting(Response config)
{
var configuration = await CreateConfiguration(builder);
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 async Task<Response> SetUpConfigFromConsul(IApplicationBuilder builder, IFileConfigurationRepository consulFileConfigRepo, IFileConfigurationSetter setter, IOptions<FileConfiguration> fileConfig)
{
Response config = null;
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 setter.Set(fileConfig.Value);
}
else
{
var ocelotConfig = await ocelotConfigurationCreator.Create(fileConfigFromConsul.Data);
if(ocelotConfig.IsError)
{
return new ErrorResponse(ocelotConfig.Errors);
}
config = await ocelotConfigurationRepository.AddOrReplace(ocelotConfig.Data);
var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller));
}
return new OkResponse();
}
private static async Task CreateAdministrationArea(IApplicationBuilder builder, IOcelotConfiguration configuration)
{
var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null)

View File

@ -32,7 +32,7 @@ namespace Ocelot.Request.Middleware
{
_logger.LogDebug("started calling request builder middleware");
var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute.ReRouteKey);
var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute);
if (qosProvider.IsError)
{

View File

@ -31,7 +31,6 @@ namespace Ocelot.Requester
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
{
_handlers
.OrderByDescending(handler => handler.Key)
.Select(handler => handler.Value)

View File

@ -0,0 +1,17 @@
using Polly.CircuitBreaker;
using Polly.Timeout;
namespace Ocelot.Requester.QoS
{
public class CircuitBreaker
{
public CircuitBreaker(CircuitBreakerPolicy circuitBreakerPolicy, TimeoutPolicy timeoutPolicy)
{
CircuitBreakerPolicy = circuitBreakerPolicy;
TimeoutPolicy = timeoutPolicy;
}
public CircuitBreakerPolicy CircuitBreakerPolicy { get; private set; }
public TimeoutPolicy TimeoutPolicy { get; private set; }
}
}

View File

@ -0,0 +1,7 @@
namespace Ocelot.Requester.QoS
{
public interface IQoSProvider
{
CircuitBreaker CircuitBreaker { get; }
}
}

View File

@ -1,13 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Configuration;
using Ocelot.Configuration;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Responses;
using Polly;
using Polly.CircuitBreaker;
using Polly.Timeout;
namespace Ocelot.Requester.QoS
{
@ -15,131 +7,4 @@ namespace Ocelot.Requester.QoS
{
IQoSProvider Get(ReRoute reRoute);
}
public class QoSProviderFactory : IQoSProviderFactory
{
private readonly IOcelotLoggerFactory _loggerFactory;
public QoSProviderFactory(IOcelotLoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public IQoSProvider Get(ReRoute reRoute)
{
if (reRoute.IsQos)
{
return new PollyQoSProvider(reRoute, _loggerFactory);
}
return new NoQoSProvider();
}
}
public interface IQoSProvider
{
CircuitBreaker CircuitBreaker { get; }
}
public class NoQoSProvider : IQoSProvider
{
public CircuitBreaker CircuitBreaker { get; }
}
public class PollyQoSProvider : IQoSProvider
{
private readonly CircuitBreakerPolicy _circuitBreakerPolicy;
private readonly TimeoutPolicy _timeoutPolicy;
private readonly IOcelotLogger _logger;
private readonly CircuitBreaker _circuitBreaker;
public PollyQoSProvider(ReRoute reRoute, IOcelotLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<PollyQoSProvider>();
_timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(reRoute.QosOptionsOptions.TimeoutValue), reRoute.QosOptionsOptions.TimeoutStrategy);
_circuitBreakerPolicy = Policy
.Handle<HttpRequestException>()
.Or<TimeoutRejectedException>()
.Or<TimeoutException>()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: reRoute.QosOptionsOptions.ExceptionsAllowedBeforeBreaking,
durationOfBreak: TimeSpan.FromMilliseconds(reRoute.QosOptionsOptions.DurationOfBreak),
onBreak: (ex, breakDelay) =>
{
_logger.LogError(
".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex);
},
onReset: () =>
{
_logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again.");
},
onHalfOpen: () =>
{
_logger.LogDebug(".Breaker logging: Half-open; next call is a trial.");
}
);
_circuitBreaker = new CircuitBreaker(_circuitBreakerPolicy, _timeoutPolicy);
}
public CircuitBreaker CircuitBreaker => _circuitBreaker;
}
public class CircuitBreaker
{
public CircuitBreaker(CircuitBreakerPolicy circuitBreakerPolicy, TimeoutPolicy timeoutPolicy)
{
CircuitBreakerPolicy = circuitBreakerPolicy;
TimeoutPolicy = timeoutPolicy;
}
public CircuitBreakerPolicy CircuitBreakerPolicy { get; private set; }
public TimeoutPolicy TimeoutPolicy { get; private set; }
}
public interface IQosProviderHouse
{
Response<IQoSProvider> Get(string key);
Response Add(string key, IQoSProvider loadBalancer);
}
public class QosProviderHouse : IQosProviderHouse
{
private readonly Dictionary<string, IQoSProvider> _qoSProviders;
public QosProviderHouse()
{
_qoSProviders = new Dictionary<string, IQoSProvider>();
}
public Response<IQoSProvider> Get(string key)
{
IQoSProvider qoSProvider;
if (_qoSProviders.TryGetValue(key, out qoSProvider))
{
return new OkResponse<IQoSProvider>(_qoSProviders[key]);
}
return new ErrorResponse<IQoSProvider>(new List<Ocelot.Errors.Error>()
{
new UnableToFindQoSProviderError($"unabe to find qos provider for {key}")
});
}
public Response Add(string key, IQoSProvider loadBalancer)
{
if (!_qoSProviders.ContainsKey(key))
{
_qoSProviders.Add(key, loadBalancer);
}
_qoSProviders.Remove(key);
_qoSProviders.Add(key, loadBalancer);
return new OkResponse();
}
}
}

View File

@ -0,0 +1,10 @@
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.Requester.QoS
{
public interface IQosProviderHouse
{
Response<IQoSProvider> Get(ReRoute reRoute);
}
}

View File

@ -0,0 +1,7 @@
namespace Ocelot.Requester.QoS
{
public class NoQoSProvider : IQoSProvider
{
public CircuitBreaker CircuitBreaker { get; }
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Net.Http;
using Ocelot.Configuration;
using Ocelot.Logging;
using Polly;
using Polly.CircuitBreaker;
using Polly.Timeout;
namespace Ocelot.Requester.QoS
{
public class PollyQoSProvider : IQoSProvider
{
private readonly CircuitBreakerPolicy _circuitBreakerPolicy;
private readonly TimeoutPolicy _timeoutPolicy;
private readonly IOcelotLogger _logger;
private readonly CircuitBreaker _circuitBreaker;
public PollyQoSProvider(ReRoute reRoute, IOcelotLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<PollyQoSProvider>();
_timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(reRoute.QosOptionsOptions.TimeoutValue), reRoute.QosOptionsOptions.TimeoutStrategy);
_circuitBreakerPolicy = Policy
.Handle<HttpRequestException>()
.Or<TimeoutRejectedException>()
.Or<TimeoutException>()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: reRoute.QosOptionsOptions.ExceptionsAllowedBeforeBreaking,
durationOfBreak: TimeSpan.FromMilliseconds(reRoute.QosOptionsOptions.DurationOfBreak),
onBreak: (ex, breakDelay) =>
{
_logger.LogError(
".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex);
},
onReset: () =>
{
_logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again.");
},
onHalfOpen: () =>
{
_logger.LogDebug(".Breaker logging: Half-open; next call is a trial.");
}
);
_circuitBreaker = new CircuitBreaker(_circuitBreakerPolicy, _timeoutPolicy);
}
public CircuitBreaker CircuitBreaker => _circuitBreaker;
}
}

View File

@ -0,0 +1,25 @@
using Ocelot.Configuration;
using Ocelot.Logging;
namespace Ocelot.Requester.QoS
{
public class QoSProviderFactory : IQoSProviderFactory
{
private readonly IOcelotLoggerFactory _loggerFactory;
public QoSProviderFactory(IOcelotLoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public IQoSProvider Get(ReRoute reRoute)
{
if (reRoute.IsQos)
{
return new PollyQoSProvider(reRoute, _loggerFactory);
}
return new NoQoSProvider();
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.Requester.QoS
{
public class QosProviderHouse : IQosProviderHouse
{
private readonly ConcurrentDictionary<string, IQoSProvider> _qoSProviders;
private readonly IQoSProviderFactory _qoSProviderFactory;
public QosProviderHouse(IQoSProviderFactory qoSProviderFactory)
{
_qoSProviderFactory = qoSProviderFactory;
_qoSProviders = new ConcurrentDictionary<string, IQoSProvider>();
}
public Response<IQoSProvider> Get(ReRoute reRoute)
{
try
{
if (_qoSProviders.TryGetValue(reRoute.ReRouteKey, out var qosProvider))
{
if (reRoute.IsQos && qosProvider.CircuitBreaker == null)
{
qosProvider = _qoSProviderFactory.Get(reRoute);
Add(reRoute.ReRouteKey, qosProvider);
}
return new OkResponse<IQoSProvider>(_qoSProviders[reRoute.ReRouteKey]);
}
qosProvider = _qoSProviderFactory.Get(reRoute);
Add(reRoute.ReRouteKey, qosProvider);
return new OkResponse<IQoSProvider>(qosProvider);
}
catch (Exception ex)
{
return new ErrorResponse<IQoSProvider>(new List<Ocelot.Errors.Error>()
{
new UnableToFindQoSProviderError($"unabe to find qos provider for {reRoute.ReRouteKey}, exception was {ex}")
});
}
}
private void Add(string key, IQoSProvider qosProvider)
{
_qoSProviders.AddOrUpdate(key, qosProvider, (x, y) => qosProvider);
}
}
}

View File

@ -2,14 +2,14 @@ namespace Ocelot.ServiceDiscovery
{
public class ConsulRegistryConfiguration
{
public ConsulRegistryConfiguration(string hostName, int port, string serviceName)
public ConsulRegistryConfiguration(string hostName, int port, string keyOfServiceInConsul)
{
HostName = hostName;
Port = port;
ServiceName = serviceName;
KeyOfServiceInConsul = keyOfServiceInConsul;
}
public string ServiceName { get; private set; }
public string KeyOfServiceInConsul { get; private set; }
public string HostName { get; private set; }
public int Port { get; private set; }
}

View File

@ -10,7 +10,7 @@ namespace Ocelot.ServiceDiscovery
{
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
{
private readonly ConsulRegistryConfiguration _configuration;
private readonly ConsulRegistryConfiguration _consulConfig;
private readonly ConsulClient _consul;
private const string VersionPrefix = "version-";
@ -18,17 +18,17 @@ namespace Ocelot.ServiceDiscovery
{
var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
_configuration = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.ServiceName);
_consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
_consul = new ConsulClient(config =>
{
config.Address = new Uri($"http://{_configuration.HostName}:{_configuration.Port}");
config.Address = new Uri($"http://{_consulConfig.HostName}:{_consulConfig.Port}");
});
}
public async Task<List<Service>> Get()
{
var queryResult = await _consul.Health.Service(_configuration.ServiceName, string.Empty, true);
var queryResult = await _consul.Health.Service(_consulConfig.KeyOfServiceInConsul, string.Empty, true);
var services = queryResult.Response.Select(BuildService);

View File

@ -4,6 +4,6 @@ namespace Ocelot.ServiceDiscovery
{
public interface IServiceDiscoveryProviderFactory
{
IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig);
IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute);
}
}

View File

@ -6,17 +6,17 @@ namespace Ocelot.ServiceDiscovery
{
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
{
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig)
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute)
{
if (serviceConfig.UseServiceDiscovery)
if (reRoute.UseServiceDiscovery)
{
return GetServiceDiscoveryProvider(serviceConfig.ServiceName, serviceConfig.ServiceDiscoveryProvider, serviceConfig.ServiceProviderHost, serviceConfig.ServiceProviderPort);
return GetServiceDiscoveryProvider(reRoute.ServiceName, serviceConfig.ServiceProviderHost, serviceConfig.ServiceProviderPort);
}
var services = new List<Service>()
{
new Service(serviceConfig.ServiceName,
new HostAndPort(serviceConfig.DownstreamHost, serviceConfig.DownstreamPort),
new Service(reRoute.ServiceName,
new HostAndPort(reRoute.DownstreamHost, reRoute.DownstreamPort),
string.Empty,
string.Empty,
new string[0])
@ -25,9 +25,9 @@ namespace Ocelot.ServiceDiscovery
return new ConfigurationServiceProvider(services);
}
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string serviceName, string serviceProviderName, string providerHostName, int providerPort)
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string keyOfServiceInConsul, string providerHostName, int providerPort)
{
var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, serviceName);
var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, keyOfServiceInConsul);
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration);
}
}

View File

@ -1,17 +0,0 @@
namespace Ocelot.Utilities
{
public static class StringExtensions
{
public static string SetLastCharacterAs(this string valueToSetLastChar,
char expectedLastChar)
{
var last = valueToSetLastChar[valueToSetLastChar.Length - 1];
if (last != expectedLastChar)
{
valueToSetLastChar = $"{valueToSetLastChar}{expectedLastChar}";
}
return valueToSetLastChar;
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class CannotStartOcelotTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
public CannotStartOcelotTests()
{
_steps = new Steps();
}
[Fact]
public void should_throw_exception_if_cannot_start()
{
var invalidConfig = new FileConfiguration()
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "api",
DownstreamPathTemplate = "test"
}
}
};
Exception exception = null;
_steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch(Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -1,28 +1,30 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using Ocelot.ServiceDiscovery;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class ConfigurationInConsul : IDisposable
public class ConfigurationInConsulTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private IWebHost _fakeConsulBuilder;
private IOcelotConfiguration _config;
private FileConfiguration _config;
public ConfigurationInConsul()
public ConfigurationInConsulTests()
{
_steps = new Steps();
}
@ -48,7 +50,6 @@ namespace Ocelot.AcceptanceTests
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Provider = "Consul",
Host = "localhost",
Port = 9500
}
@ -57,18 +58,162 @@ namespace Ocelot.AcceptanceTests
var fakeConsulServiceDiscoveryUrl = "http://localhost:9500";
var consulConfig = new ConsulRegistryConfiguration("localhost", 9500, "Ocelot");
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.GivenOcelotIsRunningUsingConsulToStoreConfig(consulConfig))
.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_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<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
}
}
};
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<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;
}
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url)
{
_fakeConsulBuilder = new WebHostBuilder()
@ -102,7 +247,7 @@ namespace Ocelot.AcceptanceTests
var json = reader.ReadToEnd();
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json);
_config = JsonConvert.DeserializeObject<FileConfiguration>(json);
var response = JsonConvert.SerializeObject(true);
@ -137,7 +282,7 @@ namespace Ocelot.AcceptanceTests
public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e";
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(url)
@ -147,6 +292,8 @@ namespace Ocelot.AcceptanceTests
.UseUrls(url)
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
context.Response.StatusCode = statusCode;

View File

@ -0,0 +1,47 @@
using System;
using CacheManager.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
namespace Ocelot.AcceptanceTests
{
public class ConsulStartup
{
public ConsulStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithDictionaryHandle();
};
services.AddOcelot(Configuration).AddStoreOcelotConfigurationInConsul();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}
}

View File

@ -159,7 +159,7 @@ namespace Ocelot.AcceptanceTests
}
[Fact]
public void should_not_care_about_no_trailing()
public void should_return_ok_when_upstream_url_ends_with_forward_slash_but_template_does_not()
{
var configuration = new FileConfiguration
{
@ -187,7 +187,7 @@ namespace Ocelot.AcceptanceTests
}
[Fact]
public void should_not_care_about_trailing()
public void should_return_not_found_when_upstream_url_ends_with_forward_slash_but_template_does_not()
{
var configuration = new FileConfiguration
{
@ -209,8 +209,7 @@ namespace Ocelot.AcceptanceTests
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
.BDDfy();
}
@ -483,6 +482,41 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
[Fact]
public void should_fix_145()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/{url}",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51899,
UpstreamPathTemplate = "/platform/{url}",
UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions {
ExceptionsAllowedBeforeBreaking = 3,
DurationOfBreak = 10,
TimeoutValue = 5000
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/platform/swagger/lib/backbone-min.js"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => ThenTheDownstreamUrlPathShouldBe("/api/swagger/lib/backbone-min.js"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
@ -495,7 +529,7 @@ namespace Ocelot.AcceptanceTests
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = context.Request.PathBase.Value;
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});

View File

@ -33,10 +33,11 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_use_service_discovery_and_load_balance_request()
{
var consulPort = 8501;
var serviceName = "product";
var downstreamServiceOneUrl = "http://localhost:50879";
var downstreamServiceTwoUrl = "http://localhost:50880";
var fakeConsulServiceDiscoveryUrl = "http://localhost:8500";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
@ -72,15 +73,15 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" },
ServiceName = serviceName,
LoadBalancer = "LeastConnection",
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Provider = "Consul",
Host = "localhost",
Port = 8500
Port = consulPort
}
}
};
@ -99,8 +100,8 @@ namespace Ocelot.AcceptanceTests
private void ThenBothServicesCalledRealisticAmountOfTimes()
{
_counterOne.ShouldBe(25);
_counterTwo.ShouldBe(25);
_counterOne.ShouldBeInRange(24,26);
_counterOne.ShouldBeInRange(24,26);
}
private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected)

View File

@ -0,0 +1,47 @@
using System;
using CacheManager.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
namespace Ocelot.AcceptanceTests
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithDictionaryHandle();
};
services.AddOcelot(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}
}

View File

@ -107,18 +107,17 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningUsingConsulToStoreConfig(ConsulRegistryConfiguration consulConfig)
public void GivenOcelotIsRunningUsingConsulToStoreConfig()
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder.ConfigureServices(s =>
{
s.AddSingleton(_webHostBuilder);
s.AddOcelotStoreConfigurationInConsul(consulConfig);
});
_ocelotServer = new TestServer(_webHostBuilder
.UseStartup<Startup>());
.UseStartup<ConsulStartup>());
_ocelotClient = _ocelotServer.CreateClient();
}
@ -131,7 +130,6 @@ namespace Ocelot.AcceptanceTests
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
response.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
for(var i = 0; i < response.ReRoutes.Count; i++)
{
@ -175,7 +173,7 @@ namespace Ocelot.AcceptanceTests
.WithDictionaryHandle();
};
s.AddOcelot(configuration, settings);
s.AddOcelot(configuration);
})
.ConfigureLogging(l =>
{
@ -381,42 +379,4 @@ namespace Ocelot.AcceptanceTests
_response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected);
}
}
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
services.AddOcelot(Configuration, settings);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}
}

View File

@ -107,7 +107,6 @@ namespace Ocelot.IntegrationTests
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "127.0.0.1",
Provider = "test"
}
},
@ -332,7 +331,6 @@ namespace Ocelot.IntegrationTests
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
response.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
for (var i = 0; i < response.ReRoutes.Count; i++)
{

View File

@ -38,7 +38,7 @@ namespace Ocelot.IntegrationTests
.WithDictionaryHandle();
};
services.AddOcelot(Configuration, settings);
services.AddOcelot(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

View File

@ -45,7 +45,7 @@ namespace Ocelot.ManualTest
x.Audience = "test";
});
services.AddOcelot(Configuration, settings);
services.AddOcelot(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

View File

@ -1,133 +1,62 @@
// using System.Collections.Generic;
// using Ocelot.Configuration;
// using Ocelot.Configuration.Builder;
// using Ocelot.Configuration.Creator;
// using Ocelot.Configuration.File;
// using Shouldly;
// using TestStack.BDDfy;
// using Xunit;
using System.Collections.Generic;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
// namespace Ocelot.UnitTests.Configuration
// {
// public class AuthenticationOptionsCreatorTests
// {
// private readonly AuthenticationOptionsCreator _authOptionsCreator;
// private FileReRoute _fileReRoute;
// private AuthenticationOptions _result;
namespace Ocelot.UnitTests.Configuration
{
public class AuthenticationOptionsCreatorTests
{
private readonly AuthenticationOptionsCreator _authOptionsCreator;
private FileReRoute _fileReRoute;
private AuthenticationOptions _result;
// public AuthenticationOptionsCreatorTests()
// {
// _authOptionsCreator = new AuthenticationOptionsCreator(new AuthenticationProviderConfigCreator());
// }
public AuthenticationOptionsCreatorTests()
{
_authOptionsCreator = new AuthenticationOptionsCreator();
}
// [Fact]
// public void should_return_auth_options()
// {
// var fileReRoute = new FileReRoute()
// {
// AuthenticationOptions = new FileAuthenticationOptions
// {
// Provider = "Geoff",
// IdentityServerConfig = new FileIdentityServerConfig()
// {
// ProviderRootUrl = "http://www.bbc.co.uk/",
// ApiName = "Laura",
// RequireHttps = true,
// ApiSecret = "secret"
// },
// AllowedScopes = new List<string> { "cheese" },
[Fact]
public void should_return_auth_options()
{
var fileReRoute = new FileReRoute()
{
AuthenticationOptions = new FileAuthenticationOptions
{
AuthenticationProviderKey = "Test",
AllowedScopes = new List<string> { "cheese" },
}
};
// }
// };
var expected = new AuthenticationOptionsBuilder()
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithAuthenticationProviderKey("Test")
.Build();
// var authenticationConfig = new IdentityServerConfigBuilder()
// .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ProviderRootUrl)
// .WithApiName(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiName)
// .WithRequireHttps(fileReRoute.AuthenticationOptions.IdentityServerConfig.RequireHttps)
// .WithApiSecret(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiSecret)
// .Build();
this.Given(x => x.GivenTheFollowing(fileReRoute))
.When(x => x.WhenICreateTheAuthenticationOptions())
.Then(x => x.ThenTheFollowingConfigIsReturned(expected))
.BDDfy();
}
// var expected = new AuthenticationOptionsBuilder()
// .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
// .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
// .WithConfig(authenticationConfig)
// .Build();
private void GivenTheFollowing(FileReRoute fileReRoute)
{
_fileReRoute = fileReRoute;
}
// this.Given(x => x.GivenTheFollowing(fileReRoute))
// .When(x => x.WhenICreateTheAuthenticationOptions())
// .Then(x => x.ThenTheFollowingIdentityServerConfigIsReturned(expected))
// .BDDfy();
// }
private void WhenICreateTheAuthenticationOptions()
{
_result = _authOptionsCreator.Create(_fileReRoute);
}
// [Fact]
// public void should_return_Jwt_auth_options()
// {
// var fileReRoute = new FileReRoute()
// {
// AuthenticationOptions = new FileAuthenticationOptions
// {
// Provider = "Jwt",
// JwtConfig = new FileJwtConfig()
// {
// Audience = "Audience",
// Authority = "Authority"
// },
// AllowedScopes = new List<string> { "cheese" }
// }
// };
// var authenticationConfig = new JwtConfigBuilder()
// .WithAudience(fileReRoute.AuthenticationOptions?.JwtConfig?.Audience)
// .WithAuthority(fileReRoute.AuthenticationOptions?.JwtConfig?.Authority)
// .Build();
// var expected = new AuthenticationOptionsBuilder()
// .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
// .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
// .WithConfig(authenticationConfig)
// .Build();
// this.Given(x => x.GivenTheFollowing(fileReRoute))
// .When(x => x.WhenICreateTheAuthenticationOptions())
// .Then(x => x.ThenTheFollowingJwtConfigIsReturned(expected))
// .BDDfy();
// }
// private void GivenTheFollowing(FileReRoute fileReRoute)
// {
// _fileReRoute = fileReRoute;
// }
// private void WhenICreateTheAuthenticationOptions()
// {
// _result = _authOptionsCreator.Create(_fileReRoute);
// }
// private void ThenTheFollowingJwtConfigIsReturned(AuthenticationOptions expected)
// {
// _result.AllowedScopes.ShouldBe(expected.AllowedScopes);
// _result.Provider.ShouldBe(expected.Provider);
// var _resultSettings = _result.Config as JwtConfig;
// var expectedSettngs = expected.Config as JwtConfig;
// _resultSettings.Audience.ShouldBe(expectedSettngs.Audience);
// _resultSettings.Authority.ShouldBe(expectedSettngs.Authority);
// }
// private void ThenTheFollowingIdentityServerConfigIsReturned(AuthenticationOptions expected)
// {
// _result.AllowedScopes.ShouldBe(expected.AllowedScopes);
// _result.Provider.ShouldBe(expected.Provider);
// var _resultSettings = _result.Config as IdentityServerConfig;
// var expectedSettngs = expected.Config as IdentityServerConfig;
// _resultSettings.ProviderRootUrl.ShouldBe(expectedSettngs.ProviderRootUrl);
// _resultSettings.RequireHttps.ShouldBe(expectedSettngs.RequireHttps);
// _resultSettings.ApiName.ShouldBe(expectedSettngs.ApiName);
// _resultSettings.ApiSecret.ShouldBe(expectedSettngs.ApiSecret);
// }
// }
// }
private void ThenTheFollowingConfigIsReturned(AuthenticationOptions expected)
{
_result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_result.AuthenticationProviderKey.ShouldBe(expected.AuthenticationProviderKey);
}
}
}

View File

@ -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();
}
}
}

View File

@ -1,5 +1,4 @@
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Ocelot.Cache;
@ -8,9 +7,7 @@ using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Validator;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
@ -18,8 +15,7 @@ using Xunit;
namespace Ocelot.UnitTests.Configuration
{
using System.Collections;
using Ocelot.Errors;
using Ocelot.UnitTests.TestData;
public class FileConfigurationCreatorTests
@ -30,12 +26,6 @@ namespace Ocelot.UnitTests.Configuration
private FileConfiguration _fileConfiguration;
private readonly Mock<IOcelotLoggerFactory> _logger;
private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator;
private readonly Mock<ILoadBalancerFactory> _loadBalancerFactory;
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
private readonly Mock<ILoadBalancer> _loadBalancer;
private readonly Mock<IQoSProviderFactory> _qosProviderFactory;
private readonly Mock<IQosProviderHouse> _qosProviderHouse;
private readonly Mock<IQoSProvider> _qosProvider;
private Mock<IClaimsToThingCreator> _claimsToThingCreator;
private Mock<IAuthenticationOptionsCreator> _authOptionsCreator;
private Mock<IUpstreamTemplatePatternCreator> _upstreamTemplatePatternCreator;
@ -49,15 +39,9 @@ namespace Ocelot.UnitTests.Configuration
public FileConfigurationCreatorTests()
{
_qosProviderFactory = new Mock<IQoSProviderFactory>();
_qosProviderHouse = new Mock<IQosProviderHouse>();
_qosProvider = new Mock<IQoSProvider>();
_logger = new Mock<IOcelotLoggerFactory>();
_validator = new Mock<IConfigurationValidator>();
_fileConfig = new Mock<IOptions<FileConfiguration>>();
_loadBalancerFactory = new Mock<ILoadBalancerFactory>();
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_loadBalancer = new Mock<ILoadBalancer>();
_claimsToThingCreator = new Mock<IClaimsToThingCreator>();
_authOptionsCreator = new Mock<IAuthenticationOptionsCreator>();
_upstreamTemplatePatternCreator = new Mock<IUpstreamTemplatePatternCreator>();
@ -71,13 +55,35 @@ namespace Ocelot.UnitTests.Configuration
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
_fileConfig.Object, _validator.Object, _logger.Object,
_loadBalancerFactory.Object, _loadBalancerHouse.Object,
_qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object,
_claimsToThingCreator.Object,
_authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object,
_serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object,
_rateLimitOptions.Object, _regionCreator.Object, _httpHandlerOptionsCreator.Object);
}
[Fact]
public void should_call_service_provider_config_creator()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "localhost",
Port = 8500,
}
}
}))
.And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig))
.And(x => x.GivenTheConfigIsValid())
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheServiceProviderCreatorIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_call_region_creator()
{
@ -109,19 +115,6 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy();
}
private void GivenTheFollowingRegionIsReturned(string region)
{
_regionCreator
.Setup(x => x.Create(It.IsAny<FileReRoute>()))
.Returns(region);
}
private void ThenTheRegionCreatorIsCalledCorrectly(string expected)
{
_regionCreator
.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once);
}
[Fact]
public void should_call_rate_limit_options_creator()
{
@ -182,40 +175,9 @@ namespace Ocelot.UnitTests.Configuration
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions))
.And(x => x.GivenTheQosProviderFactoryReturns())
.And(x => x.GivenTheQosOptionsCreatorReturns(expected))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheQosOptionsAre(expected))
.And(x => x.TheQosProviderFactoryIsCalledCorrectly())
.And(x => x.ThenTheQosProviderHouseIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_create_load_balancer()
{
var reRouteOptions = new ReRouteOptionsBuilder()
.Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHost = "127.0.0.1",
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
}
},
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig())
.Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly())
.And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly())
.BDDfy();
}
@ -310,7 +272,6 @@ namespace Ocelot.UnitTests.Configuration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Provider = "consul",
Host = "127.0.0.1"
}
}
@ -324,12 +285,8 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder()
.WithUseServiceDiscovery(true)
.WithServiceDiscoveryProvider("consul")
.WithServiceDiscoveryProviderHost("127.0.0.1")
.WithServiceName("ProductService")
.Build())
.Build()
}))
.BDDfy();
@ -363,9 +320,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder()
.WithUseServiceDiscovery(false)
.Build())
.Build()
}))
.BDDfy();
@ -474,17 +429,6 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy();
}
private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions)
{
_httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny<FileReRoute>()))
.Returns(httpHandlerOptions);
}
private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly()
{
_httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once());
}
[Theory]
[MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_headers_to_extract(FileConfiguration fileConfig)
@ -516,7 +460,6 @@ namespace Ocelot.UnitTests.Configuration
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing> { new ClaimToThing("CustomerId", "CustomerId", "", 0) }))
.And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
@ -550,7 +493,6 @@ namespace Ocelot.UnitTests.Configuration
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
@ -558,6 +500,31 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy();
}
[Fact]
public void should_return_validation_errors()
{
var errors = new List<Error> {new PathTemplateDoesntStartWithForwardSlash("some message")};
this.Given(x => x.GivenTheConfigIs(new FileConfiguration()))
.And(x => x.GivenTheConfigIsInvalid(errors))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheErrorsAreReturned(errors))
.BDDfy();
}
private void GivenTheConfigIsInvalid(List<Error> errors)
{
_validator
.Setup(x => x.IsValid(It.IsAny<FileConfiguration>()))
.ReturnsAsync(new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(true, errors)));
}
private void ThenTheErrorsAreReturned(List<Error> errors)
{
_config.IsError.ShouldBeTrue();
_config.Errors[0].ShouldBe(errors[0]);
}
private void GivenTheFollowingOptionsAreReturned(ReRouteOptions fileReRouteOptions)
{
_fileReRouteOptionsCreator
@ -588,7 +555,7 @@ namespace Ocelot.UnitTests.Configuration
private void WhenICreateTheConfig()
{
_config = _ocelotConfigurationCreator.Create().Result;
_config = _ocelotConfigurationCreator.Create(_fileConfiguration).Result;
}
private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes)
@ -610,20 +577,6 @@ namespace Ocelot.UnitTests.Configuration
}
}
private void ThenTheServiceConfigurationIs(ServiceProviderConfiguration expected)
{
for (int i = 0; i < _config.Data.ReRoutes.Count; i++)
{
var result = _config.Data.ReRoutes[i];
result.ServiceProviderConfiguraion.DownstreamHost.ShouldBe(expected.DownstreamHost);
result.ServiceProviderConfiguraion.DownstreamPort.ShouldBe(expected.DownstreamPort);
result.ServiceProviderConfiguraion.ServiceDiscoveryProvider.ShouldBe(expected.ServiceDiscoveryProvider);
result.ServiceProviderConfiguraion.ServiceName.ShouldBe(expected.ServiceName);
result.ServiceProviderConfiguraion.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost);
result.ServiceProviderConfiguraion.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort);
}
}
private void ThenTheAuthenticationOptionsAre(List<ReRoute> expectedReRoutes)
{
for (int i = 0; i < _config.Data.ReRoutes.Count; i++)
@ -634,44 +587,6 @@ namespace Ocelot.UnitTests.Configuration
}
}
private void GivenTheLoadBalancerFactoryReturns()
{
_loadBalancerFactory
.Setup(x => x.Get(It.IsAny<ReRoute>()))
.ReturnsAsync(_loadBalancer.Object);
}
private void TheLoadBalancerFactoryIsCalledCorrectly()
{
_loadBalancerFactory
.Verify(x => x.Get(It.IsAny<ReRoute>()), Times.Once);
}
private void ThenTheLoadBalancerHouseIsCalledCorrectly()
{
_loadBalancerHouse
.Verify(x => x.Add(It.IsAny<string>(), _loadBalancer.Object), Times.Once);
}
private void GivenTheQosProviderFactoryReturns()
{
_qosProviderFactory
.Setup(x => x.Get(It.IsAny<ReRoute>()))
.Returns(_qosProvider.Object);
}
private void TheQosProviderFactoryIsCalledCorrectly()
{
_qosProviderFactory
.Verify(x => x.Get(It.IsAny<ReRoute>()), Times.Once);
}
private void ThenTheQosProviderHouseIsCalledCorrectly()
{
_qosProviderHouse
.Verify(x => x.Add(It.IsAny<string>(), _qosProvider.Object), Times.Once);
}
private void GivenTheClaimsToThingCreatorReturns(List<ClaimToThing> claimsToThing)
{
_claimsToThingCreator
@ -726,5 +641,42 @@ namespace Ocelot.UnitTests.Configuration
_config.Data.ReRoutes[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking);
_config.Data.ReRoutes[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue);
}
private void ThenTheServiceProviderCreatorIsCalledCorrectly()
{
_serviceProviderConfigCreator
.Verify(x => x.Create(_fileConfiguration.GlobalConfiguration), Times.Once);
}
private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration)
{
_serviceProviderConfigCreator
.Setup(x => x.Create(It.IsAny<FileGlobalConfiguration>())).Returns(serviceProviderConfiguration);
}
private void GivenTheFollowingRegionIsReturned(string region)
{
_regionCreator
.Setup(x => x.Create(It.IsAny<FileReRoute>()))
.Returns(region);
}
private void ThenTheRegionCreatorIsCalledCorrectly(string expected)
{
_regionCreator
.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once);
}
private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions)
{
_httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny<FileReRoute>()))
.Returns(httpHandlerOptions);
}
private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly()
{
_httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once());
}
}
}

View File

@ -45,12 +45,12 @@ namespace Ocelot.UnitTests.Configuration
_fileConfiguration = fileConfiguration;
_repo
.Setup(x => x.Get())
.Returns(new OkResponse<FileConfiguration>(fileConfiguration));
.ReturnsAsync(new OkResponse<FileConfiguration>(fileConfiguration));
}
private void WhenIGetTheReRoutes()
{
_result = _provider.Get().Data;
_result = _provider.Get().Result.Data;
}
private void ThenTheRepoIsCalledCorrectly()

View File

@ -1,57 +1,35 @@
using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.File;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Newtonsoft.Json;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration.Repository;
namespace Ocelot.UnitTests.Configuration
{
public class FileConfigurationRepositoryTests
{
private readonly IFileConfigurationRepository _repo;
private readonly Mock<IHostingEnvironment> _hostingEnvironment = new Mock<IHostingEnvironment>();
private IFileConfigurationRepository _repo;
private FileConfiguration _result;
private FileConfiguration _fileConfiguration;
private string _environmentName = "DEV";
public FileConfigurationRepositoryTests()
{
_repo = new FileConfigurationRepository();
_hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName);
_repo = new FileConfigurationRepository(_hostingEnvironment.Object);
}
[Fact]
public void should_return_file_configuration()
{
var reRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHost = "localhost",
DownstreamPort = 80,
DownstreamScheme = "https",
DownstreamPathTemplate = "/test/test/{test}"
}
};
var globalConfiguration = new FileGlobalConfiguration
{
AdministrationPath = "testy",
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Provider = "consul",
Port = 198,
Host = "blah"
}
};
var config = new FileConfiguration();
config.GlobalConfiguration = globalConfiguration;
config.ReRoutes.AddRange(reRoutes);
var config = FakeFileConfigurationForGet();
this.Given(x => x.GivenTheConfigurationIs(config))
.When(x => x.WhenIGetTheReRoutes())
@ -59,8 +37,110 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy();
}
[Fact]
public void should_return_file_configuration_if_environment_name_is_unavailable()
{
var config = FakeFileConfigurationForGet();
this.Given(x => x.GivenTheEnvironmentNameIsUnavailable())
.And(x => x.GivenTheConfigurationIs(config))
.When(x => x.WhenIGetTheReRoutes())
.Then(x => x.ThenTheFollowingIsReturned(config))
.BDDfy();
}
[Fact]
public void should_set_file_configuration()
{
var config = FakeFileConfigurationForSet();
this.Given(x => GivenIHaveAConfiguration(config))
.When(x => WhenISetTheConfiguration())
.Then(x => ThenTheConfigurationIsStoredAs(config))
.BDDfy();
}
[Fact]
public void should_set_file_configuration_if_environment_name_is_unavailable()
{
var config = FakeFileConfigurationForSet();
this.Given(x => GivenIHaveAConfiguration(config))
.And(x => GivenTheEnvironmentNameIsUnavailable())
.When(x => WhenISetTheConfiguration())
.Then(x => ThenTheConfigurationIsStoredAs(config))
.BDDfy();
}
private void GivenTheEnvironmentNameIsUnavailable()
{
_environmentName = null;
_hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName);
_repo = new FileConfigurationRepository(_hostingEnvironment.Object);
}
private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration)
{
_fileConfiguration = fileConfiguration;
}
private void WhenISetTheConfiguration()
{
_repo.Set(_fileConfiguration);
_result = _repo.Get().Result.Data;
}
private void ThenTheConfigurationIsStoredAs(FileConfiguration expected)
{
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++)
{
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
}
}
private void GivenTheConfigurationIs(FileConfiguration fileConfiguration)
{
var configurationPath = $"{AppContext.BaseDirectory}/configuration{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
}
private void WhenIGetTheReRoutes()
{
_result = _repo.Get().Result.Data;
}
private void ThenTheFollowingIsReturned(FileConfiguration expected)
{
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++)
{
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
}
}
private FileConfiguration FakeFileConfigurationForSet()
{
var reRoutes = new List<FileReRoute>
{
@ -78,84 +158,46 @@ namespace Ocelot.UnitTests.Configuration
AdministrationPath = "asdas",
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Provider = "consul",
Port = 198,
Host = "blah"
}
};
var config = new FileConfiguration();
config.GlobalConfiguration = globalConfiguration;
config.ReRoutes.AddRange(reRoutes);
this.Given(x => GivenIHaveAConfiguration(config))
.When(x => WhenISetTheConfiguration())
.Then(x => ThenTheConfigurationIsStoredAs(config))
.BDDfy();
}
private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration)
return new FileConfiguration
{
_fileConfiguration = fileConfiguration;
GlobalConfiguration = globalConfiguration,
ReRoutes = reRoutes
};
}
private void WhenISetTheConfiguration()
private FileConfiguration FakeFileConfigurationForGet()
{
_repo.Set(_fileConfiguration);
_result = _repo.Get().Data;
}
private void ThenTheConfigurationIsStoredAs(FileConfiguration expected)
var reRoutes = new List<FileReRoute>
{
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
for(var i = 0; i < _result.ReRoutes.Count; i++)
new FileReRoute
{
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
}
DownstreamHost = "localhost",
DownstreamPort = 80,
DownstreamScheme = "https",
DownstreamPathTemplate = "/test/test/{test}"
}
};
private void GivenTheConfigurationIs(FileConfiguration fileConfiguration)
var globalConfiguration = new FileGlobalConfiguration
{
var configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
AdministrationPath = "testy",
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
File.Delete(configurationPath);
Port = 198,
Host = "blah"
}
};
File.WriteAllText(configurationPath, jsonConfiguration);
}
private void WhenIGetTheReRoutes()
return new FileConfiguration
{
_result = _repo.Get().Data;
}
private void ThenTheFollowingIsReturned(FileConfiguration expected)
{
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
for(var i = 0; i < _result.ReRoutes.Count; i++)
{
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
}
GlobalConfiguration = globalConfiguration,
ReRoutes = reRoutes
};
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository;
@ -36,7 +37,8 @@ namespace Ocelot.UnitTests.Configuration
public void should_set_configuration()
{
var fileConfig = new FileConfiguration();
var config = new OcelotConfiguration(new List<ReRoute>(), string.Empty);
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
var config = new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig);
this.Given(x => GivenTheFollowingConfiguration(fileConfig))
.And(x => GivenTheRepoReturns(new OkResponse()))
@ -76,7 +78,7 @@ namespace Ocelot.UnitTests.Configuration
{
_repo
.Setup(x => x.Set(It.IsAny<FileConfiguration>()))
.Returns(response);
.ReturnsAsync(response);
}
private void ThenAnErrorResponseIsReturned()

View File

@ -92,6 +92,8 @@ namespace Ocelot.UnitTests.Configuration
};
public string AdministrationPath {get;}
public ServiceProviderConfiguration ServiceProviderConfiguration => throw new NotImplementedException();
}
}
}

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
@ -27,9 +28,11 @@ namespace Ocelot.UnitTests.Configuration
[Fact]
public void should_get_config()
{
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty))))
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig))))
.When(x => x.WhenIGetTheConfig())
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty))))
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig))))
.BDDfy();
}

View File

@ -29,14 +29,12 @@ namespace Ocelot.UnitTests.Configuration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Provider = "consul",
Host = "127.0.0.1",
Port = 1234
}
};
var expected = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProvider("consul")
.WithServiceDiscoveryProviderHost("127.0.0.1")
.WithServiceDiscoveryProviderPort(1234)
.Build();
@ -60,15 +58,11 @@ namespace Ocelot.UnitTests.Configuration
private void WhenICreate()
{
_result = _creator.Create(_reRoute, _globalConfig);
_result = _creator.Create(_globalConfig);
}
private void ThenTheConfigIs(ServiceProviderConfiguration expected)
{
_result.DownstreamHost.ShouldBe(expected.DownstreamHost);
_result.DownstreamPort.ShouldBe(expected.DownstreamPort);
_result.ServiceDiscoveryProvider.ShouldBe(expected.ServiceDiscoveryProvider);
_result.ServiceName.ShouldBe(expected.ServiceName);
_result.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost);
_result.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort);
}

View File

@ -28,7 +28,23 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/.*/$"))
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/[0-9a-zA-Z].*$"))
.BDDfy();
}
[Fact]
public void should_match_forward_slash_or_no_forward_slash_if_template_end_with_forward_slash()
{
var fileReRoute = new FileReRoute
{
UpstreamPathTemplate = "/PRODUCTS/",
ReRouteIsCaseSensitive = false
};
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS(/|)$"))
.BDDfy();
}
@ -42,7 +58,7 @@ namespace Ocelot.UnitTests.Configuration
};
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.*/$"))
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/[0-9a-zA-Z].*$"))
.BDDfy();
}
@ -57,7 +73,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/$"))
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*$"))
.BDDfy();
}
@ -72,9 +88,10 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$"))
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*/variants/[0-9a-zA-Z].*$"))
.BDDfy();
}
[Fact]
public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash()
{
@ -86,7 +103,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$"))
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*/variants/[0-9a-zA-Z].*(/|)$"))
.BDDfy();
}

View File

@ -107,12 +107,12 @@ namespace Ocelot.UnitTests.Controllers
{
_configGetter
.Setup(x => x.Get())
.Returns(fileConfiguration);
.ReturnsAsync(fileConfiguration);
}
private void WhenIGetTheFileConfiguration()
{
_result = _controller.Get();
_result = _controller.Get().Result;
}
private void TheTheGetFileConfigurationIsCalledCorrectly()

View File

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using CacheManager.Core;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Cache;
using Ocelot.Configuration;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.DependencyInjection
{
public class OcelotBuilderTests
{
private IServiceCollection _services;
private IConfigurationRoot _configRoot;
private IOcelotBuilder _ocelotBuilder;
private int _maxRetries;
public OcelotBuilderTests()
{
IWebHostBuilder builder = new WebHostBuilder();
_configRoot = new ConfigurationRoot(new List<IConfigurationProvider>());
_services = new ServiceCollection();
_services.AddSingleton(builder);
_maxRetries = 100;
}
private Exception _ex;
[Fact]
public void should_set_up_services()
{
this.When(x => WhenISetUpOcelotServices())
.Then(x => ThenAnExceptionIsntThrown())
.BDDfy();
}
[Fact]
public void should_return_ocelot_builder()
{
this.When(x => WhenISetUpOcelotServices())
.Then(x => ThenAnOcelotBuilderIsReturned())
.BDDfy();
}
[Fact]
public void should_set_up_cache_manager()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpCacheManager())
.Then(x => ThenAnExceptionIsntThrown())
.And(x => OnlyOneVersionOfEachCacheIsRegistered())
.BDDfy();
}
[Fact]
public void should_set_up_consul()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpConsul())
.Then(x => ThenAnExceptionIsntThrown())
.BDDfy();
}
private void OnlyOneVersionOfEachCacheIsRegistered()
{
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<HttpResponseMessage>));
var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<HttpResponseMessage>));
var thing = (CacheManager.Core.ICacheManager<System.Net.Http.HttpResponseMessage>)outputCacheManager.ImplementationInstance;
thing.Configuration.MaxRetries.ShouldBe(_maxRetries);
var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<IOcelotConfiguration>));
var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<IOcelotConfiguration>));
var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<FileConfiguration>));
var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<FileConfiguration>));
}
private void WhenISetUpConsul()
{
try
{
_ocelotBuilder.AddStoreOcelotConfigurationInConsul();
}
catch (Exception e)
{
_ex = e;
}
}
private void ThenAnOcelotBuilderIsReturned()
{
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
}
private void WhenISetUpOcelotServices()
{
try
{
_ocelotBuilder = _services.AddOcelot(_configRoot);
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenISetUpCacheManager()
{
try
{
_ocelotBuilder.AddCacheManager(x => {
x.WithMaxRetries(_maxRetries);
x.WithDictionaryHandle();
});
}
catch (Exception e)
{
_ex = e;
}
}
private void ThenAnExceptionIsntThrown()
{
_ex.ShouldBeNull();
}
}
}

View File

@ -4,7 +4,9 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.Middleware;
@ -17,10 +19,13 @@
public class DownstreamRouteFinderMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IDownstreamRouteFinder> _downstreamRouteFinder;
private readonly Mock<IOcelotConfigurationProvider> _provider;
private Response<DownstreamRoute> _downstreamRoute;
private IOcelotConfiguration _config;
public DownstreamRouteFinderMiddlewareTests()
{
_provider = new Mock<IOcelotConfigurationProvider>();
_downstreamRouteFinder = new Mock<IDownstreamRouteFinder>();
GivenTheTestServerIsConfigured();
@ -29,6 +34,8 @@
[Fact]
public void should_call_scoped_data_repository_correctly()
{
var config = new OcelotConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build());
this.Given(x => x.GivenTheDownStreamRouteFinderReturns(
new DownstreamRoute(
new List<UrlPathPlaceholderNameAndValue>(),
@ -36,16 +43,26 @@
.WithDownstreamPathTemplate("any old string")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.And(x => GivenTheFollowingConfig(config))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy();
}
private void GivenTheFollowingConfig(IOcelotConfiguration config)
{
_config = config;
_provider
.Setup(x => x.Get())
.ReturnsAsync(new OkResponse<IOcelotConfiguration>(_config));
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_downstreamRouteFinder.Object);
services.AddSingleton(_provider.Object);
services.AddSingleton(ScopedRepository.Object);
}
@ -58,14 +75,17 @@
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_downstreamRouteFinder
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(_downstreamRoute);
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IOcelotConfiguration>()))
.Returns(_downstreamRoute);
}
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{
ScopedRepository
.Verify(x => x.Add("DownstreamRoute", _downstreamRoute.Data), Times.Once());
ScopedRepository
.Verify(x => x.Add("ServiceProviderConfiguration", _config.ServiceProviderConfiguration), Times.Once());
}
}
}

View File

@ -16,26 +16,27 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
public class DownstreamRouteFinderTests
{
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly Mock<IOcelotConfigurationProvider> _mockConfig;
private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher;
private readonly Mock<IUrlPathPlaceholderNameAndValueFinder> _finder;
private string _upstreamUrlPath;
private Response<DownstreamRoute> _result;
private List<ReRoute> _reRoutesConfig;
private OcelotConfiguration _config;
private Response<UrlMatch> _match;
private string _upstreamHttpMethod;
public DownstreamRouteFinderTests()
{
_mockConfig = new Mock<IOcelotConfigurationProvider>();
_mockMatcher = new Mock<IUrlPathToUrlTemplateMatcher>();
_finder = new Mock<IUrlPathPlaceholderNameAndValueFinder>();
_downstreamRouteFinder = new Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder(_mockConfig.Object, _mockMatcher.Object, _finder.Object);
_downstreamRouteFinder = new Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder(_mockMatcher.Object, _finder.Object);
}
[Fact]
public void should_return_route()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
@ -48,7 +49,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath")
.Build()
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -67,8 +68,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact]
public void should_append_slash_to_upstream_url_path()
public void should_not_append_slash_to_upstream_url_path()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
@ -81,7 +84,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath")
.Build()
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -94,13 +97,15 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build()
)))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher/"))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher"))
.BDDfy();
}
[Fact]
public void should_return_route_if_upstream_path_and_upstream_template_are_the_same()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(
x =>
@ -114,7 +119,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath")
.Build()
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -133,6 +138,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact]
public void should_return_correct_route_for_http_verb()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(
x =>
@ -152,7 +159,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Post" })
.WithUpstreamTemplatePattern("")
.Build()
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -170,6 +177,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact]
public void should_not_return_route()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/"))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{
@ -179,7 +188,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("somePath")
.Build(),
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(false))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -193,6 +202,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact]
public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(
x =>
@ -206,7 +217,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get", "Post" })
.WithUpstreamTemplatePattern("")
.Build()
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -224,6 +235,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact]
public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(
x =>
@ -237,7 +250,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string>())
.WithUpstreamTemplatePattern("")
.Build()
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -255,6 +268,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact]
public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method()
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(
x =>
@ -268,7 +283,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get", "Patch", "Delete" })
.WithUpstreamTemplatePattern("")
.Build()
}, string.Empty
}, string.Empty, serviceProviderConfig
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -322,12 +337,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.Returns(_match);
}
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath)
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig)
{
_reRoutesConfig = reRoutesConfig;
_mockConfig
.Setup(x => x.Get())
.ReturnsAsync(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(_reRoutesConfig, adminPath)));
_config = new OcelotConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig);
}
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)
@ -337,7 +350,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void WhenICallTheFinder()
{
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod).Result;
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config);
}
private void ThenTheFollowingIsReturned(DownstreamRoute expected)

View File

@ -30,6 +30,16 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
.BDDfy();
}
[Fact]
public void should_not_find_anything()
{
this.Given(x => x.GivenIHaveAUpstreamPath("/products"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplate("/products/"))
.When(x => x.WhenIFindTheUrlVariableNamesAndValues())
.And(x => x.ThenTheTemplatesVariablesAre(new List<UrlPathPlaceholderNameAndValue>()))
.BDDfy();
}
[Fact]
public void can_match_down_stream_url_with_no_slash()
{

View File

@ -0,0 +1,18 @@
using Ocelot.Errors;
using Ocelot.Infrastructure.RequestData;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests.Errors
{
public class ErrorTests
{
[Fact]
public void should_return_message()
{
var error = new CannotAddDataError("message");
var result = error.ToString();
result.ShouldBe("message");
}
}
}

View File

@ -14,7 +14,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{
private HostAndPort _hostAndPort;
private Response<HostAndPort> _result;
private LeastConnectionLoadBalancer _leastConnection;
private LeastConnection _leastConnection;
private List<Service> _services;
private Random _random;
@ -35,7 +35,7 @@ namespace Ocelot.UnitTests.LoadBalancer
};
_services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
_leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var tasks = new Task[100];
@ -86,7 +86,7 @@ namespace Ocelot.UnitTests.LoadBalancer
};
_services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
_leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var response = _leastConnection.Lease().Result;
@ -113,7 +113,7 @@ namespace Ocelot.UnitTests.LoadBalancer
};
_services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
_leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var response = _leastConnection.Lease().Result;
@ -144,7 +144,7 @@ namespace Ocelot.UnitTests.LoadBalancer
};
_services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
_leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var response = _leastConnection.Lease().Result;
@ -211,7 +211,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheLoadBalancerStarts(List<Service> services, string serviceName)
{
_services = services;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
_leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
}
private void WhenTheLoadBalancerStarts(List<Service> services, string serviceName)

View File

@ -17,6 +17,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private ILoadBalancer _result;
private Mock<IServiceDiscoveryProviderFactory> _serviceProviderFactory;
private Mock<IServiceDiscoveryProvider> _serviceProvider;
private ServiceProviderConfiguration _serviceProviderConfig;
public LoadBalancerFactoryTests()
{
@ -29,11 +30,11 @@ namespace Ocelot.UnitTests.LoadBalancer
public void should_return_no_load_balancer()
{
var reRoute = new ReRouteBuilder()
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
.And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build()))
.And(x => x.GivenTheServiceProviderFactoryReturns())
.When(x => x.WhenIGetTheLoadBalancer())
.Then(x => x.ThenTheLoadBalancerIsReturned<NoLoadBalancer>())
@ -46,13 +47,13 @@ namespace Ocelot.UnitTests.LoadBalancer
var reRoute = new ReRouteBuilder()
.WithLoadBalancer("RoundRobin")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
.And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build()))
.And(x => x.GivenTheServiceProviderFactoryReturns())
.When(x => x.WhenIGetTheLoadBalancer())
.Then(x => x.ThenTheLoadBalancerIsReturned<RoundRobinLoadBalancer>())
.Then(x => x.ThenTheLoadBalancerIsReturned<RoundRobin>())
.BDDfy();
}
@ -62,13 +63,13 @@ namespace Ocelot.UnitTests.LoadBalancer
var reRoute = new ReRouteBuilder()
.WithLoadBalancer("LeastConnection")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
.And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build()))
.And(x => x.GivenTheServiceProviderFactoryReturns())
.When(x => x.WhenIGetTheLoadBalancer())
.Then(x => x.ThenTheLoadBalancerIsReturned<LeastConnectionLoadBalancer>())
.Then(x => x.ThenTheLoadBalancerIsReturned<LeastConnection>())
.BDDfy();
}
@ -78,27 +79,32 @@ namespace Ocelot.UnitTests.LoadBalancer
var reRoute = new ReRouteBuilder()
.WithLoadBalancer("RoundRobin")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
.And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build()))
.And(x => x.GivenTheServiceProviderFactoryReturns())
.When(x => x.WhenIGetTheLoadBalancer())
.Then(x => x.ThenTheServiceProviderIsCalledCorrectly())
.BDDfy();
}
private void GivenAServiceProviderConfig(ServiceProviderConfiguration serviceProviderConfig)
{
_serviceProviderConfig = serviceProviderConfig;
}
private void GivenTheServiceProviderFactoryReturns()
{
_serviceProviderFactory
.Setup(x => x.Get(It.IsAny<ServiceProviderConfiguration>()))
.Setup(x => x.Get(It.IsAny<ServiceProviderConfiguration>(), It.IsAny<ReRoute>()))
.Returns(_serviceProvider.Object);
}
private void ThenTheServiceProviderIsCalledCorrectly()
{
_serviceProviderFactory
.Verify(x => x.Get(It.IsAny<ServiceProviderConfiguration>()), Times.Once);
.Verify(x => x.Get(It.IsAny<ServiceProviderConfiguration>(), It.IsAny<ReRoute>()), Times.Once);
}
private void GivenAReRoute(ReRoute reRoute)
@ -108,7 +114,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void WhenIGetTheLoadBalancer()
{
_result = _factory.Get(_reRoute).Result;
_result = _factory.Get(_reRoute, _serviceProviderConfig).Result;
}
private void ThenTheLoadBalancerIsReturned<T>()

View File

@ -1,5 +1,8 @@
using System;
using System.Threading.Tasks;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Responses;
using Ocelot.Values;
@ -11,35 +14,38 @@ namespace Ocelot.UnitTests.LoadBalancer
{
public class LoadBalancerHouseTests
{
private ReRoute _reRoute;
private ILoadBalancer _loadBalancer;
private readonly LoadBalancerHouse _loadBalancerHouse;
private Response _addResult;
private Response<ILoadBalancer> _getResult;
private string _key;
private Mock<ILoadBalancerFactory> _factory;
private ServiceProviderConfiguration _serviceProviderConfig;
public LoadBalancerHouseTests()
{
_loadBalancerHouse = new LoadBalancerHouse();
_factory = new Mock<ILoadBalancerFactory>();
_loadBalancerHouse = new LoadBalancerHouse(_factory.Object);
}
[Fact]
public void should_store_load_balancer()
public void should_store_load_balancer_on_first_request()
{
var key = "test";
var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build();
this.Given(x => x.GivenThereIsALoadBalancer(key, new FakeLoadBalancer()))
.When(x => x.WhenIAddTheLoadBalancer())
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))
.Then(x => x.ThenItIsAdded())
.BDDfy();
}
[Fact]
public void should_get_load_balancer()
public void should_not_store_load_balancer_on_second_request()
{
var key = "test";
var reRoute = new ReRouteBuilder().WithLoadBalancer("FakeLoadBalancer").WithReRouteKey("test").Build();
this.Given(x => x.GivenThereIsALoadBalancer(key, new FakeLoadBalancer()))
.When(x => x.WhenWeGetTheLoadBalancer(key))
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))
.When(x => x.WhenWeGetTheLoadBalancer(reRoute))
.Then(x => x.ThenItIsReturned())
.BDDfy();
}
@ -47,26 +53,50 @@ namespace Ocelot.UnitTests.LoadBalancer
[Fact]
public void should_store_load_balancers_by_key()
{
var key = "test";
var keyTwo = "testTwo";
var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build();
var reRouteTwo = new ReRouteBuilder().WithReRouteKey("testtwo").Build();
this.Given(x => x.GivenThereIsALoadBalancer(key, new FakeLoadBalancer()))
.And(x => x.GivenThereIsALoadBalancer(keyTwo, new FakeRoundRobinLoadBalancer()))
.When(x => x.WhenWeGetTheLoadBalancer(key))
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))
.And(x => x.GivenThereIsALoadBalancer(reRouteTwo, new FakeRoundRobinLoadBalancer()))
.When(x => x.WhenWeGetTheLoadBalancer(reRoute))
.Then(x => x.ThenTheLoadBalancerIs<FakeLoadBalancer>())
.When(x => x.WhenWeGetTheLoadBalancer(keyTwo))
.When(x => x.WhenWeGetTheLoadBalancer(reRouteTwo))
.Then(x => x.ThenTheLoadBalancerIs<FakeRoundRobinLoadBalancer>())
.BDDfy();
}
[Fact]
public void should_return_error_if_no_load_balancer_with_key()
public void should_return_error_if_exception()
{
this.When(x => x.WhenWeGetTheLoadBalancer("test"))
var reRoute = new ReRouteBuilder().Build();
this.When(x => x.WhenWeGetTheLoadBalancer(reRoute))
.Then(x => x.ThenAnErrorIsReturned())
.BDDfy();
}
[Fact]
public void should_get_new_load_balancer_if_reroute_load_balancer_has_changed()
{
var reRoute = new ReRouteBuilder().WithLoadBalancer("FakeLoadBalancer").WithReRouteKey("test").Build();
var reRouteTwo = new ReRouteBuilder().WithLoadBalancer("LeastConnection").WithReRouteKey("test").Build();
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))
.When(x => x.WhenWeGetTheLoadBalancer(reRoute))
.Then(x => x.ThenTheLoadBalancerIs<FakeLoadBalancer>())
.When(x => x.WhenIGetTheReRouteWithTheSameKeyButDifferentLoadBalancer(reRouteTwo))
.Then(x => x.ThenTheLoadBalancerIs<LeastConnection>())
.BDDfy();
}
private void WhenIGetTheReRouteWithTheSameKeyButDifferentLoadBalancer(ReRoute reRoute)
{
_reRoute = reRoute;
_factory.Setup(x => x.Get(_reRoute, _serviceProviderConfig)).ReturnsAsync(new LeastConnection(null, null));
_getResult = _loadBalancerHouse.Get(_reRoute, _serviceProviderConfig).Result;
}
private void ThenAnErrorIsReturned()
{
_getResult.IsError.ShouldBeTrue();
@ -80,31 +110,30 @@ namespace Ocelot.UnitTests.LoadBalancer
private void ThenItIsAdded()
{
_addResult.IsError.ShouldBe(false);
_addResult.ShouldBeOfType<OkResponse>();
}
private void WhenIAddTheLoadBalancer()
{
_addResult = _loadBalancerHouse.Add(_key, _loadBalancer);
_getResult.IsError.ShouldBe(false);
_getResult.ShouldBeOfType<OkResponse<ILoadBalancer>>();
_getResult.Data.ShouldBe(_loadBalancer);
_factory.Verify(x => x.Get(_reRoute, _serviceProviderConfig), Times.Once);
}
private void GivenThereIsALoadBalancer(string key, ILoadBalancer loadBalancer)
private void GivenThereIsALoadBalancer(ReRoute reRoute, ILoadBalancer loadBalancer)
{
_key = key;
_reRoute = reRoute;
_loadBalancer = loadBalancer;
WhenIAddTheLoadBalancer();
_factory.Setup(x => x.Get(_reRoute, _serviceProviderConfig)).ReturnsAsync(loadBalancer);
_getResult = _loadBalancerHouse.Get(reRoute, _serviceProviderConfig).Result;
}
private void WhenWeGetTheLoadBalancer(string key)
private void WhenWeGetTheLoadBalancer(ReRoute reRoute)
{
_getResult = _loadBalancerHouse.Get(key);
_getResult = _loadBalancerHouse.Get(reRoute, _serviceProviderConfig).Result;
}
private void ThenItIsReturned()
{
_getResult.Data.ShouldBe(_loadBalancer);
_factory.Verify(x => x.Get(_reRoute, _serviceProviderConfig), Times.Once);
}
class FakeLoadBalancer : ILoadBalancer

View File

@ -5,7 +5,9 @@ namespace Ocelot.UnitTests.LoadBalancer
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder;
using Ocelot.Errors;
using Ocelot.LoadBalancer.LoadBalancers;
@ -26,13 +28,13 @@ namespace Ocelot.UnitTests.LoadBalancer
private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError;
private ErrorResponse<HostAndPort> _getHostAndPortError;
private HttpRequestMessage _downstreamRequest;
private ServiceProviderConfiguration _config;
public LoadBalancerMiddlewareTests()
{
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_loadBalancer = new Mock<ILoadBalancer>();
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "");
ScopedRepository
@ -50,7 +52,11 @@ namespace Ocelot.UnitTests.LoadBalancer
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build());
var serviceProviderConfig = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123"))
.And(x => GivenTheConfigurationIs(serviceProviderConfig))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheLoadBalancerHouseReturns())
.And(x => x.GivenTheLoadBalancerReturns())
@ -67,7 +73,11 @@ namespace Ocelot.UnitTests.LoadBalancer
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build());
var serviceProviderConfig = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123"))
.And(x => GivenTheConfigurationIs(serviceProviderConfig))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheLoadBalancerHouseReturnsAnError())
.When(x => x.WhenICallTheMiddleware())
@ -83,7 +93,11 @@ namespace Ocelot.UnitTests.LoadBalancer
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build());
var serviceProviderConfig = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123"))
.And(x => GivenTheConfigurationIs(serviceProviderConfig))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheLoadBalancerHouseReturns())
.And(x => x.GivenTheLoadBalancerReturnsAnError())
@ -92,6 +106,13 @@ namespace Ocelot.UnitTests.LoadBalancer
.BDDfy();
}
private void GivenTheConfigurationIs(ServiceProviderConfiguration config)
{
_config = config;
ScopedRepository
.Setup(x => x.Get<ServiceProviderConfiguration>("ServiceProviderConfiguration")).Returns(new OkResponse<ServiceProviderConfiguration>(config));
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
@ -137,8 +158,8 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheLoadBalancerHouseReturns()
{
_loadBalancerHouse
.Setup(x => x.Get(It.IsAny<string>()))
.Returns(new OkResponse<ILoadBalancer>(_loadBalancer.Object));
.Setup(x => x.Get(It.IsAny<ReRoute>(), It.IsAny<ServiceProviderConfiguration>()))
.ReturnsAsync(new OkResponse<ILoadBalancer>(_loadBalancer.Object));
}
private void GivenTheLoadBalancerHouseReturnsAnError()
@ -149,8 +170,8 @@ namespace Ocelot.UnitTests.LoadBalancer
});
_loadBalancerHouse
.Setup(x => x.Get(It.IsAny<string>()))
.Returns(_getLoadBalancerHouseError);
.Setup(x => x.Get(It.IsAny<ReRoute>(), It.IsAny<ServiceProviderConfiguration>()))
.ReturnsAsync(_getLoadBalancerHouseError);
}
private void ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline()

View File

@ -12,7 +12,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{
public class RoundRobinTests
{
private readonly RoundRobinLoadBalancer _roundRobin;
private readonly RoundRobin _roundRobin;
private readonly List<Service> _services;
private Response<HostAndPort> _hostAndPort;
@ -25,7 +25,7 @@ namespace Ocelot.UnitTests.LoadBalancer
new Service("product", new HostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0])
};
_roundRobin = new RoundRobinLoadBalancer(() => Task.FromResult(_services));
_roundRobin = new RoundRobin(() => Task.FromResult(_services));
}
[Fact]

View File

@ -31,6 +31,12 @@
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<None Update="idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />

View File

@ -88,7 +88,7 @@
private void GivenTheQosProviderHouseReturns(Response<IQoSProvider> qosProvider)
{
_qosProviderHouse
.Setup(x => x.Get(It.IsAny<string>()))
.Setup(x => x.Get(It.IsAny<ReRoute>()))
.Returns(qosProvider);
}

View File

@ -1,5 +1,10 @@
using Ocelot.Requester.QoS;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using Ocelot.UnitTests.LoadBalancer;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
@ -12,31 +17,32 @@ namespace Ocelot.UnitTests.Requester
private readonly QosProviderHouse _qosProviderHouse;
private Response _addResult;
private Response<IQoSProvider> _getResult;
private string _key;
private ReRoute _reRoute;
private readonly Mock<IQoSProviderFactory> _factory;
public QosProviderHouseTests()
{
_qosProviderHouse = new QosProviderHouse();
_factory = new Mock<IQoSProviderFactory>();
_qosProviderHouse = new QosProviderHouse(_factory.Object);
}
[Fact]
public void should_store_qos_provider()
public void should_store_qos_provider_on_first_request()
{
var key = "test";
var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build();
this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider()))
.When(x => x.WhenIAddTheQoSProvider())
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.Then(x => x.ThenItIsAdded())
.BDDfy();
}
[Fact]
public void should_get_qos_provider()
public void should_not_store_qos_provider_on_first_request()
{
var key = "test";
var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build();
this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(key))
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenItIsReturned())
.BDDfy();
}
@ -44,14 +50,14 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_store_qos_providers_by_key()
{
var key = "test";
var keyTwo = "testTwo";
var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build();
var reRouteTwo = new ReRouteBuilder().WithReRouteKey("testTwo").Build();
this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider()))
.And(x => x.GivenThereIsAQoSProvider(keyTwo, new FakePollyQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(key))
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.And(x => x.GivenThereIsAQoSProvider(reRouteTwo, new FakePollyQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenTheQoSProviderIs<FakeQoSProvider>())
.When(x => x.WhenWeGetTheQoSProvider(keyTwo))
.When(x => x.WhenWeGetTheQoSProvider(reRouteTwo))
.Then(x => x.ThenTheQoSProviderIs<FakePollyQoSProvider>())
.BDDfy();
}
@ -59,11 +65,35 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_return_error_if_no_qos_provider_with_key()
{
this.When(x => x.WhenWeGetTheQoSProvider("test"))
var reRoute = new ReRouteBuilder().Build();
this.When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenAnErrorIsReturned())
.BDDfy();
}
[Fact]
public void should_get_new_qos_provider_if_reroute_qos_provider_has_changed()
{
var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build();
var reRouteTwo = new ReRouteBuilder().WithReRouteKey("test").WithIsQos(true).Build();
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenTheQoSProviderIs<FakeQoSProvider>())
.When(x => x.WhenIGetTheReRouteWithTheSameKeyButDifferentQosProvider(reRouteTwo))
.Then(x => x.ThenTheQoSProviderIs<FakePollyQoSProvider>())
.BDDfy();
}
private void WhenIGetTheReRouteWithTheSameKeyButDifferentQosProvider(ReRoute reRoute)
{
_reRoute = reRoute;
_factory.Setup(x => x.Get(_reRoute)).Returns(new FakePollyQoSProvider());
_getResult = _qosProviderHouse.Get(_reRoute);
}
private void ThenAnErrorIsReturned()
{
_getResult.IsError.ShouldBeTrue();
@ -77,31 +107,30 @@ namespace Ocelot.UnitTests.Requester
private void ThenItIsAdded()
{
_addResult.IsError.ShouldBe(false);
_addResult.ShouldBeOfType<OkResponse>();
}
private void WhenIAddTheQoSProvider()
{
_addResult = _qosProviderHouse.Add(_key, _qoSProvider);
_getResult.IsError.ShouldBe(false);
_getResult.ShouldBeOfType<OkResponse<IQoSProvider>>();
_factory.Verify(x => x.Get(_reRoute), Times.Once);
_getResult.Data.ShouldBe(_qoSProvider);
}
private void GivenThereIsAQoSProvider(string key, IQoSProvider qoSProvider)
private void GivenThereIsAQoSProvider(ReRoute reRoute, IQoSProvider qoSProvider)
{
_key = key;
_reRoute = reRoute;
_qoSProvider = qoSProvider;
WhenIAddTheQoSProvider();
_factory.Setup(x => x.Get(_reRoute)).Returns(_qoSProvider);
_getResult = _qosProviderHouse.Get(reRoute);
}
private void WhenWeGetTheQoSProvider(string key)
private void WhenWeGetTheQoSProvider(ReRoute reRoute)
{
_getResult = _qosProviderHouse.Get(key);
_getResult = _qosProviderHouse.Get(reRoute);
}
private void ThenItIsReturned()
{
_getResult.Data.ShouldBe(_qoSProvider);
_factory.Verify(x => x.Get(_reRoute), Times.Once);
}
class FakeQoSProvider : IQoSProvider

View File

@ -12,6 +12,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
private ServiceProviderConfiguration _serviceConfig;
private IServiceDiscoveryProvider _result;
private readonly ServiceDiscoveryProviderFactory _factory;
private ReRoute _reRoute;
public ServiceProviderFactoryTests()
{
@ -22,12 +23,11 @@ namespace Ocelot.UnitTests.ServiceDiscovery
public void should_return_no_service_provider()
{
var serviceConfig = new ServiceProviderConfigurationBuilder()
.WithDownstreamHost("127.0.0.1")
.WithDownstreamPort(80)
.WithUseServiceDiscovery(false)
.Build();
this.Given(x => x.GivenTheReRoute(serviceConfig))
var reRoute = new ReRouteBuilder().Build();
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<ConfigurationServiceProvider>())
.BDDfy();
@ -36,26 +36,29 @@ namespace Ocelot.UnitTests.ServiceDiscovery
[Fact]
public void should_return_consul_service_provider()
{
var serviceConfig = new ServiceProviderConfigurationBuilder()
var reRoute = new ReRouteBuilder()
.WithServiceName("product")
.WithUseServiceDiscovery(true)
.WithServiceDiscoveryProvider("Consul")
.Build();
this.Given(x => x.GivenTheReRoute(serviceConfig))
var serviceConfig = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<ConsulServiceDiscoveryProvider>())
.BDDfy();
}
private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig)
private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig, ReRoute reRoute)
{
_serviceConfig = serviceConfig;
_reRoute = reRoute;
}
private void WhenIGetTheServiceProvider()
{
_result = _factory.Get(_serviceConfig);
_result = _factory.Get(_serviceConfig, _reRoute);
}
private void ThenTheServiceProviderIs<T>()

View 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;
}
}
}

Binary file not shown.