Make Ocelot work with service fabric DNS and naming service for guest exe and stateless (#242)

* test for issue

* added service fabric sample

* working!!

* changed sample naming to Ocelot

* removed files we dont need

* removed files we dont need

* updated sample gitignore

* updated sample gitignore

* getting ocelot to work with service fabric using the reverse proxy

* #238 - added support for service fabric discovery provider, proxies requests through naming service, wont work on partioned service fabric services yet

* #238 - Manually tested service fabric using sample..all seems OK. Made some changes after testing, added docs

* #238 - added docs for servic fabric
This commit is contained in:
Tom Pallister
2018-03-03 15:24:05 +00:00
committed by GitHub
parent 9cb25ab063
commit 454ba3f9a0
71 changed files with 2394 additions and 484 deletions

View File

@ -0,0 +1,112 @@
using System.Linq;
using Microsoft.Extensions.Primitives;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class ServiceFabricTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
public ServiceFabricTests()
{
_steps = new Steps();
}
[Fact]
public void should_support_service_fabric_naming_and_dns_service()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
UpstreamPathTemplate = "/EquipmentInterfaces",
UpstreamHttpMethod = new List<string> { "Get" },
UseServiceDiscovery = true,
ServiceName = "OcelotServiceApplication/OcelotApplicationService"
}
},
GlobalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = 19081,
Type = "ServiceFabric"
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(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
{
if (context.Request.Query.TryGetValue("cmd", out var values))
{
values.First().ShouldBe("instance");
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
}
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -328,7 +328,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenAConfiguration(configuration))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "ReRoutes for aggregateReRoute / either do not exist or do not have correct Key property"))
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "ReRoutes for aggregateReRoute / either do not exist or do not have correct ServiceName property"))
.BDDfy();
}

View File

@ -30,13 +30,15 @@ namespace Ocelot.UnitTests.Configuration
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "127.0.0.1",
Port = 1234
Port = 1234,
Type = "ServiceFabric"
}
};
var expected = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderHost("127.0.0.1")
.WithServiceDiscoveryProviderPort(1234)
.WithServiceDiscoveryProviderType("ServiceFabric")
.Build();
this.Given(x => x.GivenTheFollowingReRoute(reRoute))
@ -63,8 +65,8 @@ namespace Ocelot.UnitTests.Configuration
private void ThenTheConfigIs(ServiceProviderConfiguration expected)
{
_result.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost);
_result.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort);
_result.Host.ShouldBe(expected.Host);
_result.Port.ShouldBe(expected.Port);
}
}
}
}

View File

