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) [![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 Ocelot is a .NET Api Gateway. This project is aimed at people using .NET running
This project is aimed at people using .NET running
a micro services / service orientated architecture a micro services / service orientated architecture
that need a unified point of entry into their system. that need a unified point of entry into their system.

View File

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

View File

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

View File

@ -2,44 +2,9 @@ namespace Ocelot.Configuration.Builder
{ {
public class ServiceProviderConfigurationBuilder public class ServiceProviderConfigurationBuilder
{ {
private string _serviceName;
private string _downstreamHost;
private int _downstreamPort;
private bool _userServiceDiscovery;
private string _serviceDiscoveryProvider;
private string _serviceDiscoveryProviderHost; private string _serviceDiscoveryProviderHost;
private int _serviceDiscoveryProviderPort; 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) public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
{ {
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost; _serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
@ -52,11 +17,9 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ServiceProviderConfiguration Build() public ServiceProviderConfiguration Build()
{ {
return new ServiceProviderConfiguration(_serviceName, _downstreamHost, _downstreamPort, _userServiceDiscovery, return new ServiceProviderConfiguration(_serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
_serviceDiscoveryProvider, _serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
} }
} }
} }

View File

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

View File

@ -4,6 +4,6 @@ namespace Ocelot.Configuration.Creator
{ {
public interface IServiceProviderConfigurationCreator 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 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; var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
return new ServiceProviderConfigurationBuilder() return new ServiceProviderConfigurationBuilder()
.WithServiceName(fileReRoute.ServiceName)
.WithDownstreamHost(fileReRoute.DownstreamHost)
.WithDownstreamPort(fileReRoute.DownstreamPort)
.WithUseServiceDiscovery(useServiceDiscovery)
.WithServiceDiscoveryProvider(globalConfiguration?.ServiceDiscoveryProvider?.Provider)
.WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host) .WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
.WithServiceDiscoveryProviderPort(serviceProviderPort) .WithServiceDiscoveryProviderPort(serviceProviderPort)
.Build(); .Build();

View File

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

View File

@ -37,5 +37,6 @@ namespace Ocelot.Configuration.File
public FileRateLimitRule RateLimitOptions { get; set; } public FileRateLimitRule RateLimitOptions { get; set; }
public FileAuthenticationOptions AuthenticationOptions { get; set; } public FileAuthenticationOptions AuthenticationOptions { get; set; }
public FileHttpHandlerOptions HttpHandlerOptions { 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 class FileServiceDiscoveryProvider
{ {
public string Provider {get;set;}
public string Host {get;set;} public string Host {get;set;}
public int Port { get; set; } public int Port { get; set; }
} }

View File

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

View File

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

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository; using Ocelot.Configuration.Repository;
@ -16,9 +17,9 @@ namespace Ocelot.Configuration.Provider
_repo = repo; _repo = repo;
} }
public Response<FileConfiguration> Get() public async Task<Response<FileConfiguration>> Get()
{ {
var fileConfig = _repo.Get(); var fileConfig = await _repo.Get();
return new OkResponse<FileConfiguration>(fileConfig.Data); return new OkResponse<FileConfiguration>(fileConfig.Data);
} }
} }

View File

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

View File

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

View File

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

View File

@ -25,15 +25,17 @@ namespace Ocelot.Configuration
string downstreamHost, string downstreamHost,
int downstreamPort, int downstreamPort,
string reRouteKey, string reRouteKey,
ServiceProviderConfiguration serviceProviderConfiguraion,
bool isQos, bool isQos,
QoSOptions qosOptions, QoSOptions qosOptions,
bool enableEndpointRateLimiting, bool enableEndpointRateLimiting,
RateLimitOptions ratelimitOptions, RateLimitOptions ratelimitOptions,
HttpHandlerOptions httpHandlerOptions) HttpHandlerOptions httpHandlerOptions,
bool useServiceDiscovery,
string serviceName)
{ {
ServiceName = serviceName;
UseServiceDiscovery = useServiceDiscovery;
ReRouteKey = reRouteKey; ReRouteKey = reRouteKey;
ServiceProviderConfiguraion = serviceProviderConfiguraion;
LoadBalancer = loadBalancer; LoadBalancer = loadBalancer;
DownstreamHost = downstreamHost; DownstreamHost = downstreamHost;
DownstreamPort = downstreamPort; DownstreamPort = downstreamPort;
@ -83,9 +85,10 @@ namespace Ocelot.Configuration
public string LoadBalancer {get;private set;} public string LoadBalancer {get;private set;}
public string DownstreamHost { get; private set; } public string DownstreamHost { get; private set; }
public int DownstreamPort { get; private set; } public int DownstreamPort { get; private set; }
public ServiceProviderConfiguration ServiceProviderConfiguraion { get; private set; }
public bool EnableEndpointEndpointRateLimiting { get; private set; } public bool EnableEndpointEndpointRateLimiting { get; private set; }
public RateLimitOptions RateLimitOptions { get; private set; } public RateLimitOptions RateLimitOptions { get; private set; }
public HttpHandlerOptions HttpHandlerOptions { 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 System.Threading.Tasks;
using Consul; using Consul;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery;
namespace Ocelot.Configuration.Repository namespace Ocelot.Configuration.Repository
{ {
public class ConsulOcelotConfigurationRepository : IOcelotConfigurationRepository
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
{ {
private readonly ConsulClient _consul; private readonly ConsulClient _consul;
private ConsulRegistryConfiguration _configuration;
private string _ocelotConfiguration = "OcelotConfiguration"; 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 consulHost = string.IsNullOrEmpty(serviceProviderConfig?.ServiceProviderHost) ? "localhost" : serviceProviderConfig?.ServiceProviderHost;
var consulPort = consulRegistryConfiguration?.Port ?? 8500; var consulPort = serviceProviderConfig?.ServiceProviderPort ?? 8500;
_configuration = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.ServiceName); var configuration = new ConsulRegistryConfiguration(consulHost, consulPort, _ocelotConfiguration);
_cache = cache; _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); var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
if (config != null) if (config != null)
{ {
return new OkResponse<IOcelotConfiguration>(config); return new OkResponse<FileConfiguration>(config);
} }
var queryResult = await _consul.KV.Get(_ocelotConfiguration); var queryResult = await _consul.KV.Get(_ocelotConfiguration);
if (queryResult.Response == null) if (queryResult.Response == null)
{ {
return new OkResponse<IOcelotConfiguration>(null); return new OkResponse<FileConfiguration>(null);
} }
var bytes = queryResult.Response.Value; var bytes = queryResult.Response.Value;
var json = Encoding.UTF8.GetString(bytes); var json = Encoding.UTF8.GetString(bytes);
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json); var consulConfig = JsonConvert.DeserializeObject<FileConfiguration>(json);
return new OkResponse<IOcelotConfiguration>(consulConfig); return new OkResponse<FileConfiguration>(consulConfig);
} }
public async Task<Response> AddOrReplace(IOcelotConfiguration ocelotConfiguration) public async Task<Response> Set(FileConfiguration ocelotConfiguration)
{ {
var json = JsonConvert.SerializeObject(ocelotConfiguration); var json = JsonConvert.SerializeObject(ocelotConfiguration);
@ -72,7 +73,7 @@ namespace Ocelot.Configuration.Repository
return new OkResponse(); return new OkResponse();
} }
return new ErrorResponse(new UnableToSetConfigInConsulError("Unable to set config in consul")); return new ErrorResponse(new UnableToSetConfigInConsulError($"Unable to set FileConfiguration in consul, response status code from consul was {result.StatusCode}"));
} }
} }
} }

