Feature/removed consul and its deps to other package (#539)

* #529 removed consul deps and introduced delegate to find service discovery provider

* +semver: breaking moved consul configuration to package..introduced mechanism for packages to configure Ocelot pipeline
This commit is contained in:
Tom Pallister 2018-08-12 17:28:41 +05:30 committed by GitHub
parent a91235b405
commit 87348e5d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 58 additions and 92 deletions

View File

@ -121,13 +121,18 @@ At the moment there is no validation at this stage it only happens when Ocelot v
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. The first thing you need to do is install the NuGet package that provides Consul support in Ocelot.
``Install-Package Ocelot.Provider.Consul``
Then 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(); .AddConsul()
.AddConfigStoredInConsul();
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.

View File

@ -11,6 +11,17 @@ you specify a ServiceName for at ReRoute level.
Consul Consul
^^^^^^ ^^^^^^
The first thing you need to do is install the NuGet package that provides Consul support in Ocelot.
``Install-Package Ocelot.Provider.Consul``
Then add the following to your ConfigureServices method.
.. code-block:: csharp
s.AddOcelot()
.AddConsul();
The following is required in the GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default 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. will be used.

View File

@ -67,7 +67,7 @@ namespace Ocelot.Configuration.Repository
private async Task Poll() private async Task Poll()
{ {
_logger.LogInformation("Started polling consul"); _logger.LogInformation("Started polling");
var fileConfig = await _repo.Get(); var fileConfig = await _repo.Get();
@ -91,7 +91,7 @@ namespace Ocelot.Configuration.Repository
_previousAsJson = asJson; _previousAsJson = asJson;
} }
_logger.LogInformation("Finished polling consul"); _logger.LogInformation("Finished polling");
} }
/// <summary> /// <summary>

View File

@ -1,12 +0,0 @@
using Ocelot.Errors;
namespace Ocelot.Configuration.Repository
{
public class UnableToSetConfigInConsulError : Error
{
public UnableToSetConfigInConsulError(string message)
: base(message, OcelotErrorCode.UnableToSetConfigInConsulError)
{
}
}
}

View File

@ -29,7 +29,6 @@
UnableToFindLoadBalancerError, UnableToFindLoadBalancerError,
RequestTimedOutError, RequestTimedOutError,
UnableToFindQoSProviderError, UnableToFindQoSProviderError,
UnableToSetConfigInConsulError,
UnmappableRequestError, UnmappableRequestError,
RateLimitOptionsError, RateLimitOptionsError,
PathTemplateDoesntStartWithForwardSlash, PathTemplateDoesntStartWithForwardSlash,

View File

@ -0,0 +1,7 @@
namespace Ocelot.Middleware
{
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
public delegate Task OcelotMiddlewareConfigurationDelegate(IApplicationBuilder builder);
}

View File

@ -134,16 +134,17 @@
internalConfigRepo.AddOrReplace(newInternalConfig.Data); internalConfigRepo.AddOrReplace(newInternalConfig.Data);
}); });
var fileConfigRepo = builder.ApplicationServices.GetService<IFileConfigurationRepository>();
var adminPath = builder.ApplicationServices.GetService<IAdministrationPath>(); var adminPath = builder.ApplicationServices.GetService<IAdministrationPath>();
if (UsingConsul(fileConfigRepo)) var configurations = builder.ApplicationServices.GetServices<OcelotMiddlewareConfigurationDelegate>();
// Todo - this has just been added for consul so far...will there be an ordering problem in the future? Should refactor all config into this pattern?
foreach (var configuration in configurations)
{ {
//Lots of jazz happens in here..check it out if you are using consul to store your config. await configuration(builder);
await SetFileConfigInConsul(builder, fileConfigRepo, fileConfig, internalConfigCreator, internalConfigRepo);
} }
else if(AdministrationApiInUse(adminPath))
if(AdministrationApiInUse(adminPath))
{ {
//We have to make sure the file config is set for the ocelot.env.json and ocelot.json so that if we pull it from the //We have to make sure the file config is set for the ocelot.env.json and ocelot.json so that if we pull it from the
//admin api it works...boy this is getting a spit spags boll. //admin api it works...boy this is getting a spit spags boll.
@ -160,49 +161,6 @@
return adminPath != null; return adminPath != null;
} }
private static async Task SetFileConfigInConsul(IApplicationBuilder builder,
IFileConfigurationRepository fileConfigRepo, IOptionsMonitor<FileConfiguration> fileConfig,
IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo)
{
// get the config from consul.
var fileConfigFromConsul = await fileConfigRepo.Get();
if (IsError(fileConfigFromConsul))
{
ThrowToStopOcelotStarting(fileConfigFromConsul);
}
else if (ConfigNotStoredInConsul(fileConfigFromConsul))
{
//there was no config in consul set the file in config in consul
await fileConfigRepo.Set(fileConfig.CurrentValue);
}
else
{
// create the internal config from consul data
var internalConfig = await internalConfigCreator.Create(fileConfigFromConsul.Data);
if (IsError(internalConfig))
{
ThrowToStopOcelotStarting(internalConfig);
}
else
{
// add the internal config to the internal repo
var response = internalConfigRepo.AddOrReplace(internalConfig.Data);
if (IsError(response))
{
ThrowToStopOcelotStarting(response);
}
}
if (IsError(internalConfig))
{
ThrowToStopOcelotStarting(internalConfig);
}
}
}
private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptionsMonitor<FileConfiguration> fileConfig) private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptionsMonitor<FileConfiguration> fileConfig)
{ {
var response = await fileConfigSetter.Set(fileConfig.CurrentValue); var response = await fileConfigSetter.Set(fileConfig.CurrentValue);
@ -213,11 +171,6 @@
} }
} }
private static bool ConfigNotStoredInConsul(Responses.Response<FileConfiguration> fileConfigFromConsul)
{
return fileConfigFromConsul.Data == null;
}
private static bool IsError(Response response) private static bool IsError(Response response)
{ {
return response == null || response.IsError; return response == null || response.IsError;
@ -240,12 +193,6 @@
throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}"); throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}");
} }
private static bool UsingConsul(IFileConfigurationRepository fileConfigRepo)
{
//todo - remove coupling by string
return fileConfigRepo.GetType().Name == "ConsulFileConfigurationRepository";
}
private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration) private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration)
{ {
if (!string.IsNullOrEmpty(configuration.AdministrationPath)) if (!string.IsNullOrEmpty(configuration.AdministrationPath))

View File

@ -0,0 +1,8 @@
namespace Ocelot.ServiceDiscovery
{
using System;
using Ocelot.Configuration;
using Providers;
public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, string key);
}

View File

@ -60,14 +60,16 @@ namespace Ocelot.ServiceDiscovery
return new EurekaServiceDiscoveryProvider(key, _eurekaClient); return new EurekaServiceDiscoveryProvider(key, _eurekaClient);
} }
// Todo - dont just hardcode this...only expect Consul at the momement so works. foreach (var serviceDiscoveryFinderDelegate in _delegates)
var finderDelegate = _delegates.FirstOrDefault(); {
var provider = serviceDiscoveryFinderDelegate?.Invoke(_provider, config, key);
if (provider != null)
{
return provider;
}
}
var provider = finderDelegate?.Invoke(_provider, config, key); return null;
return provider;
} }
} }
public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, string key);
} }

