Feature/fix admin api caching wrong re routes (#421)

* #383 added failing test for this issue

* #383 identified issue was with cached load balancer for a given upstream path template based on the key we use, have modified this to include more data, I guess this might be an issue again for other things so I will have a think about it

* #383 fixed failing tests after key change

* Seems to be an issue with coveralls new package not being on nuget...try same version as their nuget package

* bash the old manual tests json back in
This commit is contained in:
Tom Pallister 2018-06-21 22:45:24 +01:00 committed by GitHub
parent fffc4c8d3c
commit 3eb9b4da89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1903 additions and 1731 deletions

View File

@ -5,7 +5,7 @@
#tool "nuget:?package=OpenCover" #tool "nuget:?package=OpenCover"
#tool "nuget:?package=ReportGenerator" #tool "nuget:?package=ReportGenerator"
#tool "nuget:?package=coveralls.net&version=0.7.0" #tool "nuget:?package=coveralls.net&version=0.7.0"
#addin Cake.Coveralls #addin Cake.Coveralls&version=0.7.0
// compile // compile
var compileConfig = Argument("configuration", "Release"); var compileConfig = Argument("configuration", "Release");

View File

@ -252,7 +252,7 @@ namespace Ocelot.Configuration.Creator
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}"; return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
} }
return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}"; return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}|{string.Join(",", fileReRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}";
} }
} }
} }

View File

@ -185,6 +185,52 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_fix_issue_417()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
},
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false
}
}
},
GlobalConfiguration = new FileGlobalConfiguration
{
BaseUrl = "http://anotherapp.azurewebsites.net"
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://anotherapp.azurewebsites.net/pay/Receive"))
.BDDfy();
}
[Fact] [Fact]
public void request_should_reuse_cookies_with_cookie_container() public void request_should_reuse_cookies_with_cookie_container()
{ {
@ -317,7 +363,7 @@ namespace Ocelot.AcceptanceTests
app.UsePathBase(basePath); app.UsePathBase(basePath);
app.Run(async context => app.Run(async context =>
{ {
if(context.Request.Headers.TryGetValue(headerKey, out var values)) if (context.Request.Headers.TryGetValue(headerKey, out var values))
{ {
var result = values.First(); var result = values.First();
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
@ -330,7 +376,7 @@ namespace Ocelot.AcceptanceTests
_builder.Start(); _builder.Start();
} }
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey, string headerValue) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey, string headerValue)
{ {
_builder = new WebHostBuilder() _builder = new WebHostBuilder()
.UseUrls(baseUrl) .UseUrls(baseUrl)
@ -342,7 +388,8 @@ namespace Ocelot.AcceptanceTests
app.UsePathBase(basePath); app.UsePathBase(basePath);
app.Run(context => app.Run(context =>
{ {
context.Response.OnStarting(() => { context.Response.OnStarting(() =>
{
context.Response.Headers.Add(headerKey, headerValue); context.Response.Headers.Add(headerKey, headerValue);
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
return Task.CompletedTask; return Task.CompletedTask;

View File

@ -11,6 +11,7 @@ using IdentityServer4.Models;
using IdentityServer4.Test; using IdentityServer4.Test;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -38,6 +39,8 @@ namespace Ocelot.IntegrationTests
private IWebHostBuilder _webHostBuilderTwo; private IWebHostBuilder _webHostBuilderTwo;
private IWebHost _builderTwo; private IWebHost _builderTwo;
private IWebHost _identityServerBuilder; private IWebHost _identityServerBuilder;
private IWebHost _fooServiceBuilder;
private IWebHost _barServiceBuilder;
public AdministrationTests() public AdministrationTests()
{ {
@ -276,6 +279,80 @@ namespace Ocelot.IntegrationTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute()
{
var fooPort = 47689;
var barPort = 47690;
var initialConfiguration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = fooPort,
}
},
DownstreamScheme = "http",
DownstreamPathTemplate = "/foo",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/foo"
}
}
};
var updatedConfiguration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = barPort,
}
},
DownstreamScheme = "http",
DownstreamPathTemplate = "/bar",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/foo"
}
}
};
this.Given(x => GivenThereIsAConfiguration(initialConfiguration))
.And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}"))
.And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}"))
.And(x => GivenOcelotIsRunning())
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
.Then(x => ThenTheResponseBodyShouldBe("foo"))
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
.Then(x => ThenTheResponseBodyShouldBe("bar"))
.When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenTheResponseShouldBe(initialConfiguration))
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
.Then(x => ThenTheResponseBodyShouldBe("foo"))
.BDDfy();
}
[Fact] [Fact]
public void should_clear_region() public void should_clear_region()
{ {
@ -516,6 +593,12 @@ namespace Ocelot.IntegrationTests
result.Value.ShouldBe(expected); result.Value.ShouldBe(expected);
} }
private void ThenTheResponseBodyShouldBe(string expected)
{
var content = _response.Content.ReadAsStringAsync().Result;
content.ShouldBe(expected);
}
private void ThenTheResponseShouldBe(FileConfiguration expecteds) private void ThenTheResponseShouldBe(FileConfiguration expecteds)
{ {
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result); var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
@ -723,5 +806,47 @@ namespace Ocelot.IntegrationTests
_httpClient?.Dispose(); _httpClient?.Dispose();
_identityServerBuilder?.Dispose(); _identityServerBuilder?.Dispose();
} }
private void GivenThereIsAFooServiceRunningOn(string baseUrl)
{
_fooServiceBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase("/foo");
app.Run(async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("foo");
});
})
.Build();
_fooServiceBuilder.Start();
}
private void GivenThereIsABarServiceRunningOn(string baseUrl)
{
_barServiceBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase("/bar");
app.Run(async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("bar");
});
})
.Build();
_barServiceBuilder.Start();
}
} }
} }