View File

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

View File

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

View File

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

View File

@ -2,23 +2,12 @@
{ {
public class ServiceProviderConfiguration public class ServiceProviderConfiguration
{ {
public ServiceProviderConfiguration(string serviceName, string downstreamHost, public ServiceProviderConfiguration(string serviceProviderHost, int serviceProviderPort)
int downstreamPort, bool useServiceDiscovery, string serviceDiscoveryProvider, string serviceProviderHost, int serviceProviderPort)
{ {
ServiceName = serviceName;
DownstreamHost = downstreamHost;
DownstreamPort = downstreamPort;
UseServiceDiscovery = useServiceDiscovery;
ServiceDiscoveryProvider = serviceDiscoveryProvider;
ServiceProviderHost = serviceProviderHost; ServiceProviderHost = serviceProviderHost;
ServiceProviderPort = serviceProviderPort; 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 string ServiceProviderHost { get; private set; }
public int ServiceProviderPort { 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) public async Task<Response> Set(FileConfiguration fileConfig)
{ {
var response = _repo.Set(fileConfig); var response = await _repo.Set(fileConfig);
if(response.IsError) if(response.IsError)
{ {

View File

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

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 Microsoft.Extensions.Configuration;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; 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.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 namespace Ocelot.DependencyInjection
{ {
public static class ServiceCollectionExtensions 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); return new OcelotBuilder(services, configurationRoot);
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 }
}
};
} }
} }
} }

View File

@ -2,34 +2,28 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Provider; using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Utilities;
namespace Ocelot.DownstreamRouteFinder.Finder namespace Ocelot.DownstreamRouteFinder.Finder
{ {
public class DownstreamRouteFinder : IDownstreamRouteFinder public class DownstreamRouteFinder : IDownstreamRouteFinder
{ {
private readonly IOcelotConfigurationProvider _configProvider;
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IUrlPathPlaceholderNameAndValueFinder _urlPathPlaceholderNameAndValueFinder; private readonly IUrlPathPlaceholderNameAndValueFinder _urlPathPlaceholderNameAndValueFinder;
public DownstreamRouteFinder(IOcelotConfigurationProvider configProvider, IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder) public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
{ {
_configProvider = configProvider;
_urlMatcher = urlMatcher; _urlMatcher = urlMatcher;
_urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder; _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 applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));
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()));
foreach (var reRoute in applicableReRoutes) foreach (var reRoute in applicableReRoutes)
{ {

View File

@ -1,10 +1,11 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Responses; using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.Finder namespace Ocelot.DownstreamRouteFinder.Finder
{ {
public interface IDownstreamRouteFinder 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.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.Extensions; using Ocelot.Infrastructure.Extensions;
using Ocelot.Infrastructure.RequestData; using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.Utilities;
namespace Ocelot.DownstreamRouteFinder.Middleware namespace Ocelot.DownstreamRouteFinder.Middleware
{ {
@ -16,13 +16,17 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
private readonly IOcelotConfigurationProvider _configProvider;
public DownstreamRouteFinderMiddleware(RequestDelegate next, public DownstreamRouteFinderMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IDownstreamRouteFinder downstreamRouteFinder, IDownstreamRouteFinder downstreamRouteFinder,
IRequestScopedDataRepository requestScopedDataRepository) IRequestScopedDataRepository requestScopedDataRepository,
IOcelotConfigurationProvider configProvider)
:base(requestScopedDataRepository) :base(requestScopedDataRepository)
{ {
_configProvider = configProvider;
_next = next; _next = next;
_downstreamRouteFinder = downstreamRouteFinder; _downstreamRouteFinder = downstreamRouteFinder;
_logger = loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>(); _logger = loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>();
@ -32,9 +36,19 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
{ {
var upstreamUrlPath = context.Request.Path.ToString(); 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); _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) if (downstreamRoute.IsError)
{ {

View File

@ -13,7 +13,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
for (int counterForTemplate = 0; counterForTemplate < upstreamUrlPathTemplate.Length; counterForTemplate++) 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])) if (IsPlaceholder(upstreamUrlPathTemplate[counterForTemplate]))
{ {

View File

@ -10,5 +10,10 @@ namespace Ocelot.Errors
public string Message { get; private set; } public string Message { get; private set; }
public OcelotErrorCode Code { 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; object obj;
if(_httpContextAccessor.HttpContext == null || _httpContextAccessor.HttpContext.Items == null)
{
return new ErrorResponse<T>(new List<Error>
{
new CannotFindDataError($"Unable to find data for key: {key} because HttpContext or HttpContext.Items is null")
});
}
if(_httpContextAccessor.HttpContext.Items.TryGetValue(key, out obj)) if(_httpContextAccessor.HttpContext.Items.TryGetValue(key, out obj))
{ {
var data = (T) obj; var data = (T) obj;

View File

@ -5,6 +5,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{ {
public interface ILoadBalancerFactory 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 namespace Ocelot.LoadBalancer.LoadBalancers
{ {
public interface ILoadBalancerHouse public interface ILoadBalancerHouse
{ {
Response<ILoadBalancer> Get(string key); Task<Response<ILoadBalancer>> Get(ReRoute reRoute, ServiceProviderConfiguration config);
Response Add(string key, ILoadBalancer loadBalancer);
} }
} }

View File

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

View File

@ -12,16 +12,16 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_serviceProviderFactory = serviceProviderFactory; _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) switch (reRoute.LoadBalancer)
{ {
case "RoundRobin": case "RoundRobin":
return new RoundRobinLoadBalancer(async () => await serviceProvider.Get()); return new RoundRobin(async () => await serviceProvider.Get());
case "LeastConnection": case "LeastConnection":
return new LeastConnectionLoadBalancer(async () => await serviceProvider.Get(), reRoute.ServiceProviderConfiguraion.ServiceName); return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName);
default: default:
return new NoLoadBalancer(await serviceProvider.Get()); 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; using Ocelot.Responses;
namespace Ocelot.LoadBalancer.LoadBalancers namespace Ocelot.LoadBalancer.LoadBalancers
{ {
public class LoadBalancerHouse : ILoadBalancerHouse 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>() 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); private void AddLoadBalancer(string key, ILoadBalancer loadBalancer)
_loadBalancers.Add(key, loadBalancer); {
return new OkResponse(); _loadBalancers.AddOrUpdate(key, loadBalancer, (x, y) => loadBalancer);
} }
} }
} }

View File

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

View File

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

View File

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

View File

@ -23,16 +23,20 @@ using Ocelot.RateLimit.Middleware;
namespace Ocelot.Middleware namespace Ocelot.Middleware
{ {
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Authorisation.Middleware; using Authorisation.Middleware;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Provider; using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter; using Ocelot.Configuration.Setter;
using Ocelot.LoadBalancer.Middleware; using Ocelot.LoadBalancer.Middleware;
using Ocelot.Responses;
public static class OcelotMiddlewareExtensions public static class OcelotMiddlewareExtensions
{ {
@ -56,7 +60,9 @@ namespace Ocelot.Middleware
/// <returns></returns> /// <returns></returns>
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration) public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
{ {
await CreateAdministrationArea(builder); var configuration = await CreateConfiguration(builder);
await CreateAdministrationArea(builder, configuration);
ConfigureDiagnosticListener(builder); ConfigureDiagnosticListener(builder);
@ -145,38 +151,110 @@ namespace Ocelot.Middleware
private static async Task<IOcelotConfiguration> CreateConfiguration(IApplicationBuilder builder) 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)); if (ConfigurationNotSetUp(ocelotConfiguration))
var ocelotConfiguration = await configProvider.Get();
if (ocelotConfiguration == null || ocelotConfiguration.Data == null || ocelotConfiguration.IsError)
{ {
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) 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; 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)); var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null) if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null)

View File

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

View File

@ -31,7 +31,6 @@ namespace Ocelot.Requester
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler) private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
{ {
_handlers _handlers
.OrderByDescending(handler => handler.Key) .OrderByDescending(handler => handler.Key)
.Select(handler => handler.Value) .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 Ocelot.Configuration;
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Configuration;
using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Responses;
using Polly;
using Polly.CircuitBreaker;
using Polly.Timeout;
namespace Ocelot.Requester.QoS namespace Ocelot.Requester.QoS
{ {
@ -15,131 +7,4 @@ namespace Ocelot.Requester.QoS
{ {
IQoSProvider Get(ReRoute reRoute); 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 class ConsulRegistryConfiguration
{ {
public ConsulRegistryConfiguration(string hostName, int port, string serviceName) public ConsulRegistryConfiguration(string hostName, int port, string keyOfServiceInConsul)
{ {
HostName = hostName; HostName = hostName;
Port = port; 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 string HostName { get; private set; }
public int Port { get; private set; } public int Port { get; private set; }
} }

View File

@ -10,7 +10,7 @@ namespace Ocelot.ServiceDiscovery
{ {
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
{ {
private readonly ConsulRegistryConfiguration _configuration; private readonly ConsulRegistryConfiguration _consulConfig;
private readonly ConsulClient _consul; private readonly ConsulClient _consul;
private const string VersionPrefix = "version-"; private const string VersionPrefix = "version-";
@ -18,17 +18,17 @@ namespace Ocelot.ServiceDiscovery
{ {
var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName; var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
var consulPort = consulRegistryConfiguration?.Port ?? 8500; var consulPort = consulRegistryConfiguration?.Port ?? 8500;
_configuration = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.ServiceName); _consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
_consul = new ConsulClient(config => _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() 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); var services = queryResult.Response.Select(BuildService);

View File

@ -4,6 +4,6 @@ namespace Ocelot.ServiceDiscovery
{ {
public interface IServiceDiscoveryProviderFactory 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 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>() var services = new List<Service>()
{ {
new Service(serviceConfig.ServiceName, new Service(reRoute.ServiceName,
new HostAndPort(serviceConfig.DownstreamHost, serviceConfig.DownstreamPort), new HostAndPort(reRoute.DownstreamHost, reRoute.DownstreamPort),
string.Empty, string.Empty,
string.Empty, string.Empty,
new string[0]) new string[0])
@ -25,9 +25,9 @@ namespace Ocelot.ServiceDiscovery
return new ConfigurationServiceProvider(services); 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); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.ServiceDiscovery;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
public class ConfigurationInConsul : IDisposable public class ConfigurationInConsulTests : IDisposable
{ {
private IWebHost _builder; private IWebHost _builder;
private readonly Steps _steps; private readonly Steps _steps;
private IWebHost _fakeConsulBuilder; private IWebHost _fakeConsulBuilder;
private IOcelotConfiguration _config; private FileConfiguration _config;
public ConfigurationInConsul() public ConfigurationInConsulTests()
{ {
_steps = new Steps(); _steps = new Steps();
} }
@ -48,7 +50,6 @@ namespace Ocelot.AcceptanceTests
{ {
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{ {
Provider = "Consul",
Host = "localhost", Host = "localhost",
Port = 9500 Port = 9500
} }
@ -57,18 +58,162 @@ namespace Ocelot.AcceptanceTests
var fakeConsulServiceDiscoveryUrl = "http://localhost:9500"; var fakeConsulServiceDiscoveryUrl = "http://localhost:9500";
var consulConfig = new ConsulRegistryConfiguration("localhost", 9500, "Ocelot");
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig(consulConfig)) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
[Fact]
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) private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url)
{ {
_fakeConsulBuilder = new WebHostBuilder() _fakeConsulBuilder = new WebHostBuilder()
@ -102,7 +247,7 @@ namespace Ocelot.AcceptanceTests
var json = reader.ReadToEnd(); var json = reader.ReadToEnd();
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json); _config = JsonConvert.DeserializeObject<FileConfiguration>(json);
var response = JsonConvert.SerializeObject(true); var response = JsonConvert.SerializeObject(true);
@ -137,7 +282,7 @@ namespace Ocelot.AcceptanceTests
public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; 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() _builder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
@ -147,6 +292,8 @@ namespace Ocelot.AcceptanceTests
.UseUrls(url) .UseUrls(url)
.Configure(app => .Configure(app =>
{ {
app.UsePathBase(basePath);
app.Run(async context => app.Run(async context =>
{ {
context.Response.StatusCode = statusCode; 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] [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 var configuration = new FileConfiguration
{ {
@ -187,7 +187,7 @@ namespace Ocelot.AcceptanceTests
} }
[Fact] [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 var configuration = new FileConfiguration
{ {
@ -209,8 +209,7 @@ namespace Ocelot.AcceptanceTests
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
@ -483,6 +482,41 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .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) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{ {
_builder = new WebHostBuilder() _builder = new WebHostBuilder()
@ -495,7 +529,7 @@ namespace Ocelot.AcceptanceTests
app.UsePathBase(basePath); app.UsePathBase(basePath);
app.Run(async context => 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; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });

View File

@ -33,10 +33,11 @@ namespace Ocelot.AcceptanceTests
[Fact] [Fact]
public void should_use_service_discovery_and_load_balance_request() public void should_use_service_discovery_and_load_balance_request()
{ {
var consulPort = 8501;
var serviceName = "product"; var serviceName = "product";
var downstreamServiceOneUrl = "http://localhost:50879"; var downstreamServiceOneUrl = "http://localhost:50879";
var downstreamServiceTwoUrl = "http://localhost:50880"; var downstreamServiceTwoUrl = "http://localhost:50880";
var fakeConsulServiceDiscoveryUrl = "http://localhost:8500"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry() var serviceEntryOne = new ServiceEntry()
{ {
Service = new AgentService() Service = new AgentService()
@ -72,15 +73,15 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
ServiceName = serviceName, ServiceName = serviceName,
LoadBalancer = "LeastConnection", LoadBalancer = "LeastConnection",
UseServiceDiscovery = true,
} }
}, },
GlobalConfiguration = new FileGlobalConfiguration() GlobalConfiguration = new FileGlobalConfiguration()
{ {
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{ {
Provider = "Consul",
Host = "localhost", Host = "localhost",
Port = 8500 Port = consulPort
} }
} }
}; };
@ -99,8 +100,8 @@ namespace Ocelot.AcceptanceTests
private void ThenBothServicesCalledRealisticAmountOfTimes() private void ThenBothServicesCalledRealisticAmountOfTimes()
{ {
_counterOne.ShouldBe(25); _counterOne.ShouldBeInRange(24,26);
_counterTwo.ShouldBe(25); _counterOne.ShouldBeInRange(24,26);
} }
private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected) 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(); _ocelotClient = _ocelotServer.CreateClient();
} }
public void GivenOcelotIsRunningUsingConsulToStoreConfig(ConsulRegistryConfiguration consulConfig) public void GivenOcelotIsRunningUsingConsulToStoreConfig()
{ {
_webHostBuilder = new WebHostBuilder(); _webHostBuilder = new WebHostBuilder();
_webHostBuilder.ConfigureServices(s => _webHostBuilder.ConfigureServices(s =>
{ {
s.AddSingleton(_webHostBuilder); s.AddSingleton(_webHostBuilder);
s.AddOcelotStoreConfigurationInConsul(consulConfig);
}); });
_ocelotServer = new TestServer(_webHostBuilder _ocelotServer = new TestServer(_webHostBuilder
.UseStartup<Startup>()); .UseStartup<ConsulStartup>());
_ocelotClient = _ocelotServer.CreateClient(); _ocelotClient = _ocelotServer.CreateClient();
} }
@ -131,7 +130,6 @@ namespace Ocelot.AcceptanceTests
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey); response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host); response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port); 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++) for(var i = 0; i < response.ReRoutes.Count; i++)
{ {
@ -175,7 +173,7 @@ namespace Ocelot.AcceptanceTests
.WithDictionaryHandle(); .WithDictionaryHandle();
}; };
s.AddOcelot(configuration, settings); s.AddOcelot(configuration);
}) })
.ConfigureLogging(l => .ConfigureLogging(l =>
{ {
@ -381,42 +379,4 @@ namespace Ocelot.AcceptanceTests
_response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected); _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 ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{ {
Host = "127.0.0.1", Host = "127.0.0.1",
Provider = "test"
} }
}, },
@ -332,7 +331,6 @@ namespace Ocelot.IntegrationTests
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey); response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host); response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port); 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++) for (var i = 0; i < response.ReRoutes.Count; i++)
{ {

View File

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

View File

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

View File

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

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 System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using Ocelot.Cache; using Ocelot.Cache;
@ -8,9 +7,7 @@ using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Validator; using Ocelot.Configuration.Validator;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Requester.QoS;
using Ocelot.Responses; using Ocelot.Responses;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
@ -18,8 +15,7 @@ using Xunit;
namespace Ocelot.UnitTests.Configuration namespace Ocelot.UnitTests.Configuration
{ {
using System.Collections; using Ocelot.Errors;
using Ocelot.UnitTests.TestData; using Ocelot.UnitTests.TestData;
public class FileConfigurationCreatorTests public class FileConfigurationCreatorTests
@ -30,12 +26,6 @@ namespace Ocelot.UnitTests.Configuration
private FileConfiguration _fileConfiguration; private FileConfiguration _fileConfiguration;
private readonly Mock<IOcelotLoggerFactory> _logger; private readonly Mock<IOcelotLoggerFactory> _logger;
private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator; 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<IClaimsToThingCreator> _claimsToThingCreator;
private Mock<IAuthenticationOptionsCreator> _authOptionsCreator; private Mock<IAuthenticationOptionsCreator> _authOptionsCreator;
private Mock<IUpstreamTemplatePatternCreator> _upstreamTemplatePatternCreator; private Mock<IUpstreamTemplatePatternCreator> _upstreamTemplatePatternCreator;
@ -49,15 +39,9 @@ namespace Ocelot.UnitTests.Configuration
public FileConfigurationCreatorTests() public FileConfigurationCreatorTests()
{ {
_qosProviderFactory = new Mock<IQoSProviderFactory>();
_qosProviderHouse = new Mock<IQosProviderHouse>();
_qosProvider = new Mock<IQoSProvider>();
_logger = new Mock<IOcelotLoggerFactory>(); _logger = new Mock<IOcelotLoggerFactory>();
_validator = new Mock<IConfigurationValidator>(); _validator = new Mock<IConfigurationValidator>();
_fileConfig = new Mock<IOptions<FileConfiguration>>(); _fileConfig = new Mock<IOptions<FileConfiguration>>();
_loadBalancerFactory = new Mock<ILoadBalancerFactory>();
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_loadBalancer = new Mock<ILoadBalancer>();
_claimsToThingCreator = new Mock<IClaimsToThingCreator>(); _claimsToThingCreator = new Mock<IClaimsToThingCreator>();
_authOptionsCreator = new Mock<IAuthenticationOptionsCreator>(); _authOptionsCreator = new Mock<IAuthenticationOptionsCreator>();
_upstreamTemplatePatternCreator = new Mock<IUpstreamTemplatePatternCreator>(); _upstreamTemplatePatternCreator = new Mock<IUpstreamTemplatePatternCreator>();
@ -71,13 +55,35 @@ namespace Ocelot.UnitTests.Configuration
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator( _ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
_fileConfig.Object, _validator.Object, _logger.Object, _fileConfig.Object, _validator.Object, _logger.Object,
_loadBalancerFactory.Object, _loadBalancerHouse.Object, _claimsToThingCreator.Object,
_qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object,
_authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object, _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object,
_serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object, _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object,
_rateLimitOptions.Object, _regionCreator.Object, _httpHandlerOptionsCreator.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] [Fact]
public void should_call_region_creator() public void should_call_region_creator()
{ {
@ -109,19 +115,6 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .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] [Fact]
public void should_call_rate_limit_options_creator() public void should_call_rate_limit_options_creator()
{ {
@ -182,40 +175,9 @@ namespace Ocelot.UnitTests.Configuration
})) }))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions))
.And(x => x.GivenTheQosProviderFactoryReturns())
.And(x => x.GivenTheQosOptionsCreatorReturns(expected)) .And(x => x.GivenTheQosOptionsCreatorReturns(expected))
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheQosOptionsAre(expected)) .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(); .BDDfy();
} }
@ -310,7 +272,6 @@ namespace Ocelot.UnitTests.Configuration
{ {
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{ {
Provider = "consul",
Host = "127.0.0.1" Host = "127.0.0.1"
} }
} }
@ -324,12 +285,8 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamPathTemplate("/products/{productId}") .WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder()
.WithUseServiceDiscovery(true) .WithUseServiceDiscovery(true)
.WithServiceDiscoveryProvider("consul")
.WithServiceDiscoveryProviderHost("127.0.0.1")
.WithServiceName("ProductService") .WithServiceName("ProductService")
.Build())
.Build() .Build()
})) }))
.BDDfy(); .BDDfy();
@ -363,9 +320,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamPathTemplate("/products/{productId}") .WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder()
.WithUseServiceDiscovery(false) .WithUseServiceDiscovery(false)
.Build())
.Build() .Build()
})) }))
.BDDfy(); .BDDfy();
@ -474,17 +429,6 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .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] [Theory]
[MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_headers_to_extract(FileConfiguration fileConfig) 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.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing> { new ClaimToThing("CustomerId", "CustomerId", "", 0) })) .And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing> { new ClaimToThing("CustomerId", "CustomerId", "", 0) }))
.And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected))
@ -550,7 +493,6 @@ namespace Ocelot.UnitTests.Configuration
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected))
@ -558,6 +500,31 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .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) private void GivenTheFollowingOptionsAreReturned(ReRouteOptions fileReRouteOptions)
{ {
_fileReRouteOptionsCreator _fileReRouteOptionsCreator
@ -588,7 +555,7 @@ namespace Ocelot.UnitTests.Configuration
private void WhenICreateTheConfig() private void WhenICreateTheConfig()
{ {
_config = _ocelotConfigurationCreator.Create().Result; _config = _ocelotConfigurationCreator.Create(_fileConfiguration).Result;
} }
private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes) 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) private void ThenTheAuthenticationOptionsAre(List<ReRoute> expectedReRoutes)
{ {
for (int i = 0; i < _config.Data.ReRoutes.Count; i++) 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) private void GivenTheClaimsToThingCreatorReturns(List<ClaimToThing> claimsToThing)
{ {
_claimsToThingCreator _claimsToThingCreator
@ -726,5 +641,42 @@ namespace Ocelot.UnitTests.Configuration
_config.Data.ReRoutes[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); _config.Data.ReRoutes[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking);
_config.Data.ReRoutes[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); _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; _fileConfiguration = fileConfiguration;
_repo _repo
.Setup(x => x.Get()) .Setup(x => x.Get())
.Returns(new OkResponse<FileConfiguration>(fileConfiguration)); .ReturnsAsync(new OkResponse<FileConfiguration>(fileConfiguration));
} }
private void WhenIGetTheReRoutes() private void WhenIGetTheReRoutes()
{ {
_result = _provider.Get().Data; _result = _provider.Get().Result.Data;
} }
private void ThenTheRepoIsCalledCorrectly() private void ThenTheRepoIsCalledCorrectly()

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Moq; using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
using Ocelot.Configuration.Provider; using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository; using Ocelot.Configuration.Repository;
@ -27,9 +28,11 @@ namespace Ocelot.UnitTests.Configuration
[Fact] [Fact]
public void should_get_config() 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()) .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(); .BDDfy();
} }

