mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 10:12:51 +08:00
Feature/poll consul (#392)
* WIP - implement a consul service discovery poller, lots of shared code with existing, refactor next and a todo in the docs to finish * #374 implement polling for consul as option * #374 updated docs to remove todo * #374 fixed failing unit test * #374 fixed failing unit test * #374 fixed failing acceptance test
This commit is contained in:
parent
14308ff5fb
commit
0f2a9c1d0d
@ -1,156 +1,171 @@
|
||||
.. service-discovery:
|
||||
|
||||
Service Discovery
|
||||
=================
|
||||
|
||||
Ocelot allows you to specify a service discovery provider and will use this to find the host and port
|
||||
for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the
|
||||
GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes
|
||||
you specify a ServiceName for at ReRoute level.
|
||||
|
||||
Consul
|
||||
^^^^^^
|
||||
|
||||
The following is required in the GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
|
||||
will be used.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 9500
|
||||
}
|
||||
|
||||
In the future we can add a feature that allows ReRoute specfic configuration.
|
||||
|
||||
In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the
|
||||
ServiceName, UseServiceDiscovery and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin
|
||||
and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/posts/{postId}",
|
||||
"DownstreamScheme": "https",
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"ServiceName": "product",
|
||||
"LoadBalancerOptions": {
|
||||
"Type": "LeastConnection"
|
||||
},
|
||||
"UseServiceDiscovery": true
|
||||
}
|
||||
|
||||
When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services.
|
||||
|
||||
ACL Token
|
||||
---------
|
||||
|
||||
If you are using ACL with Consul Ocelot supports adding the X-Consul-Token header. In order so this to work you must add the additional property below.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 9500,
|
||||
"Token": "footoken"
|
||||
}
|
||||
|
||||
Ocelot will add this token to the consul client that it uses to make requests and that is then used for every request.
|
||||
|
||||
Eureka
|
||||
^^^^^^
|
||||
|
||||
This feature was requested as part of `Issue 262 <https://github.com/TomPallister/Ocelot/issue/262>`_ . to add support for Netflix's
|
||||
Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe <https://steeltoe.io/>`_ which is something
|
||||
to do with `Pivotal <https://pivotal.io/platform>`_! Anyway enough of the background.
|
||||
|
||||
In order to get this working add the following to ocelot.json..
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Type": "Eureka"
|
||||
}
|
||||
|
||||
And following the guide `Here <https://steeltoe.io/docs/steeltoe-discovery/>`_ you may also need to add some stuff to appsettings.json. For example the json below tells the steeltoe / pivotal services where to look for the service discovery server and if the service should register with it.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"eureka": {
|
||||
"client": {
|
||||
"serviceUrl": "http://localhost:8761/eureka/",
|
||||
"shouldRegisterWithEureka": false,
|
||||
"shouldFetchRegistry": true
|
||||
}
|
||||
}
|
||||
|
||||
I am told that if shouldRegisterWithEureka is false then shouldFetchRegistry will defaut to true so you don't need it explicitly but left it in there.
|
||||
|
||||
Ocelot will now register all the necessary services when it starts up and if you have the json above will register itself with
|
||||
Eureka. One of the services polls Eureka every 30 seconds (default) and gets the latest service state and persists this in memory.
|
||||
When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code
|
||||
is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.
|
||||
|
||||
Dynamic Routing
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing when using
|
||||
a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segmentof the upstream path to lookup the
|
||||
downstream service with the service discovery provider.
|
||||
|
||||
An example of this would be calling ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of
|
||||
the path which is product and use it as a key to look up the service in consul. If consul returns a service Ocelot will request it on whatever host and
|
||||
port comes back from consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products.
|
||||
Ocelot will apprend any query string to the downstream url as normal.
|
||||
|
||||
In order to enable dynamic routing you need to have 0 ReRoutes in your config. At the moment you cannot mix dynamic and configuration ReRoutes. In addition to this you
|
||||
need to specify the Service Discovery provider details as outlined above and the downstream http/https scheme as DownstreamScheme.
|
||||
|
||||
In addition to that you can set RateLimitOptions, QoSOptions, LoadBalancerOptions and HttpHandlerOptions, DownstreamScheme (You might want to call Ocelot on https but
|
||||
talk to private services over http) that will be applied to all of the dynamic ReRoutes.
|
||||
|
||||
The config might look something like
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"ReRoutes": [],
|
||||
"Aggregates": [],
|
||||
"GlobalConfiguration": {
|
||||
"RequestIdKey": null,
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 8510,
|
||||
"Type": null,
|
||||
"Token": null,
|
||||
"ConfigurationKey": null
|
||||
},
|
||||
"RateLimitOptions": {
|
||||
"ClientIdHeader": "ClientId",
|
||||
"QuotaExceededMessage": null,
|
||||
"RateLimitCounterPrefix": "ocelot",
|
||||
"DisableRateLimitHeaders": false,
|
||||
"HttpStatusCode": 429
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 0,
|
||||
"DurationOfBreak": 0,
|
||||
"TimeoutValue": 0
|
||||
},
|
||||
"BaseUrl": null,
|
||||
"LoadBalancerOptions": {
|
||||
"Type": "LeastConnection",
|
||||
"Key": null,
|
||||
"Expiry": 0
|
||||
},
|
||||
"DownstreamScheme": "http",
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": false,
|
||||
"UseCookieContainer": false,
|
||||
"UseTracing": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Please take a look through all of the docs to understand these options.
|
||||
.. service-discovery:
|
||||
|
||||
Service Discovery
|
||||
=================
|
||||
|
||||
Ocelot allows you to specify a service discovery provider and will use this to find the host and port
|
||||
for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the
|
||||
GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes
|
||||
you specify a ServiceName for at ReRoute level.
|
||||
|
||||
Consul
|
||||
^^^^^^
|
||||
|
||||
The following is required in the GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
|
||||
will be used.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 9500
|
||||
}
|
||||
|
||||
In the future we can add a feature that allows ReRoute specfic configuration.
|
||||
|
||||
In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the
|
||||
ServiceName, UseServiceDiscovery and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin
|
||||
and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/posts/{postId}",
|
||||
"DownstreamScheme": "https",
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"ServiceName": "product",
|
||||
"LoadBalancerOptions": {
|
||||
"Type": "LeastConnection"
|
||||
},
|
||||
"UseServiceDiscovery": true
|
||||
}
|
||||
|
||||
When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services.
|
||||
|
||||
A lot of people have asked me to implement a feature where Ocelot polls consul for latest service information rather than per request. If you want to poll consul for the latest services rather than per request (default behaviour) then you need to set the following configuration.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 9500,
|
||||
"Type": "PollConsul",
|
||||
"PollingInteral": 100
|
||||
}
|
||||
|
||||
The polling interval is in milliseconds and tells Ocelot how often to call Consul for changes in service configuration.
|
||||
|
||||
Please note there are tradeoffs here. If you poll Consul it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volitile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling consul per request (as sidecar agent). If you are calling a remote consul agent then polling will be a good performance improvement.
|
||||
|
||||
ACL Token
|
||||
---------
|
||||
|
||||
If you are using ACL with Consul Ocelot supports adding the X-Consul-Token header. In order so this to work you must add the additional property below.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 9500,
|
||||
"Token": "footoken"
|
||||
}
|
||||
|
||||
Ocelot will add this token to the consul client that it uses to make requests and that is then used for every request.
|
||||
|
||||
Eureka
|
||||
^^^^^^
|
||||
|
||||
This feature was requested as part of `Issue 262 <https://github.com/TomPallister/Ocelot/issue/262>`_ . to add support for Netflix's
|
||||
Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe <https://steeltoe.io/>`_ which is something
|
||||
to do with `Pivotal <https://pivotal.io/platform>`_! Anyway enough of the background.
|
||||
|
||||
In order to get this working add the following to ocelot.json..
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Type": "Eureka"
|
||||
}
|
||||
|
||||
And following the guide `Here <https://steeltoe.io/docs/steeltoe-discovery/>`_ you may also need to add some stuff to appsettings.json. For example the json below tells the steeltoe / pivotal services where to look for the service discovery server and if the service should register with it.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"eureka": {
|
||||
"client": {
|
||||
"serviceUrl": "http://localhost:8761/eureka/",
|
||||
"shouldRegisterWithEureka": false,
|
||||
"shouldFetchRegistry": true
|
||||
}
|
||||
}
|
||||
|
||||
I am told that if shouldRegisterWithEureka is false then shouldFetchRegistry will defaut to true so you don't need it explicitly but left it in there.
|
||||
|
||||
Ocelot will now register all the necessary services when it starts up and if you have the json above will register itself with
|
||||
Eureka. One of the services polls Eureka every 30 seconds (default) and gets the latest service state and persists this in memory.
|
||||
When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code
|
||||
is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.
|
||||
|
||||
Dynamic Routing
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing when using
|
||||
a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segmentof the upstream path to lookup the
|
||||
downstream service with the service discovery provider.
|
||||
|
||||
An example of this would be calling ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of
|
||||
the path which is product and use it as a key to look up the service in consul. If consul returns a service Ocelot will request it on whatever host and
|
||||
port comes back from consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products.
|
||||
Ocelot will apprend any query string to the downstream url as normal.
|
||||
|
||||
In order to enable dynamic routing you need to have 0 ReRoutes in your config. At the moment you cannot mix dynamic and configuration ReRoutes. In addition to this you
|
||||
need to specify the Service Discovery provider details as outlined above and the downstream http/https scheme as DownstreamScheme.
|
||||
|
||||
In addition to that you can set RateLimitOptions, QoSOptions, LoadBalancerOptions and HttpHandlerOptions, DownstreamScheme (You might want to call Ocelot on https but
|
||||
talk to private services over http) that will be applied to all of the dynamic ReRoutes.
|
||||
|
||||
The config might look something like
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"ReRoutes": [],
|
||||
"Aggregates": [],
|
||||
"GlobalConfiguration": {
|
||||
"RequestIdKey": null,
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 8510,
|
||||
"Type": null,
|
||||
"Token": null,
|
||||
"ConfigurationKey": null
|
||||
},
|
||||
"RateLimitOptions": {
|
||||
"ClientIdHeader": "ClientId",
|
||||
"QuotaExceededMessage": null,
|
||||
"RateLimitCounterPrefix": "ocelot",
|
||||
"DisableRateLimitHeaders": false,
|
||||
"HttpStatusCode": 429
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 0,
|
||||
"DurationOfBreak": 0,
|
||||
"TimeoutValue": 0
|
||||
},
|
||||
"BaseUrl": null,
|
||||
"LoadBalancerOptions": {
|
||||
"Type": "LeastConnection",
|
||||
"Key": null,
|
||||
"Expiry": 0
|
||||
},
|
||||
"DownstreamScheme": "http",
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": false,
|
||||
"UseCookieContainer": false,
|
||||
"UseTracing": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Please take a look through all of the docs to understand these options.
|
||||
|
@ -7,6 +7,7 @@ namespace Ocelot.Configuration.Builder
|
||||
private string _type;
|
||||
private string _token;
|
||||
private string _configurationKey;
|
||||
private int _pollingInterval;
|
||||
|
||||
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
||||
{
|
||||
@ -38,9 +39,15 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServiceProviderConfigurationBuilder WithPollingInterval(int pollingInterval)
|
||||
{
|
||||
_pollingInterval = pollingInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServiceProviderConfiguration Build()
|
||||
{
|
||||
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey);
|
||||
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
var port = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
||||
var host = globalConfiguration?.ServiceDiscoveryProvider?.Host ?? "consul";
|
||||
var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0;
|
||||
|
||||
return new ServiceProviderConfigurationBuilder()
|
||||
.WithHost(host)
|
||||
@ -16,6 +17,7 @@ namespace Ocelot.Configuration.Creator
|
||||
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
||||
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
||||
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
|
||||
.WithPollingInterval(pollingInterval)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,6 @@ namespace Ocelot.Configuration.File
|
||||
public string Type { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string ConfigurationKey { get; set; }
|
||||
public int PollingInterval { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,14 @@
|
||||
{
|
||||
public class ServiceProviderConfiguration
|
||||
{
|
||||
public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey)
|
||||
public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey, int pollingInterval)
|
||||
{
|
||||
ConfigurationKey = configurationKey;
|
||||
Host = host;
|
||||
Port = port;
|
||||
Token = token;
|
||||
Type = type;
|
||||
PollingInterval = pollingInterval;
|
||||
}
|
||||
|
||||
public string Host { get; }
|
||||
@ -16,5 +17,6 @@
|
||||
public string Type { get; }
|
||||
public string Token { get; }
|
||||
public string ConfigurationKey { get; }
|
||||
public int PollingInterval { get; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Consul;
|
||||
using Ocelot.Infrastructure.Consul;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery.Providers
|
||||
{
|
||||
public class PollingConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||
{
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IServiceDiscoveryProvider _consulServiceDiscoveryProvider;
|
||||
private readonly Timer _timer;
|
||||
private bool _polling;
|
||||
private List<Service> _services;
|
||||
private string _keyOfServiceInConsul;
|
||||
|
||||
public PollingConsulServiceDiscoveryProvider(int pollingInterval, string keyOfServiceInConsul, IOcelotLoggerFactory factory, IServiceDiscoveryProvider consulServiceDiscoveryProvider)
|
||||
{;
|
||||
_logger = factory.CreateLogger<PollingConsulServiceDiscoveryProvider>();
|
||||
_keyOfServiceInConsul = keyOfServiceInConsul;
|
||||
_consulServiceDiscoveryProvider = consulServiceDiscoveryProvider;
|
||||
_services = new List<Service>();
|
||||
|
||||
_timer = new Timer(async x =>
|
||||
{
|
||||
if(_polling)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_polling = true;
|
||||
await Poll();
|
||||
_polling = false;
|
||||
|
||||
}, null, pollingInterval, pollingInterval);
|
||||
}
|
||||
|
||||
public Task<List<Service>> Get()
|
||||
{
|
||||
return Task.FromResult(_services);
|
||||
}
|
||||
|
||||
private async Task Poll()
|
||||
{
|
||||
_services = await _consulServiceDiscoveryProvider.Get();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure.Consul;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure.Consul;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
using Steeltoe.Common.Discovery;
|
||||
|
||||
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
||||
{
|
||||
private readonly IOcelotLoggerFactory _factory;
|
||||
private readonly IConsulClientFactory _consulFactory;
|
||||
private readonly IDiscoveryClient _eurekaClient;
|
||||
|
||||
public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory, IConsulClientFactory consulFactory, IDiscoveryClient eurekaClient)
|
||||
{
|
||||
_factory = factory;
|
||||
_consulFactory = consulFactory;
|
||||
_eurekaClient = eurekaClient;
|
||||
}
|
||||
|
||||
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute)
|
||||
{
|
||||
if (reRoute.UseServiceDiscovery)
|
||||
{
|
||||
return GetServiceDiscoveryProvider(serviceConfig, reRoute.ServiceName);
|
||||
}
|
||||
|
||||
var services = new List<Service>();
|
||||
|
||||
foreach (var downstreamAddress in reRoute.DownstreamAddresses)
|
||||
{
|
||||
var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port), string.Empty, string.Empty, new string[0]);
|
||||
|
||||
services.Add(service);
|
||||
}
|
||||
|
||||
return new ConfigurationServiceProvider(services);
|
||||
}
|
||||
|
||||
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(ServiceProviderConfiguration serviceConfig, string serviceName)
|
||||
{
|
||||
if (serviceConfig.Type?.ToLower() == "servicefabric")
|
||||
{
|
||||
var config = new ServiceFabricConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName);
|
||||
return new ServiceFabricServiceDiscoveryProvider(config);
|
||||
}
|
||||
|
||||
if (serviceConfig.Type?.ToLower() == "eureka")
|
||||
{
|
||||
return new EurekaServiceDiscoveryProvider(serviceName, _eurekaClient);
|
||||
}
|
||||
|
||||
var consulRegistryConfiguration = new ConsulRegistryConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName, serviceConfig.Token);
|
||||
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory, _consulFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
||||
{
|
||||
private readonly IOcelotLoggerFactory _factory;
|
||||
private readonly IConsulClientFactory _consulFactory;
|
||||
private readonly IDiscoveryClient _eurekaClient;
|
||||
|
||||
public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory, IConsulClientFactory consulFactory, IDiscoveryClient eurekaClient)
|
||||
{
|
||||
_factory = factory;
|
||||
_consulFactory = consulFactory;
|
||||
_eurekaClient = eurekaClient;
|
||||
}
|
||||
|
||||
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute)
|
||||
{
|
||||
if (reRoute.UseServiceDiscovery)
|
||||
{
|
||||
return GetServiceDiscoveryProvider(serviceConfig, reRoute.ServiceName);
|
||||
}
|
||||
|
||||
var services = new List<Service>();
|
||||
|
||||
foreach (var downstreamAddress in reRoute.DownstreamAddresses)
|
||||
{
|
||||
var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port), string.Empty, string.Empty, new string[0]);
|
||||
|
||||
services.Add(service);
|
||||
}
|
||||
|
||||
return new ConfigurationServiceProvider(services);
|
||||
}
|
||||
|
||||
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(ServiceProviderConfiguration serviceConfig, string serviceName)
|
||||
{
|
||||
if (serviceConfig.Type?.ToLower() == "servicefabric")
|
||||
{
|
||||
var config = new ServiceFabricConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName);
|
||||
return new ServiceFabricServiceDiscoveryProvider(config);
|
||||
}
|
||||
|
||||
if (serviceConfig.Type?.ToLower() == "eureka")
|
||||
{
|
||||
return new EurekaServiceDiscoveryProvider(serviceName, _eurekaClient);
|
||||
}
|
||||
|
||||
var consulRegistryConfiguration = new ConsulRegistryConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName, serviceConfig.Token);
|
||||
|
||||
var consulServiceDiscoveryProvider = new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory, _consulFactory);
|
||||
|
||||
if (serviceConfig.Type?.ToLower() == "pollconsul")
|
||||
{
|
||||
return new PollingConsulServiceDiscoveryProvider(serviceConfig.PollingInterval, consulRegistryConfiguration.KeyOfServiceInConsul, _factory, consulServiceDiscoveryProvider);
|
||||
}
|
||||
|
||||
return consulServiceDiscoveryProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -458,6 +458,64 @@ namespace Ocelot.AcceptanceTests
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request()
|
||||
{
|
||||
const int consulPort = 8518;
|
||||
const string serviceName = "web";
|
||||
const int downstreamServicePort = 8082;
|
||||
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = downstreamServicePort,
|
||||
ID = $"web_90_0_2_224_{downstreamServicePort}",
|
||||
Tags = new[] {"version-v1"}
|
||||
},
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/home",
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/home",
|
||||
UpstreamHttpMethod = new List<string> { "Get", "Options" },
|
||||
ServiceName = serviceName,
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||
UseServiceDiscovery = true,
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort,
|
||||
Type = "PollConsul",
|
||||
PollingInterval = 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura"))
|
||||
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
|
||||
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk("/home"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo)
|
||||
{
|
||||
_consulServices.Add(serviceEntryTwo);
|
||||
|
@ -27,6 +27,7 @@ using System.Text;
|
||||
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using static Ocelot.Infrastructure.Wait;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
@ -675,6 +676,24 @@ namespace Ocelot.AcceptanceTests
|
||||
_response = _ocelotClient.GetAsync(url).Result;
|
||||
}
|
||||
|
||||
public void WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url)
|
||||
{
|
||||
var result = WaitFor(2000).Until(() => {
|
||||
try
|
||||
{
|
||||
_response = _ocelotClient.GetAsync(url).Result;
|
||||
_response.EnsureSuccessStatusCode();
|
||||
return true;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
result.ShouldBeTrue();
|
||||
}
|
||||
|
||||
public void WhenIGetUrlOnTheApiGateway(string url, string cookie, string value)
|
||||
{
|
||||
var request = _ocelotServer.CreateRequest(url);
|
||||
|
@ -26,7 +26,7 @@ namespace Ocelot.UnitTests.LoadBalancer
|
||||
{
|
||||
_factory = new Mock<ILoadBalancerFactory>();
|
||||
_loadBalancerHouse = new LoadBalancerHouse(_factory.Object);
|
||||
_serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123, string.Empty, "configKey");
|
||||
_serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123, string.Empty, "configKey", 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Consul;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Infrastructure.Consul;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Ocelot.Values;
|
||||
using Xunit;
|
||||
using TestStack.BDDfy;
|
||||
using Shouldly;
|
||||
using static Ocelot.Infrastructure.Wait;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
public class PollingConsulServiceDiscoveryProviderTests
|
||||
{
|
||||
private readonly int _delay;
|
||||
private PollingConsulServiceDiscoveryProvider _provider;
|
||||
private readonly string _serviceName;
|
||||
private List<Service> _services;
|
||||
private readonly Mock<IOcelotLoggerFactory> _factory;
|
||||
private readonly Mock<IOcelotLogger> _logger;
|
||||
private Mock<IServiceDiscoveryProvider> _consulServiceDiscoveryProvider;
|
||||
private List<Service> _result;
|
||||
|
||||
public PollingConsulServiceDiscoveryProviderTests()
|
||||
{
|
||||
_services = new List<Service>();
|
||||
_delay = 1;
|
||||
_factory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_factory.Setup(x => x.CreateLogger<PollingConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
|
||||
_consulServiceDiscoveryProvider = new Mock<IServiceDiscoveryProvider>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_service_from_consul()
|
||||
{
|
||||
var service = new Service("", new ServiceHostAndPort("", 0), "", "", new List<string>());
|
||||
|
||||
this.Given(x => GivenConsulReturns(service))
|
||||
.When(x => WhenIGetTheServices(1))
|
||||
.Then(x => ThenTheCountIs(1))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenConsulReturns(Service service)
|
||||
{
|
||||
_services.Add(service);
|
||||
_consulServiceDiscoveryProvider.Setup(x => x.Get()).ReturnsAsync(_services);
|
||||
}
|
||||
|
||||
private void ThenTheCountIs(int count)
|
||||
{
|
||||
_result.Count.ShouldBe(count);
|
||||
}
|
||||
|
||||
private void WhenIGetTheServices(int expected)
|
||||
{
|
||||
_provider = new PollingConsulServiceDiscoveryProvider(_delay, _serviceName, _factory.Object, _consulServiceDiscoveryProvider.Object);
|
||||
|
||||
var result = WaitFor(3000).Until(() => {
|
||||
try
|
||||
{
|
||||
_result = _provider.Get().GetAwaiter().GetResult();
|
||||
if(_result.Count == expected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
result.ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,154 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Infrastructure.Consul;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Infrastructure.Consul;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
using Pivotal.Discovery.Client;
|
||||
using Steeltoe.Common.Discovery;
|
||||
|
||||
public class ServiceProviderFactoryTests
|
||||
{
|
||||
private ServiceProviderConfiguration _serviceConfig;
|
||||
private IServiceDiscoveryProvider _result;
|
||||
private readonly ServiceDiscoveryProviderFactory _factory;
|
||||
private DownstreamReRoute _reRoute;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IDiscoveryClient> _discoveryClient;
|
||||
|
||||
public ServiceProviderFactoryTests()
|
||||
{
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_discoveryClient = new Mock<IDiscoveryClient>();
|
||||
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, new ConsulClientFactory(), _discoveryClient.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_no_service_provider()
|
||||
{
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder().Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConfigurationServiceProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_list_of_configuration_services()
|
||||
{
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
var downstreamAddresses = new List<DownstreamHostAndPort>()
|
||||
{
|
||||
new DownstreamHostAndPort("asdf.com", 80),
|
||||
new DownstreamHostAndPort("abc.com", 80)
|
||||
};
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder().WithDownstreamAddresses(downstreamAddresses).Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConfigurationServiceProvider>())
|
||||
.Then(x => ThenTheFollowingServicesAreReturned(downstreamAddresses))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_consul_service_provider()
|
||||
{
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithServiceName("product")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConsulServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_service_fabric_provider()
|
||||
{
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithServiceName("product")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ServiceFabricServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_eureka_provider()
|
||||
{
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithServiceName("product")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("Eureka")
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<EurekaServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheFollowingServicesAreReturned(List<DownstreamHostAndPort> downstreamAddresses)
|
||||
{
|
||||
var result = (ConfigurationServiceProvider)_result;
|
||||
var services = result.Get().Result;
|
||||
|
||||
for (int i = 0; i < services.Count; i++)
|
||||
{
|
||||
var service = services[i];
|
||||
var downstreamAddress = downstreamAddresses[i];
|
||||
|
||||
service.HostAndPort.DownstreamHost.ShouldBe(downstreamAddress.Host);
|
||||
service.HostAndPort.DownstreamPort.ShouldBe(downstreamAddress.Port);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute)
|
||||
{
|
||||
_serviceConfig = serviceConfig;
|
||||
_reRoute = reRoute;
|
||||
}
|
||||
|
||||
private void WhenIGetTheServiceProvider()
|
||||
{
|
||||
_result = _factory.Get(_serviceConfig, _reRoute);
|
||||
}
|
||||
|
||||
private void ThenTheServiceProviderIs<T>()
|
||||
{
|
||||
_result.ShouldBeOfType<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
public class ServiceProviderFactoryTests
|
||||
{
|
||||
private ServiceProviderConfiguration _serviceConfig;
|
||||
private IServiceDiscoveryProvider _result;
|
||||
private readonly ServiceDiscoveryProviderFactory _factory;
|
||||
private DownstreamReRoute _reRoute;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IDiscoveryClient> _discoveryClient;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
|
||||
public ServiceProviderFactoryTests()
|
||||
{
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<PollingConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
|
||||
_discoveryClient = new Mock<IDiscoveryClient>();
|
||||
var consulClient = new Mock<IConsulClientFactory>();
|
||||
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, consulClient.Object, _discoveryClient.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_no_service_provider()
|
||||
{
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder().Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConfigurationServiceProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_list_of_configuration_services()
|
||||
{
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
var downstreamAddresses = new List<DownstreamHostAndPort>()
|
||||
{
|
||||
new DownstreamHostAndPort("asdf.com", 80),
|
||||
new DownstreamHostAndPort("abc.com", 80)
|
||||
};
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder().WithDownstreamAddresses(downstreamAddresses).Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConfigurationServiceProvider>())
|
||||
.Then(x => ThenTheFollowingServicesAreReturned(downstreamAddresses))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_consul_service_provider()
|
||||
{
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithServiceName("product")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConsulServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_polling_consul_service_provider()
|
||||
{
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithServiceName("product")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("PollConsul")
|
||||
.WithPollingInterval(100000)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<PollingConsulServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_service_fabric_provider()
|
||||
{
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithServiceName("product")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ServiceFabricServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_eureka_provider()
|
||||
{
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithServiceName("product")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var serviceConfig = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("Eureka")
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<EurekaServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheFollowingServicesAreReturned(List<DownstreamHostAndPort> downstreamAddresses)
|
||||
{
|
||||
var result = (ConfigurationServiceProvider)_result;
|
||||
var services = result.Get().Result;
|
||||
|
||||
for (int i = 0; i < services.Count; i++)
|
||||
{
|
||||
var service = services[i];
|
||||
var downstreamAddress = downstreamAddresses[i];
|
||||
|
||||
service.HostAndPort.DownstreamHost.ShouldBe(downstreamAddress.Host);
|
||||
service.HostAndPort.DownstreamPort.ShouldBe(downstreamAddress.Port);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute)
|
||||
{
|
||||
_serviceConfig = serviceConfig;
|
||||
_reRoute = reRoute;
|
||||
}
|
||||
|
||||
private void WhenIGetTheServiceProvider()
|
||||
{
|
||||
_result = _factory.Get(_serviceConfig, _reRoute);
|
||||
}
|
||||
|
||||
private void ThenTheServiceProviderIs<T>()
|
||||
{
|
||||
_result.ShouldBeOfType<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user