mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 15:10:50 +08:00 
			
		
		
		
	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:
		@@ -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.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
using Ocelot.Errors;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Ocelot.Configuration.Repository
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class UnableToSetConfigInConsulError : Error
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public UnableToSetConfigInConsulError(string message) 
 | 
					 | 
				
			||||||
            : base(message, OcelotErrorCode.UnableToSetConfigInConsulError)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -29,7 +29,6 @@
 | 
				
			|||||||
        UnableToFindLoadBalancerError,
 | 
					        UnableToFindLoadBalancerError,
 | 
				
			||||||
        RequestTimedOutError,
 | 
					        RequestTimedOutError,
 | 
				
			||||||
        UnableToFindQoSProviderError,
 | 
					        UnableToFindQoSProviderError,
 | 
				
			||||||
        UnableToSetConfigInConsulError,
 | 
					 | 
				
			||||||
        UnmappableRequestError,
 | 
					        UnmappableRequestError,
 | 
				
			||||||
        RateLimitOptionsError,
 | 
					        RateLimitOptionsError,
 | 
				
			||||||
        PathTemplateDoesntStartWithForwardSlash,
 | 
					        PathTemplateDoesntStartWithForwardSlash,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Middleware
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using System.Threading.Tasks;
 | 
				
			||||||
 | 
					    using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public delegate Task OcelotMiddlewareConfigurationDelegate(IApplicationBuilder builder);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.ServiceDiscovery
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using System;
 | 
				
			||||||
 | 
					    using Ocelot.Configuration;
 | 
				
			||||||
 | 
					    using Providers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, string key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user