View File

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

View File

@ -28,7 +28,23 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .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(); .BDDfy();
} }
@ -42,7 +58,7 @@ namespace Ocelot.UnitTests.Configuration
}; };
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.*/$")) .Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/[0-9a-zA-Z].*$"))
.BDDfy(); .BDDfy();
} }
@ -57,7 +73,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/$")) .Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*$"))
.BDDfy(); .BDDfy();
} }
@ -72,9 +88,10 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .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(); .BDDfy();
} }
[Fact] [Fact]
public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash() 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)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .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(); .BDDfy();
} }

View File

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

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.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.Middleware;
@ -17,10 +19,13 @@
public class DownstreamRouteFinderMiddlewareTests : ServerHostedMiddlewareTest public class DownstreamRouteFinderMiddlewareTests : ServerHostedMiddlewareTest
{ {
private readonly Mock<IDownstreamRouteFinder> _downstreamRouteFinder; private readonly Mock<IDownstreamRouteFinder> _downstreamRouteFinder;
private readonly Mock<IOcelotConfigurationProvider> _provider;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private IOcelotConfiguration _config;
public DownstreamRouteFinderMiddlewareTests() public DownstreamRouteFinderMiddlewareTests()
{ {
_provider = new Mock<IOcelotConfigurationProvider>();
_downstreamRouteFinder = new Mock<IDownstreamRouteFinder>(); _downstreamRouteFinder = new Mock<IDownstreamRouteFinder>();
GivenTheTestServerIsConfigured(); GivenTheTestServerIsConfigured();
@ -29,6 +34,8 @@
[Fact] [Fact]
public void should_call_scoped_data_repository_correctly() public void should_call_scoped_data_repository_correctly()
{ {
var config = new OcelotConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build());
this.Given(x => x.GivenTheDownStreamRouteFinderReturns( this.Given(x => x.GivenTheDownStreamRouteFinderReturns(
new DownstreamRoute( new DownstreamRoute(
new List<UrlPathPlaceholderNameAndValue>(), new List<UrlPathPlaceholderNameAndValue>(),
@ -36,16 +43,26 @@
.WithDownstreamPathTemplate("any old string") .WithDownstreamPathTemplate("any old string")
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.Build()))) .Build())))
.And(x => GivenTheFollowingConfig(config))
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
private void GivenTheFollowingConfig(IOcelotConfiguration config)
{
_config = config;
_provider
.Setup(x => x.Get())
.ReturnsAsync(new OkResponse<IOcelotConfiguration>(_config));
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{ {
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>(); services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging(); services.AddLogging();
services.AddSingleton(_downstreamRouteFinder.Object); services.AddSingleton(_downstreamRouteFinder.Object);
services.AddSingleton(_provider.Object);
services.AddSingleton(ScopedRepository.Object); services.AddSingleton(ScopedRepository.Object);
} }
@ -58,14 +75,17 @@
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_downstreamRouteFinder _downstreamRouteFinder
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>())) .Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IOcelotConfiguration>()))
.ReturnsAsync(_downstreamRoute); .Returns(_downstreamRoute);
} }
private void ThenTheScopedDataRepositoryIsCalledCorrectly() private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{ {
ScopedRepository ScopedRepository
.Verify(x => x.Add("DownstreamRoute", _downstreamRoute.Data), Times.Once()); .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 public class DownstreamRouteFinderTests
{ {
private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly Mock<IOcelotConfigurationProvider> _mockConfig;
private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher; private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher;
private readonly Mock<IUrlPathPlaceholderNameAndValueFinder> _finder; private readonly Mock<IUrlPathPlaceholderNameAndValueFinder> _finder;
private string _upstreamUrlPath; private string _upstreamUrlPath;
private Response<DownstreamRoute> _result; private Response<DownstreamRoute> _result;
private List<ReRoute> _reRoutesConfig; private List<ReRoute> _reRoutesConfig;
private OcelotConfiguration _config;
private Response<UrlMatch> _match; private Response<UrlMatch> _match;
private string _upstreamHttpMethod; private string _upstreamHttpMethod;
public DownstreamRouteFinderTests() public DownstreamRouteFinderTests()
{ {
_mockConfig = new Mock<IOcelotConfigurationProvider>();
_mockMatcher = new Mock<IUrlPathToUrlTemplateMatcher>(); _mockMatcher = new Mock<IUrlPathToUrlTemplateMatcher>();
_finder = new Mock<IUrlPathPlaceholderNameAndValueFinder>(); _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] [Fact]
public void should_return_route() public void should_return_route()
{ {
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>( new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
@ -48,7 +49,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath") .WithUpstreamTemplatePattern("someUpstreamPath")
.Build() .Build()
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -67,8 +68,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [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")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>( new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
@ -81,7 +84,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath") .WithUpstreamTemplatePattern("someUpstreamPath")
.Build() .Build()
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -94,13 +97,15 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.Build() .Build()
))) )))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher/")) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() 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")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And( .And(
x => x =>
@ -114,7 +119,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath") .WithUpstreamTemplatePattern("someUpstreamPath")
.Build() .Build()
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -133,6 +138,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_return_correct_route_for_http_verb() public void should_return_correct_route_for_http_verb()
{ {
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And( .And(
x => x =>
@ -152,7 +159,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Post" }) .WithUpstreamHttpMethod(new List<string> { "Post" })
.WithUpstreamTemplatePattern("") .WithUpstreamTemplatePattern("")
.Build() .Build()
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -170,6 +177,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_not_return_route() public void should_not_return_route()
{ {
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/"))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute> .And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{ {
@ -179,7 +188,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("somePath") .WithUpstreamTemplatePattern("somePath")
.Build(), .Build(),
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(false)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(false))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
@ -193,6 +202,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method() 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")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And( .And(
x => x =>
@ -206,7 +217,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get", "Post" }) .WithUpstreamHttpMethod(new List<string> { "Get", "Post" })
.WithUpstreamTemplatePattern("") .WithUpstreamTemplatePattern("")
.Build() .Build()
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -224,6 +235,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method() 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")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And( .And(
x => x =>
@ -237,7 +250,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string>()) .WithUpstreamHttpMethod(new List<string>())
.WithUpstreamTemplatePattern("") .WithUpstreamTemplatePattern("")
.Build() .Build()
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -255,6 +268,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method() 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")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And( .And(
x => x =>
@ -268,7 +283,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamHttpMethod(new List<string> { "Get", "Patch", "Delete" }) .WithUpstreamHttpMethod(new List<string> { "Get", "Patch", "Delete" })
.WithUpstreamTemplatePattern("") .WithUpstreamTemplatePattern("")
.Build() .Build()
}, string.Empty }, string.Empty, serviceProviderConfig
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
@ -322,12 +337,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.Returns(_match); .Returns(_match);
} }
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath) private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig)
{ {
_reRoutesConfig = reRoutesConfig; _reRoutesConfig = reRoutesConfig;
_mockConfig _config = new OcelotConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig);
.Setup(x => x.Get())
.ReturnsAsync(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(_reRoutesConfig, adminPath)));
} }
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)
@ -337,7 +350,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void WhenICallTheFinder() private void WhenICallTheFinder()
{ {
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod).Result; _result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config);
} }
private void ThenTheFollowingIsReturned(DownstreamRoute expected) private void ThenTheFollowingIsReturned(DownstreamRoute expected)