View File

@ -199,7 +199,7 @@
.WithDownstreamScheme("http") .WithDownstreamScheme("http")
.WithUpstreamHttpMethod(new List<string>() {"Get"}) .WithUpstreamHttpMethod(new List<string>() {"Get"})
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() {new DownstreamHostAndPort("localhost", 51878)}) .WithDownstreamAddresses(new List<DownstreamHostAndPort>() {new DownstreamHostAndPort("localhost", 51878)})
.WithLoadBalancerKey("/laura|Get") .WithLoadBalancerKey("/laura|Get|localhost:51878")
.Build(); .Build();
var lauraReRoute = new ReRouteBuilder() var lauraReRoute = new ReRouteBuilder()
@ -218,7 +218,7 @@
.WithDownstreamScheme("http") .WithDownstreamScheme("http")
.WithUpstreamHttpMethod(new List<string>() { "Get" }) .WithUpstreamHttpMethod(new List<string>() { "Get" })
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() { new DownstreamHostAndPort("localhost", 51878) }) .WithDownstreamAddresses(new List<DownstreamHostAndPort>() { new DownstreamHostAndPort("localhost", 51878) })
.WithLoadBalancerKey("/tom|Get") .WithLoadBalancerKey("/tom|Get|localhost:51880")
.Build(); .Build();
var tomReRoute = new ReRouteBuilder() var tomReRoute = new ReRouteBuilder()
@ -409,7 +409,7 @@
.WithDownstreamPathTemplate("/products/{productId}") .WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|127.0.0.1:0")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -461,7 +461,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithDelegatingHandlers(handlers) .WithDelegatingHandlers(handlers)
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -506,7 +506,7 @@
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithUseServiceDiscovery(true) .WithUseServiceDiscovery(true)
.WithServiceName("ProductService") .WithServiceName("ProductService")
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -557,7 +557,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithUseServiceDiscovery(false) .WithUseServiceDiscovery(false)
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -600,7 +600,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1)) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1))
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -645,7 +645,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithRequestIdKey("blahhhh") .WithRequestIdKey("blahhhh")
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -740,7 +740,7 @@
{ {
new ClaimToThing("CustomerId", "CustomerId", "", 0), new ClaimToThing("CustomerId", "CustomerId", "", 0),
}) })
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|")
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -783,7 +783,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithAuthenticationOptions(authenticationOptions) .WithAuthenticationOptions(authenticationOptions)
.WithLoadBalancerKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get|")
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>