mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Fix issue #936: Kubernetes service discovery provider doesn't allow cross-namespace discovery (#938)
* Allow default k8s namespace to be overridden * Add ServiceNamespace to ReRoute configuration * Remove debug comments * Update unit tests * Unit tests (Eureka) * Update docs * Re-run build
This commit is contained in:
parent
959a92ec6a
commit
e1d7f28951
@ -75,3 +75,21 @@ The polling interval is in milliseconds and tells Ocelot how often to call kuber
|
|||||||
|
|
||||||
Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request.
|
Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request.
|
||||||
There is no way for Ocelot to work these out for you.
|
There is no way for Ocelot to work these out for you.
|
||||||
|
|
||||||
|
If your downstream service resides in a different namespace you can override the global setting at the ReRoute level by specifying a ServiceNamespace
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/values",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"UpstreamPathTemplate": "/values",
|
||||||
|
"ServiceName": "downstreamservice",
|
||||||
|
"ServiceNamespace": "downstream-namespace",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
public static class ConsulProviderFactory
|
public static class ConsulProviderFactory
|
||||||
{
|
{
|
||||||
public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) =>
|
public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) =>
|
||||||
{
|
{
|
||||||
var factory = provider.GetService<IOcelotLoggerFactory>();
|
var factory = provider.GetService<IOcelotLoggerFactory>();
|
||||||
|
|
||||||
var consulFactory = provider.GetService<IConsulClientFactory>();
|
var consulFactory = provider.GetService<IConsulClientFactory>();
|
||||||
|
|
||||||
var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Host, config.Port, name, config.Token);
|
var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Host, config.Port, reRoute.ServiceName, config.Token);
|
||||||
|
|
||||||
var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory);
|
var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory);
|
||||||
|
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
public static class EurekaProviderFactory
|
public static class EurekaProviderFactory
|
||||||
{
|
{
|
||||||
public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) =>
|
public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) =>
|
||||||
{
|
{
|
||||||
var client = provider.GetService<IDiscoveryClient>();
|
var client = provider.GetService<IDiscoveryClient>();
|
||||||
|
|
||||||
if (config.Type?.ToLower() == "eureka" && client != null)
|
if (config.Type?.ToLower() == "eureka" && client != null)
|
||||||
{
|
{
|
||||||
return new Eureka(name, client);
|
return new Eureka(reRoute.ServiceName, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -24,8 +24,7 @@ namespace Ocelot.Provider.Kubernetes
|
|||||||
|
|
||||||
public async Task<List<Service>> Get()
|
public async Task<List<Service>> Get()
|
||||||
{
|
{
|
||||||
var service = await kubeApi.ServicesV1()
|
var service = await kubeApi.ServicesV1().Get(kubeRegistryConfiguration.KeyOfServiceInK8s, kubeRegistryConfiguration.KubeNamespace);
|
||||||
.Get(kubeRegistryConfiguration.KeyOfServiceInK8s, kubeRegistryConfiguration.KubeNamespace);
|
|
||||||
var services = new List<Service>();
|
var services = new List<Service>();
|
||||||
if (IsValid(service))
|
if (IsValid(service))
|
||||||
{
|
{
|
||||||
|
@ -3,24 +3,25 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
using System;
|
using System;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
|
||||||
namespace Ocelot.Provider.Kubernetes
|
namespace Ocelot.Provider.Kubernetes
|
||||||
{
|
{
|
||||||
public static class KubernetesProviderFactory
|
public static class KubernetesProviderFactory
|
||||||
{
|
{
|
||||||
public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) =>
|
public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) =>
|
||||||
{
|
{
|
||||||
var factory = provider.GetService<IOcelotLoggerFactory>();
|
var factory = provider.GetService<IOcelotLoggerFactory>();
|
||||||
return GetkubeProvider(provider, config, name, factory);
|
return GetkubeProvider(provider, config, reRoute, factory);
|
||||||
};
|
};
|
||||||
|
|
||||||
private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, string name, IOcelotLoggerFactory factory)
|
private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, DownstreamReRoute reRoute, IOcelotLoggerFactory factory)
|
||||||
{
|
{
|
||||||
var kubeClient = provider.GetService<IKubeApiClient>();
|
var kubeClient = provider.GetService<IKubeApiClient>();
|
||||||
var k8sRegistryConfiguration = new KubeRegistryConfiguration()
|
var k8sRegistryConfiguration = new KubeRegistryConfiguration()
|
||||||
{
|
{
|
||||||
KeyOfServiceInK8s = name,
|
KeyOfServiceInK8s = reRoute.ServiceName,
|
||||||
KubeNamespace = config.Namespace,
|
KubeNamespace = string.IsNullOrEmpty(reRoute.ServiceNamespace) ? config.Namespace : reRoute.ServiceNamespace
|
||||||
};
|
};
|
||||||
|
|
||||||
var k8sServiceDiscoveryProvider = new Kube(k8sRegistryConfiguration, factory, kubeClient);
|
var k8sServiceDiscoveryProvider = new Kube(k8sRegistryConfiguration, factory, kubeClient);
|
||||||
|
@ -30,6 +30,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private RateLimitOptions _rateLimitOptions;
|
private RateLimitOptions _rateLimitOptions;
|
||||||
private bool _useServiceDiscovery;
|
private bool _useServiceDiscovery;
|
||||||
private string _serviceName;
|
private string _serviceName;
|
||||||
|
private string _serviceNamespace;
|
||||||
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
||||||
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
||||||
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
||||||
@ -186,6 +187,12 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithServiceNamespace(string serviceNamespace)
|
||||||
|
{
|
||||||
|
_serviceNamespace = serviceNamespace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public DownstreamReRouteBuilder WithUpstreamHeaderFindAndReplace(List<HeaderFindAndReplace> upstreamHeaderFindAndReplace)
|
public DownstreamReRouteBuilder WithUpstreamHeaderFindAndReplace(List<HeaderFindAndReplace> upstreamHeaderFindAndReplace)
|
||||||
{
|
{
|
||||||
_upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace;
|
_upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace;
|
||||||
@ -243,6 +250,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
_downstreamHeaderFindAndReplace,
|
_downstreamHeaderFindAndReplace,
|
||||||
_downstreamAddresses,
|
_downstreamAddresses,
|
||||||
_serviceName,
|
_serviceName,
|
||||||
|
_serviceNamespace,
|
||||||
_httpHandlerOptions,
|
_httpHandlerOptions,
|
||||||
_useServiceDiscovery,
|
_useServiceDiscovery,
|
||||||
_enableRateLimiting,
|
_enableRateLimiting,
|
||||||
|
@ -126,6 +126,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithRateLimitOptions(rateLimitOption)
|
.WithRateLimitOptions(rateLimitOption)
|
||||||
.WithHttpHandlerOptions(httpHandlerOptions)
|
.WithHttpHandlerOptions(httpHandlerOptions)
|
||||||
.WithServiceName(fileReRoute.ServiceName)
|
.WithServiceName(fileReRoute.ServiceName)
|
||||||
|
.WithServiceNamespace(fileReRoute.ServiceNamespace)
|
||||||
.WithUseServiceDiscovery(fileReRouteOptions.UseServiceDiscovery)
|
.WithUseServiceDiscovery(fileReRouteOptions.UseServiceDiscovery)
|
||||||
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
||||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||||
|
@ -13,6 +13,7 @@ namespace Ocelot.Configuration
|
|||||||
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
||||||
List<DownstreamHostAndPort> downstreamAddresses,
|
List<DownstreamHostAndPort> downstreamAddresses,
|
||||||
string serviceName,
|
string serviceName,
|
||||||
|
string serviceNamespace,
|
||||||
HttpHandlerOptions httpHandlerOptions,
|
HttpHandlerOptions httpHandlerOptions,
|
||||||
bool useServiceDiscovery,
|
bool useServiceDiscovery,
|
||||||
bool enableEndpointEndpointRateLimiting,
|
bool enableEndpointEndpointRateLimiting,
|
||||||
@ -47,6 +48,7 @@ namespace Ocelot.Configuration
|
|||||||
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
||||||
DownstreamAddresses = downstreamAddresses ?? new List<DownstreamHostAndPort>();
|
DownstreamAddresses = downstreamAddresses ?? new List<DownstreamHostAndPort>();
|
||||||
ServiceName = serviceName;
|
ServiceName = serviceName;
|
||||||
|
ServiceNamespace = serviceNamespace;
|
||||||
HttpHandlerOptions = httpHandlerOptions;
|
HttpHandlerOptions = httpHandlerOptions;
|
||||||
UseServiceDiscovery = useServiceDiscovery;
|
UseServiceDiscovery = useServiceDiscovery;
|
||||||
EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting;
|
EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting;
|
||||||
@ -76,6 +78,7 @@ namespace Ocelot.Configuration
|
|||||||
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace { get; }
|
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace { get; }
|
||||||
public List<DownstreamHostAndPort> DownstreamAddresses { get; }
|
public List<DownstreamHostAndPort> DownstreamAddresses { get; }
|
||||||
public string ServiceName { get; }
|
public string ServiceName { get; }
|
||||||
|
public string ServiceNamespace { get; }
|
||||||
public HttpHandlerOptions HttpHandlerOptions { get; }
|
public HttpHandlerOptions HttpHandlerOptions { get; }
|
||||||
public bool UseServiceDiscovery { get; }
|
public bool UseServiceDiscovery { get; }
|
||||||
public bool EnableEndpointEndpointRateLimiting { get; }
|
public bool EnableEndpointEndpointRateLimiting { get; }
|
||||||
|
@ -38,6 +38,7 @@ namespace Ocelot.Configuration.File
|
|||||||
public FileCacheOptions FileCacheOptions { get; set; }
|
public FileCacheOptions FileCacheOptions { get; set; }
|
||||||
public bool ReRouteIsCaseSensitive { get; set; }
|
public bool ReRouteIsCaseSensitive { get; set; }
|
||||||
public string ServiceName { get; set; }
|
public string ServiceName { get; set; }
|
||||||
|
public string ServiceNamespace { get; set; }
|
||||||
public string DownstreamScheme { get; set; }
|
public string DownstreamScheme { get; set; }
|
||||||
public FileQoSOptions QoSOptions { get; set; }
|
public FileQoSOptions QoSOptions { get; set; }
|
||||||
public FileLoadBalancerOptions LoadBalancerOptions { get; set; }
|
public FileLoadBalancerOptions LoadBalancerOptions { get; set; }
|
||||||
|
@ -4,5 +4,5 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
using Providers;
|
using Providers;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, string key);
|
public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamReRoute reRoute);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
{
|
{
|
||||||
if (reRoute.UseServiceDiscovery)
|
if (reRoute.UseServiceDiscovery)
|
||||||
{
|
{
|
||||||
return GetServiceDiscoveryProvider(serviceConfig, reRoute.ServiceName);
|
return GetServiceDiscoveryProvider(serviceConfig, reRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
var services = new List<Service>();
|
var services = new List<Service>();
|
||||||
@ -42,17 +42,17 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
return new OkResponse<IServiceDiscoveryProvider>(new ConfigurationServiceProvider(services));
|
return new OkResponse<IServiceDiscoveryProvider>(new ConfigurationServiceProvider(services));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response<IServiceDiscoveryProvider> GetServiceDiscoveryProvider(ServiceProviderConfiguration config, string key)
|
private Response<IServiceDiscoveryProvider> GetServiceDiscoveryProvider(ServiceProviderConfiguration config, DownstreamReRoute reRoute)
|
||||||
{
|
{
|
||||||
if (config.Type?.ToLower() == "servicefabric")
|
if (config.Type?.ToLower() == "servicefabric")
|
||||||
{
|
{
|
||||||
var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, key);
|
var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, reRoute.ServiceName);
|
||||||
return new OkResponse<IServiceDiscoveryProvider>(new ServiceFabricServiceDiscoveryProvider(sfConfig));
|
return new OkResponse<IServiceDiscoveryProvider>(new ServiceFabricServiceDiscoveryProvider(sfConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_delegates != null)
|
if (_delegates != null)
|
||||||
{
|
{
|
||||||
var provider = _delegates?.Invoke(_provider, config, key);
|
var provider = _delegates?.Invoke(_provider, config, reRoute);
|
||||||
|
|
||||||
if (provider.GetType().Name.ToLower() == config.Type.ToLower())
|
if (provider.GetType().Name.ToLower() == config.Type.ToLower())
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace Ocelot.UnitTests.Consul
|
using Ocelot.Configuration.Builder;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Consul
|
||||||
{
|
{
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Moq;
|
using Moq;
|
||||||
@ -29,7 +31,11 @@
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_ConsulServiceDiscoveryProvider()
|
public void should_return_ConsulServiceDiscoveryProvider()
|
||||||
{
|
{
|
||||||
var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("", "", 1, "", "", 1), "");
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
|
.WithServiceName("")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("", "", 1, "", "", 1), reRoute);
|
||||||
provider.ShouldBeOfType<Consul>();
|
provider.ShouldBeOfType<Consul>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +43,12 @@
|
|||||||
public void should_return_PollingConsulServiceDiscoveryProvider()
|
public void should_return_PollingConsulServiceDiscoveryProvider()
|
||||||
{
|
{
|
||||||
var stopsPollerFromPolling = 10000;
|
var stopsPollerFromPolling = 10000;
|
||||||
var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("pollconsul", "", 1, "", "", stopsPollerFromPolling), "");
|
|
||||||
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
|
.WithServiceName("")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("pollconsul", "", 1, "", "", stopsPollerFromPolling), reRoute);
|
||||||
provider.ShouldBeOfType<PollConsul>();
|
provider.ShouldBeOfType<PollConsul>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,10 @@
|
|||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<IDiscoveryClient>(client.Object);
|
services.AddSingleton<IDiscoveryClient>(client.Object);
|
||||||
var sp = services.BuildServiceProvider();
|
var sp = services.BuildServiceProvider();
|
||||||
var provider = EurekaProviderFactory.Get(sp, config, null);
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
|
.WithServiceName("")
|
||||||
|
.Build();
|
||||||
|
var provider = EurekaProviderFactory.Get(sp, config, reRoute);
|
||||||
provider.ShouldBeOfType<Eureka>();
|
provider.ShouldBeOfType<Eureka>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user