View File

@ -30,6 +30,16 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
.BDDfy(); .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] [Fact]
public void can_match_down_stream_url_with_no_slash() 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 HostAndPort _hostAndPort;
private Response<HostAndPort> _result; private Response<HostAndPort> _result;
private LeastConnectionLoadBalancer _leastConnection; private LeastConnection _leastConnection;
private List<Service> _services; private List<Service> _services;
private Random _random; private Random _random;
@ -35,7 +35,7 @@ namespace Ocelot.UnitTests.LoadBalancer
}; };
_services = availableServices; _services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName); _leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var tasks = new Task[100]; var tasks = new Task[100];
@ -86,7 +86,7 @@ namespace Ocelot.UnitTests.LoadBalancer
}; };
_services = availableServices; _services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName); _leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var response = _leastConnection.Lease().Result; var response = _leastConnection.Lease().Result;
@ -113,7 +113,7 @@ namespace Ocelot.UnitTests.LoadBalancer
}; };
_services = availableServices; _services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName); _leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var response = _leastConnection.Lease().Result; var response = _leastConnection.Lease().Result;
@ -144,7 +144,7 @@ namespace Ocelot.UnitTests.LoadBalancer
}; };
_services = availableServices; _services = availableServices;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName); _leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
var response = _leastConnection.Lease().Result; var response = _leastConnection.Lease().Result;
@ -211,7 +211,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheLoadBalancerStarts(List<Service> services, string serviceName) private void GivenTheLoadBalancerStarts(List<Service> services, string serviceName)
{ {
_services = services; _services = services;
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName); _leastConnection = new LeastConnection(() => Task.FromResult(_services), serviceName);
} }
private void WhenTheLoadBalancerStarts(List<Service> services, string serviceName) private void WhenTheLoadBalancerStarts(List<Service> services, string serviceName)

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
public class RoundRobinTests public class RoundRobinTests
{ {
private readonly RoundRobinLoadBalancer _roundRobin; private readonly RoundRobin _roundRobin;
private readonly List<Service> _services; private readonly List<Service> _services;
private Response<HostAndPort> _hostAndPort; 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]) 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] [Fact]

