From 982eebfc74217a5fef34321c97f91cd1afaa9bed Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Sun, 8 Apr 2018 15:54:58 +0100 Subject: [PATCH] Feature/#295 consul acl (#307) * removed file * updated package * updated package * updated package * updated package * updated package * updated package * updated package * all packages updated * #295 can add token to service provider config and this will be used by consul clients to get services and configuration * #295 wait longer for this test --- docs/features/servicediscovery.rst | 15 + .../ServiceProviderConfigurationBuilder.cs | 15 +- .../ServiceProviderConfigurationCreator.cs | 7 +- .../File/FileServiceDiscoveryProvider.cs | 1 + .../ConsulFileConfigurationRepository.cs | 24 +- .../ServiceProviderConfiguration.cs | 12 +- .../DependencyInjection/OcelotBuilder.cs | 8 +- .../Consul/ConsulClientFactory.cs | 22 + .../Consul/IConsulClientFactory.cs | 10 + src/Ocelot/Ocelot.csproj | 34 +- .../ConsulRegistryConfiguration.cs | 12 +- .../ConsulServiceDiscoveryProvider.cs | 18 +- .../ServiceDiscoveryProviderFactory.cs | 9 +- .../ButterflyTracingTests.cs | 2 +- test/Ocelot.AcceptanceTests/HeaderTests.cs | 18 +- .../Ocelot.AcceptanceTests.csproj | 28 +- .../ServiceDiscoveryTests.cs | 106 ++- test/Ocelot.AcceptanceTests/Steps.cs.orig | 722 ------------------ test/Ocelot.AcceptanceTests/WebSocketTests.cs | 11 +- .../Ocelot.Benchmarks.csproj | 2 +- .../Ocelot.IntegrationTests.csproj | 26 +- .../Ocelot.ManualTest.csproj | 22 +- .../ServiceProviderCreatorTests.cs | 25 +- .../DownstreamUrlCreatorMiddlewareTests.cs | 24 +- .../LoadBalancer/LoadBalancerHouseTests.cs | 2 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 24 +- .../ConsulServiceDiscoveryProviderTests.cs | 47 +- .../ServiceProviderFactoryTests.cs | 7 +- 28 files changed, 345 insertions(+), 908 deletions(-) create mode 100644 src/Ocelot/Infrastructure/Consul/ConsulClientFactory.cs create mode 100644 src/Ocelot/Infrastructure/Consul/IConsulClientFactory.cs delete mode 100644 test/Ocelot.AcceptanceTests/Steps.cs.orig diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst index f8cadd2e8..3add60f18 100644 --- a/docs/features/servicediscovery.rst +++ b/docs/features/servicediscovery.rst @@ -38,3 +38,18 @@ and LeastConnection algorithm you can use. If no load balancer is specified Ocel } When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services. + +ACL Token +--------- + +If you are using ACL with Consul Ocelot supports adding the X-Consul-Token header. In order so this to work you must add the additional property below. + +.. code-block:: json + + "ServiceDiscoveryProvider": { + "Host": "localhost", + "Port": 9500, + "Token": "footoken" + } + +Ocelot will add this token to the consul client that it uses to make requests and that is then used for every request. \ No newline at end of file diff --git a/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs b/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs index b1f4e8323..cd8446a5f 100644 --- a/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs @@ -5,28 +5,35 @@ public class ServiceProviderConfigurationBuilder private string _serviceDiscoveryProviderHost; private int _serviceDiscoveryProviderPort; private string _type; + private string _token; - public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost) + public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost) { _serviceDiscoveryProviderHost = serviceDiscoveryProviderHost; return this; } - public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort) + public ServiceProviderConfigurationBuilder WithPort(int serviceDiscoveryProviderPort) { _serviceDiscoveryProviderPort = serviceDiscoveryProviderPort; return this; } - public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderType(string type) + public ServiceProviderConfigurationBuilder WithType(string type) { _type = type; return this; } + public ServiceProviderConfigurationBuilder WithToken(string token) + { + _token = token; + return this; + } + public ServiceProviderConfiguration Build() { - return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort); + return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token); } } } diff --git a/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs index c104385a9..aa735f1ae 100644 --- a/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs @@ -11,9 +11,10 @@ public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfigu var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0; return new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host) - .WithServiceDiscoveryProviderPort(serviceProviderPort) - .WithServiceDiscoveryProviderType(globalConfiguration?.ServiceDiscoveryProvider?.Type) + .WithHost(globalConfiguration?.ServiceDiscoveryProvider?.Host) + .WithPort(serviceProviderPort) + .WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type) + .WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token) .Build(); } } diff --git a/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs b/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs index 203cc675b..9a96a6d35 100644 --- a/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs +++ b/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs @@ -5,5 +5,6 @@ public class FileServiceDiscoveryProvider public string Host {get;set;} public int Port { get; set; } public string Type { get; set; } + public string Token { get; set; } } } diff --git a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs index 10e99c105..7db04a099 100644 --- a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs @@ -4,8 +4,8 @@ using Consul; using Newtonsoft.Json; using Ocelot.Configuration.File; +using Ocelot.Infrastructure.Consul; using Ocelot.Responses; -using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery.Configuration; namespace Ocelot.Configuration.Repository @@ -13,31 +13,31 @@ namespace Ocelot.Configuration.Repository public class ConsulFileConfigurationRepository : IFileConfigurationRepository { private readonly ConsulClient _consul; - private string _ocelotConfiguration = "OcelotConfiguration"; + private const string OcelotConfiguration = "OcelotConfiguration"; private readonly Cache.IOcelotCache _cache; - public ConsulFileConfigurationRepository(Cache.IOcelotCache cache, ServiceProviderConfiguration serviceProviderConfig) + public ConsulFileConfigurationRepository( + Cache.IOcelotCache cache, + ServiceProviderConfiguration serviceProviderConfig, + IConsulClientFactory factory) { var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.Host) ? "localhost" : serviceProviderConfig?.Host; var consulPort = serviceProviderConfig?.Port ?? 8500; - var configuration = new ConsulRegistryConfiguration(consulHost, consulPort, _ocelotConfiguration); + var config = new ConsulRegistryConfiguration(consulHost, consulPort, OcelotConfiguration, serviceProviderConfig?.Token); _cache = cache; - _consul = new ConsulClient(c => - { - c.Address = new Uri($"http://{configuration.HostName}:{configuration.Port}"); - }); + _consul = factory.Get(config); } public async Task> Get() { - var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration); + var config = _cache.Get(OcelotConfiguration, OcelotConfiguration); if (config != null) { return new OkResponse(config); } - var queryResult = await _consul.KV.Get(_ocelotConfiguration); + var queryResult = await _consul.KV.Get(OcelotConfiguration); if (queryResult.Response == null) { @@ -59,7 +59,7 @@ public async Task Set(FileConfiguration ocelotConfiguration) var bytes = Encoding.UTF8.GetBytes(json); - var kvPair = new KVPair(_ocelotConfiguration) + var kvPair = new KVPair(OcelotConfiguration) { Value = bytes }; @@ -68,7 +68,7 @@ public async Task Set(FileConfiguration ocelotConfiguration) if (result.Response) { - _cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), _ocelotConfiguration); + _cache.AddAndDelete(OcelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), OcelotConfiguration); return new OkResponse(); } diff --git a/src/Ocelot/Configuration/ServiceProviderConfiguration.cs b/src/Ocelot/Configuration/ServiceProviderConfiguration.cs index aa7c492c2..129d24db9 100644 --- a/src/Ocelot/Configuration/ServiceProviderConfiguration.cs +++ b/src/Ocelot/Configuration/ServiceProviderConfiguration.cs @@ -2,15 +2,17 @@ { public class ServiceProviderConfiguration { - public ServiceProviderConfiguration(string type, string host, int port) + public ServiceProviderConfiguration(string type, string host, int port, string token) { Host = host; Port = port; + Token = token; Type = type; } - public string Host { get; private set; } - public int Port { get; private set; } - public string Type { get; private set; } + public string Host { get; } + public int Port { get; } + public string Type { get; } + public string Token { get; } } -} \ No newline at end of file +} diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 8ccb14595..c6fa20f4f 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -53,6 +53,7 @@ namespace Ocelot.DependencyInjection using System.Net.Http; using Butterfly.Client.AspNetCore; using Ocelot.Infrastructure; + using Ocelot.Infrastructure.Consul; public class OcelotBuilder : IOcelotBuilder { @@ -152,6 +153,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); + _services.TryAddSingleton(); } public IOcelotAdministrationBuilder AddAdministration(string path, string secret) @@ -236,10 +238,12 @@ public IOcelotBuilder AddStoreOcelotConfigurationInConsul() { var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0); var serviceDiscoveryHost = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Host", string.Empty); + var serviceDiscoveryToken = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Token", string.Empty); var config = new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderPort(serviceDiscoveryPort) - .WithServiceDiscoveryProviderHost(serviceDiscoveryHost) + .WithPort(serviceDiscoveryPort) + .WithHost(serviceDiscoveryHost) + .WithToken(serviceDiscoveryToken) .Build(); _services.AddSingleton(config); diff --git a/src/Ocelot/Infrastructure/Consul/ConsulClientFactory.cs b/src/Ocelot/Infrastructure/Consul/ConsulClientFactory.cs new file mode 100644 index 000000000..0e7f27823 --- /dev/null +++ b/src/Ocelot/Infrastructure/Consul/ConsulClientFactory.cs @@ -0,0 +1,22 @@ +using System; +using Consul; +using Ocelot.ServiceDiscovery.Configuration; + +namespace Ocelot.Infrastructure.Consul +{ + public class ConsulClientFactory : IConsulClientFactory + { + public ConsulClient Get(ConsulRegistryConfiguration config) + { + return new ConsulClient(c => + { + c.Address = new Uri($"http://{config.Host}:{config.Port}"); + + if (!string.IsNullOrEmpty(config?.Token)) + { + c.Token = config.Token; + } + }); + } + } +} diff --git a/src/Ocelot/Infrastructure/Consul/IConsulClientFactory.cs b/src/Ocelot/Infrastructure/Consul/IConsulClientFactory.cs new file mode 100644 index 000000000..27e413fc3 --- /dev/null +++ b/src/Ocelot/Infrastructure/Consul/IConsulClientFactory.cs @@ -0,0 +1,10 @@ +using Consul; +using Ocelot.ServiceDiscovery.Configuration; + +namespace Ocelot.Infrastructure.Consul +{ + public interface IConsulClientFactory + { + ConsulClient Get(ConsulRegistryConfiguration config); + } +} diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index 3d9b65c7c..68f1dc9f9 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -26,27 +26,27 @@ - - - - - - - - - - - + + + + + + + + + + + all - - - - - - + + + + + + diff --git a/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs b/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs index 9a9e5de82..13ae68d24 100644 --- a/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs +++ b/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs @@ -2,15 +2,17 @@ namespace Ocelot.ServiceDiscovery.Configuration { public class ConsulRegistryConfiguration { - public ConsulRegistryConfiguration(string hostName, int port, string keyOfServiceInConsul) + public ConsulRegistryConfiguration(string host, int port, string keyOfServiceInConsul, string token) { - HostName = hostName; + Host = host; Port = port; KeyOfServiceInConsul = keyOfServiceInConsul; + Token = token; } - public string KeyOfServiceInConsul { get; private set; } - public string HostName { get; private set; } - public int Port { get; private set; } + public string KeyOfServiceInConsul { get; } + public string Host { get; } + public int Port { get; } + public string Token { get; } } } diff --git a/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs b/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs index de31188e6..12c34cb20 100644 --- a/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs +++ b/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Consul; +using Ocelot.Infrastructure.Consul; using Ocelot.Infrastructure.Extensions; using Ocelot.Logging; using Ocelot.ServiceDiscovery.Configuration; @@ -12,30 +13,27 @@ namespace Ocelot.ServiceDiscovery.Providers { public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider { - private readonly ConsulRegistryConfiguration _consulConfig; + private readonly ConsulRegistryConfiguration _config; private readonly IOcelotLogger _logger; private readonly ConsulClient _consul; private const string VersionPrefix = "version-"; - public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration, IOcelotLoggerFactory factory) + public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration config, IOcelotLoggerFactory factory, IConsulClientFactory clientFactory) {; _logger = factory.CreateLogger(); - var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName; + var consulHost = string.IsNullOrEmpty(config?.Host) ? "localhost" : config.Host; - var consulPort = consulRegistryConfiguration?.Port ?? 8500; + var consulPort = config?.Port ?? 8500; - _consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul); + _config = new ConsulRegistryConfiguration(consulHost, consulPort, config?.KeyOfServiceInConsul, config?.Token); - _consul = new ConsulClient(config => - { - config.Address = new Uri($"http://{_consulConfig.HostName}:{_consulConfig.Port}"); - }); + _consul = clientFactory.Get(_config); } public async Task> Get() { - var queryResult = await _consul.Health.Service(_consulConfig.KeyOfServiceInConsul, string.Empty, true); + var queryResult = await _consul.Health.Service(_config.KeyOfServiceInConsul, string.Empty, true); var services = new List(); diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs index 95201dfb6..d13c333a2 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Ocelot.Configuration; +using Ocelot.Infrastructure.Consul; using Ocelot.Logging; using Ocelot.ServiceDiscovery.Configuration; using Ocelot.ServiceDiscovery.Providers; @@ -10,10 +11,12 @@ namespace Ocelot.ServiceDiscovery public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory { private readonly IOcelotLoggerFactory _factory; + private readonly IConsulClientFactory _clientFactory; - public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory) + public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory, IConsulClientFactory clientFactory) { _factory = factory; + _clientFactory = clientFactory; } public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute) @@ -43,8 +46,8 @@ private IServiceDiscoveryProvider GetServiceDiscoveryProvider(ServiceProviderCon return new ServiceFabricServiceDiscoveryProvider(config); } - var consulRegistryConfiguration = new ConsulRegistryConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName); - return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory); + var consulRegistryConfiguration = new ConsulRegistryConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName, serviceConfig.Token); + return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory, _clientFactory); } } } diff --git a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs index d634e6178..10cb79291 100644 --- a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs +++ b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs @@ -104,7 +104,7 @@ public void should_forward_tracing_information_from_ocelot_and_downstream_servic .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom")) .BDDfy(); - var commandOnAllStateMachines = WaitFor(5000).Until(() => _butterflyCalled == 4); + var commandOnAllStateMachines = WaitFor(10000).Until(() => _butterflyCalled == 4); commandOnAllStateMachines.ShouldBeTrue(); } diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs index 2bd3c2ab0..b964161a2 100644 --- a/test/Ocelot.AcceptanceTests/HeaderTests.cs +++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs @@ -16,7 +16,6 @@ namespace Ocelot.AcceptanceTests public class HeaderTests : IDisposable { private IWebHost _builder; - private string _cookieValue; private int _count; private readonly Steps _steps; @@ -278,26 +277,27 @@ private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int .Configure(app => { app.UsePathBase(basePath); - app.Run(async context => - { - if(_count == 0) + app.Run(context => + { + if (_count == 0) { context.Response.Cookies.Append("test", "0"); _count++; context.Response.StatusCode = statusCode; - return; - } + return Task.CompletedTask; + } - if(context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue)) + if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue)) { - if(cookieValue == "0" || headerValue == "test=1; path=/") + if (cookieValue == "0" || headerValue == "test=1; path=/") { context.Response.StatusCode = statusCode; - return; + return Task.CompletedTask; } } context.Response.StatusCode = 500; + return Task.CompletedTask; }); }) .Build(); diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 90d5bc641..4f67b970d 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -27,26 +27,26 @@ - - - - + + + + all - - - - - - - - + + + + + + + + - + - + diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs index 97e104c4c..972b165fb 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using Consul; using Microsoft.AspNetCore.Builder; @@ -22,9 +23,10 @@ public class ServiceDiscoveryTests : IDisposable private readonly List _serviceEntries; private int _counterOne; private int _counterTwo; - private static readonly object _syncLock = new object(); + private static readonly object SyncLock = new object(); private IWebHost _builder; private string _downstreamPath; + private string _receivedToken; public ServiceDiscoveryTests() { @@ -100,13 +102,13 @@ public void should_use_service_discovery_and_load_balance_request() .BDDfy(); } - //test from issue 213 + //test from issue #213 [Fact] public void should_handle_request_to_consul_for_downstream_service_and_make_request() { - var consulPort = 8505; - var serviceName = "web"; - var downstreamServiceOneUrl = "http://localhost:8080"; + const int consulPort = 8505; + const string serviceName = "web"; + const string downstreamServiceOneUrl = "http://localhost:8080"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() { @@ -116,7 +118,7 @@ public void should_handle_request_to_consul_for_downstream_service_and_make_requ Address = "localhost", Port = 8080, ID = "web_90_0_2_224_8080", - Tags = new string[1]{"version-v1"} + Tags = new[] {"version-v1"} }, }; @@ -145,14 +147,73 @@ public void should_handle_request_to_consul_for_downstream_service_and_make_requ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/home")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/home")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + //test from issue #295 + [Fact] + public void should_use_token_to_make_request_to_consul() + { + var token = "abctoken"; + var consulPort = 8515; + var serviceName = "web"; + var downstreamServiceOneUrl = "http://localhost:8081"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = 8081, + ID = "web_90_0_2_224_8080", + Tags = new[] { "version-v1" } + }, + }; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/api/home", + DownstreamScheme = "http", + UpstreamPathTemplate = "/home", + UpstreamHttpMethod = new List { "Get", "Options" }, + ServiceName = serviceName, + LoadBalancer = "LeastConnection", + UseServiceDiscovery = true, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = consulPort, + Token = token + } + } + }; + + this.Given(_ => GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) + .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(_ => _steps.GivenThereIsAConfiguration(configuration)) + .And(_ => _steps.GivenOcelotIsRunning()) + .When(_ => _steps.WhenIGetUrlOnTheApiGateway("/home")) + .Then(_ => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(_ => _receivedToken.ShouldBe(token)) .BDDfy(); } @@ -289,6 +350,11 @@ private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string { if(context.Request.Path.Value == $"/v1/health/service/{serviceName}") { + if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) + { + _receivedToken = values.First(); + } + await context.Response.WriteJsonAsync(_serviceEntries); } }); @@ -312,8 +378,8 @@ private void GivenProductServiceOneIsRunning(string url, int statusCode) { try { - var response = string.Empty; - lock (_syncLock) + string response; + lock (SyncLock) { _counterOne++; response = _counterOne.ToString(); @@ -321,7 +387,7 @@ private void GivenProductServiceOneIsRunning(string url, int statusCode) context.Response.StatusCode = statusCode; await context.Response.WriteAsync(response); } - catch (System.Exception exception) + catch (Exception exception) { await context.Response.WriteAsync(exception.StackTrace); } @@ -346,8 +412,8 @@ private void GivenProductServiceTwoIsRunning(string url, int statusCode) { try { - var response = string.Empty; - lock (_syncLock) + string response; + lock (SyncLock) { _counterTwo++; response = _counterTwo.ToString(); @@ -356,7 +422,7 @@ private void GivenProductServiceTwoIsRunning(string url, int statusCode) context.Response.StatusCode = statusCode; await context.Response.WriteAsync(response); } - catch (System.Exception exception) + catch (Exception exception) { await context.Response.WriteAsync(exception.StackTrace); } diff --git a/test/Ocelot.AcceptanceTests/Steps.cs.orig b/test/Ocelot.AcceptanceTests/Steps.cs.orig deleted file mode 100644 index 84c119ab0..000000000 --- a/test/Ocelot.AcceptanceTests/Steps.cs.orig +++ /dev/null @@ -1,722 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using CacheManager.Core; -using IdentityServer4.AccessTokenValidation; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Ocelot.Configuration.File; -using Ocelot.DependencyInjection; -using Ocelot.Middleware; -using Shouldly; -using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; -using Ocelot.AcceptanceTests.Caching; -<<<<<<< HEAD -using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests; -||||||| merged common ancestors -======= -using System.IO.Compression; -using System.Text; ->>>>>>> develop - -namespace Ocelot.AcceptanceTests -{ - public class Steps : IDisposable - { - private TestServer _ocelotServer; - private HttpClient _ocelotClient; - private HttpResponseMessage _response; - private HttpContent _postContent; - private BearerToken _token; - public HttpClient OcelotClient => _ocelotClient; - public string RequestIdKey = "OcRequestId"; - private readonly Random _random; - private IWebHostBuilder _webHostBuilder; - - public Steps() - { - _random = new Random(); - } - - public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = TestConfiguration.ConfigurationPath; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - } - - public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration, string configurationPath) - { - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - } - - /// - /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. - /// - public void GivenOcelotIsRunning() - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot(); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - internal void GivenOcelotIsRunningUsingButterfly(string butterflyUrl) - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot() - .AddOpenTracing(option => - { - //this is the url that the butterfly collector server is running on... - option.CollectorUrl = butterflyUrl; - option.Service = "Ocelot"; - }); - }) - .Configure(app => - { - app.Use(async (context, next) => - { - await next.Invoke(); - }); - app.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - -/* - public void GivenIHaveAddedXForwardedForHeader(string value) - { - _ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Forwarded-For", value); - }*/ - - public void GivenOcelotIsRunningWithMiddleareBeforePipeline(Func callback) - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot(); - }) - .Configure(app => - { - app.UseMiddleware(callback); - app.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - public void GivenOcelotIsRunningWithHandlersRegisteredInDi() - where TOne : DelegatingHandler - where TWo : DelegatingHandler - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddSingleton(_webHostBuilder); - s.AddOcelot() - .AddDelegatingHandler() - .AddDelegatingHandler(); - }) - .Configure(a => - { - a.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - public void GivenOcelotIsRunningWithHandlersRegisteredInDi(FakeDependency dependency) - where TOne : DelegatingHandler - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddSingleton(_webHostBuilder); - s.AddSingleton(dependency); - s.AddOcelot() - .AddDelegatingHandler(); - }) - .Configure(a => - { - a.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - /// - /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. - /// - public void GivenOcelotIsRunning(Action options, string authenticationProviderKey) - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot(); - s.AddAuthentication() - .AddIdentityServerAuthentication(authenticationProviderKey, options); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - public void ThenTheResponseHeaderIs(string key, string value) - { - var header = _response.Headers.GetValues(key); - header.First().ShouldBe(value); - } - - public void GivenOcelotIsRunningUsingJsonSerializedCache() - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot() - .AddCacheManager((x) => - { - x.WithMicrosoftLogging(log => - { - log.AddConsole(LogLevel.Debug); - }) - .WithJsonSerializer() - .WithHandle(typeof(InMemoryJsonHandle<>)); - }); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - public void GivenOcelotIsRunningUsingConsulToStoreConfig() - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot().AddStoreOcelotConfigurationInConsul(); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache() - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddJsonFile("configuration.json"); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot() - .AddCacheManager((x) => - { - x.WithMicrosoftLogging(log => - { - log.AddConsole(LogLevel.Debug); - }) - .WithJsonSerializer() - .WithHandle(typeof(InMemoryJsonHandle<>)); - }) - .AddStoreOcelotConfigurationInConsul(); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - - _ocelotServer = new TestServer(_webHostBuilder); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - internal void ThenTheResponseShouldBe(FileConfiguration expecteds) - { - var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); - - response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); - response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); - response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < response.ReRoutes.Count; i++) - { - for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++) - { - var result = response.ReRoutes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j]; - result.Host.ShouldBe(expected.Host); - result.Port.ShouldBe(expected.Port); - } - - response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate); - response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme); - response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate); - response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod); - } - } - - /// - /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. - /// - public void GivenOcelotIsRunning(OcelotPipelineConfiguration ocelotPipelineConfig) - { - var builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile("configuration.json") - .AddEnvironmentVariables(); - - var configuration = builder.Build(); - _webHostBuilder = new WebHostBuilder(); - _webHostBuilder.ConfigureServices(s => - { - s.AddSingleton(_webHostBuilder); - }); - - _ocelotServer = new TestServer(_webHostBuilder - .UseConfiguration(configuration) - .ConfigureServices(s => - { - Action settings = (x) => - { - x.WithMicrosoftLogging(log => - { - log.AddConsole(LogLevel.Debug); - }) - .WithDictionaryHandle(); - }; - - s.AddOcelot(configuration); - }) - .ConfigureLogging(l => - { - l.AddConsole(); - l.AddDebug(); - }) - .Configure(a => - { - a.UseOcelot(ocelotPipelineConfig).Wait(); - })); - - _ocelotClient = _ocelotServer.CreateClient(); - } - - public void GivenIHaveAddedATokenToMyRequest() - { - _ocelotClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - } - - public void GivenIHaveAToken(string url) - { - var tokenUrl = $"{url}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "client"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "api"), - new KeyValuePair("username", "test"), - new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") - }; - var content = new FormUrlEncodedContent(formData); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - } - } - - public void GivenIHaveATokenForApiReadOnlyScope(string url) - { - var tokenUrl = $"{url}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "client"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "api.readOnly"), - new KeyValuePair("username", "test"), - new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") - }; - var content = new FormUrlEncodedContent(formData); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - } - } - - public void GivenIHaveATokenForApi2(string url) - { - var tokenUrl = $"{url}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "client"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "api2"), - new KeyValuePair("username", "test"), - new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") - }; - var content = new FormUrlEncodedContent(formData); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - } - } - - public void GivenIHaveAnOcelotToken(string adminPath) - { - var tokenUrl = $"{adminPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "admin"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "admin"), - new KeyValuePair("username", "admin"), - new KeyValuePair("password", "admin"), - new KeyValuePair("grant_type", "password") - }; - var content = new FormUrlEncodedContent(formData); - - var response = _ocelotClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - } - - public void VerifyIdentiryServerStarted(string url) - { - using (var httpClient = new HttpClient()) - { - var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; - response.EnsureSuccessStatusCode(); - } - } - - public void WhenIGetUrlOnTheApiGateway(string url) - { - _response = _ocelotClient.GetAsync(url).Result; - } - - public void GivenIAddAHeader(string key, string value) - { - _ocelotClient.DefaultRequestHeaders.Add(key, value); - } - - public void WhenIGetUrlOnTheApiGatewayMultipleTimes(string url, int times) - { - var tasks = new Task[times]; - - for (int i = 0; i < times; i++) - { - var urlCopy = url; - tasks[i] = GetForServiceDiscoveryTest(urlCopy); - Thread.Sleep(_random.Next(40, 60)); - } - - Task.WaitAll(tasks); - } - - private async Task GetForServiceDiscoveryTest(string url) - { - var response = await _ocelotClient.GetAsync(url); - var content = await response.Content.ReadAsStringAsync(); - int count = int.Parse(content); - count.ShouldBeGreaterThan(0); - } - - public void WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit(string url, int times) - { - for (int i = 0; i < times; i++) - { - var clientId = "ocelotclient1"; - var request = new HttpRequestMessage(new HttpMethod("GET"), url); - request.Headers.Add("ClientId", clientId); - _response = _ocelotClient.SendAsync(request).Result; - } - } - - public void WhenIGetUrlOnTheApiGateway(string url, string requestId) - { - _ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation(RequestIdKey, requestId); - - _response = _ocelotClient.GetAsync(url).Result; - } - - public void WhenIPostUrlOnTheApiGateway(string url) - { - _response = _ocelotClient.PostAsync(url, _postContent).Result; - } - - public void GivenThePostHasContent(string postcontent) - { - _postContent = new StringContent(postcontent); - } - - public void GivenThePostHasGzipContent(object input) - { - string json = JsonConvert.SerializeObject(input); - byte[] jsonBytes = Encoding.UTF8.GetBytes(json); - MemoryStream ms = new MemoryStream(); - using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress, true)) - { - gzip.Write(jsonBytes, 0, jsonBytes.Length); - } - ms.Position = 0; - StreamContent content = new StreamContent(ms); - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - content.Headers.ContentEncoding.Add("gzip"); - _postContent = content; - } - - public void ThenTheResponseBodyShouldBe(string expectedBody) - { - _response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody); - } - - public void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) - { - _response.StatusCode.ShouldBe(expectedHttpStatusCode); - } - - public void ThenTheStatusCodeShouldBe(int expectedHttpStatusCode) - { - var responseStatusCode = (int)_response.StatusCode; - responseStatusCode.ShouldBe(expectedHttpStatusCode); - } - - public void Dispose() - { - _ocelotClient?.Dispose(); - _ocelotServer?.Dispose(); - } - - public void ThenTheRequestIdIsReturned() - { - _response.Headers.GetValues(RequestIdKey).First().ShouldNotBeNullOrEmpty(); - } - - public void ThenTheRequestIdIsReturned(string expected) - { - _response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected); - } - - public void ThenTheContentLengthIs(int expected) - { - _response.Content.Headers.ContentLength.ShouldBe(expected); - } - - public void WhenIMakeLotsOfDifferentRequestsToTheApiGateway() - { - int numberOfRequests = 100; - var aggregateUrl = "/"; - var aggregateExpected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}"; - var tomUrl = "/tom"; - var tomExpected = "{Hello from Tom}"; - var lauraUrl = "/laura"; - var lauraExpected = "{Hello from Laura}"; - var random = new Random(); - - var aggregateTasks = new Task[numberOfRequests]; - - for (int i = 0; i < numberOfRequests; i++) - { - aggregateTasks[i] = Fire(aggregateUrl, aggregateExpected, random); - } - - var tomTasks = new Task[numberOfRequests]; - - for (int i = 0; i < numberOfRequests; i++) - { - tomTasks[i] = Fire(tomUrl, tomExpected, random); - } - - var lauraTasks = new Task[numberOfRequests]; - - for (int i = 0; i < numberOfRequests; i++) - { - lauraTasks[i] = Fire(lauraUrl, lauraExpected, random); - } - - Task.WaitAll(lauraTasks); - Task.WaitAll(tomTasks); - Task.WaitAll(aggregateTasks); - } - - private async Task Fire(string url, string expectedBody, Random random) - { - var request = new HttpRequestMessage(new HttpMethod("GET"), url); - await Task.Delay(random.Next(0, 2)); - var response = await _ocelotClient.SendAsync(request); - var content = await response.Content.ReadAsStringAsync(); - content.ShouldBe(expectedBody); - } - } -} diff --git a/test/Ocelot.AcceptanceTests/WebSocketTests.cs b/test/Ocelot.AcceptanceTests/WebSocketTests.cs index 8137649a0..dce2f306b 100644 --- a/test/Ocelot.AcceptanceTests/WebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/WebSocketTests.cs @@ -37,7 +37,7 @@ public WebSocketTests() } [Fact] - public async Task should_proxy_websocket_input_to_downstream_service() + public void should_proxy_websocket_input_to_downstream_service() { var downstreamPort = 5001; var downstreamHost = "localhost"; @@ -72,7 +72,7 @@ public async Task should_proxy_websocket_input_to_downstream_service() } [Fact] - public async Task should_proxy_websocket_input_to_downstream_service_and_use_load_balancer() + public void should_proxy_websocket_input_to_downstream_service_and_use_load_balancer() { var downstreamPort = 5005; var downstreamHost = "localhost"; @@ -116,7 +116,7 @@ public async Task should_proxy_websocket_input_to_downstream_service_and_use_loa } [Fact] - public async Task should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer() + public void should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer() { var downstreamPort = 5007; var downstreamHost = "localhost"; @@ -274,7 +274,6 @@ await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, { _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); } - else if (result.MessageType == WebSocketMessageType.Close) { await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); @@ -321,7 +320,6 @@ await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, { _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); } - else if (result.MessageType == WebSocketMessageType.Close) { await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); @@ -333,7 +331,6 @@ await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, await Task.WhenAll(sending, receiving); } - private async Task StartFakeDownstreamService(string url, string path) { _firstDownstreamHost = new WebHostBuilder() @@ -380,7 +377,6 @@ private async Task StartFakeDownstreamService(string url, string path) await _firstDownstreamHost.StartAsync(); } - private async Task StartSecondFakeDownstreamService(string url, string path) { _secondDownstreamHost = new WebHostBuilder() @@ -427,7 +423,6 @@ private async Task StartSecondFakeDownstreamService(string url, string path) await _secondDownstreamHost.StartAsync(); } - private async Task Echo(WebSocket webSocket) { try diff --git a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj index 8c63914c9..7761dab13 100644 --- a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj +++ b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj @@ -19,7 +19,7 @@ - + all diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 8dc250b2d..644d217b5 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -25,26 +25,26 @@ - - + + all - - - - - - - + + + + + + + - - + + - + - + \ No newline at end of file diff --git a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj index 1e708c688..d2e3e7bcb 100644 --- a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj +++ b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj @@ -21,19 +21,19 @@ - + - - - - - - - - - - + + + + + + + + + + all diff --git a/test/Ocelot.UnitTests/Configuration/ServiceProviderCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ServiceProviderCreatorTests.cs index 8b0cc0789..214d6d544 100644 --- a/test/Ocelot.UnitTests/Configuration/ServiceProviderCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ServiceProviderCreatorTests.cs @@ -10,8 +10,7 @@ namespace Ocelot.UnitTests.Configuration { public class ServiceProviderCreatorTests { - private ServiceProviderConfigurationCreator _creator; - private FileReRoute _reRoute; + private readonly ServiceProviderConfigurationCreator _creator; private FileGlobalConfiguration _globalConfig; private ServiceProviderConfiguration _result; @@ -23,36 +22,30 @@ public ServiceProviderCreatorTests() [Fact] public void should_create_service_provider_config() { - var reRoute = new FileReRoute(); - var globalConfig = new FileGlobalConfiguration { ServiceDiscoveryProvider = new FileServiceDiscoveryProvider { Host = "127.0.0.1", Port = 1234, - Type = "ServiceFabric" + Type = "ServiceFabric", + Token = "testtoken" } }; var expected = new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderHost("127.0.0.1") - .WithServiceDiscoveryProviderPort(1234) - .WithServiceDiscoveryProviderType("ServiceFabric") + .WithHost("127.0.0.1") + .WithPort(1234) + .WithType("ServiceFabric") + .WithToken("testtoken") .Build(); - this.Given(x => x.GivenTheFollowingReRoute(reRoute)) - .And(x => x.GivenTheFollowingGlobalConfig(globalConfig)) + this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig)) .When(x => x.WhenICreate()) .Then(x => x.ThenTheConfigIs(expected)) .BDDfy(); } - private void GivenTheFollowingReRoute(FileReRoute fileReRoute) - { - _reRoute = fileReRoute; - } - private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration fileGlobalConfig) { _globalConfig = fileGlobalConfig; @@ -67,6 +60,8 @@ private void ThenTheConfigIs(ServiceProviderConfiguration expected) { _result.Host.ShouldBe(expected.Host); _result.Port.ShouldBe(expected.Port); + _result.Token.ShouldBe(expected.Token); + _result.Type.ShouldBe(expected.Type); } } } diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 571771260..636174ebf 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -82,9 +82,9 @@ public void should_not_create_service_fabric_url() .Build(); var config = new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderType("ServiceFabric") - .WithServiceDiscoveryProviderHost("localhost") - .WithServiceDiscoveryProviderPort(19081) + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) .Build(); this.Given(x => x.GivenTheDownStreamRouteIs( @@ -118,9 +118,9 @@ public void should_create_service_fabric_url() .Build()); var config = new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderType("ServiceFabric") - .WithServiceDiscoveryProviderHost("localhost") - .WithServiceDiscoveryProviderPort(19081) + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) .Build(); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) @@ -148,9 +148,9 @@ public void should_create_service_fabric_url_with_query_string_for_stateless_ser .Build()); var config = new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderType("ServiceFabric") - .WithServiceDiscoveryProviderHost("localhost") - .WithServiceDiscoveryProviderPort(19081) + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) .Build(); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) @@ -178,9 +178,9 @@ public void should_create_service_fabric_url_with_query_string_for_stateful_serv .Build()); var config = new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderType("ServiceFabric") - .WithServiceDiscoveryProviderHost("localhost") - .WithServiceDiscoveryProviderPort(19081) + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) .Build(); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs index 1e30a0d10..cdd63d5af 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs @@ -25,7 +25,7 @@ public LoadBalancerHouseTests() { _factory = new Mock(); _loadBalancerHouse = new LoadBalancerHouse(_factory.Object); - _serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123); + _serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123, string.Empty); } [Fact] diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 89af54d8c..8df6245a1 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -35,23 +35,23 @@ - - - + + + all - - - - - - - - - + + + + + + + + + diff --git a/test/Ocelot.UnitTests/ServiceDiscovery/ConsulServiceDiscoveryProviderTests.cs b/test/Ocelot.UnitTests/ServiceDiscovery/ConsulServiceDiscoveryProviderTests.cs index 299da57f3..b60330d89 100644 --- a/test/Ocelot.UnitTests/ServiceDiscovery/ConsulServiceDiscoveryProviderTests.cs +++ b/test/Ocelot.UnitTests/ServiceDiscovery/ConsulServiceDiscoveryProviderTests.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; +using System.Linq; using Consul; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Moq; +using Ocelot.Infrastructure.Consul; using Ocelot.Logging; -using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery.Configuration; using Ocelot.ServiceDiscovery.Providers; using Ocelot.Values; @@ -22,14 +22,16 @@ public class ConsulServiceDiscoveryProviderTests : IDisposable { private IWebHost _fakeConsulBuilder; private readonly List _serviceEntries; - private readonly ConsulServiceDiscoveryProvider _provider; + private ConsulServiceDiscoveryProvider _provider; private readonly string _serviceName; private readonly int _port; private readonly string _consulHost; private readonly string _fakeConsulServiceDiscoveryUrl; private List _services; - private Mock _factory; + private readonly Mock _factory; private readonly Mock _logger; + private string _receivedToken; + private IConsulClientFactory _clientFactory; public ConsulServiceDiscoveryProviderTests() { @@ -40,11 +42,12 @@ public ConsulServiceDiscoveryProviderTests() _serviceEntries = new List(); _factory = new Mock(); + _clientFactory = new ConsulClientFactory(); _logger = new Mock(); _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName); - _provider = new ConsulServiceDiscoveryProvider(config, _factory.Object); + var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName, null); + _provider = new ConsulServiceDiscoveryProvider(config, _factory.Object, _clientFactory); } [Fact] @@ -69,6 +72,33 @@ public void should_return_service_from_consul() .BDDfy(); } + [Fact] + public void should_use_token() + { + var token = "test token"; + var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName, token); + _provider = new ConsulServiceDiscoveryProvider(config, _factory.Object, _clientFactory); + + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .When(_ => WhenIGetTheServices()) + .Then(_ => ThenTheCountIs(1)) + .And(_ => _receivedToken.ShouldBe(token)) + .BDDfy(); + } + [Fact] public void should_not_return_services_with_invalid_address() { @@ -197,6 +227,11 @@ private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string { if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") { + if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) + { + _receivedToken = values.First(); + } + await context.Response.WriteJsonAsync(_serviceEntries); } }); diff --git a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs index 2eef9b785..3e998431d 100644 --- a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs @@ -3,6 +3,7 @@ using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; +using Ocelot.Infrastructure.Consul; using Ocelot.Logging; using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery.Providers; @@ -19,11 +20,13 @@ public class ServiceProviderFactoryTests private readonly ServiceDiscoveryProviderFactory _factory; private DownstreamReRoute _reRoute; private Mock _loggerFactory; + private IConsulClientFactory _clientFactory; public ServiceProviderFactoryTests() { _loggerFactory = new Mock(); - _factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object); + _clientFactory = new ConsulClientFactory(); + _factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, _clientFactory); } [Fact] @@ -87,7 +90,7 @@ public void should_return_service_fabric_provider() .Build(); var serviceConfig = new ServiceProviderConfigurationBuilder() - .WithServiceDiscoveryProviderType("ServiceFabric") + .WithType("ServiceFabric") .Build(); this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))