View File

@ -71,7 +71,7 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
this.Given(x => WhenTheConfigIsChangedInConsul(newConfig, 0)) this.Given(x => WhenTheConfigIsChanged(newConfig, 0))
.Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1)) .Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1))
.BDDfy(); .BDDfy();
} }
@ -96,13 +96,13 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
this.Given(x => WhenTheConfigIsChangedInConsul(newConfig, 10)) this.Given(x => WhenTheConfigIsChanged(newConfig, 10))
.Then(x => ThenTheSetterIsCalled(newConfig, 1)) .Then(x => ThenTheSetterIsCalled(newConfig, 1))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_do_nothing_if_call_to_consul_fails() public void should_do_nothing_if_call_to_provider_fails()
{ {
var newConfig = new FileConfiguration var newConfig = new FileConfiguration
{ {
@ -121,19 +121,19 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
this.Given(x => WhenConsulErrors()) this.Given(x => WhenProviderErrors())
.Then(x => ThenTheSetterIsCalled(newConfig, 0)) .Then(x => ThenTheSetterIsCalled(newConfig, 0))
.BDDfy(); .BDDfy();
} }
private void WhenConsulErrors() private void WhenProviderErrors()
{ {
_repo _repo
.Setup(x => x.Get()) .Setup(x => x.Get())
.ReturnsAsync(new ErrorResponse<FileConfiguration>(new AnyError())); .ReturnsAsync(new ErrorResponse<FileConfiguration>(new AnyError()));
} }
private void WhenTheConfigIsChangedInConsul(FileConfiguration newConfig, int delay) private void WhenTheConfigIsChanged(FileConfiguration newConfig, int delay)
{ {
_repo _repo
.Setup(x => x.Get()) .Setup(x => x.Get())

View File

@ -72,7 +72,6 @@ namespace Ocelot.UnitTests.Responder
[InlineData(OcelotErrorCode.UnableToFindLoadBalancerError)] [InlineData(OcelotErrorCode.UnableToFindLoadBalancerError)]
[InlineData(OcelotErrorCode.UnableToFindServiceDiscoveryProviderError)] [InlineData(OcelotErrorCode.UnableToFindServiceDiscoveryProviderError)]
[InlineData(OcelotErrorCode.UnableToFindQoSProviderError)] [InlineData(OcelotErrorCode.UnableToFindQoSProviderError)]
[InlineData(OcelotErrorCode.UnableToSetConfigInConsulError)]
[InlineData(OcelotErrorCode.UnknownError)] [InlineData(OcelotErrorCode.UnknownError)]
[InlineData(OcelotErrorCode.UnmappableRequestError)] [InlineData(OcelotErrorCode.UnmappableRequestError)]
[InlineData(OcelotErrorCode.UnsupportedAuthenticationProviderError)] [InlineData(OcelotErrorCode.UnsupportedAuthenticationProviderError)]
@ -126,7 +125,7 @@ namespace Ocelot.UnitTests.Responder
// If this test fails then it's because the number of error codes has changed. // If this test fails then it's because the number of error codes has changed.
// You should make the appropriate changes to the test cases here to ensure // You should make the appropriate changes to the test cases here to ensure
// they cover all the error codes, and then modify this assertion. // they cover all the error codes, and then modify this assertion.
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(35, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?"); Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(34, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
} }
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode) private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)