@ -1,4 +1,5 @@
using Ocelot.Middleware;
using Ocelot.Configuration;
using Ocelot.Middleware;
namespace Ocelot.UnitTests.DownstreamUrlCreator
{
@ -27,7 +28,6 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
public class DownstreamUrlCreatorMiddlewareTests
{
private readonly Mock<IDownstreamPathPlaceholderReplacer> _downstreamUrlTemplateVariableReplacer;
private readonly Mock<IUrlBuilder> _urlBuilder;
private OkResponse<DownstreamPath> _downstreamPath;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
@ -42,7 +42,6 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
_urlBuilder = new Mock<IUrlBuilder>();
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
_next = async context => {
//do nothing
@ -58,6 +57,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.WithDownstreamScheme("https")
.Build();
var config = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(
new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
@ -66,15 +68,81 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
.BDDfy();
}
[Fact]
public void should_not_create_service_fabric_url()
{
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamPathTemplate("any old string")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithDownstreamScheme("https")
.Build();
var config = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderType("ServiceFabric")
.WithServiceDiscoveryProviderHost("localhost")
.WithServiceDiscoveryProviderPort(19081)
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(
new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
.BDDfy();
}
[Fact]
public void should_create_service_fabric_url()
{
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamScheme("http")
.WithServiceName("Ocelot/OcelotApp")
.WithUseServiceDiscovery(true)
.Build();
var downstreamRoute = new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.Build());
var config = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderType("ServiceFabric")
.WithServiceDiscoveryProviderHost("localhost")
.WithServiceDiscoveryProviderPort(19081)
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081"))
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?cmd=instance"))
.BDDfy();
}
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
{
_downstreamContext.ServiceProviderConfiguration = config;
}
private void WhenICallTheMiddleware()
{
_middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object, _urlBuilder.Object);
_middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}

View File

@ -1,122 +0,0 @@
using System;
using Ocelot.DownstreamUrlCreator;
using Ocelot.Responses;
using Ocelot.Values;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.DownstreamUrlCreator
{
public class UrlBuilderTests
{
private readonly IUrlBuilder _urlBuilder;
private string _dsPath;
private string _dsScheme;
private string _dsHost;
private int _dsPort;
private Response<DownstreamUrl> _result;
public UrlBuilderTests()
{
_urlBuilder = new UrlBuilder();
}
[Fact]
public void should_return_error_when_downstream_path_is_null()
{
this.Given(x => x.GivenADownstreamPath(null))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenThereIsAnErrorOfType<DownstreamPathNullOrEmptyError>())
.BDDfy();
}
[Fact]
public void should_return_error_when_downstream_scheme_is_null()
{
this.Given(x => x.GivenADownstreamScheme(null))
.And(x => x.GivenADownstreamPath("test"))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenThereIsAnErrorOfType<DownstreamSchemeNullOrEmptyError>())
.BDDfy();
}
[Fact]
public void should_return_error_when_downstream_host_is_null()
{
this.Given(x => x.GivenADownstreamScheme(null))
.And(x => x.GivenADownstreamPath("test"))
.And(x => x.GivenADownstreamScheme("test"))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenThereIsAnErrorOfType<DownstreamHostNullOrEmptyError>())
.BDDfy();
}
[Fact]
public void should_not_use_port_if_zero()
{
this.Given(x => x.GivenADownstreamPath("/api/products/1"))
.And(x => x.GivenADownstreamScheme("http"))
.And(x => x.GivenADownstreamHost("127.0.0.1"))
.And(x => x.GivenADownstreamPort(0))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenTheUrlIsReturned("http://127.0.0.1/api/products/1"))
.And(x => x.ThenTheUrlIsWellFormed())
.BDDfy();
}
[Fact]
public void should_build_well_formed_uri()
{
this.Given(x => x.GivenADownstreamPath("/api/products/1"))
.And(x => x.GivenADownstreamScheme("http"))
.And(x => x.GivenADownstreamHost("127.0.0.1"))
.And(x => x.GivenADownstreamPort(5000))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenTheUrlIsReturned("http://127.0.0.1:5000/api/products/1"))
.And(x => x.ThenTheUrlIsWellFormed())
.BDDfy();
}
private void ThenThereIsAnErrorOfType<T>()
{
_result.Errors[0].ShouldBeOfType<T>();
}
private void GivenADownstreamPath(string dsPath)
{
_dsPath = dsPath;
}
private void GivenADownstreamScheme(string dsScheme)
{
_dsScheme = dsScheme;
}
private void GivenADownstreamHost(string dsHost)
{
_dsHost = dsHost;
}
private void GivenADownstreamPort(int dsPort)
{
_dsPort = dsPort;
}
private void WhenIBuildTheUrl()
{
_result = _urlBuilder.Build(_dsPath, _dsScheme, new ServiceHostAndPort(_dsHost, _dsPort));
}
private void ThenTheUrlIsReturned(string expected)
{
_result.Data.Value.ShouldBe(expected);
}
private void ThenTheUrlIsWellFormed()
{
Uri.IsWellFormedUriString(_result.Data.Value, UriKind.Absolute).ShouldBeTrue();
}
}
}

View File

@ -0,0 +1,58 @@
namespace Ocelot.UnitTests.ServiceDiscovery
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.Values;
using Xunit;
using TestStack.BDDfy;
using Shouldly;
public class ServiceFabricServiceDiscoveryProviderTests
{
private ServiceFabricServiceDiscoveryProvider _provider;
private ServiceFabricConfiguration _config;
private string _host;
private string _serviceName;
private int _port;
private List<Service> _services;
[Fact]
public void should_return_service_fabric_naming_service()
{
this.Given(x => GivenTheFollowing())
.When(x => WhenIGet())
.Then(x => ThenTheServiceFabricNamingServiceIsRetured())
.BDDfy();
}
private void GivenTheFollowing()
{
_host = "localhost";
_serviceName = "OcelotServiceApplication/OcelotApplicationService";
_port = 19081;
}
private void WhenIGet()
{
_config = new ServiceFabricConfiguration(_host, _port, _serviceName);
_provider = new ServiceFabricServiceDiscoveryProvider(_config);
_services = _provider.Get().GetAwaiter().GetResult();
}
private void ThenTheServiceFabricNamingServiceIsRetured()
{
_services.Count.ShouldBe(1);
_services[0].HostAndPort.DownstreamHost.ShouldBe(_host);
_services[0].HostAndPort.DownstreamPort.ShouldBe(_port);
}
}
}

View File

@ -77,6 +77,24 @@ namespace Ocelot.UnitTests.ServiceDiscovery
.BDDfy();
}
[Fact]
public void should_return_service_fabric_provider()
{
var reRoute = new DownstreamReRouteBuilder()
.WithServiceName("product")
.WithUseServiceDiscovery(true)
.Build();
var serviceConfig = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderType("ServiceFabric")
.Build();
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<ServiceFabricServiceDiscoveryProvider>())
.BDDfy();
}
private void ThenTheFollowingServicesAreReturned(List<DownstreamHostAndPort> downstreamAddresses)
{
var result = (ConfigurationServiceProvider)_result;