From 9828c3b427afe744f7f60dcf6ddf2c49bf559feb Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Fri, 3 Feb 2017 22:50:57 +0000 Subject: [PATCH] started adding consul acceptance test --- build.cake | 4 +- src/Ocelot/Configuration/ReRoute.cs | 3 +- .../ServiceCollectionExtensions.cs | 2 +- src/Ocelot/Errors/OcelotErrorCode.cs | 2 +- .../LoadBalancers/LoadBalancerFactory.cs | 9 +- .../ConfigurationServiceProvider.cs | 4 +- ...ovider.cs => IServiceDiscoveryProvider.cs} | 2 +- .../IServiceDiscoveryProviderFactory.cs | 9 ++ .../IServiceProviderFactory.cs | 9 -- .../ServiceDiscoveryProviderFactory.cs | 18 +++ .../ServiceProviderConfiguraion.cs | 21 +++ .../ServiceProviderFactory.cs | 34 ----- ...ableToFindServiceDiscoveryProviderError.cs | 12 ++ .../UnableToFindServiceProviderError.cs | 12 -- .../ServiceDiscoveryTests.cs | 135 ++++++++++++++++++ test/Ocelot.AcceptanceTests/Steps.cs | 13 ++ .../LoadBalancer/LoadBalancerFactoryTests.cs | 12 +- .../ServiceProviderFactoryTests.cs | 12 +- 18 files changed, 234 insertions(+), 79 deletions(-) rename src/Ocelot/ServiceDiscovery/{IServiceProvider.cs => IServiceDiscoveryProvider.cs} (74%) create mode 100644 src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs delete mode 100644 src/Ocelot/ServiceDiscovery/IServiceProviderFactory.cs create mode 100644 src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs create mode 100644 src/Ocelot/ServiceDiscovery/ServiceProviderConfiguraion.cs delete mode 100644 src/Ocelot/ServiceDiscovery/ServiceProviderFactory.cs create mode 100644 src/Ocelot/ServiceDiscovery/UnableToFindServiceDiscoveryProviderError.cs delete mode 100644 src/Ocelot/ServiceDiscovery/UnableToFindServiceProviderError.cs create mode 100644 test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs diff --git a/build.cake b/build.cake index 1d798d74..26b45b25 100644 --- a/build.cake +++ b/build.cake @@ -42,8 +42,8 @@ var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; // internal build variables - don't change these. var releaseTag = ""; -var buildVersion = committedVersion; var committedVersion = "0.0.0-dev"; +var buildVersion = committedVersion; var target = Argument("target", "Default"); @@ -264,7 +264,7 @@ private string GetNuGetVersionForCommit() /// Updates project version in all of our projects private void PersistVersion(string version) { - Information(string.Format("We'll search all project.json files for {0} and replace with {1}...", committedVersion, version)); + //Information(string.Format("We'll search all project.json files for {0} and replace with {1}...", committedVersion, version)); var projectJsonFiles = GetFiles("./**/project.json"); diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 987f5be7..d9fe60c9 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -11,7 +11,8 @@ namespace Ocelot.Configuration List claimsToClaims, Dictionary routeClaimsRequirement, bool isAuthorised, List claimsToQueries, string requestIdKey, bool isCached, CacheOptions fileCacheOptions, string serviceName, bool useServiceDiscovery, string serviceDiscoveryProvider, string serviceDiscoveryAddress, - string downstreamScheme, string loadBalancer, string downstreamHost, int downstreamPort, string loadBalancerKey) + string downstreamScheme, string loadBalancer, string downstreamHost, int downstreamPort, + string loadBalancerKey) { LoadBalancerKey = loadBalancerKey; LoadBalancer = loadBalancer; diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 839a825b..0a6bd42c 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -61,7 +61,7 @@ namespace Ocelot.DependencyInjection { services.AddMvcCore().AddJsonFormatters(); services.AddLogging(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index f2c479df..d24988b9 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -24,7 +24,7 @@ DownstreamHostNullOrEmptyError, ServicesAreNullError, ServicesAreEmptyError, - UnableToFindServiceProviderError, + UnableToFindServiceDiscoveryProviderError, UnableToFindLoadBalancerError } } diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs index 7e11df39..082f3b61 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs @@ -5,19 +5,20 @@ namespace Ocelot.LoadBalancer.LoadBalancers { public class LoadBalancerFactory : ILoadBalancerFactory { - private readonly IServiceProviderFactory _serviceProviderFactory; - public LoadBalancerFactory(IServiceProviderFactory serviceProviderFactory) + private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory; + public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory) { _serviceProviderFactory = serviceProviderFactory; } public ILoadBalancer Get(ReRoute reRoute) { - var serviceConfig = new ServiceConfiguraion( + var serviceConfig = new ServiceProviderConfiguraion( reRoute.ServiceName, reRoute.DownstreamHost, reRoute.DownstreamPort, - reRoute.UseServiceDiscovery); + reRoute.UseServiceDiscovery, + reRoute.ServiceDiscoveryProvider); var serviceProvider = _serviceProviderFactory.Get(serviceConfig); diff --git a/src/Ocelot/ServiceDiscovery/ConfigurationServiceProvider.cs b/src/Ocelot/ServiceDiscovery/ConfigurationServiceProvider.cs index b207f772..f1045be3 100644 --- a/src/Ocelot/ServiceDiscovery/ConfigurationServiceProvider.cs +++ b/src/Ocelot/ServiceDiscovery/ConfigurationServiceProvider.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using Ocelot.Values; - + namespace Ocelot.ServiceDiscovery { - public class ConfigurationServiceProvider : IServiceProvider + public class ConfigurationServiceProvider : IServiceDiscoveryProvider { private List _services; diff --git a/src/Ocelot/ServiceDiscovery/IServiceProvider.cs b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProvider.cs similarity index 74% rename from src/Ocelot/ServiceDiscovery/IServiceProvider.cs rename to src/Ocelot/ServiceDiscovery/IServiceDiscoveryProvider.cs index 60e428c8..2732b5e3 100644 --- a/src/Ocelot/ServiceDiscovery/IServiceProvider.cs +++ b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProvider.cs @@ -3,7 +3,7 @@ using Ocelot.Values; namespace Ocelot.ServiceDiscovery { - public interface IServiceProvider + public interface IServiceDiscoveryProvider { List Get(); } diff --git a/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs new file mode 100644 index 00000000..fe2acaa8 --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs @@ -0,0 +1,9 @@ +using System; + +namespace Ocelot.ServiceDiscovery +{ + public interface IServiceDiscoveryProviderFactory + { + IServiceDiscoveryProvider Get(ServiceProviderConfiguraion serviceConfig); + } +} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/IServiceProviderFactory.cs b/src/Ocelot/ServiceDiscovery/IServiceProviderFactory.cs deleted file mode 100644 index 62c55f53..00000000 --- a/src/Ocelot/ServiceDiscovery/IServiceProviderFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Ocelot.ServiceDiscovery -{ - public interface IServiceProviderFactory - { - IServiceProvider Get(ServiceConfiguraion serviceConfig); - } -} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs new file mode 100644 index 00000000..cb06e99c --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Ocelot.Values; + +namespace Ocelot.ServiceDiscovery +{ + public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory + { + public IServiceDiscoveryProvider Get(ServiceProviderConfiguraion serviceConfig) + { + var services = new List() + { + new Service(serviceConfig.ServiceName, new HostAndPort(serviceConfig.DownstreamHost, serviceConfig.DownstreamPort)) + }; + + return new ConfigurationServiceProvider(services); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/ServiceProviderConfiguraion.cs b/src/Ocelot/ServiceDiscovery/ServiceProviderConfiguraion.cs new file mode 100644 index 00000000..70638aaa --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/ServiceProviderConfiguraion.cs @@ -0,0 +1,21 @@ +namespace Ocelot.ServiceDiscovery +{ + public class ServiceProviderConfiguraion + { + public ServiceProviderConfiguraion(string serviceName, string downstreamHost, + int downstreamPort, bool useServiceDiscovery, string serviceDiscoveryProvider) + { + ServiceName = serviceName; + DownstreamHost = downstreamHost; + DownstreamPort = downstreamPort; + UseServiceDiscovery = useServiceDiscovery; + ServiceDiscoveryProvider = serviceDiscoveryProvider; + } + + public string ServiceName { get; } + public string DownstreamHost { get; } + public int DownstreamPort { get; } + public bool UseServiceDiscovery { get; } + public string ServiceDiscoveryProvider {get;} + } +} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/ServiceProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceProviderFactory.cs deleted file mode 100644 index be3b8b8c..00000000 --- a/src/Ocelot/ServiceDiscovery/ServiceProviderFactory.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using Ocelot.Values; - -namespace Ocelot.ServiceDiscovery -{ - public class ServiceProviderFactory : IServiceProviderFactory - { - public IServiceProvider Get(ServiceConfiguraion serviceConfig) - { - var services = new List() - { - new Service(serviceConfig.ServiceName, new HostAndPort(serviceConfig.DownstreamHost, serviceConfig.DownstreamPort)) - }; - - return new ConfigurationServiceProvider(services); - } - } - - public class ServiceConfiguraion - { - public ServiceConfiguraion(string serviceName, string downstreamHost, int downstreamPort, bool useServiceDiscovery) - { - ServiceName = serviceName; - DownstreamHost = downstreamHost; - DownstreamPort = downstreamPort; - UseServiceDiscovery = useServiceDiscovery; - } - - public string ServiceName { get; } - public string DownstreamHost { get; } - public int DownstreamPort { get; } - public bool UseServiceDiscovery { get; } - } -} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/UnableToFindServiceDiscoveryProviderError.cs b/src/Ocelot/ServiceDiscovery/UnableToFindServiceDiscoveryProviderError.cs new file mode 100644 index 00000000..163e63ef --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/UnableToFindServiceDiscoveryProviderError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.ServiceDiscovery +{ + public class UnableToFindServiceDiscoveryProviderError : Error + { + public UnableToFindServiceDiscoveryProviderError(string message) + : base(message, OcelotErrorCode.UnableToFindServiceDiscoveryProviderError) + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/UnableToFindServiceProviderError.cs b/src/Ocelot/ServiceDiscovery/UnableToFindServiceProviderError.cs deleted file mode 100644 index b8ed1a47..00000000 --- a/src/Ocelot/ServiceDiscovery/UnableToFindServiceProviderError.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ocelot.Errors; - -namespace Ocelot.ServiceDiscovery -{ - public class UnableToFindServiceProviderError : Error - { - public UnableToFindServiceProviderError(string message) - : base(message, OcelotErrorCode.UnableToFindServiceProviderError) - { - } - } -} \ No newline at end of file diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs new file mode 100644 index 00000000..5994b86b --- /dev/null +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Ocelot.Configuration.File; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + public class ServiceDiscoveryTests : IDisposable + { + private IWebHost _builder; + private IWebHost _fakeConsulBuilder; + private readonly Steps _steps; + + public ServiceDiscoveryTests() + { + _steps = new Steps(); + } + + [Fact] + public void should_use_service_discovery_and_load_balance_request() + { + var serviceName = "product"; + var downstreamServiceOneUrl = "http://localhost:51879"; + var downstreamServiceTwoUrl = "http://localhost:51880"; + var fakeConsulServiceDiscoveryUrl = "http://localhost:9500"; + var downstreamServiceOneCounter = 0; + var downstreamServiceTwoCounter = 0; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamTemplate = "/", + UpstreamHttpMethod = "Get", + ServiceName = serviceName, + LoadBalancer = "LeastConnection", + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Provider = "Consul" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, 200, downstreamServiceOneCounter)) + .And(x => x.GivenThereIsAServiceRunningOn(downstreamServiceTwoUrl, 200, downstreamServiceTwoCounter)) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceName, downstreamServiceOneUrl, downstreamServiceTwoUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50)) + .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50, downstreamServiceOneCounter, downstreamServiceTwoCounter)) + .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(downstreamServiceOneCounter,downstreamServiceTwoCounter)) + .BDDfy(); + } + + private void ThenBothServicesCalledRealisticAmountOfTimes(int counterOne, int counterTwo) + { + counterOne.ShouldBeGreaterThan(10); + counterTwo.ShouldBeGreaterThan(10); + } + + private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected, int counterOne, int counterTwo) + { + var total = counterOne + counterTwo; + total.ShouldBe(expected); + } + + private void GivenTheServicesAreRegisteredWithConsul(string serviceName, params string[] urls) + { + //register these services with fake consul + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url) + { + _fakeConsulBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + //do consul shit + }); + }) + .Build(); + + _fakeConsulBuilder.Start(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode, int counter) + { + _builder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + counter++; + context.Response.StatusCode = statusCode; + }); + }) + .Build(); + + _builder.Start(); + } + + public void Dispose() + { + _builder?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index c2bd7ee7..666c3256 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading.Tasks; using CacheManager.Core; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; @@ -153,6 +154,18 @@ namespace Ocelot.AcceptanceTests _response = _ocelotClient.GetAsync(url).Result; } + public void WhenIGetUrlOnTheApiGatewayMultipleTimes(string url, int times) + { + var tasks = new Task[times]; + + for (int i = 0; i < times; i++) + { + tasks[i] = _ocelotClient.GetAsync(url); + } + + Task.WaitAll(tasks); + } + public void WhenIGetUrlOnTheApiGateway(string url, string requestId) { _ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation(RequestIdKey, requestId); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs index d645b1b6..e8e0210b 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs @@ -14,20 +14,20 @@ namespace Ocelot.UnitTests.LoadBalancer private ReRoute _reRoute; private LoadBalancerFactory _factory; private ILoadBalancer _result; - private Mock _serviceProviderFactory; - private Mock _serviceProvider; + private Mock _serviceProviderFactory; + private Mock _serviceProvider; public LoadBalancerFactoryTests() { - _serviceProviderFactory = new Mock(); - _serviceProvider = new Mock(); + _serviceProviderFactory = new Mock(); + _serviceProvider = new Mock(); _factory = new LoadBalancerFactory(_serviceProviderFactory.Object); } private void GivenTheServiceProviderFactoryReturns() { _serviceProviderFactory - .Setup(x => x.Get(It.IsAny())) + .Setup(x => x.Get(It.IsAny())) .Returns(_serviceProvider.Object); } @@ -89,7 +89,7 @@ namespace Ocelot.UnitTests.LoadBalancer private void ThenTheServiceProviderIsCalledCorrectly() { _serviceProviderFactory - .Verify(x => x.Get(It.IsAny()), Times.Once); + .Verify(x => x.Get(It.IsAny()), Times.Once); } private void GivenAReRoute(ReRoute reRoute) diff --git a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs index 9bcc2672..82e5cb73 100644 --- a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs @@ -7,19 +7,19 @@ namespace Ocelot.UnitTests.ServiceDiscovery { public class ServiceProviderFactoryTests { - private ServiceConfiguraion _serviceConfig; - private IServiceProvider _result; - private readonly ServiceProviderFactory _factory; + private ServiceProviderConfiguraion _serviceConfig; + private IServiceDiscoveryProvider _result; + private readonly ServiceDiscoveryProviderFactory _factory; public ServiceProviderFactoryTests() { - _factory = new ServiceProviderFactory(); + _factory = new ServiceDiscoveryProviderFactory(); } [Fact] public void should_return_no_service_provider() { - var serviceConfig = new ServiceConfiguraion("product", "127.0.0.1", 80, false); + var serviceConfig = new ServiceProviderConfiguraion("product", "127.0.0.1", 80, false, "Does not matter"); this.Given(x => x.GivenTheReRoute(serviceConfig)) .When(x => x.WhenIGetTheServiceProvider()) @@ -27,7 +27,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery .BDDfy(); } - private void GivenTheReRoute(ServiceConfiguraion serviceConfig) + private void GivenTheReRoute(ServiceProviderConfiguraion serviceConfig) { _serviceConfig = serviceConfig; }