View File

@ -31,6 +31,12 @@
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" 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) private void GivenTheQosProviderHouseReturns(Response<IQoSProvider> qosProvider)
{ {
_qosProviderHouse _qosProviderHouse
.Setup(x => x.Get(It.IsAny<string>())) .Setup(x => x.Get(It.IsAny<ReRoute>()))
.Returns(qosProvider); .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.Responses;
using Ocelot.UnitTests.LoadBalancer;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -12,31 +17,32 @@ namespace Ocelot.UnitTests.Requester
private readonly QosProviderHouse _qosProviderHouse; private readonly QosProviderHouse _qosProviderHouse;
private Response _addResult; private Response _addResult;
private Response<IQoSProvider> _getResult; private Response<IQoSProvider> _getResult;
private string _key; private ReRoute _reRoute;
private readonly Mock<IQoSProviderFactory> _factory;
public QosProviderHouseTests() public QosProviderHouseTests()
{ {
_qosProviderHouse = new QosProviderHouse(); _factory = new Mock<IQoSProviderFactory>();
_qosProviderHouse = new QosProviderHouse(_factory.Object);
} }
[Fact] [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())) this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenIAddTheQoSProvider())
.Then(x => x.ThenItIsAdded()) .Then(x => x.ThenItIsAdded())
.BDDfy(); .BDDfy();
} }
[Fact] [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())) this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(key)) .When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenItIsReturned()) .Then(x => x.ThenItIsReturned())
.BDDfy(); .BDDfy();
} }
@ -44,14 +50,14 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_store_qos_providers_by_key() public void should_store_qos_providers_by_key()
{ {
var key = "test"; var reRoute = new ReRouteBuilder().WithReRouteKey("test").Build();
var keyTwo = "testTwo"; var reRouteTwo = new ReRouteBuilder().WithReRouteKey("testTwo").Build();
this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider())) this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.And(x => x.GivenThereIsAQoSProvider(keyTwo, new FakePollyQoSProvider())) .And(x => x.GivenThereIsAQoSProvider(reRouteTwo, new FakePollyQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(key)) .When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenTheQoSProviderIs<FakeQoSProvider>()) .Then(x => x.ThenTheQoSProviderIs<FakeQoSProvider>())
.When(x => x.WhenWeGetTheQoSProvider(keyTwo)) .When(x => x.WhenWeGetTheQoSProvider(reRouteTwo))
.Then(x => x.ThenTheQoSProviderIs<FakePollyQoSProvider>()) .Then(x => x.ThenTheQoSProviderIs<FakePollyQoSProvider>())
.BDDfy(); .BDDfy();
} }
@ -59,11 +65,35 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_return_error_if_no_qos_provider_with_key() 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()) .Then(x => x.ThenAnErrorIsReturned())
.BDDfy(); .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() private void ThenAnErrorIsReturned()
{ {
_getResult.IsError.ShouldBeTrue(); _getResult.IsError.ShouldBeTrue();
@ -77,31 +107,30 @@ namespace Ocelot.UnitTests.Requester
private void ThenItIsAdded() private void ThenItIsAdded()
{ {
_addResult.IsError.ShouldBe(false); _getResult.IsError.ShouldBe(false);
_addResult.ShouldBeOfType<OkResponse>(); _getResult.ShouldBeOfType<OkResponse<IQoSProvider>>();
} _factory.Verify(x => x.Get(_reRoute), Times.Once);
_getResult.Data.ShouldBe(_qoSProvider);
private void WhenIAddTheQoSProvider()
{
_addResult = _qosProviderHouse.Add(_key, _qoSProvider);
} }
private void GivenThereIsAQoSProvider(string key, IQoSProvider qoSProvider) private void GivenThereIsAQoSProvider(ReRoute reRoute, IQoSProvider qoSProvider)
{ {
_key = key; _reRoute = reRoute;
_qoSProvider = qoSProvider; _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() private void ThenItIsReturned()
{ {
_getResult.Data.ShouldBe(_qoSProvider); _getResult.Data.ShouldBe(_qoSProvider);
_factory.Verify(x => x.Get(_reRoute), Times.Once);
} }
class FakeQoSProvider : IQoSProvider class FakeQoSProvider : IQoSProvider

View File

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