mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-17 17:43:21 +08:00
kubernetes provider (#772)
* feat: Kubernetes ServiceDiscoveryProvider * 编写k8s测试例子 * feat:fix kube config * feat: remove port * feat : complete the k8s test * feat : add kubeserviceDiscovery test * feat : add kube provider unittest * feat :add kubetnetes docs how to use ocelot with kubetnetes docs * keep the configuration as simple as possible, no qos, no cache * fix: use http * add PollingKubeServiceDiscovery * feat : refactor logger * feat : add pollkube docs * feat:Remove unnecessary code * feat : code-block json
This commit is contained in:
parent
05ede70e62
commit
44dccf1fce
@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "sr
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -114,6 +116,10 @@ Global
|
|||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -132,6 +138,7 @@ Global
|
|||||||
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
|
{72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
|
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
|
||||||
|
63
docs/features/kubernetes.rst
Normal file
63
docs/features/kubernetes.rst
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
Kubernetes
|
||||||
|
==============
|
||||||
|
|
||||||
|
This feature was requested as part of `Issue 345 <https://github.com/ThreeMammals/Ocelot/issues/345>`_ . to add support for kubernetes's service discovery provider.
|
||||||
|
|
||||||
|
The first thing you need to do is install the NuGet package that provides kubernetes support in Ocelot.
|
||||||
|
|
||||||
|
``Install-Package Ocelot.Provider.Kubernetes``
|
||||||
|
|
||||||
|
Then add the following to your ConfigureServices method.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
s.AddOcelot()
|
||||||
|
.AddKubernetes();
|
||||||
|
|
||||||
|
If you have services deployed in kubernetes you will normally use the naming service to access them.
|
||||||
|
|
||||||
|
The following example shows how to set up a ReRoute that will work in kubernetes. The most important thing is the ServiceName which is made up of the
|
||||||
|
kubernetes service name. We also need to set up the ServiceDiscoveryProvider in
|
||||||
|
GlobalConfiguration. The example here shows a typical configuration. It assumes kubernetes api server is running on 192.168.0.13 and that api service is on port 443.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/values",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"UpstreamPathTemplate": "/values",
|
||||||
|
"ServiceName": "downstreamservice",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GlobalConfiguration": {
|
||||||
|
"ServiceDiscoveryProvider": {
|
||||||
|
"Host": "192.168.0.13",
|
||||||
|
"Port": 443,
|
||||||
|
"Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
|
||||||
|
"Namespace": "dev",
|
||||||
|
"Type": "kube"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
You use Ocelot to poll kubernetes for latest service information rather than per request. If you want to poll kubernetes for the latest services rather than per request (default behaviour) then you need to set the following configuration.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"ServiceDiscoveryProvider": {
|
||||||
|
"Host": "192.168.0.13",
|
||||||
|
"Port": 443,
|
||||||
|
"Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
|
||||||
|
"Namespace": "dev",
|
||||||
|
"Type": "pollkube"
|
||||||
|
"PollingInterval": 100
|
||||||
|
}
|
||||||
|
|
||||||
|
The polling interval is in milliseconds and tells Ocelot how often to call kubernetes for changes in service configuration.
|
||||||
|
|
||||||
|
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.
|
@ -24,6 +24,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
|||||||
features/graphql
|
features/graphql
|
||||||
features/servicediscovery
|
features/servicediscovery
|
||||||
features/servicefabric
|
features/servicefabric
|
||||||
|
features/kubernetes
|
||||||
features/authentication
|
features/authentication
|
||||||
features/authorisation
|
features/authorisation
|
||||||
features/websockets
|
features/websockets
|
||||||
|
9
samples/OelotKube/.dockerignore
Normal file
9
samples/OelotKube/.dockerignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.dockerignore
|
||||||
|
.env
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
*/bin
|
||||||
|
*/obj
|
||||||
|
**/.toolstarget
|
20
samples/OelotKube/ApiGateway/ApiGateway.csproj
Normal file
20
samples/OelotKube/ApiGateway/ApiGateway.csproj
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.1" PrivateAssets="All" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\src\Ocelot\Ocelot.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
21
samples/OelotKube/ApiGateway/Dockerfile
Normal file
21
samples/OelotKube/ApiGateway/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
FROM microsoft/dotnet:2.1-sdk AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["ApiGateway/ApiGateway.csproj", "ApiGateway/"]
|
||||||
|
COPY ["../../src/Ocelot/Ocelot.csproj", "../../src/Ocelot/"]
|
||||||
|
COPY ["../../src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj", "../../src/Ocelot.Provider.Kubernetes/"]
|
||||||
|
RUN dotnet restore "ApiGateway/ApiGateway.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/ApiGateway"
|
||||||
|
RUN dotnet build "ApiGateway.csproj" -c Release -o /app
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "ApiGateway.csproj" -c Release -o /app
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app .
|
||||||
|
ENTRYPOINT ["dotnet", "ApiGateway.dll"]
|
32
samples/OelotKube/ApiGateway/Program.cs
Normal file
32
samples/OelotKube/ApiGateway/Program.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.AspNetCore;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Ocelot.DependencyInjection;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Provider.Kubernetes;
|
||||||
|
|
||||||
|
namespace ApiGateway
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
BuildWebHost(args).Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IWebHost BuildWebHost(string[] args) =>
|
||||||
|
WebHost.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
|
{
|
||||||
|
config
|
||||||
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
|
.AddJsonFile("ocelot.json", false, false)
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
})
|
||||||
|
.UseStartup<Startup>()
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
32
samples/OelotKube/ApiGateway/Properties/launchSettings.json
Normal file
32
samples/OelotKube/ApiGateway/Properties/launchSettings.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:52363",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ApiGateway": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "http://localhost:5000"
|
||||||
|
},
|
||||||
|
"Docker": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://localhost:{ServicePort}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
samples/OelotKube/ApiGateway/Startup.cs
Normal file
36
samples/OelotKube/ApiGateway/Startup.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Ocelot.DependencyInjection;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Provider.Kubernetes;
|
||||||
|
|
||||||
|
namespace ApiGateway
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddOcelot()
|
||||||
|
.AddKubernetes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseOcelot().Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
samples/OelotKube/ApiGateway/appsettings.json
Normal file
8
samples/OelotKube/ApiGateway/appsettings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
20
samples/OelotKube/ApiGateway/ocelot.json
Normal file
20
samples/OelotKube/ApiGateway/ocelot.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/values",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"UpstreamPathTemplate": "/values",
|
||||||
|
"ServiceName": "downstreamservice",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GlobalConfiguration": {
|
||||||
|
"ServiceDiscoveryProvider": {
|
||||||
|
"Host": "192.168.0.13",
|
||||||
|
"Port": 443,
|
||||||
|
"Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
|
||||||
|
"Namespace": "dev",
|
||||||
|
"Type": "kube"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
samples/OelotKube/Dockerfile
Normal file
22
samples/OelotKube/Dockerfile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
FROM microsoft/dotnet:2.1-sdk AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["ApiGateway/ApiGateway.csproj", "ApiGateway/"]
|
||||||
|
COPY ["../../src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj", "../../src/Ocelot.Provider.Polly/"]
|
||||||
|
COPY ["../../src/Ocelot/Ocelot.csproj", "../../src/Ocelot/"]
|
||||||
|
COPY ["../../src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj", "../../src/Ocelot.Provider.Kubernetes/"]
|
||||||
|
RUN dotnet restore "ApiGateway/ApiGateway.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/ApiGateway"
|
||||||
|
RUN dotnet build "ApiGateway.csproj" -c Release -o /app
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "ApiGateway.csproj" -c Release -o /app
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app .
|
||||||
|
ENTRYPOINT ["dotnet", "ApiGateway.dll"]
|
@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace DownstreamService.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class ValuesController : ControllerBase
|
||||||
|
{
|
||||||
|
// GET api/values
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<IEnumerable<string>> Get()
|
||||||
|
{
|
||||||
|
return new string[] { "value1", "value2" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/values/5
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public ActionResult<string> Get(int id)
|
||||||
|
{
|
||||||
|
return "value";
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/values
|
||||||
|
[HttpPost]
|
||||||
|
public void Post([FromBody] string value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT api/values/5
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public void Put(int id, [FromBody] string value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE api/values/5
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public void Delete(int id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
samples/OelotKube/DownstreamService/Dockerfile
Normal file
19
samples/OelotKube/DownstreamService/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
FROM microsoft/dotnet:2.1-sdk AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["DownstreamService/DownstreamService.csproj", "DownstreamService/"]
|
||||||
|
RUN dotnet restore "DownstreamService/DownstreamService.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/DownstreamService"
|
||||||
|
RUN dotnet build "DownstreamService.csproj" -c Release -o /app
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "DownstreamService.csproj" -c Release -o /app
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app .
|
||||||
|
ENTRYPOINT ["dotnet", "DownstreamService.dll"]
|
15
samples/OelotKube/DownstreamService/Dockerfile.develop
Normal file
15
samples/OelotKube/DownstreamService/Dockerfile.develop
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM microsoft/dotnet:2.1-sdk
|
||||||
|
ARG BUILD_CONFIGURATION=Debug
|
||||||
|
ENV ASPNETCORE_ENVIRONMENT=Development
|
||||||
|
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["DownstreamService/DownstreamService.csproj", "DownstreamService/"]
|
||||||
|
|
||||||
|
RUN dotnet restore "DownstreamService/DownstreamService.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/DownstreamService"
|
||||||
|
RUN dotnet build --no-restore "DownstreamService.csproj" -c $BUILD_CONFIGURATION
|
||||||
|
|
||||||
|
ENTRYPOINT ["dotnet", "run", "--no-build", "--no-launch-profile", "-c", "$BUILD_CONFIGURATION", "--"]
|
15
samples/OelotKube/DownstreamService/DownstreamService.csproj
Normal file
15
samples/OelotKube/DownstreamService/DownstreamService.csproj
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
24
samples/OelotKube/DownstreamService/Program.cs
Normal file
24
samples/OelotKube/DownstreamService/Program.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace DownstreamService
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
CreateWebHostBuilder(args).Build().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||||
|
WebHost.CreateDefaultBuilder(args)
|
||||||
|
.UseStartup<Startup>();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:56411",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "api/values",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DownstreamService": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "api/values",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "http://localhost:5000"
|
||||||
|
},
|
||||||
|
"Docker": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/values"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
samples/OelotKube/DownstreamService/Startup.cs
Normal file
41
samples/OelotKube/DownstreamService/Startup.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace DownstreamService
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
samples/OelotKube/DownstreamService/appsettings.json
Normal file
8
samples/OelotKube/DownstreamService/appsettings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
43
samples/OelotKube/OelotKube.sln
Normal file
43
samples/OelotKube/OelotKube.sln
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.28010.2048
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateway", "ApiGateway\ApiGateway.csproj", "{E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "..\..\src\Ocelot\Ocelot.csproj", "{E8551073-622E-45FA-AD09-038EB8AAFFBC}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "..\..\src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{EF973868-98A6-4864-BF66-65B5A8C123FE}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "DownstreamService\DownstreamService.csproj", "{86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E8551073-622E-45FA-AD09-038EB8AAFFBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E8551073-622E-45FA-AD09-038EB8AAFFBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E8551073-622E-45FA-AD09-038EB8AAFFBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E8551073-622E-45FA-AD09-038EB8AAFFBC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EF973868-98A6-4864-BF66-65B5A8C123FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EF973868-98A6-4864-BF66-65B5A8C123FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EF973868-98A6-4864-BF66-65B5A8C123FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EF973868-98A6-4864-BF66-65B5A8C123FE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {D29790E8-4BA9-4E60-8D7D-327E21320CC9}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
12
src/Ocelot.Provider.Kubernetes/IKubeApiClientFactory.cs
Normal file
12
src/Ocelot.Provider.Kubernetes/IKubeApiClientFactory.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using KubeClient;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ocelot.Provider.Kubernetes
|
||||||
|
{
|
||||||
|
public interface IKubeApiClientFactory
|
||||||
|
{
|
||||||
|
IKubeApiClient Get(KubeRegistryConfiguration config);
|
||||||
|
}
|
||||||
|
}
|
22
src/Ocelot.Provider.Kubernetes/KubeApiClientFactory.cs
Normal file
22
src/Ocelot.Provider.Kubernetes/KubeApiClientFactory.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using KubeClient;
|
||||||
|
|
||||||
|
namespace Ocelot.Provider.Kubernetes
|
||||||
|
{
|
||||||
|
public class KubeApiClientFactory : IKubeApiClientFactory
|
||||||
|
{
|
||||||
|
public IKubeApiClient Get(KubeRegistryConfiguration config)
|
||||||
|
{
|
||||||
|
var option = new KubeClientOptions
|
||||||
|
{
|
||||||
|
ApiEndPoint = config.ApiEndPoint
|
||||||
|
};
|
||||||
|
if(!string.IsNullOrEmpty(config?.AccessToken))
|
||||||
|
{
|
||||||
|
option.AccessToken = config.AccessToken;
|
||||||
|
option.AuthStrategy = config.AuthStrategy;
|
||||||
|
option.AllowInsecure = config.AllowInsecure;
|
||||||
|
}
|
||||||
|
return KubeApiClient.Create(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
src/Ocelot.Provider.Kubernetes/KubeProvider.cs
Normal file
63
src/Ocelot.Provider.Kubernetes/KubeProvider.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using KubeClient;
|
||||||
|
using KubeClient.Models;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.ServiceDiscovery.Providers;
|
||||||
|
using Ocelot.Values;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Provider.Kubernetes
|
||||||
|
{
|
||||||
|
public class Kube : IServiceDiscoveryProvider
|
||||||
|
{
|
||||||
|
private KubeRegistryConfiguration kubeRegistryConfiguration;
|
||||||
|
private IOcelotLogger logger;
|
||||||
|
private IKubeApiClient kubeApi;
|
||||||
|
|
||||||
|
public Kube(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClientFactory kubeClientFactory)
|
||||||
|
{
|
||||||
|
this.kubeRegistryConfiguration = kubeRegistryConfiguration;
|
||||||
|
this.logger = factory.CreateLogger<Kube>();
|
||||||
|
this.kubeApi = kubeClientFactory.Get(kubeRegistryConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Service>> Get()
|
||||||
|
{
|
||||||
|
var service = await kubeApi.ServicesV1()
|
||||||
|
.Get(kubeRegistryConfiguration.KeyOfServiceInK8s, kubeRegistryConfiguration.KubeNamespace);
|
||||||
|
var services = new List<Service>();
|
||||||
|
if (IsValid(service))
|
||||||
|
{
|
||||||
|
services.Add(BuildService(service));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogWarning($"namespace:{kubeRegistryConfiguration.KubeNamespace }service:{kubeRegistryConfiguration.KeyOfServiceInK8s} Unable to use ,it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
|
||||||
|
}
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValid(ServiceV1 service)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(service.Spec.ClusterIP) || service.Spec.Ports.Count <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Service BuildService(ServiceV1 serviceEntry)
|
||||||
|
{
|
||||||
|
var servicePort = serviceEntry.Spec.Ports.FirstOrDefault();
|
||||||
|
return new Service(
|
||||||
|
serviceEntry.Metadata.Name,
|
||||||
|
new ServiceHostAndPort(serviceEntry.Spec.ClusterIP, servicePort.Port),
|
||||||
|
serviceEntry.Metadata.Uid,
|
||||||
|
string.Empty,
|
||||||
|
Enumerable.Empty<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs
Normal file
22
src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using KubeClient;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ocelot.Provider.Kubernetes
|
||||||
|
{
|
||||||
|
public class KubeRegistryConfiguration
|
||||||
|
{
|
||||||
|
public Uri ApiEndPoint { get; set; }
|
||||||
|
|
||||||
|
public string KubeNamespace { get; set; }
|
||||||
|
|
||||||
|
public string KeyOfServiceInK8s { get; set; }
|
||||||
|
|
||||||
|
public KubeAuthStrategy AuthStrategy { get; set; }
|
||||||
|
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
public bool AllowInsecure { get; set; }
|
||||||
|
}
|
||||||
|
}
|
38
src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
Normal file
38
src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using KubeClient;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.ServiceDiscovery;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ocelot.Provider.Kubernetes
|
||||||
|
{
|
||||||
|
public static class KubernetesProviderFactory
|
||||||
|
{
|
||||||
|
public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) =>
|
||||||
|
{
|
||||||
|
var factory = provider.GetService<IOcelotLoggerFactory>();
|
||||||
|
return GetkubeProvider(provider, config, name, factory);
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, string name, IOcelotLoggerFactory factory)
|
||||||
|
{
|
||||||
|
var kubeClientFactory = provider.GetService<IKubeApiClientFactory>();
|
||||||
|
var k8sRegistryConfiguration = new KubeRegistryConfiguration()
|
||||||
|
{
|
||||||
|
ApiEndPoint = new Uri($"https://{config.Host}:{config.Port}"),
|
||||||
|
KeyOfServiceInK8s = name,
|
||||||
|
KubeNamespace = config.Namesapce,
|
||||||
|
AuthStrategy = KubeAuthStrategy.BearerToken,
|
||||||
|
AccessToken = config.Token,
|
||||||
|
AllowInsecure = true // Don't validate server certificate
|
||||||
|
};
|
||||||
|
|
||||||
|
var k8sServiceDiscoveryProvider = new Kube(k8sRegistryConfiguration, factory, kubeClientFactory);
|
||||||
|
if (config.Type?.ToLower() == "pollkube")
|
||||||
|
{
|
||||||
|
return new PollKube(config.PollingInterval, factory, k8sServiceDiscoveryProvider);
|
||||||
|
}
|
||||||
|
return k8sServiceDiscoveryProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="KubeClient" Version="2.2.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
15
src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs
Normal file
15
src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Ocelot.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Ocelot.Provider.Kubernetes
|
||||||
|
{
|
||||||
|
public static class OcelotBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Services.AddSingleton(KubernetesProviderFactory.Get);
|
||||||
|
builder.Services.AddSingleton<IKubeApiClientFactory, KubeApiClientFactory>();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/Ocelot.Provider.Kubernetes/PollKube.cs
Normal file
50
src/Ocelot.Provider.Kubernetes/PollKube.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.ServiceDiscovery.Providers;
|
||||||
|
using Ocelot.Values;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Provider.Kubernetes
|
||||||
|
{
|
||||||
|
public class PollKube : IServiceDiscoveryProvider
|
||||||
|
{
|
||||||
|
private readonly IOcelotLogger _logger;
|
||||||
|
private readonly IServiceDiscoveryProvider _kubeServiceDiscoveryProvider;
|
||||||
|
private readonly Timer _timer;
|
||||||
|
private bool _polling;
|
||||||
|
private List<Service> _services;
|
||||||
|
|
||||||
|
public PollKube(int pollingInterval, IOcelotLoggerFactory factory, IServiceDiscoveryProvider kubeServiceDiscoveryProvider)
|
||||||
|
{
|
||||||
|
_logger = factory.CreateLogger<PollKube>();
|
||||||
|
_kubeServiceDiscoveryProvider = kubeServiceDiscoveryProvider;
|
||||||
|
_services = new List<Service>();
|
||||||
|
|
||||||
|
_timer = new Timer(async x =>
|
||||||
|
{
|
||||||
|
if (_polling)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_polling = true;
|
||||||
|
await Poll();
|
||||||
|
_polling = false;
|
||||||
|
}, null, pollingInterval, pollingInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<Service>> Get()
|
||||||
|
{
|
||||||
|
return Task.FromResult(_services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Poll()
|
||||||
|
{
|
||||||
|
_services = await _kubeServiceDiscoveryProvider.Get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private string _token;
|
private string _token;
|
||||||
private string _configurationKey;
|
private string _configurationKey;
|
||||||
private int _pollingInterval;
|
private int _pollingInterval;
|
||||||
|
private string _namespace;
|
||||||
|
|
||||||
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
||||||
{
|
{
|
||||||
@ -45,9 +46,15 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfigurationBuilder WithNamesapce(string @namesapce)
|
||||||
|
{
|
||||||
|
_namespace = @namesapce;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ServiceProviderConfiguration Build()
|
public ServiceProviderConfiguration Build()
|
||||||
{
|
{
|
||||||
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval);
|
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval, _namespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
? globalConfiguration?.ServiceDiscoveryProvider?.Type
|
? globalConfiguration?.ServiceDiscoveryProvider?.Type
|
||||||
: "consul";
|
: "consul";
|
||||||
var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0;
|
var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0;
|
||||||
|
var k8snamesapce = globalConfiguration?.ServiceDiscoveryProvider?.Namespace ?? string.Empty;
|
||||||
|
|
||||||
return new ServiceProviderConfigurationBuilder()
|
return new ServiceProviderConfigurationBuilder()
|
||||||
.WithHost(host)
|
.WithHost(host)
|
||||||
@ -21,6 +22,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
||||||
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
|
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
|
||||||
.WithPollingInterval(pollingInterval)
|
.WithPollingInterval(pollingInterval)
|
||||||
|
.WithNamesapce(k8snamesapce)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,6 @@ namespace Ocelot.Configuration.File
|
|||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
public string ConfigurationKey { get; set; }
|
public string ConfigurationKey { get; set; }
|
||||||
public int PollingInterval { get; set; }
|
public int PollingInterval { get; set; }
|
||||||
|
public string Namespace { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
public class ServiceProviderConfiguration
|
public class ServiceProviderConfiguration
|
||||||
{
|
{
|
||||||
public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey, int pollingInterval)
|
public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey, int pollingInterval, string @namespace = "")
|
||||||
{
|
{
|
||||||
ConfigurationKey = configurationKey;
|
ConfigurationKey = configurationKey;
|
||||||
Host = host;
|
Host = host;
|
||||||
@ -10,13 +10,21 @@
|
|||||||
Token = token;
|
Token = token;
|
||||||
Type = type;
|
Type = type;
|
||||||
PollingInterval = pollingInterval;
|
PollingInterval = pollingInterval;
|
||||||
|
Namesapce = @namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Host { get; }
|
public string Host { get; }
|
||||||
|
|
||||||
public int Port { get; }
|
public int Port { get; }
|
||||||
|
|
||||||
public string Type { get; }
|
public string Type { get; }
|
||||||
|
|
||||||
public string Token { get; }
|
public string Token { get; }
|
||||||
|
|
||||||
public string ConfigurationKey { get; }
|
public string ConfigurationKey { get; }
|
||||||
|
|
||||||
public int PollingInterval { get; }
|
public int PollingInterval { get; }
|
||||||
|
|
||||||
|
public string Namesapce { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,8 @@
|
|||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = consulPort,
|
Port = consulPort,
|
||||||
Type = "PollConsul",
|
Type = "PollConsul",
|
||||||
PollingInterval = 0
|
PollingInterval = 0,
|
||||||
|
Namespace = string.Empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,8 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
Port = 1234,
|
Port = 1234,
|
||||||
Type = "ServiceFabric",
|
Type = "ServiceFabric",
|
||||||
Token = "testtoken",
|
Token = "testtoken",
|
||||||
ConfigurationKey = "woo"
|
ConfigurationKey = "woo",
|
||||||
|
Namespace ="default"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.WithType("ServiceFabric")
|
.WithType("ServiceFabric")
|
||||||
.WithToken("testtoken")
|
.WithToken("testtoken")
|
||||||
.WithConfigurationKey("woo")
|
.WithConfigurationKey("woo")
|
||||||
|
.WithNamesapce("default")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig))
|
this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig))
|
||||||
@ -64,6 +66,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
_result.Port.ShouldBe(expected.Port);
|
_result.Port.ShouldBe(expected.Port);
|
||||||
_result.Token.ShouldBe(expected.Token);
|
_result.Token.ShouldBe(expected.Token);
|
||||||
_result.Type.ShouldBe(expected.Type);
|
_result.Type.ShouldBe(expected.Type);
|
||||||
|
_result.Namesapce.ShouldBe(expected.Namesapce);
|
||||||
_result.ConfigurationKey.ShouldBe(expected.ConfigurationKey);
|
_result.ConfigurationKey.ShouldBe(expected.ConfigurationKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
test/Ocelot.UnitTests/Kubernetes/KubeProviderFactoryTests.cs
Normal file
35
test/Ocelot.UnitTests/Kubernetes/KubeProviderFactoryTests.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Provider.Kubernetes;
|
||||||
|
using Shouldly;
|
||||||
|
using System;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Kubernetes
|
||||||
|
{
|
||||||
|
public class KubeProviderFactoryTests
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _provider;
|
||||||
|
|
||||||
|
public KubeProviderFactoryTests()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
var loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
var logger = new Mock<IOcelotLogger>();
|
||||||
|
loggerFactory.Setup(x => x.CreateLogger<Kube>()).Returns(logger.Object);
|
||||||
|
var kubeFactory = new Mock<IKubeApiClientFactory>();
|
||||||
|
services.AddSingleton(kubeFactory.Object);
|
||||||
|
services.AddSingleton(loggerFactory.Object);
|
||||||
|
_provider = services.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_KubeServiceDiscoveryProvider()
|
||||||
|
{
|
||||||
|
var provider = KubernetesProviderFactory.Get(_provider, new ServiceProviderConfiguration("kube", "localhost", 443, "", "", 1,"dev"), "");
|
||||||
|
provider.ShouldBeOfType<Kube>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
using KubeClient.Models;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Moq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Provider.Kubernetes;
|
||||||
|
using Ocelot.Values;
|
||||||
|
using Shouldly;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Kubernetes
|
||||||
|
{
|
||||||
|
public class KubeServiceDiscoveryProviderTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _fakeKubeBuilder;
|
||||||
|
private ServiceV1 _serviceEntries;
|
||||||
|
private Kube _provider;
|
||||||
|
private readonly string _serviceName;
|
||||||
|
private readonly string _namespaces;
|
||||||
|
private readonly int _port;
|
||||||
|
private readonly string _kubeHost;
|
||||||
|
private readonly string _fakekubeServiceDiscoveryUrl;
|
||||||
|
private List<Service> _services;
|
||||||
|
private readonly Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private readonly Mock<IOcelotLogger> _logger;
|
||||||
|
private string _receivedToken;
|
||||||
|
private readonly IKubeApiClientFactory _clientFactory;
|
||||||
|
|
||||||
|
public KubeServiceDiscoveryProviderTests()
|
||||||
|
{
|
||||||
|
_serviceName = "test";
|
||||||
|
_namespaces = "dev";
|
||||||
|
_port = 8001;
|
||||||
|
_kubeHost = "localhost";
|
||||||
|
_fakekubeServiceDiscoveryUrl = $"http://{_kubeHost}:{_port}";
|
||||||
|
_serviceEntries = new ServiceV1();
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_clientFactory = new KubeApiClientFactory();
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<Kube>()).Returns(_logger.Object);
|
||||||
|
var config = new KubeRegistryConfiguration()
|
||||||
|
{
|
||||||
|
ApiEndPoint = new Uri(_fakekubeServiceDiscoveryUrl),
|
||||||
|
AccessToken = "txpc696iUhbVoudg164r93CxDTrKRVWG",
|
||||||
|
AllowInsecure = true,
|
||||||
|
AuthStrategy = KubeClient.KubeAuthStrategy.BearerToken,
|
||||||
|
KeyOfServiceInK8s = _serviceName,
|
||||||
|
KubeNamespace = _namespaces
|
||||||
|
};
|
||||||
|
_provider = new Kube(config, _factory.Object, _clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_service_from_k8s()
|
||||||
|
{
|
||||||
|
var token = "Bearer txpc696iUhbVoudg164r93CxDTrKRVWG";
|
||||||
|
var serviceEntryOne = new ServiceV1()
|
||||||
|
{
|
||||||
|
Kind = "service",
|
||||||
|
ApiVersion = "1.0",
|
||||||
|
Metadata = new ObjectMetaV1()
|
||||||
|
{
|
||||||
|
Namespace = "dev"
|
||||||
|
},
|
||||||
|
Spec = new ServiceSpecV1()
|
||||||
|
{
|
||||||
|
ClusterIP = "localhost"
|
||||||
|
},
|
||||||
|
Status = new ServiceStatusV1() {
|
||||||
|
LoadBalancer = new LoadBalancerStatusV1()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceEntryOne.Spec.Ports.Add(
|
||||||
|
new ServicePortV1()
|
||||||
|
{
|
||||||
|
Port = 80
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.Given(x => GivenThereIsAFakeKubeServiceDiscoveryProvider(_fakekubeServiceDiscoveryUrl, _serviceName, _namespaces))
|
||||||
|
.And(x => GivenTheServicesAreRegisteredWithKube(serviceEntryOne))
|
||||||
|
.When(x => WhenIGetTheServices())
|
||||||
|
.Then(x => ThenTheCountIs(1))
|
||||||
|
.And(_ => _receivedToken.ShouldBe(token))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheCountIs(int count)
|
||||||
|
{
|
||||||
|
_services.Count.ShouldBe(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheServices()
|
||||||
|
{
|
||||||
|
_services = _provider.Get().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheServicesAreRegisteredWithKube(ServiceV1 serviceEntries)
|
||||||
|
{
|
||||||
|
_serviceEntries = serviceEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void GivenThereIsAFakeKubeServiceDiscoveryProvider(string url, string serviceName, string namespaces)
|
||||||
|
{
|
||||||
|
_fakeKubeBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(url)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
if (context.Request.Path.Value == $"/api/v1/namespaces/{namespaces}/services/{serviceName}")
|
||||||
|
{
|
||||||
|
if (context.Request.Headers.TryGetValue("Authorization", out var values))
|
||||||
|
{
|
||||||
|
_receivedToken = values.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(_serviceEntries);
|
||||||
|
context.Response.Headers.Add("Content-Type", "application/json");
|
||||||
|
await context.Response.WriteAsync(json);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_fakeKubeBuilder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_fakeKubeBuilder?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Internal;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Ocelot.DependencyInjection;
|
||||||
|
using Ocelot.Provider.Kubernetes;
|
||||||
|
using Shouldly;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Kubernetes
|
||||||
|
{
|
||||||
|
public class OcelotBuilderExtensionsTests
|
||||||
|
{
|
||||||
|
private readonly IServiceCollection _services;
|
||||||
|
private IServiceProvider _serviceProvider;
|
||||||
|
private readonly IConfiguration _configRoot;
|
||||||
|
private IOcelotBuilder _ocelotBuilder;
|
||||||
|
private Exception _ex;
|
||||||
|
|
||||||
|
public OcelotBuilderExtensionsTests()
|
||||||
|
{
|
||||||
|
_configRoot = new ConfigurationRoot(new List<IConfigurationProvider>());
|
||||||
|
_services = new ServiceCollection();
|
||||||
|
_services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
|
||||||
|
_services.AddSingleton(_configRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_set_up_kubernetes()
|
||||||
|
{
|
||||||
|
this.Given(x => WhenISetUpOcelotServices())
|
||||||
|
.When(x => WhenISetUpKubernetes())
|
||||||
|
.Then(x => ThenAnExceptionIsntThrown())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenISetUpOcelotServices()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ocelotBuilder = _services.AddOcelot(_configRoot);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_ex = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenISetUpKubernetes()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ocelotBuilder.AddKubernetes();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_ex = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenAnExceptionIsntThrown()
|
||||||
|
{
|
||||||
|
_ex.ShouldBeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Provider.Kubernetes;
|
||||||
|
using Ocelot.ServiceDiscovery.Providers;
|
||||||
|
using Ocelot.Values;
|
||||||
|
using Shouldly;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Kubernetes
|
||||||
|
{
|
||||||
|
public class PollingKubeServiceDiscoveryProviderTests
|
||||||
|
{
|
||||||
|
private readonly int _delay;
|
||||||
|
private PollKube _provider;
|
||||||
|
private readonly List<Service> _services;
|
||||||
|
private readonly Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private readonly Mock<IOcelotLogger> _logger;
|
||||||
|
private readonly Mock<IServiceDiscoveryProvider> _kubeServiceDiscoveryProvider;
|
||||||
|
private List<Service> _result;
|
||||||
|
|
||||||
|
public PollingKubeServiceDiscoveryProviderTests()
|
||||||
|
{
|
||||||
|
_services = new List<Service>();
|
||||||
|
_delay = 1;
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<PollKube>()).Returns(_logger.Object);
|
||||||
|
_kubeServiceDiscoveryProvider = new Mock<IServiceDiscoveryProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_service_from_kube()
|
||||||
|
{
|
||||||
|
var service = new Service("", new ServiceHostAndPort("", 0), "", "", new List<string>());
|
||||||
|
|
||||||
|
this.Given(x => GivenKubeReturns(service))
|
||||||
|
.When(x => WhenIGetTheServices(1))
|
||||||
|
.Then(x => ThenTheCountIs(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenKubeReturns(Service service)
|
||||||
|
{
|
||||||
|
_services.Add(service);
|
||||||
|
_kubeServiceDiscoveryProvider.Setup(x => x.Get()).ReturnsAsync(_services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheCountIs(int count)
|
||||||
|
{
|
||||||
|
_result.Count.ShouldBe(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheServices(int expected)
|
||||||
|
{
|
||||||
|
_provider = new PollKube(_delay, _factory.Object, _kubeServiceDiscoveryProvider.Object);
|
||||||
|
|
||||||
|
var result = Wait.WaitFor(3000).Until(() => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_result = _provider.Get().GetAwaiter().GetResult();
|
||||||
|
if (_result.Count == expected)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Administration\Ocelot.Administration.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Administration\Ocelot.Administration.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user