mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 02:42:52 +08:00
Feature/service discovery config key (#347)
* #346 make service discoery config key configurable * #346 missed this test * #346 updated docs
This commit is contained in:
parent
6793278597
commit
4e17190b3f
@ -1,174 +1,193 @@
|
|||||||
Configuration
|
Configuration
|
||||||
============
|
============
|
||||||
|
|
||||||
An example configuration can be found `here <https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/ocelot.json>`_.
|
An example configuration can be found `here <https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/ocelot.json>`_.
|
||||||
There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration.
|
There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration.
|
||||||
The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global
|
The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global
|
||||||
configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
|
configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
|
||||||
if you don't want to manage lots of ReRoute specific settings.
|
if you don't want to manage lots of ReRoute specific settings.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
{
|
{
|
||||||
"ReRoutes": [],
|
"ReRoutes": [],
|
||||||
"GlobalConfiguration": {}
|
"GlobalConfiguration": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment:
|
Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment:
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/",
|
"DownstreamPathTemplate": "/",
|
||||||
"UpstreamPathTemplate": "/",
|
"UpstreamPathTemplate": "/",
|
||||||
"UpstreamHttpMethod": [
|
"UpstreamHttpMethod": [
|
||||||
"Get"
|
"Get"
|
||||||
],
|
],
|
||||||
"AddHeadersToRequest": {},
|
"AddHeadersToRequest": {},
|
||||||
"AddClaimsToRequest": {},
|
"AddClaimsToRequest": {},
|
||||||
"RouteClaimsRequirement": {},
|
"RouteClaimsRequirement": {},
|
||||||
"AddQueriesToRequest": {},
|
"AddQueriesToRequest": {},
|
||||||
"RequestIdKey": "",
|
"RequestIdKey": "",
|
||||||
"FileCacheOptions": {
|
"FileCacheOptions": {
|
||||||
"TtlSeconds": 0,
|
"TtlSeconds": 0,
|
||||||
"Region": ""
|
"Region": ""
|
||||||
},
|
},
|
||||||
"ReRouteIsCaseSensitive": false,
|
"ReRouteIsCaseSensitive": false,
|
||||||
"ServiceName": "",
|
"ServiceName": "",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHostAndPorts": [
|
"DownstreamHostAndPorts": [
|
||||||
{
|
{
|
||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Port": 51876,
|
"Port": 51876,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
"ExceptionsAllowedBeforeBreaking": 0,
|
"ExceptionsAllowedBeforeBreaking": 0,
|
||||||
"DurationOfBreak": 0,
|
"DurationOfBreak": 0,
|
||||||
"TimeoutValue": 0
|
"TimeoutValue": 0
|
||||||
},
|
},
|
||||||
"LoadBalancer": "",
|
"LoadBalancer": "",
|
||||||
"RateLimitOptions": {
|
"RateLimitOptions": {
|
||||||
"ClientWhitelist": [],
|
"ClientWhitelist": [],
|
||||||
"EnableRateLimiting": false,
|
"EnableRateLimiting": false,
|
||||||
"Period": "",
|
"Period": "",
|
||||||
"PeriodTimespan": 0,
|
"PeriodTimespan": 0,
|
||||||
"Limit": 0
|
"Limit": 0
|
||||||
},
|
},
|
||||||
"AuthenticationOptions": {
|
"AuthenticationOptions": {
|
||||||
"AuthenticationProviderKey": "",
|
"AuthenticationProviderKey": "",
|
||||||
"AllowedScopes": []
|
"AllowedScopes": []
|
||||||
},
|
},
|
||||||
"HttpHandlerOptions": {
|
"HttpHandlerOptions": {
|
||||||
"AllowAutoRedirect": true,
|
"AllowAutoRedirect": true,
|
||||||
"UseCookieContainer": true,
|
"UseCookieContainer": true,
|
||||||
"UseTracing": true
|
"UseTracing": true
|
||||||
},
|
},
|
||||||
"UseServiceDiscovery": false,
|
"UseServiceDiscovery": false,
|
||||||
"DangerousAcceptAnyServerCertificateValidator": false
|
"DangerousAcceptAnyServerCertificateValidator": false
|
||||||
}
|
}
|
||||||
|
|
||||||
More information on how to use these options is below..
|
More information on how to use these options is below..
|
||||||
|
|
||||||
Multiple environments
|
Multiple environments
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following
|
Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following
|
||||||
to you
|
to you
|
||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
{
|
{
|
||||||
config
|
config
|
||||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", true, true)
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
.AddJsonFile("ocelot.json")
|
.AddJsonFile("ocelot.json")
|
||||||
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
|
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
|
||||||
.AddEnvironmentVariables();
|
.AddEnvironmentVariables();
|
||||||
})
|
})
|
||||||
|
|
||||||
Ocelot will now use the environment specific configuration and fall back to ocelot.json if there isnt one.
|
Ocelot will now use the environment specific configuration and fall back to ocelot.json if there isnt one.
|
||||||
|
|
||||||
You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs <https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments>`_.
|
You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs <https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments>`_.
|
||||||
|
|
||||||
Merging configuration files
|
Merging configuration files
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
This feature was requested in `Issue 296 <https://github.com/ThreeMammals/Ocelot/issues/296>`_ and allows users to have multiple configuration files to make managing large configurations easier.
|
This feature was requested in `Issue 296 <https://github.com/ThreeMammals/Ocelot/issues/296>`_ and allows users to have multiple configuration files to make managing large configurations easier.
|
||||||
|
|
||||||
Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below.
|
Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below.
|
||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
{
|
{
|
||||||
config
|
config
|
||||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", true, true)
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
.AddOcelot()
|
.AddOcelot()
|
||||||
.AddEnvironmentVariables();
|
.AddEnvironmentVariables();
|
||||||
})
|
})
|
||||||
|
|
||||||
In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json.
|
In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json.
|
||||||
|
|
||||||
The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running.
|
The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running.
|
||||||
|
|
||||||
At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems.
|
At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems.
|
||||||
|
|
||||||
Store configuration in consul
|
Store configuration in consul
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
If you add the following when you register your services Ocelot will attempt to store and retrieve its configuration in consul KV store.
|
If you add the following when you register your services Ocelot will attempt to store and retrieve its configuration in consul KV store.
|
||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddOcelot()
|
.AddOcelot()
|
||||||
.AddStoreOcelotConfigurationInConsul();
|
.AddStoreOcelotConfigurationInConsul();
|
||||||
|
|
||||||
You also need to add the following to your ocelot.json. This is how Ocelot
|
You also need to add the following to your ocelot.json. This is how Ocelot
|
||||||
finds your Consul agent and interacts to load and store the configuration from Consul.
|
finds your Consul agent and interacts to load and store the configuration from Consul.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
"GlobalConfiguration": {
|
"GlobalConfiguration": {
|
||||||
"ServiceDiscoveryProvider": {
|
"ServiceDiscoveryProvider": {
|
||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Port": 9500
|
"Port": 9500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
I decided to create this feature after working on the raft consensus algorithm and finding out its super hard. Why not take advantage of the fact Consul already gives you this!
|
I decided to create this feature after working on the raft consensus algorithm and finding out its super hard. Why not take advantage of the fact Consul already gives you this!
|
||||||
I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now.
|
I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now.
|
||||||
|
|
||||||
This feature has a 3 second ttl cache before making a new request to your local consul agent.
|
This feature has a 3 second ttl cache before making a new request to your local consul agent.
|
||||||
|
|
||||||
Follow Redirects / Use CookieContainer
|
Configuration Key
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-----------------
|
||||||
|
|
||||||
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
|
If you are using Consul for configuration (or other providers in the future) you might want to key your configurations so you can have multiple configurations :) This feature was requested in `issue 346 <https://github.com/ThreeMammals/Ocelot/issues/346>`_! In order to specify the key you need to set the ConfigurationKey property in the ServiceDiscoveryProvider section of the configuration json file e.g.
|
||||||
|
|
||||||
1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
|
.. code-block:: json
|
||||||
follow redirection responses from the Downstream resource; otherwise false. The default value is false.
|
|
||||||
2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer
|
"GlobalConfiguration": {
|
||||||
property to store server cookies and uses these cookies when sending requests. The default value is false. Please note
|
"ServiceDiscoveryProvider": {
|
||||||
that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests
|
"Host": "localhost",
|
||||||
to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user
|
"Port": 9500,
|
||||||
noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients
|
"ConfigurationKey": "Oceolot_A"
|
||||||
that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight
|
}
|
||||||
requests. This would also mean that subsequent requests dont use the cookies from the previous response! All in all not a great situation. I would avoid setting
|
}
|
||||||
UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request!
|
|
||||||
|
In this example Ocelot will use Oceolot_A as the key for your configuration when looking it up in Consul.
|
||||||
SSL Errors
|
|
||||||
^^^^^^^^^^
|
If you do not set the ConfigurationKey Ocelot will use the string InternalConfiguration as the key.
|
||||||
|
|
||||||
Id you want to ignore SSL warnings / errors set the following in your ReRoute config.
|
Follow Redirects / Use CookieContainer
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
.. code-block:: json
|
|
||||||
|
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
|
||||||
"DangerousAcceptAnyServerCertificateValidator": false
|
|
||||||
|
1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
|
||||||
I don't reccomend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
|
follow redirection responses from the Downstream resource; otherwise false. The default value is false.
|
||||||
|
2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer
|
||||||
|
property to store server cookies and uses these cookies when sending requests. The default value is false. Please note
|
||||||
|
that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests
|
||||||
|
to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user
|
||||||
|
noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients
|
||||||
|
that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight
|
||||||
|
requests. This would also mean that subsequent requests dont use the cookies from the previous response! All in all not a great situation. I would avoid setting
|
||||||
|
UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request!
|
||||||
|
|
||||||
|
SSL Errors
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
Id you want to ignore SSL warnings / errors set the following in your ReRoute config.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"DangerousAcceptAnyServerCertificateValidator": false
|
||||||
|
|
||||||
|
I don't reccomend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
|
||||||
|
@ -1,39 +1,46 @@
|
|||||||
namespace Ocelot.Configuration.Builder
|
namespace Ocelot.Configuration.Builder
|
||||||
{
|
{
|
||||||
public class ServiceProviderConfigurationBuilder
|
public class ServiceProviderConfigurationBuilder
|
||||||
{
|
{
|
||||||
private string _serviceDiscoveryProviderHost;
|
private string _serviceDiscoveryProviderHost;
|
||||||
private int _serviceDiscoveryProviderPort;
|
private int _serviceDiscoveryProviderPort;
|
||||||
private string _type;
|
private string _type;
|
||||||
private string _token;
|
private string _token;
|
||||||
|
private string _configurationKey;
|
||||||
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
|
||||||
{
|
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
||||||
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
|
{
|
||||||
return this;
|
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
public ServiceProviderConfigurationBuilder WithPort(int serviceDiscoveryProviderPort)
|
|
||||||
{
|
public ServiceProviderConfigurationBuilder WithPort(int serviceDiscoveryProviderPort)
|
||||||
_serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
|
{
|
||||||
return this;
|
_serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
public ServiceProviderConfigurationBuilder WithType(string type)
|
|
||||||
{
|
public ServiceProviderConfigurationBuilder WithType(string type)
|
||||||
_type = type;
|
{
|
||||||
return this;
|
_type = type;
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
public ServiceProviderConfigurationBuilder WithToken(string token)
|
|
||||||
{
|
public ServiceProviderConfigurationBuilder WithToken(string token)
|
||||||
_token = token;
|
{
|
||||||
return this;
|
_token = token;
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
public ServiceProviderConfiguration Build()
|
|
||||||
{
|
public ServiceProviderConfigurationBuilder WithConfigurationKey(string configurationKey)
|
||||||
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token);
|
{
|
||||||
}
|
_configurationKey = configurationKey;
|
||||||
}
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfiguration Build()
|
||||||
|
{
|
||||||
|
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
public class ServiceProviderConfigurationCreator : IServiceProviderConfigurationCreator
|
public class ServiceProviderConfigurationCreator : IServiceProviderConfigurationCreator
|
||||||
{
|
{
|
||||||
public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration)
|
public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration)
|
||||||
{
|
{
|
||||||
//todo log or return error here dont just default to something that wont work..
|
//todo log or return error here dont just default to something that wont work..
|
||||||
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
||||||
|
|
||||||
return new ServiceProviderConfigurationBuilder()
|
return new ServiceProviderConfigurationBuilder()
|
||||||
.WithHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
|
.WithHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
|
||||||
.WithPort(serviceProviderPort)
|
.WithPort(serviceProviderPort)
|
||||||
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
||||||
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
||||||
.Build();
|
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
|
||||||
}
|
.Build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
namespace Ocelot.Configuration.File
|
namespace Ocelot.Configuration.File
|
||||||
{
|
{
|
||||||
public class FileServiceDiscoveryProvider
|
public class FileServiceDiscoveryProvider
|
||||||
{
|
{
|
||||||
public string Host {get;set;}
|
public string Host {get;set;}
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
}
|
public string ConfigurationKey { get; set; }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,96 +1,99 @@
|
|||||||
namespace Ocelot.Configuration.Repository
|
namespace Ocelot.Configuration.Repository
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
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.Configuration.File;
|
||||||
using Ocelot.Infrastructure.Consul;
|
using Ocelot.Infrastructure.Consul;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.ServiceDiscovery.Configuration;
|
using Ocelot.ServiceDiscovery.Configuration;
|
||||||
|
|
||||||
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
|
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
|
||||||
{
|
{
|
||||||
private readonly IConsulClient _consul;
|
private readonly IConsulClient _consul;
|
||||||
private const string OcelotConfiguration = "InternalConfiguration";
|
private readonly string _configurationKey;
|
||||||
private readonly Cache.IOcelotCache<FileConfiguration> _cache;
|
private readonly Cache.IOcelotCache<FileConfiguration> _cache;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public ConsulFileConfigurationRepository(
|
public ConsulFileConfigurationRepository(
|
||||||
Cache.IOcelotCache<FileConfiguration> cache,
|
Cache.IOcelotCache<FileConfiguration> cache,
|
||||||
IInternalConfigurationRepository repo,
|
IInternalConfigurationRepository repo,
|
||||||
IConsulClientFactory factory,
|
IConsulClientFactory factory,
|
||||||
IOcelotLoggerFactory loggerFactory)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory.CreateLogger<ConsulFileConfigurationRepository>();
|
_logger = loggerFactory.CreateLogger<ConsulFileConfigurationRepository>();
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
|
|
||||||
var internalConfig = repo.Get();
|
var internalConfig = repo.Get();
|
||||||
|
|
||||||
var consulHost = "localhost";
|
_configurationKey = "InternalConfiguration";
|
||||||
var consulPort = 8500;
|
var consulHost = "localhost";
|
||||||
string token = null;
|
var consulPort = 8500;
|
||||||
|
string token = null;
|
||||||
if (!internalConfig.IsError)
|
|
||||||
{
|
if (!internalConfig.IsError)
|
||||||
consulHost = string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration?.Host) ? consulHost : internalConfig.Data.ServiceProviderConfiguration?.Host;
|
{
|
||||||
consulPort = internalConfig.Data.ServiceProviderConfiguration?.Port ?? consulPort;
|
consulHost = string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration?.Host) ? consulHost : internalConfig.Data.ServiceProviderConfiguration?.Host;
|
||||||
token = internalConfig.Data.ServiceProviderConfiguration?.Token;
|
consulPort = internalConfig.Data.ServiceProviderConfiguration?.Port ?? consulPort;
|
||||||
}
|
token = internalConfig.Data.ServiceProviderConfiguration?.Token;
|
||||||
|
_configurationKey = !string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration?.ConfigurationKey) ?
|
||||||
var config = new ConsulRegistryConfiguration(consulHost, consulPort, OcelotConfiguration, token);
|
internalConfig.Data.ServiceProviderConfiguration?.ConfigurationKey : _configurationKey;
|
||||||
|
}
|
||||||
_consul = factory.Get(config);
|
|
||||||
}
|
var config = new ConsulRegistryConfiguration(consulHost, consulPort, _configurationKey, token);
|
||||||
|
|
||||||
public async Task<Response<FileConfiguration>> Get()
|
_consul = factory.Get(config);
|
||||||
{
|
}
|
||||||
var config = _cache.Get(OcelotConfiguration, OcelotConfiguration);
|
|
||||||
|
public async Task<Response<FileConfiguration>> Get()
|
||||||
if (config != null)
|
{
|
||||||
{
|
var config = _cache.Get(_configurationKey, _configurationKey);
|
||||||
return new OkResponse<FileConfiguration>(config);
|
|
||||||
}
|
if (config != null)
|
||||||
|
{
|
||||||
var queryResult = await _consul.KV.Get(OcelotConfiguration);
|
return new OkResponse<FileConfiguration>(config);
|
||||||
|
}
|
||||||
if (queryResult.Response == null)
|
|
||||||
{
|
var queryResult = await _consul.KV.Get(_configurationKey);
|
||||||
return new OkResponse<FileConfiguration>(null);
|
|
||||||
}
|
if (queryResult.Response == null)
|
||||||
|
{
|
||||||
var bytes = queryResult.Response.Value;
|
return new OkResponse<FileConfiguration>(null);
|
||||||
|
}
|
||||||
var json = Encoding.UTF8.GetString(bytes);
|
|
||||||
|
var bytes = queryResult.Response.Value;
|
||||||
var consulConfig = JsonConvert.DeserializeObject<FileConfiguration>(json);
|
|
||||||
|
var json = Encoding.UTF8.GetString(bytes);
|
||||||
return new OkResponse<FileConfiguration>(consulConfig);
|
|
||||||
}
|
var consulConfig = JsonConvert.DeserializeObject<FileConfiguration>(json);
|
||||||
|
|
||||||
public async Task<Response> Set(FileConfiguration ocelotConfiguration)
|
return new OkResponse<FileConfiguration>(consulConfig);
|
||||||
{
|
}
|
||||||
var json = JsonConvert.SerializeObject(ocelotConfiguration, Formatting.Indented);
|
|
||||||
|
public async Task<Response> Set(FileConfiguration ocelotConfiguration)
|
||||||
var bytes = Encoding.UTF8.GetBytes(json);
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(ocelotConfiguration, Formatting.Indented);
|
||||||
var kvPair = new KVPair(OcelotConfiguration)
|
|
||||||
{
|
var bytes = Encoding.UTF8.GetBytes(json);
|
||||||
Value = bytes
|
|
||||||
};
|
var kvPair = new KVPair(_configurationKey)
|
||||||
|
{
|
||||||
var result = await _consul.KV.Put(kvPair);
|
Value = bytes
|
||||||
|
};
|
||||||
if (result.Response)
|
|
||||||
{
|
var result = await _consul.KV.Put(kvPair);
|
||||||
_cache.AddAndDelete(OcelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), OcelotConfiguration);
|
|
||||||
|
if (result.Response)
|
||||||
return new OkResponse();
|
{
|
||||||
}
|
_cache.AddAndDelete(_configurationKey, ocelotConfiguration, TimeSpan.FromSeconds(3), _configurationKey);
|
||||||
|
|
||||||
return new ErrorResponse(new UnableToSetConfigInConsulError($"Unable to set FileConfiguration in consul, response status code from consul was {result.StatusCode}"));
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
return new ErrorResponse(new UnableToSetConfigInConsulError($"Unable to set FileConfiguration in consul, response status code from consul was {result.StatusCode}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
{
|
{
|
||||||
public class ServiceProviderConfiguration
|
public class ServiceProviderConfiguration
|
||||||
{
|
{
|
||||||
public ServiceProviderConfiguration(string type, string host, int port, string token)
|
public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey)
|
||||||
{
|
{
|
||||||
|
ConfigurationKey = configurationKey;
|
||||||
Host = host;
|
Host = host;
|
||||||
Port = port;
|
Port = port;
|
||||||
Token = token;
|
Token = token;
|
||||||
@ -14,5 +15,6 @@
|
|||||||
public int Port { get; }
|
public int Port { get; }
|
||||||
public string Type { get; }
|
public string Type { get; }
|
||||||
public string Token { get; }
|
public string Token { get; }
|
||||||
|
public string ConfigurationKey { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,219 +1,260 @@
|
|||||||
namespace Ocelot.UnitTests.Configuration
|
namespace Ocelot.UnitTests.Configuration
|
||||||
{
|
{
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Ocelot.Configuration.Repository;
|
using Ocelot.Configuration.Repository;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Ocelot.Infrastructure.Consul;
|
using Ocelot.Infrastructure.Consul;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Cache;
|
using Ocelot.Cache;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.ServiceDiscovery.Configuration;
|
using Ocelot.ServiceDiscovery.Configuration;
|
||||||
using Consul;
|
using Consul;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
public class ConsulFileConfigurationRepositoryTests
|
public class ConsulFileConfigurationRepositoryTests
|
||||||
{
|
{
|
||||||
private ConsulFileConfigurationRepository _repo;
|
private ConsulFileConfigurationRepository _repo;
|
||||||
private Mock<IOcelotCache<FileConfiguration>> _cache;
|
private Mock<IOcelotCache<FileConfiguration>> _cache;
|
||||||
private Mock<IInternalConfigurationRepository> _internalRepo;
|
private Mock<IInternalConfigurationRepository> _internalRepo;
|
||||||
private Mock<IConsulClientFactory> _factory;
|
private Mock<IConsulClientFactory> _factory;
|
||||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
private Mock<IConsulClient> _client;
|
private Mock<IConsulClient> _client;
|
||||||
private Mock<IKVEndpoint> _kvEndpoint;
|
private Mock<IKVEndpoint> _kvEndpoint;
|
||||||
private FileConfiguration _fileConfiguration;
|
private FileConfiguration _fileConfiguration;
|
||||||
private Response _setResult;
|
private Response _setResult;
|
||||||
private Response<FileConfiguration> _getResult;
|
private Response<FileConfiguration> _getResult;
|
||||||
|
|
||||||
public ConsulFileConfigurationRepositoryTests()
|
public ConsulFileConfigurationRepositoryTests()
|
||||||
{
|
{
|
||||||
_cache = new Mock<IOcelotCache<FileConfiguration>>();
|
_cache = new Mock<IOcelotCache<FileConfiguration>>();
|
||||||
_internalRepo = new Mock<IInternalConfigurationRepository>();
|
_internalRepo = new Mock<IInternalConfigurationRepository>();
|
||||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
|
||||||
_factory = new Mock<IConsulClientFactory>();
|
_factory = new Mock<IConsulClientFactory>();
|
||||||
_client = new Mock<IConsulClient>();
|
_client = new Mock<IConsulClient>();
|
||||||
_kvEndpoint = new Mock<IKVEndpoint>();
|
_kvEndpoint = new Mock<IKVEndpoint>();
|
||||||
|
|
||||||
_client
|
_client
|
||||||
.Setup(x => x.KV)
|
.Setup(x => x.KV)
|
||||||
.Returns(_kvEndpoint.Object);
|
.Returns(_kvEndpoint.Object);
|
||||||
_factory
|
|
||||||
.Setup(x => x.Get(It.IsAny<ConsulRegistryConfiguration>()))
|
_factory
|
||||||
.Returns(_client.Object);
|
.Setup(x => x.Get(It.IsAny<ConsulRegistryConfiguration>()))
|
||||||
|
.Returns(_client.Object);
|
||||||
_internalRepo
|
|
||||||
.Setup(x => x.Get())
|
_internalRepo
|
||||||
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "", new ServiceProviderConfigurationBuilder().Build(), "")));
|
.Setup(x => x.Get())
|
||||||
|
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "", new ServiceProviderConfigurationBuilder().Build(), "")));
|
||||||
_repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object);
|
|
||||||
}
|
_repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object);
|
||||||
|
}
|
||||||
[Fact]
|
|
||||||
public void should_set_config()
|
[Fact]
|
||||||
{
|
public void should_set_config()
|
||||||
var config = FakeFileConfiguration();
|
{
|
||||||
|
var config = FakeFileConfiguration();
|
||||||
this.Given(_ => GivenIHaveAConfiguration(config))
|
|
||||||
.And(_ => GivenWritingToConsulSucceeds())
|
this.Given(_ => GivenIHaveAConfiguration(config))
|
||||||
.When(_ => WhenISetTheConfiguration())
|
.And(_ => GivenWritingToConsulSucceeds())
|
||||||
.Then(_ => ThenTheConfigurationIsStoredAs(config))
|
.When(_ => WhenISetTheConfiguration())
|
||||||
.BDDfy();
|
.Then(_ => ThenTheConfigurationIsStoredAs(config))
|
||||||
}
|
.BDDfy();
|
||||||
|
}
|
||||||
[Fact]
|
|
||||||
public void should_get_config()
|
[Fact]
|
||||||
{
|
public void should_get_config()
|
||||||
var config = FakeFileConfiguration();
|
{
|
||||||
|
var config = FakeFileConfiguration();
|
||||||
this.Given(_ => GivenIHaveAConfiguration(config))
|
|
||||||
.And(_ => GivenFetchFromConsulSucceeds())
|
this.Given(_ => GivenIHaveAConfiguration(config))
|
||||||
.When(_ => WhenIGetTheConfiguration())
|
.And(_ => GivenFetchFromConsulSucceeds())
|
||||||
.Then(_ => ThenTheConfigurationIs(config))
|
.When(_ => WhenIGetTheConfiguration())
|
||||||
.BDDfy();
|
.Then(_ => ThenTheConfigurationIs(config))
|
||||||
}
|
.BDDfy();
|
||||||
|
}
|
||||||
[Fact]
|
|
||||||
public void should_get_null_config()
|
[Fact]
|
||||||
{
|
public void should_get_null_config()
|
||||||
this.Given(_ => GivenFetchFromConsulReturnsNull())
|
{
|
||||||
.When(_ => WhenIGetTheConfiguration())
|
this.Given(_ => GivenFetchFromConsulReturnsNull())
|
||||||
.Then(_ => ThenTheConfigurationIsNull())
|
.When(_ => WhenIGetTheConfiguration())
|
||||||
.BDDfy();
|
.Then(_ => ThenTheConfigurationIsNull())
|
||||||
}
|
.BDDfy();
|
||||||
|
}
|
||||||
[Fact]
|
|
||||||
public void should_get_config_from_cache()
|
[Fact]
|
||||||
{
|
public void should_get_config_from_cache()
|
||||||
var config = FakeFileConfiguration();
|
{
|
||||||
|
var config = FakeFileConfiguration();
|
||||||
this.Given(_ => GivenIHaveAConfiguration(config))
|
|
||||||
.And(_ => GivenFetchFromCacheSucceeds())
|
this.Given(_ => GivenIHaveAConfiguration(config))
|
||||||
.When(_ => WhenIGetTheConfiguration())
|
.And(_ => GivenFetchFromCacheSucceeds())
|
||||||
.Then(_ => ThenTheConfigurationIs(config))
|
.When(_ => WhenIGetTheConfiguration())
|
||||||
.BDDfy();
|
.Then(_ => ThenTheConfigurationIs(config))
|
||||||
}
|
.BDDfy();
|
||||||
|
}
|
||||||
private void ThenTheConfigurationIsNull()
|
|
||||||
{
|
[Fact]
|
||||||
_getResult.Data.ShouldBeNull();
|
public void should_set_config_key()
|
||||||
}
|
{
|
||||||
|
var config = FakeFileConfiguration();
|
||||||
private void ThenTheConfigurationIs(FileConfiguration config)
|
|
||||||
{
|
this.Given(_ => GivenIHaveAConfiguration(config))
|
||||||
var expected = JsonConvert.SerializeObject(config, Formatting.Indented);
|
.And(_ => GivenTheConfigKeyComesFromFileConfig("Tom"))
|
||||||
var result = JsonConvert.SerializeObject(_getResult.Data, Formatting.Indented);
|
.And(_ => GivenFetchFromConsulSucceeds())
|
||||||
result.ShouldBe(expected);
|
.When(_ => WhenIGetTheConfiguration())
|
||||||
}
|
.And(_ => ThenTheConfigKeyIs("Tom"))
|
||||||
|
.BDDfy();
|
||||||
private async Task WhenIGetTheConfiguration()
|
}
|
||||||
{
|
|
||||||
_getResult = await _repo.Get();
|
[Fact]
|
||||||
}
|
public void should_set_default_config_key()
|
||||||
|
{
|
||||||
private void GivenWritingToConsulSucceeds()
|
var config = FakeFileConfiguration();
|
||||||
{
|
|
||||||
var response = new WriteResult<bool>();
|
this.Given(_ => GivenIHaveAConfiguration(config))
|
||||||
response.Response = true;
|
.And(_ => GivenFetchFromConsulSucceeds())
|
||||||
|
.When(_ => WhenIGetTheConfiguration())
|
||||||
_kvEndpoint
|
.And(_ => ThenTheConfigKeyIs("InternalConfiguration"))
|
||||||
.Setup(x => x.Put(It.IsAny<KVPair>(), It.IsAny<CancellationToken>())).ReturnsAsync(response);
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenFetchFromCacheSucceeds()
|
private void ThenTheConfigKeyIs(string expected)
|
||||||
{
|
{
|
||||||
_cache.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>())).Returns(_fileConfiguration);
|
_kvEndpoint
|
||||||
}
|
.Verify(x => x.Get(expected, It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
}
|
||||||
private void GivenFetchFromConsulReturnsNull()
|
|
||||||
{
|
private void GivenTheConfigKeyComesFromFileConfig(string key)
|
||||||
QueryResult<KVPair> result = new QueryResult<KVPair>();
|
{
|
||||||
|
_internalRepo
|
||||||
_kvEndpoint
|
.Setup(x => x.Get())
|
||||||
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "", new ServiceProviderConfigurationBuilder().WithConfigurationKey(key).Build(), "")));
|
||||||
.ReturnsAsync(result);
|
|
||||||
}
|
_repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object);
|
||||||
|
}
|
||||||
private void GivenFetchFromConsulSucceeds()
|
|
||||||
{
|
private void ThenTheConfigurationIsNull()
|
||||||
var json = JsonConvert.SerializeObject(_fileConfiguration, Formatting.Indented);
|
{
|
||||||
|
_getResult.Data.ShouldBeNull();
|
||||||
var bytes = Encoding.UTF8.GetBytes(json);
|
}
|
||||||
|
|
||||||
var kvp = new KVPair("OcelotConfiguration");
|
private void ThenTheConfigurationIs(FileConfiguration config)
|
||||||
kvp.Value = bytes;
|
{
|
||||||
|
var expected = JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||||
var query = new QueryResult<KVPair>();
|
var result = JsonConvert.SerializeObject(_getResult.Data, Formatting.Indented);
|
||||||
query.Response = kvp;
|
result.ShouldBe(expected);
|
||||||
|
}
|
||||||
_kvEndpoint
|
|
||||||
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
private async Task WhenIGetTheConfiguration()
|
||||||
.ReturnsAsync(query);
|
{
|
||||||
}
|
_getResult = await _repo.Get();
|
||||||
|
}
|
||||||
private void ThenTheConfigurationIsStoredAs(FileConfiguration config)
|
|
||||||
{
|
private void GivenWritingToConsulSucceeds()
|
||||||
var json = JsonConvert.SerializeObject(config, Formatting.Indented);
|
{
|
||||||
|
var response = new WriteResult<bool>();
|
||||||
var bytes = Encoding.UTF8.GetBytes(json);
|
response.Response = true;
|
||||||
|
|
||||||
_kvEndpoint
|
_kvEndpoint
|
||||||
.Verify(x => x.Put(It.Is<KVPair>(k => k.Value.SequenceEqual(bytes)), It.IsAny<CancellationToken>()), Times.Once);
|
.Setup(x => x.Put(It.IsAny<KVPair>(), It.IsAny<CancellationToken>())).ReturnsAsync(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WhenISetTheConfiguration()
|
private void GivenFetchFromCacheSucceeds()
|
||||||
{
|
{
|
||||||
_setResult = await _repo.Set(_fileConfiguration);
|
_cache.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>())).Returns(_fileConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenIHaveAConfiguration(FileConfiguration config)
|
private void GivenFetchFromConsulReturnsNull()
|
||||||
{
|
{
|
||||||
_fileConfiguration = config;
|
QueryResult<KVPair> result = new QueryResult<KVPair>();
|
||||||
}
|
|
||||||
|
_kvEndpoint
|
||||||
private FileConfiguration FakeFileConfiguration()
|
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
{
|
.ReturnsAsync(result);
|
||||||
var reRoutes = new List<FileReRoute>
|
}
|
||||||
{
|
|
||||||
new FileReRoute
|
private void GivenFetchFromConsulSucceeds()
|
||||||
{
|
{
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
var json = JsonConvert.SerializeObject(_fileConfiguration, Formatting.Indented);
|
||||||
{
|
|
||||||
new FileHostAndPort
|
var bytes = Encoding.UTF8.GetBytes(json);
|
||||||
{
|
|
||||||
Host = "123.12.12.12",
|
var kvp = new KVPair("OcelotConfiguration");
|
||||||
Port = 80,
|
kvp.Value = bytes;
|
||||||
}
|
|
||||||
},
|
var query = new QueryResult<KVPair>();
|
||||||
DownstreamScheme = "https",
|
query.Response = kvp;
|
||||||
DownstreamPathTemplate = "/asdfs/test/{test}"
|
|
||||||
}
|
_kvEndpoint
|
||||||
};
|
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(query);
|
||||||
var globalConfiguration = new FileGlobalConfiguration
|
}
|
||||||
{
|
|
||||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
private void ThenTheConfigurationIsStoredAs(FileConfiguration config)
|
||||||
{
|
{
|
||||||
Port = 198,
|
var json = JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||||
Host = "blah"
|
|
||||||
}
|
var bytes = Encoding.UTF8.GetBytes(json);
|
||||||
};
|
|
||||||
|
_kvEndpoint
|
||||||
return new FileConfiguration
|
.Verify(x => x.Put(It.Is<KVPair>(k => k.Value.SequenceEqual(bytes)), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
{
|
}
|
||||||
GlobalConfiguration = globalConfiguration,
|
|
||||||
ReRoutes = reRoutes
|
private async Task WhenISetTheConfiguration()
|
||||||
};
|
{
|
||||||
}
|
_setResult = await _repo.Set(_fileConfiguration);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void GivenIHaveAConfiguration(FileConfiguration config)
|
||||||
|
{
|
||||||
|
_fileConfiguration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileConfiguration FakeFileConfiguration()
|
||||||
|
{
|
||||||
|
var reRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "123.12.12.12",
|
||||||
|
Port = 80,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/asdfs/test/{test}"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var globalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||||
|
{
|
||||||
|
Port = 198,
|
||||||
|
Host = "blah"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new FileConfiguration
|
||||||
|
{
|
||||||
|
GlobalConfiguration = globalConfiguration,
|
||||||
|
ReRoutes = reRoutes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,67 +1,70 @@
|
|||||||
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 ServiceProviderCreatorTests
|
public class ServiceProviderCreatorTests
|
||||||
{
|
{
|
||||||
private readonly ServiceProviderConfigurationCreator _creator;
|
private readonly ServiceProviderConfigurationCreator _creator;
|
||||||
private FileGlobalConfiguration _globalConfig;
|
private FileGlobalConfiguration _globalConfig;
|
||||||
private ServiceProviderConfiguration _result;
|
private ServiceProviderConfiguration _result;
|
||||||
|
|
||||||
public ServiceProviderCreatorTests()
|
public ServiceProviderCreatorTests()
|
||||||
{
|
{
|
||||||
_creator = new ServiceProviderConfigurationCreator();
|
_creator = new ServiceProviderConfigurationCreator();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_create_service_provider_config()
|
public void should_create_service_provider_config()
|
||||||
{
|
{
|
||||||
var globalConfig = new FileGlobalConfiguration
|
var globalConfig = new FileGlobalConfiguration
|
||||||
{
|
{
|
||||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||||
{
|
{
|
||||||
Host = "127.0.0.1",
|
Host = "127.0.0.1",
|
||||||
Port = 1234,
|
Port = 1234,
|
||||||
Type = "ServiceFabric",
|
Type = "ServiceFabric",
|
||||||
Token = "testtoken"
|
Token = "testtoken",
|
||||||
}
|
ConfigurationKey = "woo"
|
||||||
};
|
}
|
||||||
|
};
|
||||||
var expected = new ServiceProviderConfigurationBuilder()
|
|
||||||
.WithHost("127.0.0.1")
|
var expected = new ServiceProviderConfigurationBuilder()
|
||||||
.WithPort(1234)
|
.WithHost("127.0.0.1")
|
||||||
.WithType("ServiceFabric")
|
.WithPort(1234)
|
||||||
.WithToken("testtoken")
|
.WithType("ServiceFabric")
|
||||||
.Build();
|
.WithToken("testtoken")
|
||||||
|
.WithConfigurationKey("woo")
|
||||||
this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig))
|
.Build();
|
||||||
.When(x => x.WhenICreate())
|
|
||||||
.Then(x => x.ThenTheConfigIs(expected))
|
this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig))
|
||||||
.BDDfy();
|
.When(x => x.WhenICreate())
|
||||||
}
|
.Then(x => x.ThenTheConfigIs(expected))
|
||||||
|
.BDDfy();
|
||||||
private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration fileGlobalConfig)
|
}
|
||||||
{
|
|
||||||
_globalConfig = fileGlobalConfig;
|
private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration fileGlobalConfig)
|
||||||
}
|
{
|
||||||
|
_globalConfig = fileGlobalConfig;
|
||||||
private void WhenICreate()
|
}
|
||||||
{
|
|
||||||
_result = _creator.Create(_globalConfig);
|
private void WhenICreate()
|
||||||
}
|
{
|
||||||
|
_result = _creator.Create(_globalConfig);
|
||||||
private void ThenTheConfigIs(ServiceProviderConfiguration expected)
|
}
|
||||||
{
|
|
||||||
_result.Host.ShouldBe(expected.Host);
|
private void ThenTheConfigIs(ServiceProviderConfiguration expected)
|
||||||
_result.Port.ShouldBe(expected.Port);
|
{
|
||||||
_result.Token.ShouldBe(expected.Token);
|
_result.Host.ShouldBe(expected.Host);
|
||||||
_result.Type.ShouldBe(expected.Type);
|
_result.Port.ShouldBe(expected.Port);
|
||||||
}
|
_result.Token.ShouldBe(expected.Token);
|
||||||
}
|
_result.Type.ShouldBe(expected.Type);
|
||||||
}
|
_result.ConfigurationKey.ShouldBe(expected.ConfigurationKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,7 +26,7 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
{
|
{
|
||||||
_factory = new Mock<ILoadBalancerFactory>();
|
_factory = new Mock<ILoadBalancerFactory>();
|
||||||
_loadBalancerHouse = new LoadBalancerHouse(_factory.Object);
|
_loadBalancerHouse = new LoadBalancerHouse(_factory.Object);
|
||||||
_serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123, string.Empty);
|
_serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123, string.Empty, "configKey");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user