#296 will now do a crappy merge on the configuration

This commit is contained in:
Tom Gardham-Pallister 2018-04-16 20:28:15 +01:00
parent c5aa11f7fb
commit 2ed37cbc83
4 changed files with 191 additions and 32 deletions

View File

@ -69,22 +69,6 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
More information on how to use these options is below..
Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
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!
Multiple environments
^^^^^^^^^^^^^^^^^^^^^
@ -104,10 +88,35 @@ to you
.AddEnvironmentVariables();
})
Ocelot should 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>`_.
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.
Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below.
.. code-block:: csharp
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddOcelot()
.AddEnvironmentVariables();
})
In this scenario Ocelot will look for any files that match the pattern ocleot.*.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.
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -135,3 +144,19 @@ I decided to create this feature after working on the raft consensus algorithm a
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.
Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
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!

View File

@ -45,6 +45,7 @@ namespace Ocelot.DependencyInjection
}
var lines = File.ReadAllText(file);
var config = JsonConvert.DeserializeObject<FileConfiguration>(lines);
if(file == "./ocelot.global.json")
@ -57,8 +58,11 @@ namespace Ocelot.DependencyInjection
}
var json = JsonConvert.SerializeObject(ocelotConfig);
File.WriteAllText("ocelot.json", json);
builder.AddJsonFile("ocelot.json");
return builder;
}
}

View File

@ -22,12 +22,6 @@ namespace Ocelot.ManualTest
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json")
//.AddOcelot();
//load all the ocelot.xxx.json files that are not environments from asp.net core
//merge them into megaconfig
//save megaconfig to disk as ocelot.json
//then add to asp.net config stuff..
.AddEnvironmentVariables();
})
.ConfigureServices(s => {

View File

@ -15,20 +15,33 @@ namespace Ocelot.UnitTests.DependencyInjection
{
private IConfigurationRoot _configuration;
private string _result;
private IConfigurationRoot _configRoot;
private FileConfiguration _globalConfig;
private FileConfiguration _reRoute;
private FileConfiguration _reRouteB;
private FileConfiguration _aggregate;
[Fact]
public void should_add_base_url_to_config()
{
this.Given(x => GivenTheBaseUrl("test"))
.When(x => WhenIGet("BaseUrl"))
.Then(x => ThenTheResultIs("test"))
this.Given(_ => GivenTheBaseUrl("test"))
.When(_ => WhenIGet("BaseUrl"))
.Then(_ => ThenTheResultIs("test"))
.BDDfy();
}
[Fact]
public void should_merge_files()
{
var globalConfig = new FileConfiguration
this.Given(_ => GivenMultipleConfigurationFiles())
.When(_ => WhenIAddOcelotConfiguration())
.Then(_ => ThenTheConfigsAreMerged())
.BDDfy();
}
private void GivenMultipleConfigurationFiles()
{
_globalConfig = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
@ -40,17 +53,25 @@ namespace Ocelot.UnitTests.DependencyInjection
DisableRateLimitHeaders = true,
QuotaExceededMessage = "QuotaExceededMessage",
RateLimitCounterPrefix = "RateLimitCounterPrefix"
}
},
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "Host",
Port = 80,
Type = "Type"
},
RequestIdKey = "RequestIdKey"
}
};
var reRoute = new FileConfiguration
_reRoute = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamScheme = "DownstreamScheme",
DownstreamPathTemplate = "DownstreamPathTemplate",
Key = "Key",
UpstreamHost = "UpstreamHost",
UpstreamHttpMethod = new List<string>
@ -69,15 +90,130 @@ namespace Ocelot.UnitTests.DependencyInjection
}
};
var globalJson = JsonConvert.SerializeObject(globalConfig);
_reRouteB = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamScheme = "DownstreamSchemeB",
DownstreamPathTemplate = "DownstreamPathTemplateB",
Key = "KeyB",
UpstreamHost = "UpstreamHostB",
UpstreamHttpMethod = new List<string>
{
"UpstreamHttpMethodB"
},
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "HostB",
Port = 80
}
}
},
new FileReRoute
{
DownstreamScheme = "DownstreamSchemeBB",
DownstreamPathTemplate = "DownstreamPathTemplateBB",
Key = "KeyBB",
UpstreamHost = "UpstreamHostBB",
UpstreamHttpMethod = new List<string>
{
"UpstreamHttpMethodBB"
},
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "HostBB",
Port = 80
}
}
}
}
};
_aggregate = new FileConfiguration
{
Aggregates = new List<FileAggregateReRoute>
{
new FileAggregateReRoute
{
ReRouteKeys = new List<string>
{
"KeyB",
"KeyBB"
},
UpstreamPathTemplate = "UpstreamPathTemplate",
},
new FileAggregateReRoute
{
ReRouteKeys = new List<string>
{
"KeyB",
"KeyBB"
},
UpstreamPathTemplate = "UpstreamPathTemplate",
}
}
};
var globalJson = JsonConvert.SerializeObject(_globalConfig);
File.WriteAllText("ocelot.global.json", globalJson);
var reRouteJson = JsonConvert.SerializeObject(reRoute);
var reRouteJson = JsonConvert.SerializeObject(_reRoute);
File.WriteAllText("ocelot.reRoutes.json", reRouteJson);
var reRouteJsonB = JsonConvert.SerializeObject(_reRouteB);
File.WriteAllText("ocelot.reRoutesB.json", reRouteJsonB);
var aggregates = JsonConvert.SerializeObject(_aggregate);
File.WriteAllText("ocelot.aggregates.json", aggregates);
}
private void WhenIAddOcelotConfiguration()
{
IConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddOcelot();
var configRoot = builder.Build();
_configRoot = builder.Build();
}
private void ThenTheConfigsAreMerged()
{
var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration));
fc.GlobalConfiguration.BaseUrl.ShouldBe(_globalConfig.GlobalConfiguration.BaseUrl);
fc.GlobalConfiguration.RateLimitOptions.ClientIdHeader.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.ClientIdHeader);
fc.GlobalConfiguration.RateLimitOptions.DisableRateLimitHeaders.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.DisableRateLimitHeaders);
fc.GlobalConfiguration.RateLimitOptions.HttpStatusCode.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.HttpStatusCode);
fc.GlobalConfiguration.RateLimitOptions.QuotaExceededMessage.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.QuotaExceededMessage);
fc.GlobalConfiguration.RateLimitOptions.RateLimitCounterPrefix.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.RateLimitCounterPrefix);
fc.GlobalConfiguration.RequestIdKey.ShouldBe(_globalConfig.GlobalConfiguration.RequestIdKey);
fc.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Host);
fc.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Port);
fc.GlobalConfiguration.ServiceDiscoveryProvider.Type.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Type);
fc.ReRoutes.Count.ShouldBe(_reRoute.ReRoutes.Count + _reRouteB.ReRoutes.Count);
fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRoute.ReRoutes[0].DownstreamPathTemplate);
fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[0].DownstreamPathTemplate);
fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[1].DownstreamPathTemplate);
fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRoute.ReRoutes[0].DownstreamScheme);
fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[0].DownstreamScheme);
fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[1].DownstreamScheme);
fc.ReRoutes.ShouldContain(x => x.Key == _reRoute.ReRoutes[0].Key);
fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[0].Key);
fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[1].Key);
fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRoute.ReRoutes[0].UpstreamHost);
fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[0].UpstreamHost);
fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[1].UpstreamHost);
fc.Aggregates.Count.ShouldBe(_aggregate.Aggregates.Count);
}
private void GivenTheBaseUrl(string baseUrl)