diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index d09d738c..24c4db72 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -7,6 +7,7 @@ using Ocelot.Configuration.File; using Ocelot.Configuration.Parser; using Ocelot.Configuration.Validator; using Ocelot.Responses; +using Ocelot.ServiceDiscovery; using Ocelot.Utilities; using Ocelot.Values; @@ -104,7 +105,22 @@ namespace Ocelot.Configuration.Creator //ideal world we would get the host and port, then make the request using it, then release the connection to the lb - Func downstreamHostAndPortFunc = () => new HostAndPort(reRoute.DownstreamHost.Trim('/'), reRoute.DownstreamPort); + Func downstreamHostAndPortFunc = () => { + + //service provider factory takes the reRoute + //can return no service provider (just use ocelot config) + //can return consol service provider + //returns a service provider + //we call get on the service provider + //could reutrn services from consol or just configuration.json + //this returns a list of services and we take the first one + var hostAndPort = new HostAndPort(reRoute.DownstreamHost.Trim('/'), reRoute.DownstreamPort); + var services = new List(); + var serviceProvider = new NoServiceProvider(services); + var service = serviceProvider.Get(); + var firstHostAndPort = service[0].HostAndPort; + return firstHostAndPort; + }; if (isAuthenticated) { diff --git a/src/Ocelot/ServiceDiscovery/IServiceProvider.cs b/src/Ocelot/ServiceDiscovery/IServiceProvider.cs new file mode 100644 index 00000000..60e428c8 --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/IServiceProvider.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Ocelot.Values; + +namespace Ocelot.ServiceDiscovery +{ + public interface IServiceProvider + { + List Get(); + } +} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/IServiceProviderFactory.cs b/src/Ocelot/ServiceDiscovery/IServiceProviderFactory.cs new file mode 100644 index 00000000..0043edaa --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/IServiceProviderFactory.cs @@ -0,0 +1,9 @@ +using Ocelot.Configuration; + +namespace Ocelot.ServiceDiscovery +{ + public interface IServiceProviderFactory + { + Ocelot.ServiceDiscovery.IServiceProvider Get(ReRoute reRoute); + } +} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/NoServiceProvider.cs b/src/Ocelot/ServiceDiscovery/NoServiceProvider.cs new file mode 100644 index 00000000..9f8b0239 --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/NoServiceProvider.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Ocelot.Values; + +namespace Ocelot.ServiceDiscovery +{ + public class NoServiceProvider : IServiceProvider + { + private List _services; + + public NoServiceProvider(List services) + { + _services = services; + } + + public List Get() + { + return _services; + } + } +} \ No newline at end of file diff --git a/src/Ocelot/ServiceDiscovery/ServiceProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceProviderFactory.cs new file mode 100644 index 00000000..583774e7 --- /dev/null +++ b/src/Ocelot/ServiceDiscovery/ServiceProviderFactory.cs @@ -0,0 +1,13 @@ +using System; +using Ocelot.Configuration; + +namespace Ocelot.ServiceDiscovery +{ + public class ServiceProviderFactory : IServiceProviderFactory + { + public Ocelot.ServiceDiscovery.IServiceProvider Get(ReRoute reRoute) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Values/Service.cs b/src/Ocelot/Values/Service.cs new file mode 100644 index 00000000..104fbc09 --- /dev/null +++ b/src/Ocelot/Values/Service.cs @@ -0,0 +1,13 @@ +namespace Ocelot.Values +{ + public class Service + { + public Service(string name, HostAndPort hostAndPort) + { + Name = name; + HostAndPort = hostAndPort; + } + public string Name {get; private set;} + public HostAndPort HostAndPort {get; private set;} + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/LeastConnectionTests.cs b/test/Ocelot.UnitTests/LeastConnectionTests.cs index 758d1917..ede2105f 100644 --- a/test/Ocelot.UnitTests/LeastConnectionTests.cs +++ b/test/Ocelot.UnitTests/LeastConnectionTests.cs @@ -14,7 +14,7 @@ namespace Ocelot.UnitTests { private HostAndPort _hostAndPort; private Response _result; - private LeastConnection _leastConnection; + private LeastConnectionLoadBalancer _leastConnection; private List _services; public LeastConnectionTests() @@ -53,7 +53,7 @@ namespace Ocelot.UnitTests }; _services = availableServices; - _leastConnection = new LeastConnection(() => _services, serviceName); + _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); var response = _leastConnection.Lease(); @@ -80,7 +80,7 @@ namespace Ocelot.UnitTests }; _services = availableServices; - _leastConnection = new LeastConnection(() => _services, serviceName); + _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); var response = _leastConnection.Lease(); @@ -111,7 +111,7 @@ namespace Ocelot.UnitTests }; _services = availableServices; - _leastConnection = new LeastConnection(() => _services, serviceName); + _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); var response = _leastConnection.Lease(); @@ -178,7 +178,7 @@ namespace Ocelot.UnitTests private void GivenTheLoadBalancerStarts(List services, string serviceName) { _services = services; - _leastConnection = new LeastConnection(() => _services, serviceName); + _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); } private void WhenTheLoadBalancerStarts(List services, string serviceName) @@ -203,13 +203,13 @@ namespace Ocelot.UnitTests } } - public class LeastConnection : ILoadBalancer + public class LeastConnectionLoadBalancer : ILoadBalancer { private Func> _services; private List _leases; private string _serviceName; - public LeastConnection(Func> services, string serviceName) + public LeastConnectionLoadBalancer(Func> services, string serviceName) { _services = services; _serviceName = serviceName; diff --git a/test/Ocelot.UnitTests/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancerFactoryTests.cs index 6d2a3a59..12f59ceb 100644 --- a/test/Ocelot.UnitTests/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancerFactoryTests.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Responses; @@ -14,10 +17,12 @@ namespace Ocelot.UnitTests private ReRoute _reRoute; private LoadBalancerFactory _factory; private ILoadBalancer _result; - + private Mock _serviceProvider; + public LoadBalancerFactoryTests() { - _factory = new LoadBalancerFactory(); + _serviceProvider = new Mock(); + _factory = new LoadBalancerFactory(_serviceProvider.Object); } [Fact] @@ -36,8 +41,8 @@ namespace Ocelot.UnitTests public void should_return_round_robin_load_balancer() { var reRoute = new ReRouteBuilder() - .WithLoadBalancer("RoundRobin") - .Build(); + .WithLoadBalancer("RoundRobin") + .Build(); this.Given(x => x.GivenAReRoute(reRoute)) .When(x => x.WhenIGetTheLoadBalancer()) @@ -45,6 +50,38 @@ namespace Ocelot.UnitTests .BDDfy(); } + [Fact] + public void should_return_round_least_connection_balancer() + { + var reRoute = new ReRouteBuilder() + .WithLoadBalancer("LeastConnection") + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_call_service_provider() + { + var reRoute = new ReRouteBuilder() + .WithLoadBalancer("RoundRobin") + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheServiceProviderIsCalledCorrectly(reRoute)) + .BDDfy(); + } + + private void ThenTheServiceProviderIsCalledCorrectly(ReRoute reRoute) + { + _serviceProvider + .Verify(x => x.Get(), Times.Once); + } + private void GivenAReRoute(ReRoute reRoute) { _reRoute = reRoute; @@ -61,16 +98,62 @@ namespace Ocelot.UnitTests } } - public class NoLoadBalancer : ILoadBalancer + public class NoLoadBalancerTests { - Response ILoadBalancer.Lease() + private List _services; + private NoLoadBalancer _loadBalancer; + private Response _result; + + [Fact] + public void should_return_host_and_port() { - throw new NotImplementedException(); + var hostAndPort = new HostAndPort("127.0.0.1", 80); + + var services = new List + { + new Service("product", hostAndPort) + }; + this.Given(x => x.GivenServices(services)) + .When(x => x.WhenIGetTheNextHostAndPort()) + .Then(x => x.ThenTheHostAndPortIs(hostAndPort)) + .BDDfy(); } - Response ILoadBalancer.Release(HostAndPort hostAndPort) + private void GivenServices(List services) { - throw new NotImplementedException(); + _services = services; + } + + private void WhenIGetTheNextHostAndPort() + { + _loadBalancer = new NoLoadBalancer(_services); + _result = _loadBalancer.Lease(); + } + + private void ThenTheHostAndPortIs(HostAndPort expected) + { + _result.Data.ShouldBe(expected); + } + } + + public class NoLoadBalancer : ILoadBalancer + { + private List _services; + + public NoLoadBalancer(List services) + { + _services = services; + } + + public Response Lease() + { + var service = _services.FirstOrDefault(); + return new OkResponse(service.HostAndPort); + } + + public Response Release(HostAndPort hostAndPort) + { + return new OkResponse(); } } @@ -81,14 +164,23 @@ namespace Ocelot.UnitTests public class LoadBalancerFactory : ILoadBalancerFactory { + private Ocelot.ServiceDiscovery.IServiceProvider _serviceProvider; + + public LoadBalancerFactory(Ocelot.ServiceDiscovery.IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + public ILoadBalancer Get(ReRoute reRoute) { switch (reRoute.LoadBalancer) { case "RoundRobin": - return new RoundRobinLoadBalancer(null); + return new RoundRobinLoadBalancer(_serviceProvider.Get()); + case "LeastConnection": + return new LeastConnectionLoadBalancer(() => _serviceProvider.Get(), reRoute.ServiceName); default: - return new NoLoadBalancer(); + return new NoLoadBalancer(_serviceProvider.Get()); } } } diff --git a/test/Ocelot.UnitTests/NoServiceProviderTests.cs b/test/Ocelot.UnitTests/NoServiceProviderTests.cs new file mode 100644 index 00000000..f85ef13c --- /dev/null +++ b/test/Ocelot.UnitTests/NoServiceProviderTests.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using Ocelot.Configuration; +using Ocelot.ServiceDiscovery; +using Ocelot.Values; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests +{ + public class NoServiceProviderTests + { + private NoServiceProvider _serviceProvider; + private HostAndPort _hostAndPort; + private List _result; + private List _expected; + + [Fact] + public void should_return_services() + { + var hostAndPort = new HostAndPort("127.0.0.1", 80); + + var services = new List + { + new Service("product", hostAndPort) + }; + + this.Given(x => x.GivenAHostAndPort(services)) + .When(x => x.WhenIGetTheService()) + .Then(x => x.ThenTheFollowingIsReturned(services)) + .BDDfy(); + } + + private void GivenAHostAndPort(List services) + { + _expected = services; + } + + private void WhenIGetTheService() + { + _serviceProvider = new NoServiceProvider(_expected); + _result = _serviceProvider.Get(); + } + + private void ThenTheFollowingIsReturned(List services) + { + _result[0].HostAndPort.DownstreamHost.ShouldBe(services[0].HostAndPort.DownstreamHost); + + _result[0].HostAndPort.DownstreamPort.ShouldBe(services[0].HostAndPort.DownstreamPort); + + _result[0].Name.ShouldBe(services[0].Name); + } + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/RoundRobinTests.cs b/test/Ocelot.UnitTests/RoundRobinTests.cs index f1271460..39d33441 100644 --- a/test/Ocelot.UnitTests/RoundRobinTests.cs +++ b/test/Ocelot.UnitTests/RoundRobinTests.cs @@ -11,19 +11,19 @@ namespace Ocelot.UnitTests public class RoundRobinTests { private readonly RoundRobinLoadBalancer _roundRobin; - private readonly List _hostAndPorts; + private readonly List _services; private Response _hostAndPort; public RoundRobinTests() { - _hostAndPorts = new List + _services = new List { - new HostAndPort("127.0.0.1", 5000), - new HostAndPort("127.0.0.1", 5001), - new HostAndPort("127.0.0.1", 5001) + new Service("product", new HostAndPort("127.0.0.1", 5000)), + new Service("product", new HostAndPort("127.0.0.1", 5001)), + new Service("product", new HostAndPort("127.0.0.1", 5001)) }; - _roundRobin = new RoundRobinLoadBalancer(_hostAndPorts); + _roundRobin = new RoundRobinLoadBalancer(_services); } [Fact] @@ -46,11 +46,11 @@ namespace Ocelot.UnitTests while (stopWatch.ElapsedMilliseconds < 1000) { var address = _roundRobin.Lease(); - address.Data.ShouldBe(_hostAndPorts[0]); + address.Data.ShouldBe(_services[0].HostAndPort); address = _roundRobin.Lease(); - address.Data.ShouldBe(_hostAndPorts[1]); + address.Data.ShouldBe(_services[1].HostAndPort); address = _roundRobin.Lease(); - address.Data.ShouldBe(_hostAndPorts[2]); + address.Data.ShouldBe(_services[2].HostAndPort); } } @@ -61,7 +61,7 @@ namespace Ocelot.UnitTests private void ThenTheNextAddressIndexIs(int index) { - _hostAndPort.Data.ShouldBe(_hostAndPorts[index]); + _hostAndPort.Data.ShouldBe(_services[index].HostAndPort); } } @@ -73,24 +73,24 @@ namespace Ocelot.UnitTests public class RoundRobinLoadBalancer : ILoadBalancer { - private readonly List _hostAndPorts; + private readonly List _services; private int _last; - public RoundRobinLoadBalancer(List hostAndPorts) + public RoundRobinLoadBalancer(List services) { - _hostAndPorts = hostAndPorts; + _services = services; } public Response Lease() { - if (_last >= _hostAndPorts.Count) + if (_last >= _services.Count) { _last = 0; } - var next = _hostAndPorts[_last]; + var next = _services[_last]; _last++; - return new OkResponse(next); + return new OkResponse(next.HostAndPort); } public Response Release(HostAndPort hostAndPort) diff --git a/test/Ocelot.UnitTests/ServiceProviderFactoryTests.cs b/test/Ocelot.UnitTests/ServiceProviderFactoryTests.cs new file mode 100644 index 00000000..05609816 --- /dev/null +++ b/test/Ocelot.UnitTests/ServiceProviderFactoryTests.cs @@ -0,0 +1,50 @@ +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.ServiceDiscovery; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests +{ + public class ServiceProviderFactoryTests + { + private ReRoute _reRote; + private IServiceProvider _result; + private ServiceProviderFactory _factory; + + public ServiceProviderFactoryTests() + { + _factory = new ServiceProviderFactory(); + } + + [Fact] + public void should_return_no_service_provider() + { + var reRoute = new ReRouteBuilder() + .WithDownstreamHost("127.0.0.1") + .WithDownstreamPort(80) + .Build(); + + this.Given(x => x.GivenTheReRoute(reRoute)) + .When(x => x.WhenIGetTheServiceProvider()) + .Then(x => x.ThenTheServiceProviderIs()) + .BDDfy(); + } + + private void GivenTheReRoute(ReRoute reRoute) + { + _reRote = reRoute; + } + + private void WhenIGetTheServiceProvider() + { + _result = _factory.Get(_reRote); + } + + private void ThenTheServiceProviderIs() + { + _result.ShouldBeOfType(); + } + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/ServiceRegistryTests.cs b/test/Ocelot.UnitTests/ServiceRegistryTests.cs index 27987d42..09012213 100644 --- a/test/Ocelot.UnitTests/ServiceRegistryTests.cs +++ b/test/Ocelot.UnitTests/ServiceRegistryTests.cs @@ -86,6 +86,7 @@ namespace Ocelot.UnitTests { _repository = repository; } + public void Register(Service serviceNameAndAddress) { _repository.Set(serviceNameAndAddress); @@ -97,17 +98,6 @@ namespace Ocelot.UnitTests } } - public class Service - { - public Service(string name, HostAndPort hostAndPort) - { - Name = name; - HostAndPort = hostAndPort; - } - public string Name {get; private set;} - public HostAndPort HostAndPort {get; private set;} - } - public interface IServiceRepository { List Get(string serviceName);