mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 18:28:18 +08:00
This commit is contained in:
@ -1,469 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Consul;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using static Ocelot.Infrastructure.Wait;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
using Cache;
|
||||
|
||||
public class ConfigurationInConsulTests : IDisposable
|
||||
{
|
||||
private IWebHost _builder;
|
||||
private readonly Steps _steps;
|
||||
private IWebHost _fakeConsulBuilder;
|
||||
private FileConfiguration _config;
|
||||
private List<ServiceEntry> _consulServices;
|
||||
|
||||
public ConfigurationInConsulTests()
|
||||
{
|
||||
_consulServices = new List<ServiceEntry>();
|
||||
_steps = new Steps();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_with_simple_url()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51779,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 9500
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fakeConsulServiceDiscoveryUrl = "http://localhost:9500";
|
||||
|
||||
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, ""))
|
||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_load_configuration_out_of_consul()
|
||||
{
|
||||
var consulPort = 8500;
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
|
||||
var consulConfig = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/status",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51779,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/cs/status",
|
||||
UpstreamHttpMethod = new List<string> {"Get"}
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
|
||||
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, ""))
|
||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_load_configuration_out_of_consul_if_it_is_changed()
|
||||
{
|
||||
var consulPort = 8506;
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
|
||||
var consulConfig = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/status",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51780,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/cs/status",
|
||||
UpstreamHttpMethod = new List<string> {"Get"}
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var secondConsulConfig = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/status",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51780,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/cs/status/awesome",
|
||||
UpstreamHttpMethod = new List<string> {"Get"}
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
|
||||
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, ""))
|
||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
|
||||
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))
|
||||
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.When(x => GivenTheConsulConfigurationIs(secondConsulConfig))
|
||||
.Then(x => ThenTheConfigIsUpdatedInOcelot())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes_and_rate_limit()
|
||||
{
|
||||
const int consulPort = 8523;
|
||||
const string serviceName = "web";
|
||||
const int downstreamServicePort = 8187;
|
||||
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = downstreamServicePort,
|
||||
ID = "web_90_0_2_224_8080",
|
||||
Tags = new[] {"version-v1"}
|
||||
},
|
||||
};
|
||||
|
||||
var consulConfig = new FileConfiguration
|
||||
{
|
||||
DynamicReRoutes = new List<FileDynamicReRoute>
|
||||
{
|
||||
new FileDynamicReRoute
|
||||
{
|
||||
ServiceName = serviceName,
|
||||
RateLimitRule = new FileRateLimitRule()
|
||||
{
|
||||
EnableRateLimiting = true,
|
||||
ClientWhitelist = new List<string>(),
|
||||
Limit = 3,
|
||||
Period = "1s",
|
||||
PeriodTimespan = 1000
|
||||
}
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
},
|
||||
RateLimitOptions = new FileRateLimitOptions()
|
||||
{
|
||||
ClientIdHeader = "ClientId",
|
||||
DisableRateLimitHeaders = false,
|
||||
QuotaExceededMessage = "",
|
||||
RateLimitCounterPrefix = "",
|
||||
HttpStatusCode = 428
|
||||
},
|
||||
DownstreamScheme = "http",
|
||||
}
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura"))
|
||||
.And(x => GivenTheConsulConfigurationIs(consulConfig))
|
||||
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
|
||||
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something",1))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(200))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 2))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(200))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something",1))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(428))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheConfigIsUpdatedInOcelot()
|
||||
{
|
||||
var result = WaitFor(20000).Until(() => {
|
||||
try
|
||||
{
|
||||
_steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome");
|
||||
_steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK);
|
||||
_steps.ThenTheResponseBodyShouldBe("Hello from Laura");
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
result.ShouldBeTrue();
|
||||
}
|
||||
|
||||
private void GivenTheConsulConfigurationIs(FileConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
|
||||
{
|
||||
foreach(var serviceEntry in serviceEntries)
|
||||
{
|
||||
_consulServices.Add(serviceEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
|
||||
{
|
||||
_fakeConsulBuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Run(async context =>
|
||||
{
|
||||
if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration")
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(_config);
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
|
||||
var base64 = Convert.ToBase64String(bytes);
|
||||
|
||||
var kvp = new FakeConsulGetResponse(base64);
|
||||
|
||||
await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp });
|
||||
}
|
||||
else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration")
|
||||
{
|
||||
try
|
||||
{
|
||||
var reader = new StreamReader(context.Request.Body);
|
||||
|
||||
var json = reader.ReadToEnd();
|
||||
|
||||
_config = JsonConvert.DeserializeObject<FileConfiguration>(json);
|
||||
|
||||
var response = JsonConvert.SerializeObject(true);
|
||||
|
||||
await context.Response.WriteAsync(response);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
|
||||
{
|
||||
await context.Response.WriteJsonAsync(_consulServices);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_fakeConsulBuilder.Start();
|
||||
}
|
||||
|
||||
public class FakeConsulGetResponse
|
||||
{
|
||||
public FakeConsulGetResponse(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public int CreateIndex => 100;
|
||||
public int ModifyIndex => 200;
|
||||
public int LockIndex => 200;
|
||||
public string Key => "InternalConfiguration";
|
||||
public int Flags => 0;
|
||||
public string Value { get; private set; }
|
||||
public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e";
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_builder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UsePathBase(basePath);
|
||||
|
||||
app.Run(async context =>
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_builder.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_builder?.Dispose();
|
||||
_steps.Dispose();
|
||||
}
|
||||
|
||||
class FakeCache : IOcelotCache<FileConfiguration>
|
||||
{
|
||||
public void Add(string key, FileConfiguration value, TimeSpan ttl, string region)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void AddAndDelete(string key, FileConfiguration value, TimeSpan ttl, string region)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FileConfiguration Get(string key, string region)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ClearRegion(string region)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -50,7 +50,6 @@
|
||||
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
|
||||
<PackageReference Include="Shouldly" Version="3.0.0" />
|
||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.5" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
||||
<PackageReference Include="Rafty" Version="0.4.4" />
|
||||
|
@ -2,12 +2,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Consul;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Newtonsoft.Json;
|
||||
@ -16,20 +13,13 @@ namespace Ocelot.AcceptanceTests
|
||||
public class ServiceDiscoveryTests : IDisposable
|
||||
{
|
||||
private readonly Steps _steps;
|
||||
private readonly List<ServiceEntry> _consulServices;
|
||||
private readonly List<IServiceInstance> _eurekaInstances;
|
||||
private int _counterOne;
|
||||
private int _counterTwo;
|
||||
private static readonly object SyncLock = new object();
|
||||
private string _downstreamPath;
|
||||
private string _receivedToken;
|
||||
private readonly ServiceHandler _serviceHandler;
|
||||
|
||||
public ServiceDiscoveryTests()
|
||||
{
|
||||
_serviceHandler = new ServiceHandler();
|
||||
_steps = new Steps();
|
||||
_consulServices = new List<ServiceEntry>();
|
||||
_eurekaInstances = new List<IServiceInstance>();
|
||||
}
|
||||
|
||||
@ -80,479 +70,6 @@ namespace Ocelot.AcceptanceTests
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_use_consul_service_discovery_and_load_balance_request()
|
||||
{
|
||||
var consulPort = 8502;
|
||||
var serviceName = "product";
|
||||
var downstreamServiceOneUrl = "http://localhost:50881";
|
||||
var downstreamServiceTwoUrl = "http://localhost:50882";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = 50881,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
var serviceEntryTwo = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = 50882,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
ServiceName = serviceName,
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||
UseServiceDiscovery = true,
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
|
||||
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
|
||||
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
|
||||
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50))
|
||||
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50))
|
||||
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_handle_request_to_consul_for_downstream_service_and_make_request()
|
||||
{
|
||||
const int consulPort = 8505;
|
||||
const string serviceName = "web";
|
||||
const string downstreamServiceOneUrl = "http://localhost:8080";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = 8080,
|
||||
ID = "web_90_0_2_224_8080",
|
||||
Tags = new[] {"version-v1"}
|
||||
},
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/home",
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/home",
|
||||
UpstreamHttpMethod = new List<string> { "Get", "Options" },
|
||||
ServiceName = serviceName,
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||
UseServiceDiscovery = true,
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes()
|
||||
{
|
||||
const int consulPort = 8513;
|
||||
const string serviceName = "web";
|
||||
const int downstreamServicePort = 8087;
|
||||
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = downstreamServicePort,
|
||||
ID = "web_90_0_2_224_8080",
|
||||
Tags = new[] {"version-v1"}
|
||||
},
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
},
|
||||
DownstreamScheme = "http",
|
||||
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||
{
|
||||
AllowAutoRedirect = true,
|
||||
UseCookieContainer = true,
|
||||
UseTracing = false
|
||||
},
|
||||
QoSOptions = new FileQoSOptions
|
||||
{
|
||||
TimeoutValue = 100,
|
||||
DurationOfBreak = 1000,
|
||||
ExceptionsAllowedBeforeBreaking = 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 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("/web/something"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes()
|
||||
{
|
||||
var consulPort = 8510;
|
||||
var serviceName = "product";
|
||||
var serviceOnePort = 50888;
|
||||
var serviceTwoPort = 50889;
|
||||
var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}";
|
||||
var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = serviceOnePort,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
var serviceEntryTwo = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = serviceTwoPort,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
},
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||
DownstreamScheme = "http"
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
|
||||
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
|
||||
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
|
||||
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes($"/{serviceName}/", 50))
|
||||
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50))
|
||||
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[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<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/home",
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/home",
|
||||
UpstreamHttpMethod = new List<string> { "Get", "Options" },
|
||||
ServiceName = serviceName,
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_send_request_to_service_after_it_becomes_available_in_consul()
|
||||
{
|
||||
var consulPort = 8501;
|
||||
var serviceName = "product";
|
||||
var downstreamServiceOneUrl = "http://localhost:50879";
|
||||
var downstreamServiceTwoUrl = "http://localhost:50880";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = 50879,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
var serviceEntryTwo = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = 50880,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
ServiceName = serviceName,
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||
UseServiceDiscovery = true,
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
|
||||
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
|
||||
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
|
||||
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10))
|
||||
.And(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10))
|
||||
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6))
|
||||
.And(x => WhenIRemoveAService(serviceEntryTwo))
|
||||
.And(x => GivenIResetCounters())
|
||||
.And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10))
|
||||
.And(x => ThenOnlyOneServiceHasBeenCalled())
|
||||
.And(x => WhenIAddAServiceBackIn(serviceEntryTwo))
|
||||
.And(x => GivenIResetCounters())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10))
|
||||
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10))
|
||||
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request()
|
||||
{
|
||||
const int consulPort = 8518;
|
||||
const string serviceName = "web";
|
||||
const int downstreamServicePort = 8082;
|
||||
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = "localhost",
|
||||
Port = downstreamServicePort,
|
||||
ID = $"web_90_0_2_224_{downstreamServicePort}",
|
||||
Tags = new[] {"version-v1"}
|
||||
},
|
||||
};
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/home",
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/home",
|
||||
UpstreamHttpMethod = new List<string> { "Get", "Options" },
|
||||
ServiceName = serviceName,
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||
UseServiceDiscovery = true,
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort,
|
||||
Type = "PollConsul",
|
||||
PollingInterval = 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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.WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk("/home"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo)
|
||||
{
|
||||
_consulServices.Add(serviceEntryTwo);
|
||||
}
|
||||
|
||||
private void ThenOnlyOneServiceHasBeenCalled()
|
||||
{
|
||||
_counterOne.ShouldBe(10);
|
||||
_counterTwo.ShouldBe(0);
|
||||
}
|
||||
|
||||
private void WhenIRemoveAService(ServiceEntry serviceEntryTwo)
|
||||
{
|
||||
_consulServices.Remove(serviceEntryTwo);
|
||||
}
|
||||
|
||||
private void GivenIResetCounters()
|
||||
{
|
||||
_counterOne = 0;
|
||||
_counterTwo = 0;
|
||||
}
|
||||
|
||||
private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
|
||||
{
|
||||
_counterOne.ShouldBeInRange(bottom, top);
|
||||
_counterOne.ShouldBeInRange(bottom, top);
|
||||
}
|
||||
|
||||
private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected)
|
||||
{
|
||||
var total = _counterOne + _counterTwo;
|
||||
total.ShouldBe(expected);
|
||||
}
|
||||
|
||||
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
|
||||
{
|
||||
foreach(var serviceEntry in serviceEntries)
|
||||
{
|
||||
_consulServices.Add(serviceEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheServicesAreRegisteredWithEureka(params IServiceInstance[] serviceInstances)
|
||||
{
|
||||
foreach (var instance in serviceInstances)
|
||||
@ -631,68 +148,6 @@ namespace Ocelot.AcceptanceTests
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||
{
|
||||
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(_consulServices);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenProductServiceOneIsRunning(string url, int statusCode)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string response;
|
||||
lock (SyncLock)
|
||||
{
|
||||
_counterOne++;
|
||||
response = _counterOne.ToString();
|
||||
}
|
||||
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(response);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await context.Response.WriteAsync(exception.StackTrace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenProductServiceTwoIsRunning(string url, int statusCode)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string response;
|
||||
lock (SyncLock)
|
||||
{
|
||||
_counterTwo++;
|
||||
response = _counterTwo.ToString();
|
||||
}
|
||||
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(response);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await context.Response.WriteAsync(exception.StackTrace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenEurekaProductServiceOneIsRunning(string url)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||
@ -709,25 +164,6 @@ namespace Ocelot.AcceptanceTests
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
||||
{
|
||||
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||
|
||||
if (_downstreamPath != basePath)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_serviceHandler?.Dispose();
|
||||
|
@ -422,34 +422,6 @@
|
||||
header.First().ShouldBe(value);
|
||||
}
|
||||
|
||||
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: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
{
|
||||
s.AddOcelot().AddStoreOcelotConfigurationInConsul();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
});
|
||||
|
||||
_ocelotServer = new TestServer(_webHostBuilder);
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
@ -576,24 +548,6 @@
|
||||
_response = _ocelotClient.GetAsync(url).Result;
|
||||
}
|
||||
|
||||
public void WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url)
|
||||
{
|
||||
var result = WaitFor(2000).Until(() => {
|
||||
try
|
||||
{
|
||||
_response = _ocelotClient.GetAsync(url).Result;
|
||||
_response.EnsureSuccessStatusCode();
|
||||
return true;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
result.ShouldBeTrue();
|
||||
}
|
||||
|
||||
public void WhenIGetUrlOnTheApiGateway(string url, string cookie, string value)
|
||||
{
|
||||
var request = _ocelotServer.CreateRequest(url);
|
||||
|
@ -1,151 +0,0 @@
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Consul;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class TwoDownstreamServicesTests : IDisposable
|
||||
{
|
||||
private readonly Steps _steps;
|
||||
private readonly List<ServiceEntry> _serviceEntries;
|
||||
private string _downstreamPathOne;
|
||||
private string _downstreamPathTwo;
|
||||
private readonly ServiceHandler _serviceHandler;
|
||||
|
||||
public TwoDownstreamServicesTests()
|
||||
{
|
||||
_serviceHandler = new ServiceHandler();
|
||||
_steps = new Steps();
|
||||
_serviceEntries = new List<ServiceEntry>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_fix_issue_194()
|
||||
{
|
||||
var consulPort = 8503;
|
||||
var downstreamServiceOneUrl = "http://localhost:8362";
|
||||
var downstreamServiceTwoUrl = "http://localhost:8330";
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/user/{user}",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 8362,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/api/user/{user}",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
},
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/product/{product}",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 8330,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/api/product/{product}",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, "/api/user/info", 200, "user"))
|
||||
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, "/api/product/info", 200, "product"))
|
||||
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/user/info?id=1"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("user"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/product/info?id=1"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("product"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||
{
|
||||
if (context.Request.Path.Value == "/v1/health/service/product")
|
||||
{
|
||||
await context.Response.WriteJsonAsync(_serviceEntries);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenProductServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
||||
{
|
||||
_downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value)
|
||||
? context.Request.PathBase.Value
|
||||
: context.Request.Path.Value;
|
||||
|
||||
if (_downstreamPathOne != basePath)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenProductServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
||||
{
|
||||
_downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||
|
||||
if (_downstreamPathTwo != basePath)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_serviceHandler?.Dispose();
|
||||
_steps.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,6 @@ namespace Ocelot.AcceptanceTests
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Consul;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
@ -17,7 +15,6 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
private readonly List<string> _secondRecieved;
|
||||
private readonly List<string> _firstRecieved;
|
||||
private readonly List<ServiceEntry> _serviceEntries;
|
||||
private readonly Steps _steps;
|
||||
private readonly ServiceHandler _serviceHandler;
|
||||
|
||||
@ -27,7 +24,6 @@ namespace Ocelot.AcceptanceTests
|
||||
_steps = new Steps();
|
||||
_firstRecieved = new List<string>();
|
||||
_secondRecieved = new List<string>();
|
||||
_serviceEntries = new List<ServiceEntry>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -109,77 +105,6 @@ namespace Ocelot.AcceptanceTests
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer()
|
||||
{
|
||||
var downstreamPort = 5007;
|
||||
var downstreamHost = "localhost";
|
||||
|
||||
var secondDownstreamPort = 5008;
|
||||
var secondDownstreamHost = "localhost";
|
||||
|
||||
var serviceName = "websockets";
|
||||
var consulPort = 8509;
|
||||
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = downstreamHost,
|
||||
Port = downstreamPort,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
var serviceEntryTwo = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = serviceName,
|
||||
Address = secondDownstreamHost,
|
||||
Port = secondDownstreamPort,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
var config = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
UpstreamPathTemplate = "/",
|
||||
DownstreamPathTemplate = "/ws",
|
||||
DownstreamScheme = "ws",
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" },
|
||||
ServiceName = serviceName,
|
||||
UseServiceDiscovery = true
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = consulPort,
|
||||
Type = "consul"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(_ => _steps.GivenThereIsAConfiguration(config))
|
||||
.And(_ => _steps.StartFakeOcelotWithWebSockets())
|
||||
.And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
|
||||
.And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||
.And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
|
||||
.And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws"))
|
||||
.When(_ => WhenIStartTheClients())
|
||||
.Then(_ => ThenBothDownstreamServicesAreCalled())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenBothDownstreamServicesAreCalled()
|
||||
{
|
||||
_firstRecieved.Count.ShouldBe(10);
|
||||
@ -195,25 +120,6 @@ namespace Ocelot.AcceptanceTests
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
|
||||
{
|
||||
foreach (var serviceEntry in serviceEntries)
|
||||
{
|
||||
_serviceEntries.Add(serviceEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||
{
|
||||
if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
|
||||
{
|
||||
await context.Response.WriteJsonAsync(_serviceEntries);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task WhenIStartTheClients()
|
||||
{
|
||||
var firstClient = StartClient("ws://localhost:5000/");
|
||||
|
Reference in New Issue
Block a user